| Server IP : 146.59.209.152 / Your IP : 216.73.216.46 Web Server : Apache System : Linux webm005.cluster131.gra.hosting.ovh.net 5.15.167-ovh-vps-grsec-zfs-classid #1 SMP Tue Sep 17 08:14:20 UTC 2024 x86_64 User : infrafs ( 43850) PHP Version : 8.2.29 Disable Function : _dyuweyrj4,_dyuweyrj4r,dl MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : ON | Sudo : OFF | Pkexec : OFF Directory : /home/i/n/f/infrafs/INFRABIKEIT/wp-content/plugins/ |
Upload File : |
Compat.php 0000644 00000445302 15133021213 0006477 0 ustar 00 <?php
/**
* Libsodium compatibility layer
*
* This is the only class you should be interfacing with, as a user of
* sodium_compat.
*
* If the PHP extension for libsodium is installed, it will always use that
* instead of our implementations. You get better performance and stronger
* guarantees against side-channels that way.
*
* However, if your users don't have the PHP extension installed, we offer a
* compatible interface here. It will give you the correct results as if the
* PHP extension was installed. It won't be as fast, of course.
*
* CAUTION * CAUTION * CAUTION * CAUTION * CAUTION * CAUTION * CAUTION * CAUTION *
* *
* Until audited, this is probably not safe to use! DANGER WILL ROBINSON *
* *
* CAUTION * CAUTION * CAUTION * CAUTION * CAUTION * CAUTION * CAUTION * CAUTION *
*/
if (class_exists('ParagonIE_Sodium_Compat', false)) {
return;
}
class ParagonIE_Sodium_Compat
{
/**
* This parameter prevents the use of the PECL extension.
* It should only be used for unit testing.
*
* @var bool
*/
public static $disableFallbackForUnitTests = false;
/**
* Use fast multiplication rather than our constant-time multiplication
* implementation. Can be enabled at runtime. Only enable this if you
* are absolutely certain that there is no timing leak on your platform.
*
* @var bool
*/
public static $fastMult = false;
const LIBRARY_MAJOR_VERSION = 9;
const LIBRARY_MINOR_VERSION = 1;
const LIBRARY_VERSION_MAJOR = 9;
const LIBRARY_VERSION_MINOR = 1;
const VERSION_STRING = 'polyfill-1.0.8';
// From libsodium
const BASE64_VARIANT_ORIGINAL = 1;
const BASE64_VARIANT_ORIGINAL_NO_PADDING = 3;
const BASE64_VARIANT_URLSAFE = 5;
const BASE64_VARIANT_URLSAFE_NO_PADDING = 7;
const CRYPTO_AEAD_AES256GCM_KEYBYTES = 32;
const CRYPTO_AEAD_AES256GCM_NSECBYTES = 0;
const CRYPTO_AEAD_AES256GCM_NPUBBYTES = 12;
const CRYPTO_AEAD_AES256GCM_ABYTES = 16;
const CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES = 32;
const CRYPTO_AEAD_CHACHA20POLY1305_NSECBYTES = 0;
const CRYPTO_AEAD_CHACHA20POLY1305_NPUBBYTES = 8;
const CRYPTO_AEAD_CHACHA20POLY1305_ABYTES = 16;
const CRYPTO_AEAD_CHACHA20POLY1305_IETF_KEYBYTES = 32;
const CRYPTO_AEAD_CHACHA20POLY1305_IETF_NSECBYTES = 0;
const CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES = 12;
const CRYPTO_AEAD_CHACHA20POLY1305_IETF_ABYTES = 16;
const CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES = 32;
const CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NSECBYTES = 0;
const CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES = 24;
const CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES = 16;
const CRYPTO_AUTH_BYTES = 32;
const CRYPTO_AUTH_KEYBYTES = 32;
const CRYPTO_BOX_SEALBYTES = 16;
const CRYPTO_BOX_SECRETKEYBYTES = 32;
const CRYPTO_BOX_PUBLICKEYBYTES = 32;
const CRYPTO_BOX_KEYPAIRBYTES = 64;
const CRYPTO_BOX_MACBYTES = 16;
const CRYPTO_BOX_NONCEBYTES = 24;
const CRYPTO_BOX_SEEDBYTES = 32;
const CRYPTO_CORE_RISTRETTO255_BYTES = 32;
const CRYPTO_CORE_RISTRETTO255_SCALARBYTES = 32;
const CRYPTO_CORE_RISTRETTO255_HASHBYTES = 64;
const CRYPTO_CORE_RISTRETTO255_NONREDUCEDSCALARBYTES = 64;
const CRYPTO_KDF_BYTES_MIN = 16;
const CRYPTO_KDF_BYTES_MAX = 64;
const CRYPTO_KDF_CONTEXTBYTES = 8;
const CRYPTO_KDF_KEYBYTES = 32;
const CRYPTO_KX_BYTES = 32;
const CRYPTO_KX_PRIMITIVE = 'x25519blake2b';
const CRYPTO_KX_SEEDBYTES = 32;
const CRYPTO_KX_KEYPAIRBYTES = 64;
const CRYPTO_KX_PUBLICKEYBYTES = 32;
const CRYPTO_KX_SECRETKEYBYTES = 32;
const CRYPTO_KX_SESSIONKEYBYTES = 32;
const CRYPTO_GENERICHASH_BYTES = 32;
const CRYPTO_GENERICHASH_BYTES_MIN = 16;
const CRYPTO_GENERICHASH_BYTES_MAX = 64;
const CRYPTO_GENERICHASH_KEYBYTES = 32;
const CRYPTO_GENERICHASH_KEYBYTES_MIN = 16;
const CRYPTO_GENERICHASH_KEYBYTES_MAX = 64;
const CRYPTO_PWHASH_SALTBYTES = 16;
const CRYPTO_PWHASH_STRPREFIX = '$argon2id$';
const CRYPTO_PWHASH_ALG_ARGON2I13 = 1;
const CRYPTO_PWHASH_ALG_ARGON2ID13 = 2;
const CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE = 33554432;
const CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE = 4;
const CRYPTO_PWHASH_MEMLIMIT_MODERATE = 134217728;
const CRYPTO_PWHASH_OPSLIMIT_MODERATE = 6;
const CRYPTO_PWHASH_MEMLIMIT_SENSITIVE = 536870912;
const CRYPTO_PWHASH_OPSLIMIT_SENSITIVE = 8;
const CRYPTO_PWHASH_SCRYPTSALSA208SHA256_SALTBYTES = 32;
const CRYPTO_PWHASH_SCRYPTSALSA208SHA256_STRPREFIX = '$7$';
const CRYPTO_PWHASH_SCRYPTSALSA208SHA256_OPSLIMIT_INTERACTIVE = 534288;
const CRYPTO_PWHASH_SCRYPTSALSA208SHA256_MEMLIMIT_INTERACTIVE = 16777216;
const CRYPTO_PWHASH_SCRYPTSALSA208SHA256_OPSLIMIT_SENSITIVE = 33554432;
const CRYPTO_PWHASH_SCRYPTSALSA208SHA256_MEMLIMIT_SENSITIVE = 1073741824;
const CRYPTO_SCALARMULT_BYTES = 32;
const CRYPTO_SCALARMULT_SCALARBYTES = 32;
const CRYPTO_SCALARMULT_RISTRETTO255_BYTES = 32;
const CRYPTO_SCALARMULT_RISTRETTO255_SCALARBYTES = 32;
const CRYPTO_SHORTHASH_BYTES = 8;
const CRYPTO_SHORTHASH_KEYBYTES = 16;
const CRYPTO_SECRETBOX_KEYBYTES = 32;
const CRYPTO_SECRETBOX_MACBYTES = 16;
const CRYPTO_SECRETBOX_NONCEBYTES = 24;
const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_ABYTES = 17;
const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_HEADERBYTES = 24;
const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_KEYBYTES = 32;
const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_PUSH = 0;
const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_PULL = 1;
const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_REKEY = 2;
const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_FINAL = 3;
const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_MESSAGEBYTES_MAX = 0x3fffffff80;
const CRYPTO_SIGN_BYTES = 64;
const CRYPTO_SIGN_SEEDBYTES = 32;
const CRYPTO_SIGN_PUBLICKEYBYTES = 32;
const CRYPTO_SIGN_SECRETKEYBYTES = 64;
const CRYPTO_SIGN_KEYPAIRBYTES = 96;
const CRYPTO_STREAM_KEYBYTES = 32;
const CRYPTO_STREAM_NONCEBYTES = 24;
const CRYPTO_STREAM_XCHACHA20_KEYBYTES = 32;
const CRYPTO_STREAM_XCHACHA20_NONCEBYTES = 24;
/**
* Add two numbers (little-endian unsigned), storing the value in the first
* parameter.
*
* This mutates $val.
*
* @param string $val
* @param string $addv
* @return void
* @throws SodiumException
*/
public static function add(&$val, $addv)
{
$val_len = ParagonIE_Sodium_Core_Util::strlen($val);
$addv_len = ParagonIE_Sodium_Core_Util::strlen($addv);
if ($val_len !== $addv_len) {
throw new SodiumException('values must have the same length');
}
$A = ParagonIE_Sodium_Core_Util::stringToIntArray($val);
$B = ParagonIE_Sodium_Core_Util::stringToIntArray($addv);
$c = 0;
for ($i = 0; $i < $val_len; $i++) {
$c += ($A[$i] + $B[$i]);
$A[$i] = ($c & 0xff);
$c >>= 8;
}
$val = ParagonIE_Sodium_Core_Util::intArrayToString($A);
}
/**
* @param string $encoded
* @param int $variant
* @param string $ignore
* @return string
* @throws SodiumException
*/
public static function base642bin($encoded, $variant, $ignore = '')
{
/* Type checks: */
ParagonIE_Sodium_Core_Util::declareScalarType($encoded, 'string', 1);
/** @var string $encoded */
$encoded = (string) $encoded;
if (ParagonIE_Sodium_Core_Util::strlen($encoded) === 0) {
return '';
}
// Just strip before decoding
if (!empty($ignore)) {
$encoded = str_replace($ignore, '', $encoded);
}
try {
switch ($variant) {
case self::BASE64_VARIANT_ORIGINAL:
return ParagonIE_Sodium_Core_Base64_Original::decode($encoded, true);
case self::BASE64_VARIANT_ORIGINAL_NO_PADDING:
return ParagonIE_Sodium_Core_Base64_Original::decode($encoded, false);
case self::BASE64_VARIANT_URLSAFE:
return ParagonIE_Sodium_Core_Base64_UrlSafe::decode($encoded, true);
case self::BASE64_VARIANT_URLSAFE_NO_PADDING:
return ParagonIE_Sodium_Core_Base64_UrlSafe::decode($encoded, false);
default:
throw new SodiumException('invalid base64 variant identifier');
}
} catch (Exception $ex) {
if ($ex instanceof SodiumException) {
throw $ex;
}
throw new SodiumException('invalid base64 string');
}
}
/**
* @param string $decoded
* @param int $variant
* @return string
* @throws SodiumException
*/
public static function bin2base64($decoded, $variant)
{
/* Type checks: */
ParagonIE_Sodium_Core_Util::declareScalarType($decoded, 'string', 1);
/** @var string $decoded */
$decoded = (string) $decoded;
if (ParagonIE_Sodium_Core_Util::strlen($decoded) === 0) {
return '';
}
switch ($variant) {
case self::BASE64_VARIANT_ORIGINAL:
return ParagonIE_Sodium_Core_Base64_Original::encode($decoded);
case self::BASE64_VARIANT_ORIGINAL_NO_PADDING:
return ParagonIE_Sodium_Core_Base64_Original::encodeUnpadded($decoded);
case self::BASE64_VARIANT_URLSAFE:
return ParagonIE_Sodium_Core_Base64_UrlSafe::encode($decoded);
case self::BASE64_VARIANT_URLSAFE_NO_PADDING:
return ParagonIE_Sodium_Core_Base64_UrlSafe::encodeUnpadded($decoded);
default:
throw new SodiumException('invalid base64 variant identifier');
}
}
/**
* Cache-timing-safe implementation of bin2hex().
*
* @param string $string A string (probably raw binary)
* @return string A hexadecimal-encoded string
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArgument
*/
public static function bin2hex($string)
{
/* Type checks: */
ParagonIE_Sodium_Core_Util::declareScalarType($string, 'string', 1);
if (self::useNewSodiumAPI()) {
return (string) sodium_bin2hex($string);
}
if (self::use_fallback('bin2hex')) {
return (string) call_user_func('\\Sodium\\bin2hex', $string);
}
return ParagonIE_Sodium_Core_Util::bin2hex($string);
}
/**
* Compare two strings, in constant-time.
* Compared to memcmp(), compare() is more useful for sorting.
*
* @param string $left The left operand; must be a string
* @param string $right The right operand; must be a string
* @return int If < 0 if the left operand is less than the right
* If = 0 if both strings are equal
* If > 0 if the right operand is less than the left
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArgument
*/
public static function compare($left, $right)
{
/* Type checks: */
ParagonIE_Sodium_Core_Util::declareScalarType($left, 'string', 1);
ParagonIE_Sodium_Core_Util::declareScalarType($right, 'string', 2);
if (self::useNewSodiumAPI()) {
return (int) sodium_compare($left, $right);
}
if (self::use_fallback('compare')) {
return (int) call_user_func('\\Sodium\\compare', $left, $right);
}
return ParagonIE_Sodium_Core_Util::compare($left, $right);
}
/**
* Is AES-256-GCM even available to use?
*
* @return bool
* @psalm-suppress UndefinedFunction
* @psalm-suppress MixedInferredReturnType
* @psalm-suppress MixedReturnStatement
*/
public static function crypto_aead_aes256gcm_is_available()
{
if (self::useNewSodiumAPI()) {
return sodium_crypto_aead_aes256gcm_is_available();
}
if (self::use_fallback('crypto_aead_aes256gcm_is_available')) {
return call_user_func('\\Sodium\\crypto_aead_aes256gcm_is_available');
}
if (PHP_VERSION_ID < 70100) {
// OpenSSL doesn't support AEAD before 7.1.0
return false;
}
if (!is_callable('openssl_encrypt') || !is_callable('openssl_decrypt')) {
// OpenSSL isn't installed
return false;
}
return (bool) in_array('aes-256-gcm', openssl_get_cipher_methods());
}
/**
* Authenticated Encryption with Associated Data: Decryption
*
* Algorithm:
* AES-256-GCM
*
* This mode uses a 64-bit random nonce with a 64-bit counter.
* IETF mode uses a 96-bit random nonce with a 32-bit counter.
*
* @param string $ciphertext Encrypted message (with Poly1305 MAC appended)
* @param string $assocData Authenticated Associated Data (unencrypted)
* @param string $nonce Number to be used only Once; must be 8 bytes
* @param string $key Encryption key
*
* @return string|bool The original plaintext message
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArgument
* @psalm-suppress MixedInferredReturnType
* @psalm-suppress MixedReturnStatement
*/
public static function crypto_aead_aes256gcm_decrypt(
$ciphertext = '',
$assocData = '',
$nonce = '',
$key = ''
) {
if (!self::crypto_aead_aes256gcm_is_available()) {
throw new SodiumException('AES-256-GCM is not available');
}
ParagonIE_Sodium_Core_Util::declareScalarType($ciphertext, 'string', 1);
ParagonIE_Sodium_Core_Util::declareScalarType($assocData, 'string', 2);
ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 3);
ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 4);
/* Input validation: */
if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_AEAD_AES256GCM_NPUBBYTES) {
throw new SodiumException('Nonce must be CRYPTO_AEAD_AES256GCM_NPUBBYTES long');
}
if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_AEAD_AES256GCM_KEYBYTES) {
throw new SodiumException('Key must be CRYPTO_AEAD_AES256GCM_KEYBYTES long');
}
if (ParagonIE_Sodium_Core_Util::strlen($ciphertext) < self::CRYPTO_AEAD_AES256GCM_ABYTES) {
throw new SodiumException('Message must be at least CRYPTO_AEAD_AES256GCM_ABYTES long');
}
if (!is_callable('openssl_decrypt')) {
throw new SodiumException('The OpenSSL extension is not installed, or openssl_decrypt() is not available');
}
/** @var string $ctext */
$ctext = ParagonIE_Sodium_Core_Util::substr($ciphertext, 0, -self::CRYPTO_AEAD_AES256GCM_ABYTES);
/** @var string $authTag */
$authTag = ParagonIE_Sodium_Core_Util::substr($ciphertext, -self::CRYPTO_AEAD_AES256GCM_ABYTES, 16);
return openssl_decrypt(
$ctext,
'aes-256-gcm',
$key,
OPENSSL_RAW_DATA,
$nonce,
$authTag,
$assocData
);
}
/**
* Authenticated Encryption with Associated Data: Encryption
*
* Algorithm:
* AES-256-GCM
*
* @param string $plaintext Message to be encrypted
* @param string $assocData Authenticated Associated Data (unencrypted)
* @param string $nonce Number to be used only Once; must be 8 bytes
* @param string $key Encryption key
*
* @return string Ciphertext with a 16-byte GCM message
* authentication code appended
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArgument
*/
public static function crypto_aead_aes256gcm_encrypt(
$plaintext = '',
$assocData = '',
$nonce = '',
$key = ''
) {
if (!self::crypto_aead_aes256gcm_is_available()) {
throw new SodiumException('AES-256-GCM is not available');
}
ParagonIE_Sodium_Core_Util::declareScalarType($plaintext, 'string', 1);
ParagonIE_Sodium_Core_Util::declareScalarType($assocData, 'string', 2);
ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 3);
ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 4);
/* Input validation: */
if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_AEAD_AES256GCM_NPUBBYTES) {
throw new SodiumException('Nonce must be CRYPTO_AEAD_AES256GCM_NPUBBYTES long');
}
if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_AEAD_AES256GCM_KEYBYTES) {
throw new SodiumException('Key must be CRYPTO_AEAD_AES256GCM_KEYBYTES long');
}
if (!is_callable('openssl_encrypt')) {
throw new SodiumException('The OpenSSL extension is not installed, or openssl_encrypt() is not available');
}
$authTag = '';
$ciphertext = openssl_encrypt(
$plaintext,
'aes-256-gcm',
$key,
OPENSSL_RAW_DATA,
$nonce,
$authTag,
$assocData
);
return $ciphertext . $authTag;
}
/**
* Return a secure random key for use with the AES-256-GCM
* symmetric AEAD interface.
*
* @return string
* @throws Exception
* @throws Error
*/
public static function crypto_aead_aes256gcm_keygen()
{
return random_bytes(self::CRYPTO_AEAD_AES256GCM_KEYBYTES);
}
/**
* Authenticated Encryption with Associated Data: Decryption
*
* Algorithm:
* ChaCha20-Poly1305
*
* This mode uses a 64-bit random nonce with a 64-bit counter.
* IETF mode uses a 96-bit random nonce with a 32-bit counter.
*
* @param string $ciphertext Encrypted message (with Poly1305 MAC appended)
* @param string $assocData Authenticated Associated Data (unencrypted)
* @param string $nonce Number to be used only Once; must be 8 bytes
* @param string $key Encryption key
*
* @return string The original plaintext message
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArgument
* @psalm-suppress MixedInferredReturnType
* @psalm-suppress MixedReturnStatement
*/
public static function crypto_aead_chacha20poly1305_decrypt(
$ciphertext = '',
$assocData = '',
$nonce = '',
$key = ''
) {
/* Type checks: */
ParagonIE_Sodium_Core_Util::declareScalarType($ciphertext, 'string', 1);
ParagonIE_Sodium_Core_Util::declareScalarType($assocData, 'string', 2);
ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 3);
ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 4);
/* Input validation: */
if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_AEAD_CHACHA20POLY1305_NPUBBYTES) {
throw new SodiumException('Nonce must be CRYPTO_AEAD_CHACHA20POLY1305_NPUBBYTES long');
}
if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES) {
throw new SodiumException('Key must be CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES long');
}
if (ParagonIE_Sodium_Core_Util::strlen($ciphertext) < self::CRYPTO_AEAD_CHACHA20POLY1305_ABYTES) {
throw new SodiumException('Message must be at least CRYPTO_AEAD_CHACHA20POLY1305_ABYTES long');
}
if (self::useNewSodiumAPI()) {
/**
* @psalm-suppress InvalidReturnStatement
* @psalm-suppress FalsableReturnStatement
*/
return sodium_crypto_aead_chacha20poly1305_decrypt(
$ciphertext,
$assocData,
$nonce,
$key
);
}
if (self::use_fallback('crypto_aead_chacha20poly1305_decrypt')) {
return call_user_func(
'\\Sodium\\crypto_aead_chacha20poly1305_decrypt',
$ciphertext,
$assocData,
$nonce,
$key
);
}
if (PHP_INT_SIZE === 4) {
return ParagonIE_Sodium_Crypto32::aead_chacha20poly1305_decrypt(
$ciphertext,
$assocData,
$nonce,
$key
);
}
return ParagonIE_Sodium_Crypto::aead_chacha20poly1305_decrypt(
$ciphertext,
$assocData,
$nonce,
$key
);
}
/**
* Authenticated Encryption with Associated Data
*
* Algorithm:
* ChaCha20-Poly1305
*
* This mode uses a 64-bit random nonce with a 64-bit counter.
* IETF mode uses a 96-bit random nonce with a 32-bit counter.
*
* @param string $plaintext Message to be encrypted
* @param string $assocData Authenticated Associated Data (unencrypted)
* @param string $nonce Number to be used only Once; must be 8 bytes
* @param string $key Encryption key
*
* @return string Ciphertext with a 16-byte Poly1305 message
* authentication code appended
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArgument
*/
public static function crypto_aead_chacha20poly1305_encrypt(
$plaintext = '',
$assocData = '',
$nonce = '',
$key = ''
) {
/* Type checks: */
ParagonIE_Sodium_Core_Util::declareScalarType($plaintext, 'string', 1);
ParagonIE_Sodium_Core_Util::declareScalarType($assocData, 'string', 2);
ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 3);
ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 4);
/* Input validation: */
if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_AEAD_CHACHA20POLY1305_NPUBBYTES) {
throw new SodiumException('Nonce must be CRYPTO_AEAD_CHACHA20POLY1305_NPUBBYTES long');
}
if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES) {
throw new SodiumException('Key must be CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES long');
}
if (self::useNewSodiumAPI()) {
return (string) sodium_crypto_aead_chacha20poly1305_encrypt(
$plaintext,
$assocData,
$nonce,
$key
);
}
if (self::use_fallback('crypto_aead_chacha20poly1305_encrypt')) {
return (string) call_user_func(
'\\Sodium\\crypto_aead_chacha20poly1305_encrypt',
$plaintext,
$assocData,
$nonce,
$key
);
}
if (PHP_INT_SIZE === 4) {
return ParagonIE_Sodium_Crypto32::aead_chacha20poly1305_encrypt(
$plaintext,
$assocData,
$nonce,
$key
);
}
return ParagonIE_Sodium_Crypto::aead_chacha20poly1305_encrypt(
$plaintext,
$assocData,
$nonce,
$key
);
}
/**
* Authenticated Encryption with Associated Data: Decryption
*
* Algorithm:
* ChaCha20-Poly1305
*
* IETF mode uses a 96-bit random nonce with a 32-bit counter.
* Regular mode uses a 64-bit random nonce with a 64-bit counter.
*
* @param string $ciphertext Encrypted message (with Poly1305 MAC appended)
* @param string $assocData Authenticated Associated Data (unencrypted)
* @param string $nonce Number to be used only Once; must be 12 bytes
* @param string $key Encryption key
*
* @return string The original plaintext message
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArgument
* @psalm-suppress MixedInferredReturnType
* @psalm-suppress MixedReturnStatement
*/
public static function crypto_aead_chacha20poly1305_ietf_decrypt(
$ciphertext = '',
$assocData = '',
$nonce = '',
$key = ''
) {
/* Type checks: */
ParagonIE_Sodium_Core_Util::declareScalarType($ciphertext, 'string', 1);
ParagonIE_Sodium_Core_Util::declareScalarType($assocData, 'string', 2);
ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 3);
ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 4);
/* Input validation: */
if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES) {
throw new SodiumException('Nonce must be CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES long');
}
if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES) {
throw new SodiumException('Key must be CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES long');
}
if (ParagonIE_Sodium_Core_Util::strlen($ciphertext) < self::CRYPTO_AEAD_CHACHA20POLY1305_ABYTES) {
throw new SodiumException('Message must be at least CRYPTO_AEAD_CHACHA20POLY1305_ABYTES long');
}
if (self::useNewSodiumAPI()) {
/**
* @psalm-suppress InvalidReturnStatement
* @psalm-suppress FalsableReturnStatement
*/
return sodium_crypto_aead_chacha20poly1305_ietf_decrypt(
$ciphertext,
$assocData,
$nonce,
$key
);
}
if (self::use_fallback('crypto_aead_chacha20poly1305_ietf_decrypt')) {
return call_user_func(
'\\Sodium\\crypto_aead_chacha20poly1305_ietf_decrypt',
$ciphertext,
$assocData,
$nonce,
$key
);
}
if (PHP_INT_SIZE === 4) {
return ParagonIE_Sodium_Crypto32::aead_chacha20poly1305_ietf_decrypt(
$ciphertext,
$assocData,
$nonce,
$key
);
}
return ParagonIE_Sodium_Crypto::aead_chacha20poly1305_ietf_decrypt(
$ciphertext,
$assocData,
$nonce,
$key
);
}
/**
* Return a secure random key for use with the ChaCha20-Poly1305
* symmetric AEAD interface.
*
* @return string
* @throws Exception
* @throws Error
*/
public static function crypto_aead_chacha20poly1305_keygen()
{
return random_bytes(self::CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES);
}
/**
* Authenticated Encryption with Associated Data
*
* Algorithm:
* ChaCha20-Poly1305
*
* IETF mode uses a 96-bit random nonce with a 32-bit counter.
* Regular mode uses a 64-bit random nonce with a 64-bit counter.
*
* @param string $plaintext Message to be encrypted
* @param string $assocData Authenticated Associated Data (unencrypted)
* @param string $nonce Number to be used only Once; must be 8 bytes
* @param string $key Encryption key
*
* @return string Ciphertext with a 16-byte Poly1305 message
* authentication code appended
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArgument
*/
public static function crypto_aead_chacha20poly1305_ietf_encrypt(
$plaintext = '',
$assocData = '',
$nonce = '',
$key = ''
) {
/* Type checks: */
ParagonIE_Sodium_Core_Util::declareScalarType($plaintext, 'string', 1);
if (!is_null($assocData)) {
ParagonIE_Sodium_Core_Util::declareScalarType($assocData, 'string', 2);
}
ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 3);
ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 4);
/* Input validation: */
if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES) {
throw new SodiumException('Nonce must be CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES long');
}
if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES) {
throw new SodiumException('Key must be CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES long');
}
if (self::useNewSodiumAPI()) {
return (string) sodium_crypto_aead_chacha20poly1305_ietf_encrypt(
$plaintext,
$assocData,
$nonce,
$key
);
}
if (self::use_fallback('crypto_aead_chacha20poly1305_ietf_encrypt')) {
return (string) call_user_func(
'\\Sodium\\crypto_aead_chacha20poly1305_ietf_encrypt',
$plaintext,
$assocData,
$nonce,
$key
);
}
if (PHP_INT_SIZE === 4) {
return ParagonIE_Sodium_Crypto32::aead_chacha20poly1305_ietf_encrypt(
$plaintext,
$assocData,
$nonce,
$key
);
}
return ParagonIE_Sodium_Crypto::aead_chacha20poly1305_ietf_encrypt(
$plaintext,
$assocData,
$nonce,
$key
);
}
/**
* Return a secure random key for use with the ChaCha20-Poly1305
* symmetric AEAD interface. (IETF version)
*
* @return string
* @throws Exception
* @throws Error
*/
public static function crypto_aead_chacha20poly1305_ietf_keygen()
{
return random_bytes(self::CRYPTO_AEAD_CHACHA20POLY1305_IETF_KEYBYTES);
}
/**
* Authenticated Encryption with Associated Data: Decryption
*
* Algorithm:
* XChaCha20-Poly1305
*
* This mode uses a 64-bit random nonce with a 64-bit counter.
* IETF mode uses a 96-bit random nonce with a 32-bit counter.
*
* @param string $ciphertext Encrypted message (with Poly1305 MAC appended)
* @param string $assocData Authenticated Associated Data (unencrypted)
* @param string $nonce Number to be used only Once; must be 8 bytes
* @param string $key Encryption key
* @param bool $dontFallback Don't fallback to ext/sodium
*
* @return string|bool The original plaintext message
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArgument
*/
public static function crypto_aead_xchacha20poly1305_ietf_decrypt(
$ciphertext = '',
$assocData = '',
$nonce = '',
$key = '',
$dontFallback = false
) {
/* Type checks: */
ParagonIE_Sodium_Core_Util::declareScalarType($ciphertext, 'string', 1);
if (!is_null($assocData)) {
ParagonIE_Sodium_Core_Util::declareScalarType($assocData, 'string', 2);
} else {
$assocData = '';
}
ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 3);
ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 4);
/* Input validation: */
if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES) {
throw new SodiumException('Nonce must be CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES long');
}
if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES) {
throw new SodiumException('Key must be CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES long');
}
if (ParagonIE_Sodium_Core_Util::strlen($ciphertext) < self::CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES) {
throw new SodiumException('Message must be at least CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES long');
}
if (self::useNewSodiumAPI() && !$dontFallback) {
if (is_callable('sodium_crypto_aead_xchacha20poly1305_ietf_decrypt')) {
return sodium_crypto_aead_xchacha20poly1305_ietf_decrypt(
$ciphertext,
$assocData,
$nonce,
$key
);
}
}
if (PHP_INT_SIZE === 4) {
return ParagonIE_Sodium_Crypto32::aead_xchacha20poly1305_ietf_decrypt(
$ciphertext,
$assocData,
$nonce,
$key
);
}
return ParagonIE_Sodium_Crypto::aead_xchacha20poly1305_ietf_decrypt(
$ciphertext,
$assocData,
$nonce,
$key
);
}
/**
* Authenticated Encryption with Associated Data
*
* Algorithm:
* XChaCha20-Poly1305
*
* This mode uses a 64-bit random nonce with a 64-bit counter.
* IETF mode uses a 96-bit random nonce with a 32-bit counter.
*
* @param string $plaintext Message to be encrypted
* @param string $assocData Authenticated Associated Data (unencrypted)
* @param string $nonce Number to be used only Once; must be 8 bytes
* @param string $key Encryption key
* @param bool $dontFallback Don't fallback to ext/sodium
*
* @return string Ciphertext with a 16-byte Poly1305 message
* authentication code appended
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArgument
*/
public static function crypto_aead_xchacha20poly1305_ietf_encrypt(
$plaintext = '',
$assocData = '',
$nonce = '',
$key = '',
$dontFallback = false
) {
/* Type checks: */
ParagonIE_Sodium_Core_Util::declareScalarType($plaintext, 'string', 1);
if (!is_null($assocData)) {
ParagonIE_Sodium_Core_Util::declareScalarType($assocData, 'string', 2);
} else {
$assocData = '';
}
ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 3);
ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 4);
/* Input validation: */
if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES) {
throw new SodiumException('Nonce must be CRYPTO_AEAD_XCHACHA20POLY1305_NPUBBYTES long');
}
if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES) {
throw new SodiumException('Key must be CRYPTO_AEAD_XCHACHA20POLY1305_KEYBYTES long');
}
if (self::useNewSodiumAPI() && !$dontFallback) {
if (is_callable('sodium_crypto_aead_xchacha20poly1305_ietf_encrypt')) {
return sodium_crypto_aead_xchacha20poly1305_ietf_encrypt(
$plaintext,
$assocData,
$nonce,
$key
);
}
}
if (PHP_INT_SIZE === 4) {
return ParagonIE_Sodium_Crypto32::aead_xchacha20poly1305_ietf_encrypt(
$plaintext,
$assocData,
$nonce,
$key
);
}
return ParagonIE_Sodium_Crypto::aead_xchacha20poly1305_ietf_encrypt(
$plaintext,
$assocData,
$nonce,
$key
);
}
/**
* Return a secure random key for use with the XChaCha20-Poly1305
* symmetric AEAD interface.
*
* @return string
* @throws Exception
* @throws Error
*/
public static function crypto_aead_xchacha20poly1305_ietf_keygen()
{
return random_bytes(self::CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES);
}
/**
* Authenticate a message. Uses symmetric-key cryptography.
*
* Algorithm:
* HMAC-SHA512-256. Which is HMAC-SHA-512 truncated to 256 bits.
* Not to be confused with HMAC-SHA-512/256 which would use the
* SHA-512/256 hash function (uses different initial parameters
* but still truncates to 256 bits to sidestep length-extension
* attacks).
*
* @param string $message Message to be authenticated
* @param string $key Symmetric authentication key
* @return string Message authentication code
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArgument
*/
public static function crypto_auth($message, $key)
{
/* Type checks: */
ParagonIE_Sodium_Core_Util::declareScalarType($message, 'string', 1);
ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 2);
/* Input validation: */
if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_AUTH_KEYBYTES) {
throw new SodiumException('Argument 2 must be CRYPTO_AUTH_KEYBYTES long.');
}
if (self::useNewSodiumAPI()) {
return (string) sodium_crypto_auth($message, $key);
}
if (self::use_fallback('crypto_auth')) {
return (string) call_user_func('\\Sodium\\crypto_auth', $message, $key);
}
if (PHP_INT_SIZE === 4) {
return ParagonIE_Sodium_Crypto32::auth($message, $key);
}
return ParagonIE_Sodium_Crypto::auth($message, $key);
}
/**
* @return string
* @throws Exception
* @throws Error
*/
public static function crypto_auth_keygen()
{
return random_bytes(self::CRYPTO_AUTH_KEYBYTES);
}
/**
* Verify the MAC of a message previously authenticated with crypto_auth.
*
* @param string $mac Message authentication code
* @param string $message Message whose authenticity you are attempting to
* verify (with a given MAC and key)
* @param string $key Symmetric authentication key
* @return bool TRUE if authenticated, FALSE otherwise
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArgument
*/
public static function crypto_auth_verify($mac, $message, $key)
{
/* Type checks: */
ParagonIE_Sodium_Core_Util::declareScalarType($mac, 'string', 1);
ParagonIE_Sodium_Core_Util::declareScalarType($message, 'string', 2);
ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 3);
/* Input validation: */
if (ParagonIE_Sodium_Core_Util::strlen($mac) !== self::CRYPTO_AUTH_BYTES) {
throw new SodiumException('Argument 1 must be CRYPTO_AUTH_BYTES long.');
}
if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_AUTH_KEYBYTES) {
throw new SodiumException('Argument 3 must be CRYPTO_AUTH_KEYBYTES long.');
}
if (self::useNewSodiumAPI()) {
return (bool) sodium_crypto_auth_verify($mac, $message, $key);
}
if (self::use_fallback('crypto_auth_verify')) {
return (bool) call_user_func('\\Sodium\\crypto_auth_verify', $mac, $message, $key);
}
if (PHP_INT_SIZE === 4) {
return ParagonIE_Sodium_Crypto32::auth_verify($mac, $message, $key);
}
return ParagonIE_Sodium_Crypto::auth_verify($mac, $message, $key);
}
/**
* Authenticated asymmetric-key encryption. Both the sender and recipient
* may decrypt messages.
*
* Algorithm: X25519-XSalsa20-Poly1305.
* X25519: Elliptic-Curve Diffie Hellman over Curve25519.
* XSalsa20: Extended-nonce variant of salsa20.
* Poyl1305: Polynomial MAC for one-time message authentication.
*
* @param string $plaintext The message to be encrypted
* @param string $nonce A Number to only be used Once; must be 24 bytes
* @param string $keypair Your secret key and your recipient's public key
* @return string Ciphertext with 16-byte Poly1305 MAC
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArgument
*/
public static function crypto_box($plaintext, $nonce, $keypair)
{
/* Type checks: */
ParagonIE_Sodium_Core_Util::declareScalarType($plaintext, 'string', 1);
ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 2);
ParagonIE_Sodium_Core_Util::declareScalarType($keypair, 'string', 3);
/* Input validation: */
if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_BOX_NONCEBYTES) {
throw new SodiumException('Argument 2 must be CRYPTO_BOX_NONCEBYTES long.');
}
if (ParagonIE_Sodium_Core_Util::strlen($keypair) !== self::CRYPTO_BOX_KEYPAIRBYTES) {
throw new SodiumException('Argument 3 must be CRYPTO_BOX_KEYPAIRBYTES long.');
}
if (self::useNewSodiumAPI()) {
return (string) sodium_crypto_box($plaintext, $nonce, $keypair);
}
if (self::use_fallback('crypto_box')) {
return (string) call_user_func('\\Sodium\\crypto_box', $plaintext, $nonce, $keypair);
}
if (PHP_INT_SIZE === 4) {
return ParagonIE_Sodium_Crypto32::box($plaintext, $nonce, $keypair);
}
return ParagonIE_Sodium_Crypto::box($plaintext, $nonce, $keypair);
}
/**
* Anonymous public-key encryption. Only the recipient may decrypt messages.
*
* Algorithm: X25519-XSalsa20-Poly1305, as with crypto_box.
* The sender's X25519 keypair is ephemeral.
* Nonce is generated from the BLAKE2b hash of both public keys.
*
* This provides ciphertext integrity.
*
* @param string $plaintext Message to be sealed
* @param string $publicKey Your recipient's public key
* @return string Sealed message that only your recipient can
* decrypt
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArgument
*/
public static function crypto_box_seal($plaintext, $publicKey)
{
/* Type checks: */
ParagonIE_Sodium_Core_Util::declareScalarType($plaintext, 'string', 1);
ParagonIE_Sodium_Core_Util::declareScalarType($publicKey, 'string', 2);
/* Input validation: */
if (ParagonIE_Sodium_Core_Util::strlen($publicKey) !== self::CRYPTO_BOX_PUBLICKEYBYTES) {
throw new SodiumException('Argument 2 must be CRYPTO_BOX_PUBLICKEYBYTES long.');
}
if (self::useNewSodiumAPI()) {
return (string) sodium_crypto_box_seal($plaintext, $publicKey);
}
if (self::use_fallback('crypto_box_seal')) {
return (string) call_user_func('\\Sodium\\crypto_box_seal', $plaintext, $publicKey);
}
if (PHP_INT_SIZE === 4) {
return ParagonIE_Sodium_Crypto32::box_seal($plaintext, $publicKey);
}
return ParagonIE_Sodium_Crypto::box_seal($plaintext, $publicKey);
}
/**
* Opens a message encrypted with crypto_box_seal(). Requires
* the recipient's keypair (sk || pk) to decrypt successfully.
*
* This validates ciphertext integrity.
*
* @param string $ciphertext Sealed message to be opened
* @param string $keypair Your crypto_box keypair
* @return string The original plaintext message
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArgument
* @psalm-suppress MixedInferredReturnType
* @psalm-suppress MixedReturnStatement
*/
public static function crypto_box_seal_open($ciphertext, $keypair)
{
/* Type checks: */
ParagonIE_Sodium_Core_Util::declareScalarType($ciphertext, 'string', 1);
ParagonIE_Sodium_Core_Util::declareScalarType($keypair, 'string', 2);
/* Input validation: */
if (ParagonIE_Sodium_Core_Util::strlen($keypair) !== self::CRYPTO_BOX_KEYPAIRBYTES) {
throw new SodiumException('Argument 2 must be CRYPTO_BOX_KEYPAIRBYTES long.');
}
if (self::useNewSodiumAPI()) {
/**
* @psalm-suppress InvalidReturnStatement
* @psalm-suppress FalsableReturnStatement
*/
return sodium_crypto_box_seal_open($ciphertext, $keypair);
}
if (self::use_fallback('crypto_box_seal_open')) {
return call_user_func('\\Sodium\\crypto_box_seal_open', $ciphertext, $keypair);
}
if (PHP_INT_SIZE === 4) {
return ParagonIE_Sodium_Crypto32::box_seal_open($ciphertext, $keypair);
}
return ParagonIE_Sodium_Crypto::box_seal_open($ciphertext, $keypair);
}
/**
* Generate a new random X25519 keypair.
*
* @return string A 64-byte string; the first 32 are your secret key, while
* the last 32 are your public key. crypto_box_secretkey()
* and crypto_box_publickey() exist to separate them so you
* don't accidentally get them mixed up!
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArgument
*/
public static function crypto_box_keypair()
{
if (self::useNewSodiumAPI()) {
return (string) sodium_crypto_box_keypair();
}
if (self::use_fallback('crypto_box_keypair')) {
return (string) call_user_func('\\Sodium\\crypto_box_keypair');
}
if (PHP_INT_SIZE === 4) {
return ParagonIE_Sodium_Crypto32::box_keypair();
}
return ParagonIE_Sodium_Crypto::box_keypair();
}
/**
* Combine two keys into a keypair for use in library methods that expect
* a keypair. This doesn't necessarily have to be the same person's keys.
*
* @param string $secretKey Secret key
* @param string $publicKey Public key
* @return string Keypair
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArgument
*/
public static function crypto_box_keypair_from_secretkey_and_publickey($secretKey, $publicKey)
{
/* Type checks: */
ParagonIE_Sodium_Core_Util::declareScalarType($secretKey, 'string', 1);
ParagonIE_Sodium_Core_Util::declareScalarType($publicKey, 'string', 2);
/* Input validation: */
if (ParagonIE_Sodium_Core_Util::strlen($secretKey) !== self::CRYPTO_BOX_SECRETKEYBYTES) {
throw new SodiumException('Argument 1 must be CRYPTO_BOX_SECRETKEYBYTES long.');
}
if (ParagonIE_Sodium_Core_Util::strlen($publicKey) !== self::CRYPTO_BOX_PUBLICKEYBYTES) {
throw new SodiumException('Argument 2 must be CRYPTO_BOX_PUBLICKEYBYTES long.');
}
if (self::useNewSodiumAPI()) {
return (string) sodium_crypto_box_keypair_from_secretkey_and_publickey($secretKey, $publicKey);
}
if (self::use_fallback('crypto_box_keypair_from_secretkey_and_publickey')) {
return (string) call_user_func('\\Sodium\\crypto_box_keypair_from_secretkey_and_publickey', $secretKey, $publicKey);
}
if (PHP_INT_SIZE === 4) {
return ParagonIE_Sodium_Crypto32::box_keypair_from_secretkey_and_publickey($secretKey, $publicKey);
}
return ParagonIE_Sodium_Crypto::box_keypair_from_secretkey_and_publickey($secretKey, $publicKey);
}
/**
* Decrypt a message previously encrypted with crypto_box().
*
* @param string $ciphertext Encrypted message
* @param string $nonce Number to only be used Once; must be 24 bytes
* @param string $keypair Your secret key and the sender's public key
* @return string The original plaintext message
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArgument
* @psalm-suppress MixedInferredReturnType
* @psalm-suppress MixedReturnStatement
*/
public static function crypto_box_open($ciphertext, $nonce, $keypair)
{
/* Type checks: */
ParagonIE_Sodium_Core_Util::declareScalarType($ciphertext, 'string', 1);
ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 2);
ParagonIE_Sodium_Core_Util::declareScalarType($keypair, 'string', 3);
/* Input validation: */
if (ParagonIE_Sodium_Core_Util::strlen($ciphertext) < self::CRYPTO_BOX_MACBYTES) {
throw new SodiumException('Argument 1 must be at least CRYPTO_BOX_MACBYTES long.');
}
if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_BOX_NONCEBYTES) {
throw new SodiumException('Argument 2 must be CRYPTO_BOX_NONCEBYTES long.');
}
if (ParagonIE_Sodium_Core_Util::strlen($keypair) !== self::CRYPTO_BOX_KEYPAIRBYTES) {
throw new SodiumException('Argument 3 must be CRYPTO_BOX_KEYPAIRBYTES long.');
}
if (self::useNewSodiumAPI()) {
/**
* @psalm-suppress InvalidReturnStatement
* @psalm-suppress FalsableReturnStatement
*/
return sodium_crypto_box_open($ciphertext, $nonce, $keypair);
}
if (self::use_fallback('crypto_box_open')) {
return call_user_func('\\Sodium\\crypto_box_open', $ciphertext, $nonce, $keypair);
}
if (PHP_INT_SIZE === 4) {
return ParagonIE_Sodium_Crypto32::box_open($ciphertext, $nonce, $keypair);
}
return ParagonIE_Sodium_Crypto::box_open($ciphertext, $nonce, $keypair);
}
/**
* Extract the public key from a crypto_box keypair.
*
* @param string $keypair Keypair containing secret and public key
* @return string Your crypto_box public key
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArgument
*/
public static function crypto_box_publickey($keypair)
{
/* Type checks: */
ParagonIE_Sodium_Core_Util::declareScalarType($keypair, 'string', 1);
/* Input validation: */
if (ParagonIE_Sodium_Core_Util::strlen($keypair) !== self::CRYPTO_BOX_KEYPAIRBYTES) {
throw new SodiumException('Argument 1 must be CRYPTO_BOX_KEYPAIRBYTES long.');
}
if (self::useNewSodiumAPI()) {
return (string) sodium_crypto_box_publickey($keypair);
}
if (self::use_fallback('crypto_box_publickey')) {
return (string) call_user_func('\\Sodium\\crypto_box_publickey', $keypair);
}
if (PHP_INT_SIZE === 4) {
return ParagonIE_Sodium_Crypto32::box_publickey($keypair);
}
return ParagonIE_Sodium_Crypto::box_publickey($keypair);
}
/**
* Calculate the X25519 public key from a given X25519 secret key.
*
* @param string $secretKey Any X25519 secret key
* @return string The corresponding X25519 public key
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArgument
*/
public static function crypto_box_publickey_from_secretkey($secretKey)
{
/* Type checks: */
ParagonIE_Sodium_Core_Util::declareScalarType($secretKey, 'string', 1);
/* Input validation: */
if (ParagonIE_Sodium_Core_Util::strlen($secretKey) !== self::CRYPTO_BOX_SECRETKEYBYTES) {
throw new SodiumException('Argument 1 must be CRYPTO_BOX_SECRETKEYBYTES long.');
}
if (self::useNewSodiumAPI()) {
return (string) sodium_crypto_box_publickey_from_secretkey($secretKey);
}
if (self::use_fallback('crypto_box_publickey_from_secretkey')) {
return (string) call_user_func('\\Sodium\\crypto_box_publickey_from_secretkey', $secretKey);
}
if (PHP_INT_SIZE === 4) {
return ParagonIE_Sodium_Crypto32::box_publickey_from_secretkey($secretKey);
}
return ParagonIE_Sodium_Crypto::box_publickey_from_secretkey($secretKey);
}
/**
* Extract the secret key from a crypto_box keypair.
*
* @param string $keypair
* @return string Your crypto_box secret key
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArgument
*/
public static function crypto_box_secretkey($keypair)
{
/* Type checks: */
ParagonIE_Sodium_Core_Util::declareScalarType($keypair, 'string', 1);
/* Input validation: */
if (ParagonIE_Sodium_Core_Util::strlen($keypair) !== self::CRYPTO_BOX_KEYPAIRBYTES) {
throw new SodiumException('Argument 1 must be CRYPTO_BOX_KEYPAIRBYTES long.');
}
if (self::useNewSodiumAPI()) {
return (string) sodium_crypto_box_secretkey($keypair);
}
if (self::use_fallback('crypto_box_secretkey')) {
return (string) call_user_func('\\Sodium\\crypto_box_secretkey', $keypair);
}
if (PHP_INT_SIZE === 4) {
return ParagonIE_Sodium_Crypto32::box_secretkey($keypair);
}
return ParagonIE_Sodium_Crypto::box_secretkey($keypair);
}
/**
* Generate an X25519 keypair from a seed.
*
* @param string $seed
* @return string
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArgument
* @psalm-suppress UndefinedFunction
*/
public static function crypto_box_seed_keypair($seed)
{
/* Type checks: */
ParagonIE_Sodium_Core_Util::declareScalarType($seed, 'string', 1);
if (self::useNewSodiumAPI()) {
return (string) sodium_crypto_box_seed_keypair($seed);
}
if (self::use_fallback('crypto_box_seed_keypair')) {
return (string) call_user_func('\\Sodium\\crypto_box_seed_keypair', $seed);
}
if (PHP_INT_SIZE === 4) {
return ParagonIE_Sodium_Crypto32::box_seed_keypair($seed);
}
return ParagonIE_Sodium_Crypto::box_seed_keypair($seed);
}
/**
* Calculates a BLAKE2b hash, with an optional key.
*
* @param string $message The message to be hashed
* @param string|null $key If specified, must be a string between 16
* and 64 bytes long
* @param int $length Output length in bytes; must be between 16
* and 64 (default = 32)
* @return string Raw binary
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArgument
*/
public static function crypto_generichash($message, $key = '', $length = self::CRYPTO_GENERICHASH_BYTES)
{
/* Type checks: */
ParagonIE_Sodium_Core_Util::declareScalarType($message, 'string', 1);
if (is_null($key)) {
$key = '';
}
ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 2);
ParagonIE_Sodium_Core_Util::declareScalarType($length, 'int', 3);
/* Input validation: */
if (!empty($key)) {
if (ParagonIE_Sodium_Core_Util::strlen($key) < self::CRYPTO_GENERICHASH_KEYBYTES_MIN) {
throw new SodiumException('Unsupported key size. Must be at least CRYPTO_GENERICHASH_KEYBYTES_MIN bytes long.');
}
if (ParagonIE_Sodium_Core_Util::strlen($key) > self::CRYPTO_GENERICHASH_KEYBYTES_MAX) {
throw new SodiumException('Unsupported key size. Must be at most CRYPTO_GENERICHASH_KEYBYTES_MAX bytes long.');
}
}
if (self::useNewSodiumAPI()) {
return (string) sodium_crypto_generichash($message, $key, $length);
}
if (self::use_fallback('crypto_generichash')) {
return (string) call_user_func('\\Sodium\\crypto_generichash', $message, $key, $length);
}
if (PHP_INT_SIZE === 4) {
return ParagonIE_Sodium_Crypto32::generichash($message, $key, $length);
}
return ParagonIE_Sodium_Crypto::generichash($message, $key, $length);
}
/**
* Get the final BLAKE2b hash output for a given context.
*
* @param string $ctx BLAKE2 hashing context. Generated by crypto_generichash_init().
* @param int $length Hash output size.
* @return string Final BLAKE2b hash.
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArgument
* @psalm-suppress ReferenceConstraintViolation
* @psalm-suppress ConflictingReferenceConstraint
*/
public static function crypto_generichash_final(&$ctx, $length = self::CRYPTO_GENERICHASH_BYTES)
{
/* Type checks: */
ParagonIE_Sodium_Core_Util::declareScalarType($ctx, 'string', 1);
ParagonIE_Sodium_Core_Util::declareScalarType($length, 'int', 2);
if (self::useNewSodiumAPI()) {
return sodium_crypto_generichash_final($ctx, $length);
}
if (self::use_fallback('crypto_generichash_final')) {
$func = '\\Sodium\\crypto_generichash_final';
return (string) $func($ctx, $length);
}
if ($length < 1) {
try {
self::memzero($ctx);
} catch (SodiumException $ex) {
unset($ctx);
}
return '';
}
if (PHP_INT_SIZE === 4) {
$result = ParagonIE_Sodium_Crypto32::generichash_final($ctx, $length);
} else {
$result = ParagonIE_Sodium_Crypto::generichash_final($ctx, $length);
}
try {
self::memzero($ctx);
} catch (SodiumException $ex) {
unset($ctx);
}
return $result;
}
/**
* Initialize a BLAKE2b hashing context, for use in a streaming interface.
*
* @param string|null $key If specified must be a string between 16 and 64 bytes
* @param int $length The size of the desired hash output
* @return string A BLAKE2 hashing context, encoded as a string
* (To be 100% compatible with ext/libsodium)
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArgument
*/
public static function crypto_generichash_init($key = '', $length = self::CRYPTO_GENERICHASH_BYTES)
{
/* Type checks: */
if (is_null($key)) {
$key = '';
}
ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 1);
ParagonIE_Sodium_Core_Util::declareScalarType($length, 'int', 2);
/* Input validation: */
if (!empty($key)) {
if (ParagonIE_Sodium_Core_Util::strlen($key) < self::CRYPTO_GENERICHASH_KEYBYTES_MIN) {
throw new SodiumException('Unsupported key size. Must be at least CRYPTO_GENERICHASH_KEYBYTES_MIN bytes long.');
}
if (ParagonIE_Sodium_Core_Util::strlen($key) > self::CRYPTO_GENERICHASH_KEYBYTES_MAX) {
throw new SodiumException('Unsupported key size. Must be at most CRYPTO_GENERICHASH_KEYBYTES_MAX bytes long.');
}
}
if (self::useNewSodiumAPI()) {
return sodium_crypto_generichash_init($key, $length);
}
if (self::use_fallback('crypto_generichash_init')) {
return (string) call_user_func('\\Sodium\\crypto_generichash_init', $key, $length);
}
if (PHP_INT_SIZE === 4) {
return ParagonIE_Sodium_Crypto32::generichash_init($key, $length);
}
return ParagonIE_Sodium_Crypto::generichash_init($key, $length);
}
/**
* Initialize a BLAKE2b hashing context, for use in a streaming interface.
*
* @param string|null $key If specified must be a string between 16 and 64 bytes
* @param int $length The size of the desired hash output
* @param string $salt Salt (up to 16 bytes)
* @param string $personal Personalization string (up to 16 bytes)
* @return string A BLAKE2 hashing context, encoded as a string
* (To be 100% compatible with ext/libsodium)
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArgument
*/
public static function crypto_generichash_init_salt_personal(
$key = '',
$length = self::CRYPTO_GENERICHASH_BYTES,
$salt = '',
$personal = ''
) {
/* Type checks: */
if (is_null($key)) {
$key = '';
}
ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 1);
ParagonIE_Sodium_Core_Util::declareScalarType($length, 'int', 2);
ParagonIE_Sodium_Core_Util::declareScalarType($salt, 'string', 3);
ParagonIE_Sodium_Core_Util::declareScalarType($personal, 'string', 4);
$salt = str_pad($salt, 16, "\0", STR_PAD_RIGHT);
$personal = str_pad($personal, 16, "\0", STR_PAD_RIGHT);
/* Input validation: */
if (!empty($key)) {
/*
if (ParagonIE_Sodium_Core_Util::strlen($key) < self::CRYPTO_GENERICHASH_KEYBYTES_MIN) {
throw new SodiumException('Unsupported key size. Must be at least CRYPTO_GENERICHASH_KEYBYTES_MIN bytes long.');
}
*/
if (ParagonIE_Sodium_Core_Util::strlen($key) > self::CRYPTO_GENERICHASH_KEYBYTES_MAX) {
throw new SodiumException('Unsupported key size. Must be at most CRYPTO_GENERICHASH_KEYBYTES_MAX bytes long.');
}
}
if (PHP_INT_SIZE === 4) {
return ParagonIE_Sodium_Crypto32::generichash_init_salt_personal($key, $length, $salt, $personal);
}
return ParagonIE_Sodium_Crypto::generichash_init_salt_personal($key, $length, $salt, $personal);
}
/**
* Update a BLAKE2b hashing context with additional data.
*
* @param string $ctx BLAKE2 hashing context. Generated by crypto_generichash_init().
* $ctx is passed by reference and gets updated in-place.
* @param-out string $ctx
* @param string $message The message to append to the existing hash state.
* @return void
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArgument
* @psalm-suppress ReferenceConstraintViolation
*/
public static function crypto_generichash_update(&$ctx, $message)
{
/* Type checks: */
ParagonIE_Sodium_Core_Util::declareScalarType($ctx, 'string', 1);
ParagonIE_Sodium_Core_Util::declareScalarType($message, 'string', 2);
if (self::useNewSodiumAPI()) {
sodium_crypto_generichash_update($ctx, $message);
return;
}
if (self::use_fallback('crypto_generichash_update')) {
$func = '\\Sodium\\crypto_generichash_update';
$func($ctx, $message);
return;
}
if (PHP_INT_SIZE === 4) {
$ctx = ParagonIE_Sodium_Crypto32::generichash_update($ctx, $message);
} else {
$ctx = ParagonIE_Sodium_Crypto::generichash_update($ctx, $message);
}
}
/**
* @return string
* @throws Exception
* @throws Error
*/
public static function crypto_generichash_keygen()
{
return random_bytes(self::CRYPTO_GENERICHASH_KEYBYTES);
}
/**
* @param int $subkey_len
* @param int $subkey_id
* @param string $context
* @param string $key
* @return string
* @throws SodiumException
*/
public static function crypto_kdf_derive_from_key(
$subkey_len,
$subkey_id,
$context,
$key
) {
ParagonIE_Sodium_Core_Util::declareScalarType($subkey_len, 'int', 1);
ParagonIE_Sodium_Core_Util::declareScalarType($subkey_id, 'int', 2);
ParagonIE_Sodium_Core_Util::declareScalarType($context, 'string', 3);
ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 4);
$subkey_id = (int) $subkey_id;
$subkey_len = (int) $subkey_len;
$context = (string) $context;
$key = (string) $key;
if ($subkey_len < self::CRYPTO_KDF_BYTES_MIN) {
throw new SodiumException('subkey cannot be smaller than SODIUM_CRYPTO_KDF_BYTES_MIN');
}
if ($subkey_len > self::CRYPTO_KDF_BYTES_MAX) {
throw new SodiumException('subkey cannot be larger than SODIUM_CRYPTO_KDF_BYTES_MAX');
}
if ($subkey_id < 0) {
throw new SodiumException('subkey_id cannot be negative');
}
if (ParagonIE_Sodium_Core_Util::strlen($context) !== self::CRYPTO_KDF_CONTEXTBYTES) {
throw new SodiumException('context should be SODIUM_CRYPTO_KDF_CONTEXTBYTES bytes');
}
if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_KDF_KEYBYTES) {
throw new SodiumException('key should be SODIUM_CRYPTO_KDF_KEYBYTES bytes');
}
$salt = ParagonIE_Sodium_Core_Util::store64_le($subkey_id);
$state = self::crypto_generichash_init_salt_personal(
$key,
$subkey_len,
$salt,
$context
);
return self::crypto_generichash_final($state, $subkey_len);
}
/**
* @return string
* @throws Exception
* @throws Error
*/
public static function crypto_kdf_keygen()
{
return random_bytes(self::CRYPTO_KDF_KEYBYTES);
}
/**
* Perform a key exchange, between a designated client and a server.
*
* Typically, you would designate one machine to be the client and the
* other to be the server. The first two keys are what you'd expect for
* scalarmult() below, but the latter two public keys don't swap places.
*
* | ALICE | BOB |
* | Client | Server |
* |--------------------------------|-------------------------------------|
* | shared = crypto_kx( | shared = crypto_kx( |
* | alice_sk, | bob_sk, | <- contextual
* | bob_pk, | alice_pk, | <- contextual
* | alice_pk, | alice_pk, | <----- static
* | bob_pk | bob_pk | <----- static
* | ) | ) |
*
* They are used along with the scalarmult product to generate a 256-bit
* BLAKE2b hash unique to the client and server keys.
*
* @param string $my_secret
* @param string $their_public
* @param string $client_public
* @param string $server_public
* @param bool $dontFallback
* @return string
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArgument
*/
public static function crypto_kx($my_secret, $their_public, $client_public, $server_public, $dontFallback = false)
{
/* Type checks: */
ParagonIE_Sodium_Core_Util::declareScalarType($my_secret, 'string', 1);
ParagonIE_Sodium_Core_Util::declareScalarType($their_public, 'string', 2);
ParagonIE_Sodium_Core_Util::declareScalarType($client_public, 'string', 3);
ParagonIE_Sodium_Core_Util::declareScalarType($server_public, 'string', 4);
/* Input validation: */
if (ParagonIE_Sodium_Core_Util::strlen($my_secret) !== self::CRYPTO_BOX_SECRETKEYBYTES) {
throw new SodiumException('Argument 1 must be CRYPTO_BOX_SECRETKEYBYTES long.');
}
if (ParagonIE_Sodium_Core_Util::strlen($their_public) !== self::CRYPTO_BOX_PUBLICKEYBYTES) {
throw new SodiumException('Argument 2 must be CRYPTO_BOX_PUBLICKEYBYTES long.');
}
if (ParagonIE_Sodium_Core_Util::strlen($client_public) !== self::CRYPTO_BOX_PUBLICKEYBYTES) {
throw new SodiumException('Argument 3 must be CRYPTO_BOX_PUBLICKEYBYTES long.');
}
if (ParagonIE_Sodium_Core_Util::strlen($server_public) !== self::CRYPTO_BOX_PUBLICKEYBYTES) {
throw new SodiumException('Argument 4 must be CRYPTO_BOX_PUBLICKEYBYTES long.');
}
if (self::useNewSodiumAPI() && !$dontFallback) {
if (is_callable('sodium_crypto_kx')) {
return (string) sodium_crypto_kx(
$my_secret,
$their_public,
$client_public,
$server_public
);
}
}
if (self::use_fallback('crypto_kx')) {
return (string) call_user_func(
'\\Sodium\\crypto_kx',
$my_secret,
$their_public,
$client_public,
$server_public
);
}
if (PHP_INT_SIZE === 4) {
return ParagonIE_Sodium_Crypto32::keyExchange(
$my_secret,
$their_public,
$client_public,
$server_public
);
}
return ParagonIE_Sodium_Crypto::keyExchange(
$my_secret,
$their_public,
$client_public,
$server_public
);
}
/**
* @param string $seed
* @return string
* @throws SodiumException
*/
public static function crypto_kx_seed_keypair($seed)
{
ParagonIE_Sodium_Core_Util::declareScalarType($seed, 'string', 1);
$seed = (string) $seed;
if (ParagonIE_Sodium_Core_Util::strlen($seed) !== self::CRYPTO_KX_SEEDBYTES) {
throw new SodiumException('seed must be SODIUM_CRYPTO_KX_SEEDBYTES bytes');
}
$sk = self::crypto_generichash($seed, '', self::CRYPTO_KX_SECRETKEYBYTES);
$pk = self::crypto_scalarmult_base($sk);
return $sk . $pk;
}
/**
* @return string
* @throws Exception
*/
public static function crypto_kx_keypair()
{
$sk = self::randombytes_buf(self::CRYPTO_KX_SECRETKEYBYTES);
$pk = self::crypto_scalarmult_base($sk);
return $sk . $pk;
}
/**
* @param string $keypair
* @param string $serverPublicKey
* @return array{0: string, 1: string}
* @throws SodiumException
*/
public static function crypto_kx_client_session_keys($keypair, $serverPublicKey)
{
ParagonIE_Sodium_Core_Util::declareScalarType($keypair, 'string', 1);
ParagonIE_Sodium_Core_Util::declareScalarType($serverPublicKey, 'string', 2);
$keypair = (string) $keypair;
$serverPublicKey = (string) $serverPublicKey;
if (ParagonIE_Sodium_Core_Util::strlen($keypair) !== self::CRYPTO_KX_KEYPAIRBYTES) {
throw new SodiumException('keypair should be SODIUM_CRYPTO_KX_KEYPAIRBYTES bytes');
}
if (ParagonIE_Sodium_Core_Util::strlen($serverPublicKey) !== self::CRYPTO_KX_PUBLICKEYBYTES) {
throw new SodiumException('public keys must be SODIUM_CRYPTO_KX_PUBLICKEYBYTES bytes');
}
$sk = self::crypto_kx_secretkey($keypair);
$pk = self::crypto_kx_publickey($keypair);
$h = self::crypto_generichash_init(null, self::CRYPTO_KX_SESSIONKEYBYTES * 2);
self::crypto_generichash_update($h, self::crypto_scalarmult($sk, $serverPublicKey));
self::crypto_generichash_update($h, $pk);
self::crypto_generichash_update($h, $serverPublicKey);
$sessionKeys = self::crypto_generichash_final($h, self::CRYPTO_KX_SESSIONKEYBYTES * 2);
return array(
ParagonIE_Sodium_Core_Util::substr(
$sessionKeys,
0,
self::CRYPTO_KX_SESSIONKEYBYTES
),
ParagonIE_Sodium_Core_Util::substr(
$sessionKeys,
self::CRYPTO_KX_SESSIONKEYBYTES,
self::CRYPTO_KX_SESSIONKEYBYTES
)
);
}
/**
* @param string $keypair
* @param string $clientPublicKey
* @return array{0: string, 1: string}
* @throws SodiumException
*/
public static function crypto_kx_server_session_keys($keypair, $clientPublicKey)
{
ParagonIE_Sodium_Core_Util::declareScalarType($keypair, 'string', 1);
ParagonIE_Sodium_Core_Util::declareScalarType($clientPublicKey, 'string', 2);
$keypair = (string) $keypair;
$clientPublicKey = (string) $clientPublicKey;
if (ParagonIE_Sodium_Core_Util::strlen($keypair) !== self::CRYPTO_KX_KEYPAIRBYTES) {
throw new SodiumException('keypair should be SODIUM_CRYPTO_KX_KEYPAIRBYTES bytes');
}
if (ParagonIE_Sodium_Core_Util::strlen($clientPublicKey) !== self::CRYPTO_KX_PUBLICKEYBYTES) {
throw new SodiumException('public keys must be SODIUM_CRYPTO_KX_PUBLICKEYBYTES bytes');
}
$sk = self::crypto_kx_secretkey($keypair);
$pk = self::crypto_kx_publickey($keypair);
$h = self::crypto_generichash_init(null, self::CRYPTO_KX_SESSIONKEYBYTES * 2);
self::crypto_generichash_update($h, self::crypto_scalarmult($sk, $clientPublicKey));
self::crypto_generichash_update($h, $clientPublicKey);
self::crypto_generichash_update($h, $pk);
$sessionKeys = self::crypto_generichash_final($h, self::CRYPTO_KX_SESSIONKEYBYTES * 2);
return array(
ParagonIE_Sodium_Core_Util::substr(
$sessionKeys,
self::CRYPTO_KX_SESSIONKEYBYTES,
self::CRYPTO_KX_SESSIONKEYBYTES
),
ParagonIE_Sodium_Core_Util::substr(
$sessionKeys,
0,
self::CRYPTO_KX_SESSIONKEYBYTES
)
);
}
/**
* @param string $kp
* @return string
* @throws SodiumException
*/
public static function crypto_kx_secretkey($kp)
{
return ParagonIE_Sodium_Core_Util::substr(
$kp,
0,
self::CRYPTO_KX_SECRETKEYBYTES
);
}
/**
* @param string $kp
* @return string
* @throws SodiumException
*/
public static function crypto_kx_publickey($kp)
{
return ParagonIE_Sodium_Core_Util::substr(
$kp,
self::CRYPTO_KX_SECRETKEYBYTES,
self::CRYPTO_KX_PUBLICKEYBYTES
);
}
/**
* @param int $outlen
* @param string $passwd
* @param string $salt
* @param int $opslimit
* @param int $memlimit
* @param int|null $alg
* @return string
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArgument
*/
public static function crypto_pwhash($outlen, $passwd, $salt, $opslimit, $memlimit, $alg = null)
{
ParagonIE_Sodium_Core_Util::declareScalarType($outlen, 'int', 1);
ParagonIE_Sodium_Core_Util::declareScalarType($passwd, 'string', 2);
ParagonIE_Sodium_Core_Util::declareScalarType($salt, 'string', 3);
ParagonIE_Sodium_Core_Util::declareScalarType($opslimit, 'int', 4);
ParagonIE_Sodium_Core_Util::declareScalarType($memlimit, 'int', 5);
if (self::useNewSodiumAPI()) {
if (!is_null($alg)) {
ParagonIE_Sodium_Core_Util::declareScalarType($alg, 'int', 6);
return sodium_crypto_pwhash($outlen, $passwd, $salt, $opslimit, $memlimit, $alg);
}
return sodium_crypto_pwhash($outlen, $passwd, $salt, $opslimit, $memlimit);
}
if (self::use_fallback('crypto_pwhash')) {
return (string) call_user_func('\\Sodium\\crypto_pwhash', $outlen, $passwd, $salt, $opslimit, $memlimit);
}
// This is the best we can do.
throw new SodiumException(
'This is not implemented, as it is not possible to implement Argon2i with acceptable performance in pure-PHP'
);
}
/**
* !Exclusive to sodium_compat!
*
* This returns TRUE if the native crypto_pwhash API is available by libsodium.
* This returns FALSE if only sodium_compat is available.
*
* @return bool
*/
public static function crypto_pwhash_is_available()
{
if (self::useNewSodiumAPI()) {
return true;
}
if (self::use_fallback('crypto_pwhash')) {
return true;
}
return false;
}
/**
* @param string $passwd
* @param int $opslimit
* @param int $memlimit
* @return string
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArgument
*/
public static function crypto_pwhash_str($passwd, $opslimit, $memlimit)
{
ParagonIE_Sodium_Core_Util::declareScalarType($passwd, 'string', 1);
ParagonIE_Sodium_Core_Util::declareScalarType($opslimit, 'int', 2);
ParagonIE_Sodium_Core_Util::declareScalarType($memlimit, 'int', 3);
if (self::useNewSodiumAPI()) {
return sodium_crypto_pwhash_str($passwd, $opslimit, $memlimit);
}
if (self::use_fallback('crypto_pwhash_str')) {
return (string) call_user_func('\\Sodium\\crypto_pwhash_str', $passwd, $opslimit, $memlimit);
}
// This is the best we can do.
throw new SodiumException(
'This is not implemented, as it is not possible to implement Argon2i with acceptable performance in pure-PHP'
);
}
/**
* Do we need to rehash this password?
*
* @param string $hash
* @param int $opslimit
* @param int $memlimit
* @return bool
* @throws SodiumException
*/
public static function crypto_pwhash_str_needs_rehash($hash, $opslimit, $memlimit)
{
ParagonIE_Sodium_Core_Util::declareScalarType($hash, 'string', 1);
ParagonIE_Sodium_Core_Util::declareScalarType($opslimit, 'int', 2);
ParagonIE_Sodium_Core_Util::declareScalarType($memlimit, 'int', 3);
// Just grab the first 4 pieces.
$pieces = explode('$', (string) $hash);
$prefix = implode('$', array_slice($pieces, 0, 4));
// Rebuild the expected header.
/** @var int $ops */
$ops = (int) $opslimit;
/** @var int $mem */
$mem = (int) $memlimit >> 10;
$encoded = self::CRYPTO_PWHASH_STRPREFIX . 'v=19$m=' . $mem . ',t=' . $ops . ',p=1';
// Do they match? If so, we don't need to rehash, so return false.
return !ParagonIE_Sodium_Core_Util::hashEquals($encoded, $prefix);
}
/**
* @param string $passwd
* @param string $hash
* @return bool
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArgument
*/
public static function crypto_pwhash_str_verify($passwd, $hash)
{
ParagonIE_Sodium_Core_Util::declareScalarType($passwd, 'string', 1);
ParagonIE_Sodium_Core_Util::declareScalarType($hash, 'string', 2);
if (self::useNewSodiumAPI()) {
return (bool) sodium_crypto_pwhash_str_verify($passwd, $hash);
}
if (self::use_fallback('crypto_pwhash_str_verify')) {
return (bool) call_user_func('\\Sodium\\crypto_pwhash_str_verify', $passwd, $hash);
}
// This is the best we can do.
throw new SodiumException(
'This is not implemented, as it is not possible to implement Argon2i with acceptable performance in pure-PHP'
);
}
/**
* @param int $outlen
* @param string $passwd
* @param string $salt
* @param int $opslimit
* @param int $memlimit
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function crypto_pwhash_scryptsalsa208sha256($outlen, $passwd, $salt, $opslimit, $memlimit)
{
ParagonIE_Sodium_Core_Util::declareScalarType($outlen, 'int', 1);
ParagonIE_Sodium_Core_Util::declareScalarType($passwd, 'string', 2);
ParagonIE_Sodium_Core_Util::declareScalarType($salt, 'string', 3);
ParagonIE_Sodium_Core_Util::declareScalarType($opslimit, 'int', 4);
ParagonIE_Sodium_Core_Util::declareScalarType($memlimit, 'int', 5);
if (self::useNewSodiumAPI()) {
return (string) sodium_crypto_pwhash_scryptsalsa208sha256(
(int) $outlen,
(string) $passwd,
(string) $salt,
(int) $opslimit,
(int) $memlimit
);
}
if (self::use_fallback('crypto_pwhash_scryptsalsa208sha256')) {
return (string) call_user_func(
'\\Sodium\\crypto_pwhash_scryptsalsa208sha256',
(int) $outlen,
(string) $passwd,
(string) $salt,
(int) $opslimit,
(int) $memlimit
);
}
// This is the best we can do.
throw new SodiumException(
'This is not implemented, as it is not possible to implement Scrypt with acceptable performance in pure-PHP'
);
}
/**
* !Exclusive to sodium_compat!
*
* This returns TRUE if the native crypto_pwhash API is available by libsodium.
* This returns FALSE if only sodium_compat is available.
*
* @return bool
*/
public static function crypto_pwhash_scryptsalsa208sha256_is_available()
{
if (self::useNewSodiumAPI()) {
return true;
}
if (self::use_fallback('crypto_pwhash_scryptsalsa208sha256')) {
return true;
}
return false;
}
/**
* @param string $passwd
* @param int $opslimit
* @param int $memlimit
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function crypto_pwhash_scryptsalsa208sha256_str($passwd, $opslimit, $memlimit)
{
ParagonIE_Sodium_Core_Util::declareScalarType($passwd, 'string', 1);
ParagonIE_Sodium_Core_Util::declareScalarType($opslimit, 'int', 2);
ParagonIE_Sodium_Core_Util::declareScalarType($memlimit, 'int', 3);
if (self::useNewSodiumAPI()) {
return (string) sodium_crypto_pwhash_scryptsalsa208sha256_str(
(string) $passwd,
(int) $opslimit,
(int) $memlimit
);
}
if (self::use_fallback('crypto_pwhash_scryptsalsa208sha256_str')) {
return (string) call_user_func(
'\\Sodium\\crypto_pwhash_scryptsalsa208sha256_str',
(string) $passwd,
(int) $opslimit,
(int) $memlimit
);
}
// This is the best we can do.
throw new SodiumException(
'This is not implemented, as it is not possible to implement Scrypt with acceptable performance in pure-PHP'
);
}
/**
* @param string $passwd
* @param string $hash
* @return bool
* @throws SodiumException
* @throws TypeError
*/
public static function crypto_pwhash_scryptsalsa208sha256_str_verify($passwd, $hash)
{
ParagonIE_Sodium_Core_Util::declareScalarType($passwd, 'string', 1);
ParagonIE_Sodium_Core_Util::declareScalarType($hash, 'string', 2);
if (self::useNewSodiumAPI()) {
return (bool) sodium_crypto_pwhash_scryptsalsa208sha256_str_verify(
(string) $passwd,
(string) $hash
);
}
if (self::use_fallback('crypto_pwhash_scryptsalsa208sha256_str_verify')) {
return (bool) call_user_func(
'\\Sodium\\crypto_pwhash_scryptsalsa208sha256_str_verify',
(string) $passwd,
(string) $hash
);
}
// This is the best we can do.
throw new SodiumException(
'This is not implemented, as it is not possible to implement Scrypt with acceptable performance in pure-PHP'
);
}
/**
* Calculate the shared secret between your secret key and your
* recipient's public key.
*
* Algorithm: X25519 (ECDH over Curve25519)
*
* @param string $secretKey
* @param string $publicKey
* @return string
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArgument
*/
public static function crypto_scalarmult($secretKey, $publicKey)
{
/* Type checks: */
ParagonIE_Sodium_Core_Util::declareScalarType($secretKey, 'string', 1);
ParagonIE_Sodium_Core_Util::declareScalarType($publicKey, 'string', 2);
/* Input validation: */
if (ParagonIE_Sodium_Core_Util::strlen($secretKey) !== self::CRYPTO_BOX_SECRETKEYBYTES) {
throw new SodiumException('Argument 1 must be CRYPTO_BOX_SECRETKEYBYTES long.');
}
if (ParagonIE_Sodium_Core_Util::strlen($publicKey) !== self::CRYPTO_BOX_PUBLICKEYBYTES) {
throw new SodiumException('Argument 2 must be CRYPTO_BOX_PUBLICKEYBYTES long.');
}
if (self::useNewSodiumAPI()) {
return sodium_crypto_scalarmult($secretKey, $publicKey);
}
if (self::use_fallback('crypto_scalarmult')) {
return (string) call_user_func('\\Sodium\\crypto_scalarmult', $secretKey, $publicKey);
}
/* Output validation: Forbid all-zero keys */
if (ParagonIE_Sodium_Core_Util::hashEquals($secretKey, str_repeat("\0", self::CRYPTO_BOX_SECRETKEYBYTES))) {
throw new SodiumException('Zero secret key is not allowed');
}
if (ParagonIE_Sodium_Core_Util::hashEquals($publicKey, str_repeat("\0", self::CRYPTO_BOX_PUBLICKEYBYTES))) {
throw new SodiumException('Zero public key is not allowed');
}
if (PHP_INT_SIZE === 4) {
return ParagonIE_Sodium_Crypto32::scalarmult($secretKey, $publicKey);
}
return ParagonIE_Sodium_Crypto::scalarmult($secretKey, $publicKey);
}
/**
* Calculate an X25519 public key from an X25519 secret key.
*
* @param string $secretKey
* @return string
* @throws SodiumException
* @throws TypeError
* @psalm-suppress TooFewArguments
* @psalm-suppress MixedArgument
*/
public static function crypto_scalarmult_base($secretKey)
{
/* Type checks: */
ParagonIE_Sodium_Core_Util::declareScalarType($secretKey, 'string', 1);
/* Input validation: */
if (ParagonIE_Sodium_Core_Util::strlen($secretKey) !== self::CRYPTO_BOX_SECRETKEYBYTES) {
throw new SodiumException('Argument 1 must be CRYPTO_BOX_SECRETKEYBYTES long.');
}
if (self::useNewSodiumAPI()) {
return sodium_crypto_scalarmult_base($secretKey);
}
if (self::use_fallback('crypto_scalarmult_base')) {
return (string) call_user_func('\\Sodium\\crypto_scalarmult_base', $secretKey);
}
if (ParagonIE_Sodium_Core_Util::hashEquals($secretKey, str_repeat("\0", self::CRYPTO_BOX_SECRETKEYBYTES))) {
throw new SodiumException('Zero secret key is not allowed');
}
if (PHP_INT_SIZE === 4) {
return ParagonIE_Sodium_Crypto32::scalarmult_base($secretKey);
}
return ParagonIE_Sodium_Crypto::scalarmult_base($secretKey);
}
/**
* Authenticated symmetric-key encryption.
*
* Algorithm: XSalsa20-Poly1305
*
* @param string $plaintext The message you're encrypting
* @param string $nonce A Number to be used Once; must be 24 bytes
* @param string $key Symmetric encryption key
* @return string Ciphertext with Poly1305 MAC
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArgument
*/
public static function crypto_secretbox($plaintext, $nonce, $key)
{
/* Type checks: */
ParagonIE_Sodium_Core_Util::declareScalarType($plaintext, 'string', 1);
ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 2);
ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 3);
/* Input validation: */
if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_SECRETBOX_NONCEBYTES) {
throw new SodiumException('Argument 2 must be CRYPTO_SECRETBOX_NONCEBYTES long.');
}
if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_SECRETBOX_KEYBYTES) {
throw new SodiumException('Argument 3 must be CRYPTO_SECRETBOX_KEYBYTES long.');
}
if (self::useNewSodiumAPI()) {
return sodium_crypto_secretbox($plaintext, $nonce, $key);
}
if (self::use_fallback('crypto_secretbox')) {
return (string) call_user_func('\\Sodium\\crypto_secretbox', $plaintext, $nonce, $key);
}
if (PHP_INT_SIZE === 4) {
return ParagonIE_Sodium_Crypto32::secretbox($plaintext, $nonce, $key);
}
return ParagonIE_Sodium_Crypto::secretbox($plaintext, $nonce, $key);
}
/**
* Decrypts a message previously encrypted with crypto_secretbox().
*
* @param string $ciphertext Ciphertext with Poly1305 MAC
* @param string $nonce A Number to be used Once; must be 24 bytes
* @param string $key Symmetric encryption key
* @return string Original plaintext message
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArgument
* @psalm-suppress MixedInferredReturnType
* @psalm-suppress MixedReturnStatement
*/
public static function crypto_secretbox_open($ciphertext, $nonce, $key)
{
/* Type checks: */
ParagonIE_Sodium_Core_Util::declareScalarType($ciphertext, 'string', 1);
ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 2);
ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 3);
/* Input validation: */
if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_SECRETBOX_NONCEBYTES) {
throw new SodiumException('Argument 2 must be CRYPTO_SECRETBOX_NONCEBYTES long.');
}
if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_SECRETBOX_KEYBYTES) {
throw new SodiumException('Argument 3 must be CRYPTO_SECRETBOX_KEYBYTES long.');
}
if (self::useNewSodiumAPI()) {
/**
* @psalm-suppress InvalidReturnStatement
* @psalm-suppress FalsableReturnStatement
*/
return sodium_crypto_secretbox_open($ciphertext, $nonce, $key);
}
if (self::use_fallback('crypto_secretbox_open')) {
return call_user_func('\\Sodium\\crypto_secretbox_open', $ciphertext, $nonce, $key);
}
if (PHP_INT_SIZE === 4) {
return ParagonIE_Sodium_Crypto32::secretbox_open($ciphertext, $nonce, $key);
}
return ParagonIE_Sodium_Crypto::secretbox_open($ciphertext, $nonce, $key);
}
/**
* Return a secure random key for use with crypto_secretbox
*
* @return string
* @throws Exception
* @throws Error
*/
public static function crypto_secretbox_keygen()
{
return random_bytes(self::CRYPTO_SECRETBOX_KEYBYTES);
}
/**
* Authenticated symmetric-key encryption.
*
* Algorithm: XChaCha20-Poly1305
*
* @param string $plaintext The message you're encrypting
* @param string $nonce A Number to be used Once; must be 24 bytes
* @param string $key Symmetric encryption key
* @return string Ciphertext with Poly1305 MAC
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArgument
*/
public static function crypto_secretbox_xchacha20poly1305($plaintext, $nonce, $key)
{
/* Type checks: */
ParagonIE_Sodium_Core_Util::declareScalarType($plaintext, 'string', 1);
ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 2);
ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 3);
/* Input validation: */
if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_SECRETBOX_NONCEBYTES) {
throw new SodiumException('Argument 2 must be CRYPTO_SECRETBOX_NONCEBYTES long.');
}
if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_SECRETBOX_KEYBYTES) {
throw new SodiumException('Argument 3 must be CRYPTO_SECRETBOX_KEYBYTES long.');
}
if (PHP_INT_SIZE === 4) {
return ParagonIE_Sodium_Crypto32::secretbox_xchacha20poly1305($plaintext, $nonce, $key);
}
return ParagonIE_Sodium_Crypto::secretbox_xchacha20poly1305($plaintext, $nonce, $key);
}
/**
* Decrypts a message previously encrypted with crypto_secretbox_xchacha20poly1305().
*
* @param string $ciphertext Ciphertext with Poly1305 MAC
* @param string $nonce A Number to be used Once; must be 24 bytes
* @param string $key Symmetric encryption key
* @return string Original plaintext message
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArgument
*/
public static function crypto_secretbox_xchacha20poly1305_open($ciphertext, $nonce, $key)
{
/* Type checks: */
ParagonIE_Sodium_Core_Util::declareScalarType($ciphertext, 'string', 1);
ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 2);
ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 3);
/* Input validation: */
if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_SECRETBOX_NONCEBYTES) {
throw new SodiumException('Argument 2 must be CRYPTO_SECRETBOX_NONCEBYTES long.');
}
if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_SECRETBOX_KEYBYTES) {
throw new SodiumException('Argument 3 must be CRYPTO_SECRETBOX_KEYBYTES long.');
}
if (PHP_INT_SIZE === 4) {
return ParagonIE_Sodium_Crypto32::secretbox_xchacha20poly1305_open($ciphertext, $nonce, $key);
}
return ParagonIE_Sodium_Crypto::secretbox_xchacha20poly1305_open($ciphertext, $nonce, $key);
}
/**
* @param string $key
* @return array<int, string> Returns a state and a header.
* @throws Exception
* @throws SodiumException
*/
public static function crypto_secretstream_xchacha20poly1305_init_push($key)
{
if (PHP_INT_SIZE === 4) {
return ParagonIE_Sodium_Crypto32::secretstream_xchacha20poly1305_init_push($key);
}
return ParagonIE_Sodium_Crypto::secretstream_xchacha20poly1305_init_push($key);
}
/**
* @param string $header
* @param string $key
* @return string Returns a state.
* @throws Exception
*/
public static function crypto_secretstream_xchacha20poly1305_init_pull($header, $key)
{
if (ParagonIE_Sodium_Core_Util::strlen($header) < self::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_HEADERBYTES) {
throw new SodiumException(
'header size should be SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_HEADERBYTES bytes'
);
}
if (PHP_INT_SIZE === 4) {
return ParagonIE_Sodium_Crypto32::secretstream_xchacha20poly1305_init_pull($key, $header);
}
return ParagonIE_Sodium_Crypto::secretstream_xchacha20poly1305_init_pull($key, $header);
}
/**
* @param string $state
* @param string $msg
* @param string $aad
* @param int $tag
* @return string
* @throws SodiumException
*/
public static function crypto_secretstream_xchacha20poly1305_push(&$state, $msg, $aad = '', $tag = 0)
{
if (PHP_INT_SIZE === 4) {
return ParagonIE_Sodium_Crypto32::secretstream_xchacha20poly1305_push(
$state,
$msg,
$aad,
$tag
);
}
return ParagonIE_Sodium_Crypto::secretstream_xchacha20poly1305_push(
$state,
$msg,
$aad,
$tag
);
}
/**
* @param string $state
* @param string $msg
* @param string $aad
* @return bool|array{0: string, 1: int}
* @throws SodiumException
*/
public static function crypto_secretstream_xchacha20poly1305_pull(&$state, $msg, $aad = '')
{
if (PHP_INT_SIZE === 4) {
return ParagonIE_Sodium_Crypto32::secretstream_xchacha20poly1305_pull(
$state,
$msg,
$aad
);
}
return ParagonIE_Sodium_Crypto::secretstream_xchacha20poly1305_pull(
$state,
$msg,
$aad
);
}
/**
* @return string
* @throws Exception
*/
public static function crypto_secretstream_xchacha20poly1305_keygen()
{
return random_bytes(self::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_KEYBYTES);
}
/**
* @param string $state
* @return void
* @throws SodiumException
*/
public static function crypto_secretstream_xchacha20poly1305_rekey(&$state)
{
if (PHP_INT_SIZE === 4) {
ParagonIE_Sodium_Crypto32::secretstream_xchacha20poly1305_rekey($state);
} else {
ParagonIE_Sodium_Crypto::secretstream_xchacha20poly1305_rekey($state);
}
}
/**
* Calculates a SipHash-2-4 hash of a message for a given key.
*
* @param string $message Input message
* @param string $key SipHash-2-4 key
* @return string Hash
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArgument
* @psalm-suppress MixedInferredReturnType
* @psalm-suppress MixedReturnStatement
*/
public static function crypto_shorthash($message, $key)
{
/* Type checks: */
ParagonIE_Sodium_Core_Util::declareScalarType($message, 'string', 1);
ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 2);
/* Input validation: */
if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_SHORTHASH_KEYBYTES) {
throw new SodiumException('Argument 2 must be CRYPTO_SHORTHASH_KEYBYTES long.');
}
if (self::useNewSodiumAPI()) {
return sodium_crypto_shorthash($message, $key);
}
if (self::use_fallback('crypto_shorthash')) {
return (string) call_user_func('\\Sodium\\crypto_shorthash', $message, $key);
}
if (PHP_INT_SIZE === 4) {
return ParagonIE_Sodium_Core32_SipHash::sipHash24($message, $key);
}
return ParagonIE_Sodium_Core_SipHash::sipHash24($message, $key);
}
/**
* Return a secure random key for use with crypto_shorthash
*
* @return string
* @throws Exception
* @throws Error
*/
public static function crypto_shorthash_keygen()
{
return random_bytes(self::CRYPTO_SHORTHASH_KEYBYTES);
}
/**
* Returns a signed message. You probably want crypto_sign_detached()
* instead, which only returns the signature.
*
* Algorithm: Ed25519 (EdDSA over Curve25519)
*
* @param string $message Message to be signed.
* @param string $secretKey Secret signing key.
* @return string Signed message (signature is prefixed).
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArgument
* @psalm-suppress MixedInferredReturnType
* @psalm-suppress MixedReturnStatement
*/
public static function crypto_sign($message, $secretKey)
{
/* Type checks: */
ParagonIE_Sodium_Core_Util::declareScalarType($message, 'string', 1);
ParagonIE_Sodium_Core_Util::declareScalarType($secretKey, 'string', 2);
/* Input validation: */
if (ParagonIE_Sodium_Core_Util::strlen($secretKey) !== self::CRYPTO_SIGN_SECRETKEYBYTES) {
throw new SodiumException('Argument 2 must be CRYPTO_SIGN_SECRETKEYBYTES long.');
}
if (self::useNewSodiumAPI()) {
return sodium_crypto_sign($message, $secretKey);
}
if (self::use_fallback('crypto_sign')) {
return (string) call_user_func('\\Sodium\\crypto_sign', $message, $secretKey);
}
if (PHP_INT_SIZE === 4) {
return ParagonIE_Sodium_Crypto32::sign($message, $secretKey);
}
return ParagonIE_Sodium_Crypto::sign($message, $secretKey);
}
/**
* Validates a signed message then returns the message.
*
* @param string $signedMessage A signed message
* @param string $publicKey A public key
* @return string The original message (if the signature is
* valid for this public key)
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArgument
* @psalm-suppress MixedInferredReturnType
* @psalm-suppress MixedReturnStatement
*/
public static function crypto_sign_open($signedMessage, $publicKey)
{
/* Type checks: */
ParagonIE_Sodium_Core_Util::declareScalarType($signedMessage, 'string', 1);
ParagonIE_Sodium_Core_Util::declareScalarType($publicKey, 'string', 2);
/* Input validation: */
if (ParagonIE_Sodium_Core_Util::strlen($signedMessage) < self::CRYPTO_SIGN_BYTES) {
throw new SodiumException('Argument 1 must be at least CRYPTO_SIGN_BYTES long.');
}
if (ParagonIE_Sodium_Core_Util::strlen($publicKey) !== self::CRYPTO_SIGN_PUBLICKEYBYTES) {
throw new SodiumException('Argument 2 must be CRYPTO_SIGN_PUBLICKEYBYTES long.');
}
if (self::useNewSodiumAPI()) {
/**
* @psalm-suppress InvalidReturnStatement
* @psalm-suppress FalsableReturnStatement
*/
return sodium_crypto_sign_open($signedMessage, $publicKey);
}
if (self::use_fallback('crypto_sign_open')) {
return call_user_func('\\Sodium\\crypto_sign_open', $signedMessage, $publicKey);
}
if (PHP_INT_SIZE === 4) {
return ParagonIE_Sodium_Crypto32::sign_open($signedMessage, $publicKey);
}
return ParagonIE_Sodium_Crypto::sign_open($signedMessage, $publicKey);
}
/**
* Generate a new random Ed25519 keypair.
*
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function crypto_sign_keypair()
{
if (self::useNewSodiumAPI()) {
return sodium_crypto_sign_keypair();
}
if (self::use_fallback('crypto_sign_keypair')) {
return (string) call_user_func('\\Sodium\\crypto_sign_keypair');
}
if (PHP_INT_SIZE === 4) {
return ParagonIE_Sodium_Core32_Ed25519::keypair();
}
return ParagonIE_Sodium_Core_Ed25519::keypair();
}
/**
* @param string $sk
* @param string $pk
* @return string
* @throws SodiumException
*/
public static function crypto_sign_keypair_from_secretkey_and_publickey($sk, $pk)
{
ParagonIE_Sodium_Core_Util::declareScalarType($sk, 'string', 1);
ParagonIE_Sodium_Core_Util::declareScalarType($pk, 'string', 1);
$sk = (string) $sk;
$pk = (string) $pk;
if (ParagonIE_Sodium_Core_Util::strlen($sk) !== self::CRYPTO_SIGN_SECRETKEYBYTES) {
throw new SodiumException('secretkey should be SODIUM_CRYPTO_SIGN_SECRETKEYBYTES bytes');
}
if (ParagonIE_Sodium_Core_Util::strlen($pk) !== self::CRYPTO_SIGN_PUBLICKEYBYTES) {
throw new SodiumException('publickey should be SODIUM_CRYPTO_SIGN_PUBLICKEYBYTES bytes');
}
if (self::useNewSodiumAPI()) {
return sodium_crypto_sign_keypair_from_secretkey_and_publickey($sk, $pk);
}
return $sk . $pk;
}
/**
* Generate an Ed25519 keypair from a seed.
*
* @param string $seed Input seed
* @return string Keypair
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArgument
*/
public static function crypto_sign_seed_keypair($seed)
{
ParagonIE_Sodium_Core_Util::declareScalarType($seed, 'string', 1);
if (self::useNewSodiumAPI()) {
return sodium_crypto_sign_seed_keypair($seed);
}
if (self::use_fallback('crypto_sign_keypair')) {
return (string) call_user_func('\\Sodium\\crypto_sign_seed_keypair', $seed);
}
$publicKey = '';
$secretKey = '';
if (PHP_INT_SIZE === 4) {
ParagonIE_Sodium_Core32_Ed25519::seed_keypair($publicKey, $secretKey, $seed);
} else {
ParagonIE_Sodium_Core_Ed25519::seed_keypair($publicKey, $secretKey, $seed);
}
return $secretKey . $publicKey;
}
/**
* Extract an Ed25519 public key from an Ed25519 keypair.
*
* @param string $keypair Keypair
* @return string Public key
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArgument
*/
public static function crypto_sign_publickey($keypair)
{
/* Type checks: */
ParagonIE_Sodium_Core_Util::declareScalarType($keypair, 'string', 1);
/* Input validation: */
if (ParagonIE_Sodium_Core_Util::strlen($keypair) !== self::CRYPTO_SIGN_KEYPAIRBYTES) {
throw new SodiumException('Argument 1 must be CRYPTO_SIGN_KEYPAIRBYTES long.');
}
if (self::useNewSodiumAPI()) {
return sodium_crypto_sign_publickey($keypair);
}
if (self::use_fallback('crypto_sign_publickey')) {
return (string) call_user_func('\\Sodium\\crypto_sign_publickey', $keypair);
}
if (PHP_INT_SIZE === 4) {
return ParagonIE_Sodium_Core32_Ed25519::publickey($keypair);
}
return ParagonIE_Sodium_Core_Ed25519::publickey($keypair);
}
/**
* Calculate an Ed25519 public key from an Ed25519 secret key.
*
* @param string $secretKey Your Ed25519 secret key
* @return string The corresponding Ed25519 public key
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArgument
*/
public static function crypto_sign_publickey_from_secretkey($secretKey)
{
/* Type checks: */
ParagonIE_Sodium_Core_Util::declareScalarType($secretKey, 'string', 1);
/* Input validation: */
if (ParagonIE_Sodium_Core_Util::strlen($secretKey) !== self::CRYPTO_SIGN_SECRETKEYBYTES) {
throw new SodiumException('Argument 1 must be CRYPTO_SIGN_SECRETKEYBYTES long.');
}
if (self::useNewSodiumAPI()) {
return sodium_crypto_sign_publickey_from_secretkey($secretKey);
}
if (self::use_fallback('crypto_sign_publickey_from_secretkey')) {
return (string) call_user_func('\\Sodium\\crypto_sign_publickey_from_secretkey', $secretKey);
}
if (PHP_INT_SIZE === 4) {
return ParagonIE_Sodium_Core32_Ed25519::publickey_from_secretkey($secretKey);
}
return ParagonIE_Sodium_Core_Ed25519::publickey_from_secretkey($secretKey);
}
/**
* Extract an Ed25519 secret key from an Ed25519 keypair.
*
* @param string $keypair Keypair
* @return string Secret key
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArgument
*/
public static function crypto_sign_secretkey($keypair)
{
/* Type checks: */
ParagonIE_Sodium_Core_Util::declareScalarType($keypair, 'string', 1);
/* Input validation: */
if (ParagonIE_Sodium_Core_Util::strlen($keypair) !== self::CRYPTO_SIGN_KEYPAIRBYTES) {
throw new SodiumException('Argument 1 must be CRYPTO_SIGN_KEYPAIRBYTES long.');
}
if (self::useNewSodiumAPI()) {
return sodium_crypto_sign_secretkey($keypair);
}
if (self::use_fallback('crypto_sign_secretkey')) {
return (string) call_user_func('\\Sodium\\crypto_sign_secretkey', $keypair);
}
if (PHP_INT_SIZE === 4) {
return ParagonIE_Sodium_Core32_Ed25519::secretkey($keypair);
}
return ParagonIE_Sodium_Core_Ed25519::secretkey($keypair);
}
/**
* Calculate the Ed25519 signature of a message and return ONLY the signature.
*
* Algorithm: Ed25519 (EdDSA over Curve25519)
*
* @param string $message Message to be signed
* @param string $secretKey Secret signing key
* @return string Digital signature
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArgument
*/
public static function crypto_sign_detached($message, $secretKey)
{
/* Type checks: */
ParagonIE_Sodium_Core_Util::declareScalarType($message, 'string', 1);
ParagonIE_Sodium_Core_Util::declareScalarType($secretKey, 'string', 2);
/* Input validation: */
if (ParagonIE_Sodium_Core_Util::strlen($secretKey) !== self::CRYPTO_SIGN_SECRETKEYBYTES) {
throw new SodiumException('Argument 2 must be CRYPTO_SIGN_SECRETKEYBYTES long.');
}
if (self::useNewSodiumAPI()) {
return sodium_crypto_sign_detached($message, $secretKey);
}
if (self::use_fallback('crypto_sign_detached')) {
return (string) call_user_func('\\Sodium\\crypto_sign_detached', $message, $secretKey);
}
if (PHP_INT_SIZE === 4) {
return ParagonIE_Sodium_Crypto32::sign_detached($message, $secretKey);
}
return ParagonIE_Sodium_Crypto::sign_detached($message, $secretKey);
}
/**
* Verify the Ed25519 signature of a message.
*
* @param string $signature Digital sginature
* @param string $message Message to be verified
* @param string $publicKey Public key
* @return bool TRUE if this signature is good for this public key;
* FALSE otherwise
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArgument
*/
public static function crypto_sign_verify_detached($signature, $message, $publicKey)
{
/* Type checks: */
ParagonIE_Sodium_Core_Util::declareScalarType($signature, 'string', 1);
ParagonIE_Sodium_Core_Util::declareScalarType($message, 'string', 2);
ParagonIE_Sodium_Core_Util::declareScalarType($publicKey, 'string', 3);
/* Input validation: */
if (ParagonIE_Sodium_Core_Util::strlen($signature) !== self::CRYPTO_SIGN_BYTES) {
throw new SodiumException('Argument 1 must be CRYPTO_SIGN_BYTES long.');
}
if (ParagonIE_Sodium_Core_Util::strlen($publicKey) !== self::CRYPTO_SIGN_PUBLICKEYBYTES) {
throw new SodiumException('Argument 3 must be CRYPTO_SIGN_PUBLICKEYBYTES long.');
}
if (self::useNewSodiumAPI()) {
return sodium_crypto_sign_verify_detached($signature, $message, $publicKey);
}
if (self::use_fallback('crypto_sign_verify_detached')) {
return (bool) call_user_func(
'\\Sodium\\crypto_sign_verify_detached',
$signature,
$message,
$publicKey
);
}
if (PHP_INT_SIZE === 4) {
return ParagonIE_Sodium_Crypto32::sign_verify_detached($signature, $message, $publicKey);
}
return ParagonIE_Sodium_Crypto::sign_verify_detached($signature, $message, $publicKey);
}
/**
* Convert an Ed25519 public key to a Curve25519 public key
*
* @param string $pk
* @return string
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArgument
*/
public static function crypto_sign_ed25519_pk_to_curve25519($pk)
{
/* Type checks: */
ParagonIE_Sodium_Core_Util::declareScalarType($pk, 'string', 1);
/* Input validation: */
if (ParagonIE_Sodium_Core_Util::strlen($pk) < self::CRYPTO_SIGN_PUBLICKEYBYTES) {
throw new SodiumException('Argument 1 must be at least CRYPTO_SIGN_PUBLICKEYBYTES long.');
}
if (self::useNewSodiumAPI()) {
if (is_callable('crypto_sign_ed25519_pk_to_curve25519')) {
return (string) sodium_crypto_sign_ed25519_pk_to_curve25519($pk);
}
}
if (self::use_fallback('crypto_sign_ed25519_pk_to_curve25519')) {
return (string) call_user_func('\\Sodium\\crypto_sign_ed25519_pk_to_curve25519', $pk);
}
if (PHP_INT_SIZE === 4) {
return ParagonIE_Sodium_Core32_Ed25519::pk_to_curve25519($pk);
}
return ParagonIE_Sodium_Core_Ed25519::pk_to_curve25519($pk);
}
/**
* Convert an Ed25519 secret key to a Curve25519 secret key
*
* @param string $sk
* @return string
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArgument
*/
public static function crypto_sign_ed25519_sk_to_curve25519($sk)
{
/* Type checks: */
ParagonIE_Sodium_Core_Util::declareScalarType($sk, 'string', 1);
/* Input validation: */
if (ParagonIE_Sodium_Core_Util::strlen($sk) < self::CRYPTO_SIGN_SEEDBYTES) {
throw new SodiumException('Argument 1 must be at least CRYPTO_SIGN_SEEDBYTES long.');
}
if (self::useNewSodiumAPI()) {
if (is_callable('crypto_sign_ed25519_sk_to_curve25519')) {
return sodium_crypto_sign_ed25519_sk_to_curve25519($sk);
}
}
if (self::use_fallback('crypto_sign_ed25519_sk_to_curve25519')) {
return (string) call_user_func('\\Sodium\\crypto_sign_ed25519_sk_to_curve25519', $sk);
}
$h = hash('sha512', ParagonIE_Sodium_Core_Util::substr($sk, 0, 32), true);
$h[0] = ParagonIE_Sodium_Core_Util::intToChr(
ParagonIE_Sodium_Core_Util::chrToInt($h[0]) & 248
);
$h[31] = ParagonIE_Sodium_Core_Util::intToChr(
(ParagonIE_Sodium_Core_Util::chrToInt($h[31]) & 127) | 64
);
return ParagonIE_Sodium_Core_Util::substr($h, 0, 32);
}
/**
* Expand a key and nonce into a keystream of pseudorandom bytes.
*
* @param int $len Number of bytes desired
* @param string $nonce Number to be used Once; must be 24 bytes
* @param string $key XSalsa20 key
* @return string Pseudorandom stream that can be XORed with messages
* to provide encryption (but not authentication; see
* Poly1305 or crypto_auth() for that, which is not
* optional for security)
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArgument
*/
public static function crypto_stream($len, $nonce, $key)
{
/* Type checks: */
ParagonIE_Sodium_Core_Util::declareScalarType($len, 'int', 1);
ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 2);
ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 3);
/* Input validation: */
if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_STREAM_NONCEBYTES) {
throw new SodiumException('Argument 2 must be CRYPTO_SECRETBOX_NONCEBYTES long.');
}
if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_STREAM_KEYBYTES) {
throw new SodiumException('Argument 3 must be CRYPTO_STREAM_KEYBYTES long.');
}
if (self::useNewSodiumAPI()) {
return sodium_crypto_stream($len, $nonce, $key);
}
if (self::use_fallback('crypto_stream')) {
return (string) call_user_func('\\Sodium\\crypto_stream', $len, $nonce, $key);
}
if (PHP_INT_SIZE === 4) {
return ParagonIE_Sodium_Core32_XSalsa20::xsalsa20($len, $nonce, $key);
}
return ParagonIE_Sodium_Core_XSalsa20::xsalsa20($len, $nonce, $key);
}
/**
* DANGER! UNAUTHENTICATED ENCRYPTION!
*
* Unless you are following expert advice, do not use this feature.
*
* Algorithm: XSalsa20
*
* This DOES NOT provide ciphertext integrity.
*
* @param string $message Plaintext message
* @param string $nonce Number to be used Once; must be 24 bytes
* @param string $key Encryption key
* @return string Encrypted text which is vulnerable to chosen-
* ciphertext attacks unless you implement some
* other mitigation to the ciphertext (i.e.
* Encrypt then MAC)
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArgument
*/
public static function crypto_stream_xor($message, $nonce, $key)
{
/* Type checks: */
ParagonIE_Sodium_Core_Util::declareScalarType($message, 'string', 1);
ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 2);
ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 3);
/* Input validation: */
if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_STREAM_NONCEBYTES) {
throw new SodiumException('Argument 2 must be CRYPTO_SECRETBOX_NONCEBYTES long.');
}
if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_STREAM_KEYBYTES) {
throw new SodiumException('Argument 3 must be CRYPTO_SECRETBOX_KEYBYTES long.');
}
if (self::useNewSodiumAPI()) {
return sodium_crypto_stream_xor($message, $nonce, $key);
}
if (self::use_fallback('crypto_stream_xor')) {
return (string) call_user_func('\\Sodium\\crypto_stream_xor', $message, $nonce, $key);
}
if (PHP_INT_SIZE === 4) {
return ParagonIE_Sodium_Core32_XSalsa20::xsalsa20_xor($message, $nonce, $key);
}
return ParagonIE_Sodium_Core_XSalsa20::xsalsa20_xor($message, $nonce, $key);
}
/**
* Return a secure random key for use with crypto_stream
*
* @return string
* @throws Exception
* @throws Error
*/
public static function crypto_stream_keygen()
{
return random_bytes(self::CRYPTO_STREAM_KEYBYTES);
}
/**
* Expand a key and nonce into a keystream of pseudorandom bytes.
*
* @param int $len Number of bytes desired
* @param string $nonce Number to be used Once; must be 24 bytes
* @param string $key XChaCha20 key
* @param bool $dontFallback
* @return string Pseudorandom stream that can be XORed with messages
* to provide encryption (but not authentication; see
* Poly1305 or crypto_auth() for that, which is not
* optional for security)
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArgument
*/
public static function crypto_stream_xchacha20($len, $nonce, $key, $dontFallback = false)
{
/* Type checks: */
ParagonIE_Sodium_Core_Util::declareScalarType($len, 'int', 1);
ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 2);
ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 3);
/* Input validation: */
if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_STREAM_XCHACHA20_NONCEBYTES) {
throw new SodiumException('Argument 2 must be CRYPTO_SECRETBOX_XCHACHA20_NONCEBYTES long.');
}
if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_STREAM_XCHACHA20_KEYBYTES) {
throw new SodiumException('Argument 3 must be CRYPTO_STREAM_XCHACHA20_KEYBYTES long.');
}
if (self::useNewSodiumAPI() && !$dontFallback) {
return sodium_crypto_stream_xchacha20($len, $nonce, $key);
}
if (PHP_INT_SIZE === 4) {
return ParagonIE_Sodium_Core32_XChaCha20::stream($len, $nonce, $key);
}
return ParagonIE_Sodium_Core_XChaCha20::stream($len, $nonce, $key);
}
/**
* DANGER! UNAUTHENTICATED ENCRYPTION!
*
* Unless you are following expert advice, do not use this feature.
*
* Algorithm: XChaCha20
*
* This DOES NOT provide ciphertext integrity.
*
* @param string $message Plaintext message
* @param string $nonce Number to be used Once; must be 24 bytes
* @param string $key Encryption key
* @return string Encrypted text which is vulnerable to chosen-
* ciphertext attacks unless you implement some
* other mitigation to the ciphertext (i.e.
* Encrypt then MAC)
* @param bool $dontFallback
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArgument
*/
public static function crypto_stream_xchacha20_xor($message, $nonce, $key, $dontFallback = false)
{
/* Type checks: */
ParagonIE_Sodium_Core_Util::declareScalarType($message, 'string', 1);
ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 2);
ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 3);
/* Input validation: */
if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_STREAM_XCHACHA20_NONCEBYTES) {
throw new SodiumException('Argument 2 must be CRYPTO_SECRETBOX_XCHACHA20_NONCEBYTES long.');
}
if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_STREAM_XCHACHA20_KEYBYTES) {
throw new SodiumException('Argument 3 must be CRYPTO_SECRETBOX_XCHACHA20_KEYBYTES long.');
}
if (self::useNewSodiumAPI() && !$dontFallback) {
return sodium_crypto_stream_xchacha20_xor($message, $nonce, $key);
}
if (PHP_INT_SIZE === 4) {
return ParagonIE_Sodium_Core32_XChaCha20::streamXorIc($message, $nonce, $key);
}
return ParagonIE_Sodium_Core_XChaCha20::streamXorIc($message, $nonce, $key);
}
/**
* DANGER! UNAUTHENTICATED ENCRYPTION!
*
* Unless you are following expert advice, do not use this feature.
*
* Algorithm: XChaCha20
*
* This DOES NOT provide ciphertext integrity.
*
* @param string $message Plaintext message
* @param string $nonce Number to be used Once; must be 24 bytes
* @param int $counter
* @param string $key Encryption key
* @return string Encrypted text which is vulnerable to chosen-
* ciphertext attacks unless you implement some
* other mitigation to the ciphertext (i.e.
* Encrypt then MAC)
* @param bool $dontFallback
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArgument
*/
public static function crypto_stream_xchacha20_xor_ic($message, $nonce, $counter, $key, $dontFallback = false)
{
/* Type checks: */
ParagonIE_Sodium_Core_Util::declareScalarType($message, 'string', 1);
ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 2);
ParagonIE_Sodium_Core_Util::declareScalarType($counter, 'int', 3);
ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 4);
/* Input validation: */
if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_STREAM_XCHACHA20_NONCEBYTES) {
throw new SodiumException('Argument 2 must be CRYPTO_SECRETBOX_XCHACHA20_NONCEBYTES long.');
}
if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_STREAM_XCHACHA20_KEYBYTES) {
throw new SodiumException('Argument 3 must be CRYPTO_SECRETBOX_XCHACHA20_KEYBYTES long.');
}
if (is_callable('sodium_crypto_stream_xchacha20_xor_ic') && !$dontFallback) {
return sodium_crypto_stream_xchacha20_xor_ic($message, $nonce, $counter, $key);
}
$ic = ParagonIE_Sodium_Core_Util::store64_le($counter);
if (PHP_INT_SIZE === 4) {
return ParagonIE_Sodium_Core32_XChaCha20::streamXorIc($message, $nonce, $key, $ic);
}
return ParagonIE_Sodium_Core_XChaCha20::streamXorIc($message, $nonce, $key, $ic);
}
/**
* Return a secure random key for use with crypto_stream_xchacha20
*
* @return string
* @throws Exception
* @throws Error
*/
public static function crypto_stream_xchacha20_keygen()
{
return random_bytes(self::CRYPTO_STREAM_XCHACHA20_KEYBYTES);
}
/**
* Cache-timing-safe implementation of hex2bin().
*
* @param string $string Hexadecimal string
* @param string $ignore List of characters to ignore; useful for whitespace
* @return string Raw binary string
* @throws SodiumException
* @throws TypeError
* @psalm-suppress TooFewArguments
* @psalm-suppress MixedArgument
*/
public static function hex2bin($string, $ignore = '')
{
/* Type checks: */
ParagonIE_Sodium_Core_Util::declareScalarType($string, 'string', 1);
ParagonIE_Sodium_Core_Util::declareScalarType($ignore, 'string', 2);
if (self::useNewSodiumAPI()) {
if (is_callable('sodium_hex2bin')) {
return (string) sodium_hex2bin($string, $ignore);
}
}
if (self::use_fallback('hex2bin')) {
return (string) call_user_func('\\Sodium\\hex2bin', $string, $ignore);
}
return ParagonIE_Sodium_Core_Util::hex2bin($string, $ignore);
}
/**
* Increase a string (little endian)
*
* @param string $var
*
* @return void
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArgument
*/
public static function increment(&$var)
{
/* Type checks: */
ParagonIE_Sodium_Core_Util::declareScalarType($var, 'string', 1);
if (self::useNewSodiumAPI()) {
sodium_increment($var);
return;
}
if (self::use_fallback('increment')) {
$func = '\\Sodium\\increment';
$func($var);
return;
}
$len = ParagonIE_Sodium_Core_Util::strlen($var);
$c = 1;
$copy = '';
for ($i = 0; $i < $len; ++$i) {
$c += ParagonIE_Sodium_Core_Util::chrToInt(
ParagonIE_Sodium_Core_Util::substr($var, $i, 1)
);
$copy .= ParagonIE_Sodium_Core_Util::intToChr($c);
$c >>= 8;
}
$var = $copy;
}
/**
* @param string $str
* @return bool
*
* @throws SodiumException
*/
public static function is_zero($str)
{
$d = 0;
for ($i = 0; $i < 32; ++$i) {
$d |= ParagonIE_Sodium_Core_Util::chrToInt($str[$i]);
}
return ((($d - 1) >> 31) & 1) === 1;
}
/**
* The equivalent to the libsodium minor version we aim to be compatible
* with (sans pwhash and memzero).
*
* @return int
*/
public static function library_version_major()
{
if (self::useNewSodiumAPI() && defined('SODIUM_LIBRARY_MAJOR_VERSION')) {
return SODIUM_LIBRARY_MAJOR_VERSION;
}
if (self::use_fallback('library_version_major')) {
/** @psalm-suppress UndefinedFunction */
return (int) call_user_func('\\Sodium\\library_version_major');
}
return self::LIBRARY_VERSION_MAJOR;
}
/**
* The equivalent to the libsodium minor version we aim to be compatible
* with (sans pwhash and memzero).
*
* @return int
*/
public static function library_version_minor()
{
if (self::useNewSodiumAPI() && defined('SODIUM_LIBRARY_MINOR_VERSION')) {
return SODIUM_LIBRARY_MINOR_VERSION;
}
if (self::use_fallback('library_version_minor')) {
/** @psalm-suppress UndefinedFunction */
return (int) call_user_func('\\Sodium\\library_version_minor');
}
return self::LIBRARY_VERSION_MINOR;
}
/**
* Compare two strings.
*
* @param string $left
* @param string $right
* @return int
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArgument
*/
public static function memcmp($left, $right)
{
/* Type checks: */
ParagonIE_Sodium_Core_Util::declareScalarType($left, 'string', 1);
ParagonIE_Sodium_Core_Util::declareScalarType($right, 'string', 2);
if (self::useNewSodiumAPI()) {
return sodium_memcmp($left, $right);
}
if (self::use_fallback('memcmp')) {
return (int) call_user_func('\\Sodium\\memcmp', $left, $right);
}
/** @var string $left */
/** @var string $right */
return ParagonIE_Sodium_Core_Util::memcmp($left, $right);
}
/**
* It's actually not possible to zero memory buffers in PHP. You need the
* native library for that.
*
* @param string|null $var
* @param-out string|null $var
*
* @return void
* @throws SodiumException (Unless libsodium is installed)
* @throws TypeError
* @psalm-suppress TooFewArguments
*/
public static function memzero(&$var)
{
/* Type checks: */
ParagonIE_Sodium_Core_Util::declareScalarType($var, 'string', 1);
if (self::useNewSodiumAPI()) {
/** @psalm-suppress MixedArgument */
sodium_memzero($var);
return;
}
if (self::use_fallback('memzero')) {
$func = '\\Sodium\\memzero';
$func($var);
if ($var === null) {
return;
}
}
// This is the best we can do.
throw new SodiumException(
'This is not implemented in sodium_compat, as it is not possible to securely wipe memory from PHP. ' .
'To fix this error, make sure libsodium is installed and the PHP extension is enabled.'
);
}
/**
* @param string $unpadded
* @param int $blockSize
* @param bool $dontFallback
* @return string
* @throws SodiumException
*/
public static function pad($unpadded, $blockSize, $dontFallback = false)
{
/* Type checks: */
ParagonIE_Sodium_Core_Util::declareScalarType($unpadded, 'string', 1);
ParagonIE_Sodium_Core_Util::declareScalarType($blockSize, 'int', 2);
$unpadded = (string) $unpadded;
$blockSize = (int) $blockSize;
if (self::useNewSodiumAPI() && !$dontFallback) {
return (string) sodium_pad($unpadded, $blockSize);
}
if ($blockSize <= 0) {
throw new SodiumException(
'block size cannot be less than 1'
);
}
$unpadded_len = ParagonIE_Sodium_Core_Util::strlen($unpadded);
$xpadlen = ($blockSize - 1);
if (($blockSize & ($blockSize - 1)) === 0) {
$xpadlen -= $unpadded_len & ($blockSize - 1);
} else {
$xpadlen -= $unpadded_len % $blockSize;
}
$xpadded_len = $unpadded_len + $xpadlen;
$padded = str_repeat("\0", $xpadded_len - 1);
if ($unpadded_len > 0) {
$st = 1;
$i = 0;
$k = $unpadded_len;
for ($j = 0; $j <= $xpadded_len; ++$j) {
$i = (int) $i;
$k = (int) $k;
$st = (int) $st;
if ($j >= $unpadded_len) {
$padded[$j] = "\0";
} else {
$padded[$j] = $unpadded[$j];
}
/** @var int $k */
$k -= $st;
$st = (int) (~(
(
(
($k >> 48)
|
($k >> 32)
|
($k >> 16)
|
$k
) - 1
) >> 16
)
) & 1;
$i += $st;
}
}
$mask = 0;
$tail = $xpadded_len;
for ($i = 0; $i < $blockSize; ++$i) {
# barrier_mask = (unsigned char)
# (((i ^ xpadlen) - 1U) >> ((sizeof(size_t) - 1U) * CHAR_BIT));
$barrier_mask = (($i ^ $xpadlen) -1) >> ((PHP_INT_SIZE << 3) - 1);
# tail[-i] = (tail[-i] & mask) | (0x80 & barrier_mask);
$padded[$tail - $i] = ParagonIE_Sodium_Core_Util::intToChr(
(ParagonIE_Sodium_Core_Util::chrToInt($padded[$tail - $i]) & $mask)
|
(0x80 & $barrier_mask)
);
# mask |= barrier_mask;
$mask |= $barrier_mask;
}
return $padded;
}
/**
* @param string $padded
* @param int $blockSize
* @param bool $dontFallback
* @return string
* @throws SodiumException
*/
public static function unpad($padded, $blockSize, $dontFallback = false)
{
/* Type checks: */
ParagonIE_Sodium_Core_Util::declareScalarType($padded, 'string', 1);
ParagonIE_Sodium_Core_Util::declareScalarType($blockSize, 'int', 2);
$padded = (string) $padded;
$blockSize = (int) $blockSize;
if (self::useNewSodiumAPI() && !$dontFallback) {
return (string) sodium_unpad($padded, $blockSize);
}
if ($blockSize <= 0) {
throw new SodiumException('block size cannot be less than 1');
}
$padded_len = ParagonIE_Sodium_Core_Util::strlen($padded);
if ($padded_len < $blockSize) {
throw new SodiumException('invalid padding');
}
# tail = &padded[padded_len - 1U];
$tail = $padded_len - 1;
$acc = 0;
$valid = 0;
$pad_len = 0;
$found = 0;
for ($i = 0; $i < $blockSize; ++$i) {
# c = tail[-i];
$c = ParagonIE_Sodium_Core_Util::chrToInt($padded[$tail - $i]);
# is_barrier =
# (( (acc - 1U) & (pad_len - 1U) & ((c ^ 0x80) - 1U) ) >> 8) & 1U;
$is_barrier = (
(
($acc - 1) & ($pad_len - 1) & (($c ^ 80) - 1)
) >> 7
) & 1;
$is_barrier &= ~$found;
$found |= $is_barrier;
# acc |= c;
$acc |= $c;
# pad_len |= i & (1U + ~is_barrier);
$pad_len |= $i & (1 + ~$is_barrier);
# valid |= (unsigned char) is_barrier;
$valid |= ($is_barrier & 0xff);
}
# unpadded_len = padded_len - 1U - pad_len;
$unpadded_len = $padded_len - 1 - $pad_len;
if ($valid !== 1) {
throw new SodiumException('invalid padding');
}
return ParagonIE_Sodium_Core_Util::substr($padded, 0, $unpadded_len);
}
/**
* Will sodium_compat run fast on the current hardware and PHP configuration?
*
* @return bool
*/
public static function polyfill_is_fast()
{
if (extension_loaded('sodium')) {
return true;
}
if (extension_loaded('libsodium')) {
return true;
}
return PHP_INT_SIZE === 8;
}
/**
* Generate a string of bytes from the kernel's CSPRNG.
* Proudly uses /dev/urandom (if getrandom(2) is not available).
*
* @param int $numBytes
* @return string
* @throws Exception
* @throws TypeError
*/
public static function randombytes_buf($numBytes)
{
/* Type checks: */
if (!is_int($numBytes)) {
if (is_numeric($numBytes)) {
$numBytes = (int) $numBytes;
} else {
throw new TypeError(
'Argument 1 must be an integer, ' . gettype($numBytes) . ' given.'
);
}
}
/** @var positive-int $numBytes */
if (self::use_fallback('randombytes_buf')) {
return (string) call_user_func('\\Sodium\\randombytes_buf', $numBytes);
}
if ($numBytes < 0) {
throw new SodiumException("Number of bytes must be a positive integer");
}
return random_bytes($numBytes);
}
/**
* Generate an integer between 0 and $range (non-inclusive).
*
* @param int $range
* @return int
* @throws Exception
* @throws Error
* @throws TypeError
*/
public static function randombytes_uniform($range)
{
/* Type checks: */
if (!is_int($range)) {
if (is_numeric($range)) {
$range = (int) $range;
} else {
throw new TypeError(
'Argument 1 must be an integer, ' . gettype($range) . ' given.'
);
}
}
if (self::use_fallback('randombytes_uniform')) {
return (int) call_user_func('\\Sodium\\randombytes_uniform', $range);
}
return random_int(0, $range - 1);
}
/**
* Generate a random 16-bit integer.
*
* @return int
* @throws Exception
* @throws Error
* @throws TypeError
*/
public static function randombytes_random16()
{
if (self::use_fallback('randombytes_random16')) {
return (int) call_user_func('\\Sodium\\randombytes_random16');
}
return random_int(0, 65535);
}
/**
* @param string $p
* @param bool $dontFallback
* @return bool
* @throws SodiumException
*/
public static function ristretto255_is_valid_point($p, $dontFallback = false)
{
if (self::useNewSodiumAPI() && !$dontFallback) {
return sodium_crypto_core_ristretto255_is_valid_point($p);
}
try {
$r = ParagonIE_Sodium_Core_Ristretto255::ristretto255_frombytes($p);
return $r['res'] === 0 &&
ParagonIE_Sodium_Core_Ristretto255::ristretto255_point_is_canonical($p) === 1;
} catch (SodiumException $ex) {
if ($ex->getMessage() === 'S is not canonical') {
return false;
}
throw $ex;
}
}
/**
* @param string $p
* @param string $q
* @param bool $dontFallback
* @return string
* @throws SodiumException
*/
public static function ristretto255_add($p, $q, $dontFallback = false)
{
if (self::useNewSodiumAPI() && !$dontFallback) {
return sodium_crypto_core_ristretto255_add($p, $q);
}
return ParagonIE_Sodium_Core_Ristretto255::ristretto255_add($p, $q);
}
/**
* @param string $p
* @param string $q
* @param bool $dontFallback
* @return string
* @throws SodiumException
*/
public static function ristretto255_sub($p, $q, $dontFallback = false)
{
if (self::useNewSodiumAPI() && !$dontFallback) {
return sodium_crypto_core_ristretto255_sub($p, $q);
}
return ParagonIE_Sodium_Core_Ristretto255::ristretto255_sub($p, $q);
}
/**
* @param string $r
* @param bool $dontFallback
* @return string
*
* @throws SodiumException
*/
public static function ristretto255_from_hash($r, $dontFallback = false)
{
if (self::useNewSodiumAPI() && !$dontFallback) {
return sodium_crypto_core_ristretto255_from_hash($r);
}
return ParagonIE_Sodium_Core_Ristretto255::ristretto255_from_hash($r);
}
/**
* @param bool $dontFallback
* @return string
*
* @throws SodiumException
*/
public static function ristretto255_random($dontFallback = false)
{
if (self::useNewSodiumAPI() && !$dontFallback) {
return sodium_crypto_core_ristretto255_random();
}
return ParagonIE_Sodium_Core_Ristretto255::ristretto255_random();
}
/**
* @param bool $dontFallback
* @return string
*
* @throws SodiumException
*/
public static function ristretto255_scalar_random($dontFallback = false)
{
if (self::useNewSodiumAPI() && !$dontFallback) {
return sodium_crypto_core_ristretto255_scalar_random();
}
return ParagonIE_Sodium_Core_Ristretto255::ristretto255_scalar_random();
}
/**
* @param string $s
* @param bool $dontFallback
* @return string
* @throws SodiumException
*/
public static function ristretto255_scalar_invert($s, $dontFallback = false)
{
if (self::useNewSodiumAPI() && !$dontFallback) {
return sodium_crypto_core_ristretto255_scalar_invert($s);
}
return ParagonIE_Sodium_Core_Ristretto255::ristretto255_scalar_invert($s);
}
/**
* @param string $s
* @param bool $dontFallback
* @return string
* @throws SodiumException
*/
public static function ristretto255_scalar_negate($s, $dontFallback = false)
{
if (self::useNewSodiumAPI() && !$dontFallback) {
return sodium_crypto_core_ristretto255_scalar_negate($s);
}
return ParagonIE_Sodium_Core_Ristretto255::ristretto255_scalar_negate($s);
}
/**
* @param string $s
* @param bool $dontFallback
* @return string
* @throws SodiumException
*/
public static function ristretto255_scalar_complement($s, $dontFallback = false)
{
if (self::useNewSodiumAPI() && !$dontFallback) {
return sodium_crypto_core_ristretto255_scalar_complement($s);
}
return ParagonIE_Sodium_Core_Ristretto255::ristretto255_scalar_complement($s);
}
/**
* @param string $x
* @param string $y
* @param bool $dontFallback
* @return string
* @throws SodiumException
*/
public static function ristretto255_scalar_add($x, $y, $dontFallback = false)
{
if (self::useNewSodiumAPI() && !$dontFallback) {
return sodium_crypto_core_ristretto255_scalar_add($x, $y);
}
return ParagonIE_Sodium_Core_Ristretto255::ristretto255_scalar_add($x, $y);
}
/**
* @param string $x
* @param string $y
* @param bool $dontFallback
* @return string
* @throws SodiumException
*/
public static function ristretto255_scalar_sub($x, $y, $dontFallback = false)
{
if (self::useNewSodiumAPI() && !$dontFallback) {
return sodium_crypto_core_ristretto255_scalar_sub($x, $y);
}
return ParagonIE_Sodium_Core_Ristretto255::ristretto255_scalar_sub($x, $y);
}
/**
* @param string $x
* @param string $y
* @param bool $dontFallback
* @return string
* @throws SodiumException
*/
public static function ristretto255_scalar_mul($x, $y, $dontFallback = false)
{
if (self::useNewSodiumAPI() && !$dontFallback) {
return sodium_crypto_core_ristretto255_scalar_mul($x, $y);
}
return ParagonIE_Sodium_Core_Ristretto255::ristretto255_scalar_mul($x, $y);
}
/**
* @param string $n
* @param string $p
* @param bool $dontFallback
* @return string
* @throws SodiumException
*/
public static function scalarmult_ristretto255($n, $p, $dontFallback = false)
{
if (self::useNewSodiumAPI() && !$dontFallback) {
return sodium_crypto_scalarmult_ristretto255($n, $p);
}
return ParagonIE_Sodium_Core_Ristretto255::scalarmult_ristretto255($n, $p);
}
/**
* @param string $n
* @param string $p
* @param bool $dontFallback
* @return string
* @throws SodiumException
*/
public static function scalarmult_ristretto255_base($n, $dontFallback = false)
{
if (self::useNewSodiumAPI() && !$dontFallback) {
return sodium_crypto_scalarmult_ristretto255_base($n);
}
return ParagonIE_Sodium_Core_Ristretto255::scalarmult_ristretto255_base($n);
}
/**
* @param string $s
* @param bool $dontFallback
* @return string
* @throws SodiumException
*/
public static function ristretto255_scalar_reduce($s, $dontFallback = false)
{
if (self::useNewSodiumAPI() && !$dontFallback) {
return sodium_crypto_core_ristretto255_scalar_reduce($s);
}
return ParagonIE_Sodium_Core_Ristretto255::sc_reduce($s);
}
/**
* Runtime testing method for 32-bit platforms.
*
* Usage: If runtime_speed_test() returns FALSE, then our 32-bit
* implementation is to slow to use safely without risking timeouts.
* If this happens, install sodium from PECL to get acceptable
* performance.
*
* @param int $iterations Number of multiplications to attempt
* @param int $maxTimeout Milliseconds
* @return bool TRUE if we're fast enough, FALSE is not
* @throws SodiumException
*/
public static function runtime_speed_test($iterations, $maxTimeout)
{
if (self::polyfill_is_fast()) {
return true;
}
/** @var float $end */
$end = 0.0;
/** @var float $start */
$start = microtime(true);
/** @var ParagonIE_Sodium_Core32_Int64 $a */
$a = ParagonIE_Sodium_Core32_Int64::fromInt(random_int(3, 1 << 16));
for ($i = 0; $i < $iterations; ++$i) {
/** @var ParagonIE_Sodium_Core32_Int64 $b */
$b = ParagonIE_Sodium_Core32_Int64::fromInt(random_int(3, 1 << 16));
$a->mulInt64($b);
}
/** @var float $end */
$end = microtime(true);
/** @var int $diff */
$diff = (int) ceil(($end - $start) * 1000);
return $diff < $maxTimeout;
}
/**
* Add two numbers (little-endian unsigned), storing the value in the first
* parameter.
*
* This mutates $val.
*
* @param string $val
* @param string $addv
* @return void
* @throws SodiumException
*/
public static function sub(&$val, $addv)
{
$val_len = ParagonIE_Sodium_Core_Util::strlen($val);
$addv_len = ParagonIE_Sodium_Core_Util::strlen($addv);
if ($val_len !== $addv_len) {
throw new SodiumException('values must have the same length');
}
$A = ParagonIE_Sodium_Core_Util::stringToIntArray($val);
$B = ParagonIE_Sodium_Core_Util::stringToIntArray($addv);
$c = 0;
for ($i = 0; $i < $val_len; $i++) {
$c = ($A[$i] - $B[$i] - $c);
$A[$i] = ($c & 0xff);
$c = ($c >> 8) & 1;
}
$val = ParagonIE_Sodium_Core_Util::intArrayToString($A);
}
/**
* This emulates libsodium's version_string() function, except ours is
* prefixed with 'polyfill-'.
*
* @return string
* @psalm-suppress MixedInferredReturnType
* @psalm-suppress UndefinedFunction
*/
public static function version_string()
{
if (self::useNewSodiumAPI()) {
return (string) sodium_version_string();
}
if (self::use_fallback('version_string')) {
return (string) call_user_func('\\Sodium\\version_string');
}
return (string) self::VERSION_STRING;
}
/**
* Should we use the libsodium core function instead?
* This is always a good idea, if it's available. (Unless we're in the
* middle of running our unit test suite.)
*
* If ext/libsodium is available, use it. Return TRUE.
* Otherwise, we have to use the code provided herein. Return FALSE.
*
* @param string $sodium_func_name
*
* @return bool
*/
protected static function use_fallback($sodium_func_name = '')
{
static $res = null;
if ($res === null) {
$res = extension_loaded('libsodium') && PHP_VERSION_ID >= 50300;
}
if ($res === false) {
// No libsodium installed
return false;
}
if (self::$disableFallbackForUnitTests) {
// Don't fallback. Use the PHP implementation.
return false;
}
if (!empty($sodium_func_name)) {
return is_callable('\\Sodium\\' . $sodium_func_name);
}
return true;
}
/**
* Libsodium as implemented in PHP 7.2
* and/or ext/sodium (via PECL)
*
* @ref https://wiki.php.net/rfc/libsodium
* @return bool
*/
protected static function useNewSodiumAPI()
{
static $res = null;
if ($res === null) {
$res = PHP_VERSION_ID >= 70000 && extension_loaded('sodium');
}
if (self::$disableFallbackForUnitTests) {
// Don't fallback. Use the PHP implementation.
return false;
}
return (bool) $res;
}
}
Core32/Int64.php 0000644 00000074704 15133021213 0007221 0 ustar 00 <?php
/**
* Class ParagonIE_Sodium_Core32_Int64
*
* Encapsulates a 64-bit integer.
*
* These are immutable. It always returns a new instance.
*/
class ParagonIE_Sodium_Core32_Int64
{
/**
* @var array<int, int> - four 16-bit integers
*/
public $limbs = array(0, 0, 0, 0);
/**
* @var int
*/
public $overflow = 0;
/**
* @var bool
*/
public $unsignedInt = false;
/**
* ParagonIE_Sodium_Core32_Int64 constructor.
* @param array $array
* @param bool $unsignedInt
*/
public function __construct($array = array(0, 0, 0, 0), $unsignedInt = false)
{
$this->limbs = array(
(int) $array[0],
(int) $array[1],
(int) $array[2],
(int) $array[3]
);
$this->overflow = 0;
$this->unsignedInt = $unsignedInt;
}
/**
* Adds two int64 objects
*
* @param ParagonIE_Sodium_Core32_Int64 $addend
* @return ParagonIE_Sodium_Core32_Int64
*/
public function addInt64(ParagonIE_Sodium_Core32_Int64 $addend)
{
$i0 = $this->limbs[0];
$i1 = $this->limbs[1];
$i2 = $this->limbs[2];
$i3 = $this->limbs[3];
$j0 = $addend->limbs[0];
$j1 = $addend->limbs[1];
$j2 = $addend->limbs[2];
$j3 = $addend->limbs[3];
$r3 = $i3 + ($j3 & 0xffff);
$carry = $r3 >> 16;
$r2 = $i2 + ($j2 & 0xffff) + $carry;
$carry = $r2 >> 16;
$r1 = $i1 + ($j1 & 0xffff) + $carry;
$carry = $r1 >> 16;
$r0 = $i0 + ($j0 & 0xffff) + $carry;
$carry = $r0 >> 16;
$r0 &= 0xffff;
$r1 &= 0xffff;
$r2 &= 0xffff;
$r3 &= 0xffff;
$return = new ParagonIE_Sodium_Core32_Int64(
array($r0, $r1, $r2, $r3)
);
$return->overflow = $carry;
$return->unsignedInt = $this->unsignedInt;
return $return;
}
/**
* Adds a normal integer to an int64 object
*
* @param int $int
* @return ParagonIE_Sodium_Core32_Int64
* @throws SodiumException
* @throws TypeError
*/
public function addInt($int)
{
ParagonIE_Sodium_Core32_Util::declareScalarType($int, 'int', 1);
/** @var int $int */
$int = (int) $int;
$i0 = $this->limbs[0];
$i1 = $this->limbs[1];
$i2 = $this->limbs[2];
$i3 = $this->limbs[3];
$r3 = $i3 + ($int & 0xffff);
$carry = $r3 >> 16;
$r2 = $i2 + (($int >> 16) & 0xffff) + $carry;
$carry = $r2 >> 16;
$r1 = $i1 + $carry;
$carry = $r1 >> 16;
$r0 = $i0 + $carry;
$carry = $r0 >> 16;
$r0 &= 0xffff;
$r1 &= 0xffff;
$r2 &= 0xffff;
$r3 &= 0xffff;
$return = new ParagonIE_Sodium_Core32_Int64(
array($r0, $r1, $r2, $r3)
);
$return->overflow = $carry;
$return->unsignedInt = $this->unsignedInt;
return $return;
}
/**
* @param int $b
* @return int
*/
public function compareInt($b = 0)
{
$gt = 0;
$eq = 1;
$i = 4;
$j = 0;
while ($i > 0) {
--$i;
/** @var int $x1 */
$x1 = $this->limbs[$i];
/** @var int $x2 */
$x2 = ($b >> ($j << 4)) & 0xffff;
/** int */
$gt |= (($x2 - $x1) >> 8) & $eq;
/** int */
$eq &= (($x2 ^ $x1) - 1) >> 8;
}
return ($gt + $gt - $eq) + 1;
}
/**
* @param int $b
* @return bool
*/
public function isGreaterThan($b = 0)
{
return $this->compareInt($b) > 0;
}
/**
* @param int $b
* @return bool
*/
public function isLessThanInt($b = 0)
{
return $this->compareInt($b) < 0;
}
/**
* @param int $hi
* @param int $lo
* @return ParagonIE_Sodium_Core32_Int64
*/
public function mask64($hi = 0, $lo = 0)
{
/** @var int $a */
$a = ($hi >> 16) & 0xffff;
/** @var int $b */
$b = ($hi) & 0xffff;
/** @var int $c */
$c = ($lo >> 16) & 0xffff;
/** @var int $d */
$d = ($lo & 0xffff);
return new ParagonIE_Sodium_Core32_Int64(
array(
$this->limbs[0] & $a,
$this->limbs[1] & $b,
$this->limbs[2] & $c,
$this->limbs[3] & $d
),
$this->unsignedInt
);
}
/**
* @param int $int
* @param int $size
* @return ParagonIE_Sodium_Core32_Int64
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedAssignment
*/
public function mulInt($int = 0, $size = 0)
{
if (ParagonIE_Sodium_Compat::$fastMult) {
return $this->mulIntFast($int);
}
ParagonIE_Sodium_Core32_Util::declareScalarType($int, 'int', 1);
ParagonIE_Sodium_Core32_Util::declareScalarType($size, 'int', 2);
/** @var int $int */
$int = (int) $int;
/** @var int $size */
$size = (int) $size;
if (!$size) {
$size = 63;
}
$a = clone $this;
$return = new ParagonIE_Sodium_Core32_Int64();
$return->unsignedInt = $this->unsignedInt;
// Initialize:
$ret0 = 0;
$ret1 = 0;
$ret2 = 0;
$ret3 = 0;
$a0 = $a->limbs[0];
$a1 = $a->limbs[1];
$a2 = $a->limbs[2];
$a3 = $a->limbs[3];
/** @var int $size */
/** @var int $i */
for ($i = $size; $i >= 0; --$i) {
$mask = -($int & 1);
$x0 = $a0 & $mask;
$x1 = $a1 & $mask;
$x2 = $a2 & $mask;
$x3 = $a3 & $mask;
$ret3 += $x3;
$c = $ret3 >> 16;
$ret2 += $x2 + $c;
$c = $ret2 >> 16;
$ret1 += $x1 + $c;
$c = $ret1 >> 16;
$ret0 += $x0 + $c;
$ret0 &= 0xffff;
$ret1 &= 0xffff;
$ret2 &= 0xffff;
$ret3 &= 0xffff;
$a3 = $a3 << 1;
$x3 = $a3 >> 16;
$a2 = ($a2 << 1) | $x3;
$x2 = $a2 >> 16;
$a1 = ($a1 << 1) | $x2;
$x1 = $a1 >> 16;
$a0 = ($a0 << 1) | $x1;
$a0 &= 0xffff;
$a1 &= 0xffff;
$a2 &= 0xffff;
$a3 &= 0xffff;
$int >>= 1;
}
$return->limbs[0] = $ret0;
$return->limbs[1] = $ret1;
$return->limbs[2] = $ret2;
$return->limbs[3] = $ret3;
return $return;
}
/**
* @param ParagonIE_Sodium_Core32_Int64 $A
* @param ParagonIE_Sodium_Core32_Int64 $B
* @return array<int, ParagonIE_Sodium_Core32_Int64>
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedInferredReturnType
*/
public static function ctSelect(
ParagonIE_Sodium_Core32_Int64 $A,
ParagonIE_Sodium_Core32_Int64 $B
) {
$a = clone $A;
$b = clone $B;
/** @var int $aNeg */
$aNeg = ($a->limbs[0] >> 15) & 1;
/** @var int $bNeg */
$bNeg = ($b->limbs[0] >> 15) & 1;
/** @var int $m */
$m = (-($aNeg & $bNeg)) | 1;
/** @var int $swap */
$swap = $bNeg & ~$aNeg;
/** @var int $d */
$d = -$swap;
/*
if ($bNeg && !$aNeg) {
$a = clone $int;
$b = clone $this;
} elseif($bNeg && $aNeg) {
$a = $this->mulInt(-1);
$b = $int->mulInt(-1);
}
*/
$x = $a->xorInt64($b)->mask64($d, $d);
return array(
$a->xorInt64($x)->mulInt($m),
$b->xorInt64($x)->mulInt($m)
);
}
/**
* @param array<int, int> $a
* @param array<int, int> $b
* @param int $baseLog2
* @return array<int, int>
*/
public function multiplyLong(array $a, array $b, $baseLog2 = 16)
{
$a_l = count($a);
$b_l = count($b);
/** @var array<int, int> $r */
$r = array_fill(0, $a_l + $b_l + 1, 0);
$base = 1 << $baseLog2;
for ($i = 0; $i < $a_l; ++$i) {
$a_i = $a[$i];
for ($j = 0; $j < $a_l; ++$j) {
$b_j = $b[$j];
$product = (($a_i * $b_j) + $r[$i + $j]);
$carry = (((int) $product >> $baseLog2) & 0xffff);
$r[$i + $j] = ((int) $product - (int) ($carry * $base)) & 0xffff;
$r[$i + $j + 1] += $carry;
}
}
return array_slice($r, 0, 5);
}
/**
* @param int $int
* @return ParagonIE_Sodium_Core32_Int64
*/
public function mulIntFast($int)
{
// Handle negative numbers
$aNeg = ($this->limbs[0] >> 15) & 1;
$bNeg = ($int >> 31) & 1;
$a = array_reverse($this->limbs);
$b = array(
$int & 0xffff,
($int >> 16) & 0xffff,
-$bNeg & 0xffff,
-$bNeg & 0xffff
);
if ($aNeg) {
for ($i = 0; $i < 4; ++$i) {
$a[$i] = ($a[$i] ^ 0xffff) & 0xffff;
}
++$a[0];
}
if ($bNeg) {
for ($i = 0; $i < 4; ++$i) {
$b[$i] = ($b[$i] ^ 0xffff) & 0xffff;
}
++$b[0];
}
// Multiply
$res = $this->multiplyLong($a, $b);
// Re-apply negation to results
if ($aNeg !== $bNeg) {
for ($i = 0; $i < 4; ++$i) {
$res[$i] = (0xffff ^ $res[$i]) & 0xffff;
}
// Handle integer overflow
$c = 1;
for ($i = 0; $i < 4; ++$i) {
$res[$i] += $c;
$c = $res[$i] >> 16;
$res[$i] &= 0xffff;
}
}
// Return our values
$return = new ParagonIE_Sodium_Core32_Int64();
$return->limbs = array(
$res[3] & 0xffff,
$res[2] & 0xffff,
$res[1] & 0xffff,
$res[0] & 0xffff
);
if (count($res) > 4) {
$return->overflow = $res[4] & 0xffff;
}
$return->unsignedInt = $this->unsignedInt;
return $return;
}
/**
* @param ParagonIE_Sodium_Core32_Int64 $right
* @return ParagonIE_Sodium_Core32_Int64
*/
public function mulInt64Fast(ParagonIE_Sodium_Core32_Int64 $right)
{
$aNeg = ($this->limbs[0] >> 15) & 1;
$bNeg = ($right->limbs[0] >> 15) & 1;
$a = array_reverse($this->limbs);
$b = array_reverse($right->limbs);
if ($aNeg) {
for ($i = 0; $i < 4; ++$i) {
$a[$i] = ($a[$i] ^ 0xffff) & 0xffff;
}
++$a[0];
}
if ($bNeg) {
for ($i = 0; $i < 4; ++$i) {
$b[$i] = ($b[$i] ^ 0xffff) & 0xffff;
}
++$b[0];
}
$res = $this->multiplyLong($a, $b);
if ($aNeg !== $bNeg) {
if ($aNeg !== $bNeg) {
for ($i = 0; $i < 4; ++$i) {
$res[$i] = ($res[$i] ^ 0xffff) & 0xffff;
}
$c = 1;
for ($i = 0; $i < 4; ++$i) {
$res[$i] += $c;
$c = $res[$i] >> 16;
$res[$i] &= 0xffff;
}
}
}
$return = new ParagonIE_Sodium_Core32_Int64();
$return->limbs = array(
$res[3] & 0xffff,
$res[2] & 0xffff,
$res[1] & 0xffff,
$res[0] & 0xffff
);
if (count($res) > 4) {
$return->overflow = $res[4];
}
return $return;
}
/**
* @param ParagonIE_Sodium_Core32_Int64 $int
* @param int $size
* @return ParagonIE_Sodium_Core32_Int64
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedAssignment
*/
public function mulInt64(ParagonIE_Sodium_Core32_Int64 $int, $size = 0)
{
if (ParagonIE_Sodium_Compat::$fastMult) {
return $this->mulInt64Fast($int);
}
ParagonIE_Sodium_Core32_Util::declareScalarType($size, 'int', 2);
if (!$size) {
$size = 63;
}
list($a, $b) = self::ctSelect($this, $int);
$return = new ParagonIE_Sodium_Core32_Int64();
$return->unsignedInt = $this->unsignedInt;
// Initialize:
$ret0 = 0;
$ret1 = 0;
$ret2 = 0;
$ret3 = 0;
$a0 = $a->limbs[0];
$a1 = $a->limbs[1];
$a2 = $a->limbs[2];
$a3 = $a->limbs[3];
$b0 = $b->limbs[0];
$b1 = $b->limbs[1];
$b2 = $b->limbs[2];
$b3 = $b->limbs[3];
/** @var int $size */
/** @var int $i */
for ($i = (int) $size; $i >= 0; --$i) {
$mask = -($b3 & 1);
$x0 = $a0 & $mask;
$x1 = $a1 & $mask;
$x2 = $a2 & $mask;
$x3 = $a3 & $mask;
$ret3 += $x3;
$c = $ret3 >> 16;
$ret2 += $x2 + $c;
$c = $ret2 >> 16;
$ret1 += $x1 + $c;
$c = $ret1 >> 16;
$ret0 += $x0 + $c;
$ret0 &= 0xffff;
$ret1 &= 0xffff;
$ret2 &= 0xffff;
$ret3 &= 0xffff;
$a3 = $a3 << 1;
$x3 = $a3 >> 16;
$a2 = ($a2 << 1) | $x3;
$x2 = $a2 >> 16;
$a1 = ($a1 << 1) | $x2;
$x1 = $a1 >> 16;
$a0 = ($a0 << 1) | $x1;
$a0 &= 0xffff;
$a1 &= 0xffff;
$a2 &= 0xffff;
$a3 &= 0xffff;
$x0 = ($b0 & 1) << 16;
$x1 = ($b1 & 1) << 16;
$x2 = ($b2 & 1) << 16;
$b0 = ($b0 >> 1);
$b1 = (($b1 | $x0) >> 1);
$b2 = (($b2 | $x1) >> 1);
$b3 = (($b3 | $x2) >> 1);
$b0 &= 0xffff;
$b1 &= 0xffff;
$b2 &= 0xffff;
$b3 &= 0xffff;
}
$return->limbs[0] = $ret0;
$return->limbs[1] = $ret1;
$return->limbs[2] = $ret2;
$return->limbs[3] = $ret3;
return $return;
}
/**
* OR this 64-bit integer with another.
*
* @param ParagonIE_Sodium_Core32_Int64 $b
* @return ParagonIE_Sodium_Core32_Int64
*/
public function orInt64(ParagonIE_Sodium_Core32_Int64 $b)
{
$return = new ParagonIE_Sodium_Core32_Int64();
$return->unsignedInt = $this->unsignedInt;
$return->limbs = array(
(int) ($this->limbs[0] | $b->limbs[0]),
(int) ($this->limbs[1] | $b->limbs[1]),
(int) ($this->limbs[2] | $b->limbs[2]),
(int) ($this->limbs[3] | $b->limbs[3])
);
return $return;
}
/**
* @param int $c
* @return ParagonIE_Sodium_Core32_Int64
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArrayAccess
*/
public function rotateLeft($c = 0)
{
ParagonIE_Sodium_Core32_Util::declareScalarType($c, 'int', 1);
/** @var int $c */
$c = (int) $c;
$return = new ParagonIE_Sodium_Core32_Int64();
$return->unsignedInt = $this->unsignedInt;
$c &= 63;
if ($c === 0) {
// NOP, but we want a copy.
$return->limbs = $this->limbs;
} else {
/** @var array<int, int> $limbs */
$limbs =& $return->limbs;
/** @var array<int, int> $myLimbs */
$myLimbs =& $this->limbs;
/** @var int $idx_shift */
$idx_shift = ($c >> 4) & 3;
/** @var int $sub_shift */
$sub_shift = $c & 15;
for ($i = 3; $i >= 0; --$i) {
/** @var int $j */
$j = ($i + $idx_shift) & 3;
/** @var int $k */
$k = ($i + $idx_shift + 1) & 3;
$limbs[$i] = (int) (
(
((int) ($myLimbs[$j]) << $sub_shift)
|
((int) ($myLimbs[$k]) >> (16 - $sub_shift))
) & 0xffff
);
}
}
return $return;
}
/**
* Rotate to the right
*
* @param int $c
* @return ParagonIE_Sodium_Core32_Int64
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArrayAccess
*/
public function rotateRight($c = 0)
{
ParagonIE_Sodium_Core32_Util::declareScalarType($c, 'int', 1);
/** @var int $c */
$c = (int) $c;
/** @var ParagonIE_Sodium_Core32_Int64 $return */
$return = new ParagonIE_Sodium_Core32_Int64();
$return->unsignedInt = $this->unsignedInt;
$c &= 63;
/** @var int $c */
if ($c === 0) {
// NOP, but we want a copy.
$return->limbs = $this->limbs;
} else {
/** @var array<int, int> $limbs */
$limbs =& $return->limbs;
/** @var array<int, int> $myLimbs */
$myLimbs =& $this->limbs;
/** @var int $idx_shift */
$idx_shift = ($c >> 4) & 3;
/** @var int $sub_shift */
$sub_shift = $c & 15;
for ($i = 3; $i >= 0; --$i) {
/** @var int $j */
$j = ($i - $idx_shift) & 3;
/** @var int $k */
$k = ($i - $idx_shift - 1) & 3;
$limbs[$i] = (int) (
(
((int) ($myLimbs[$j]) >> (int) ($sub_shift))
|
((int) ($myLimbs[$k]) << (16 - (int) ($sub_shift)))
) & 0xffff
);
}
}
return $return;
}
/**
* @param int $c
* @return ParagonIE_Sodium_Core32_Int64
* @throws SodiumException
* @throws TypeError
*/
public function shiftLeft($c = 0)
{
ParagonIE_Sodium_Core32_Util::declareScalarType($c, 'int', 1);
/** @var int $c */
$c = (int) $c;
$return = new ParagonIE_Sodium_Core32_Int64();
$return->unsignedInt = $this->unsignedInt;
$c &= 63;
if ($c >= 16) {
if ($c >= 48) {
$return->limbs = array(
$this->limbs[3], 0, 0, 0
);
} elseif ($c >= 32) {
$return->limbs = array(
$this->limbs[2], $this->limbs[3], 0, 0
);
} else {
$return->limbs = array(
$this->limbs[1], $this->limbs[2], $this->limbs[3], 0
);
}
return $return->shiftLeft($c & 15);
}
if ($c === 0) {
$return->limbs = $this->limbs;
} elseif ($c < 0) {
/** @var int $c */
return $this->shiftRight(-$c);
} else {
if (!is_int($c)) {
throw new TypeError();
}
/** @var int $carry */
$carry = 0;
for ($i = 3; $i >= 0; --$i) {
/** @var int $tmp */
$tmp = ($this->limbs[$i] << $c) | ($carry & 0xffff);
$return->limbs[$i] = (int) ($tmp & 0xffff);
/** @var int $carry */
$carry = $tmp >> 16;
}
}
return $return;
}
/**
* @param int $c
* @return ParagonIE_Sodium_Core32_Int64
* @throws SodiumException
* @throws TypeError
*/
public function shiftRight($c = 0)
{
ParagonIE_Sodium_Core32_Util::declareScalarType($c, 'int', 1);
$c = (int) $c;
/** @var int $c */
$return = new ParagonIE_Sodium_Core32_Int64();
$return->unsignedInt = $this->unsignedInt;
$c &= 63;
$negative = -(($this->limbs[0] >> 15) & 1);
if ($c >= 16) {
if ($c >= 48) {
$return->limbs = array(
(int) ($negative & 0xffff),
(int) ($negative & 0xffff),
(int) ($negative & 0xffff),
(int) $this->limbs[0]
);
} elseif ($c >= 32) {
$return->limbs = array(
(int) ($negative & 0xffff),
(int) ($negative & 0xffff),
(int) $this->limbs[0],
(int) $this->limbs[1]
);
} else {
$return->limbs = array(
(int) ($negative & 0xffff),
(int) $this->limbs[0],
(int) $this->limbs[1],
(int) $this->limbs[2]
);
}
return $return->shiftRight($c & 15);
}
if ($c === 0) {
$return->limbs = $this->limbs;
} elseif ($c < 0) {
return $this->shiftLeft(-$c);
} else {
if (!is_int($c)) {
throw new TypeError();
}
/** @var int $carryRight */
$carryRight = ($negative & 0xffff);
$mask = (int) (((1 << ($c + 1)) - 1) & 0xffff);
for ($i = 0; $i < 4; ++$i) {
$return->limbs[$i] = (int) (
(($this->limbs[$i] >> $c) | ($carryRight << (16 - $c))) & 0xffff
);
$carryRight = (int) ($this->limbs[$i] & $mask);
}
}
return $return;
}
/**
* Subtract a normal integer from an int64 object.
*
* @param int $int
* @return ParagonIE_Sodium_Core32_Int64
* @throws SodiumException
* @throws TypeError
*/
public function subInt($int)
{
ParagonIE_Sodium_Core32_Util::declareScalarType($int, 'int', 1);
$int = (int) $int;
$return = new ParagonIE_Sodium_Core32_Int64();
$return->unsignedInt = $this->unsignedInt;
/** @var int $carry */
$carry = 0;
for ($i = 3; $i >= 0; --$i) {
/** @var int $tmp */
$tmp = $this->limbs[$i] - (($int >> 16) & 0xffff) + $carry;
/** @var int $carry */
$carry = $tmp >> 16;
$return->limbs[$i] = (int) ($tmp & 0xffff);
}
return $return;
}
/**
* The difference between two Int64 objects.
*
* @param ParagonIE_Sodium_Core32_Int64 $b
* @return ParagonIE_Sodium_Core32_Int64
*/
public function subInt64(ParagonIE_Sodium_Core32_Int64 $b)
{
$return = new ParagonIE_Sodium_Core32_Int64();
$return->unsignedInt = $this->unsignedInt;
/** @var int $carry */
$carry = 0;
for ($i = 3; $i >= 0; --$i) {
/** @var int $tmp */
$tmp = $this->limbs[$i] - $b->limbs[$i] + $carry;
/** @var int $carry */
$carry = ($tmp >> 16);
$return->limbs[$i] = (int) ($tmp & 0xffff);
}
return $return;
}
/**
* XOR this 64-bit integer with another.
*
* @param ParagonIE_Sodium_Core32_Int64 $b
* @return ParagonIE_Sodium_Core32_Int64
*/
public function xorInt64(ParagonIE_Sodium_Core32_Int64 $b)
{
$return = new ParagonIE_Sodium_Core32_Int64();
$return->unsignedInt = $this->unsignedInt;
$return->limbs = array(
(int) ($this->limbs[0] ^ $b->limbs[0]),
(int) ($this->limbs[1] ^ $b->limbs[1]),
(int) ($this->limbs[2] ^ $b->limbs[2]),
(int) ($this->limbs[3] ^ $b->limbs[3])
);
return $return;
}
/**
* @param int $low
* @param int $high
* @return self
* @throws SodiumException
* @throws TypeError
*/
public static function fromInts($low, $high)
{
ParagonIE_Sodium_Core32_Util::declareScalarType($low, 'int', 1);
ParagonIE_Sodium_Core32_Util::declareScalarType($high, 'int', 2);
$high = (int) $high;
$low = (int) $low;
return new ParagonIE_Sodium_Core32_Int64(
array(
(int) (($high >> 16) & 0xffff),
(int) ($high & 0xffff),
(int) (($low >> 16) & 0xffff),
(int) ($low & 0xffff)
)
);
}
/**
* @param int $low
* @return self
* @throws SodiumException
* @throws TypeError
*/
public static function fromInt($low)
{
ParagonIE_Sodium_Core32_Util::declareScalarType($low, 'int', 1);
$low = (int) $low;
return new ParagonIE_Sodium_Core32_Int64(
array(
0,
0,
(int) (($low >> 16) & 0xffff),
(int) ($low & 0xffff)
)
);
}
/**
* @return int
*/
public function toInt()
{
return (int) (
(($this->limbs[2] & 0xffff) << 16)
|
($this->limbs[3] & 0xffff)
);
}
/**
* @param string $string
* @return self
* @throws SodiumException
* @throws TypeError
*/
public static function fromString($string)
{
ParagonIE_Sodium_Core32_Util::declareScalarType($string, 'string', 1);
$string = (string) $string;
if (ParagonIE_Sodium_Core32_Util::strlen($string) !== 8) {
throw new RangeException(
'String must be 8 bytes; ' . ParagonIE_Sodium_Core32_Util::strlen($string) . ' given.'
);
}
$return = new ParagonIE_Sodium_Core32_Int64();
$return->limbs[0] = (int) ((ParagonIE_Sodium_Core32_Util::chrToInt($string[0]) & 0xff) << 8);
$return->limbs[0] |= (ParagonIE_Sodium_Core32_Util::chrToInt($string[1]) & 0xff);
$return->limbs[1] = (int) ((ParagonIE_Sodium_Core32_Util::chrToInt($string[2]) & 0xff) << 8);
$return->limbs[1] |= (ParagonIE_Sodium_Core32_Util::chrToInt($string[3]) & 0xff);
$return->limbs[2] = (int) ((ParagonIE_Sodium_Core32_Util::chrToInt($string[4]) & 0xff) << 8);
$return->limbs[2] |= (ParagonIE_Sodium_Core32_Util::chrToInt($string[5]) & 0xff);
$return->limbs[3] = (int) ((ParagonIE_Sodium_Core32_Util::chrToInt($string[6]) & 0xff) << 8);
$return->limbs[3] |= (ParagonIE_Sodium_Core32_Util::chrToInt($string[7]) & 0xff);
return $return;
}
/**
* @param string $string
* @return self
* @throws SodiumException
* @throws TypeError
*/
public static function fromReverseString($string)
{
ParagonIE_Sodium_Core32_Util::declareScalarType($string, 'string', 1);
$string = (string) $string;
if (ParagonIE_Sodium_Core32_Util::strlen($string) !== 8) {
throw new RangeException(
'String must be 8 bytes; ' . ParagonIE_Sodium_Core32_Util::strlen($string) . ' given.'
);
}
$return = new ParagonIE_Sodium_Core32_Int64();
$return->limbs[0] = (int) ((ParagonIE_Sodium_Core32_Util::chrToInt($string[7]) & 0xff) << 8);
$return->limbs[0] |= (ParagonIE_Sodium_Core32_Util::chrToInt($string[6]) & 0xff);
$return->limbs[1] = (int) ((ParagonIE_Sodium_Core32_Util::chrToInt($string[5]) & 0xff) << 8);
$return->limbs[1] |= (ParagonIE_Sodium_Core32_Util::chrToInt($string[4]) & 0xff);
$return->limbs[2] = (int) ((ParagonIE_Sodium_Core32_Util::chrToInt($string[3]) & 0xff) << 8);
$return->limbs[2] |= (ParagonIE_Sodium_Core32_Util::chrToInt($string[2]) & 0xff);
$return->limbs[3] = (int) ((ParagonIE_Sodium_Core32_Util::chrToInt($string[1]) & 0xff) << 8);
$return->limbs[3] |= (ParagonIE_Sodium_Core32_Util::chrToInt($string[0]) & 0xff);
return $return;
}
/**
* @return array<int, int>
*/
public function toArray()
{
return array(
(int) ((($this->limbs[0] & 0xffff) << 16) | ($this->limbs[1] & 0xffff)),
(int) ((($this->limbs[2] & 0xffff) << 16) | ($this->limbs[3] & 0xffff))
);
}
/**
* @return ParagonIE_Sodium_Core32_Int32
*/
public function toInt32()
{
$return = new ParagonIE_Sodium_Core32_Int32();
$return->limbs[0] = (int) ($this->limbs[2]);
$return->limbs[1] = (int) ($this->limbs[3]);
$return->unsignedInt = $this->unsignedInt;
$return->overflow = (int) (ParagonIE_Sodium_Core32_Util::abs($this->limbs[1], 16) & 0xffff);
return $return;
}
/**
* @return ParagonIE_Sodium_Core32_Int64
*/
public function toInt64()
{
$return = new ParagonIE_Sodium_Core32_Int64();
$return->limbs[0] = (int) ($this->limbs[0]);
$return->limbs[1] = (int) ($this->limbs[1]);
$return->limbs[2] = (int) ($this->limbs[2]);
$return->limbs[3] = (int) ($this->limbs[3]);
$return->unsignedInt = $this->unsignedInt;
$return->overflow = ParagonIE_Sodium_Core32_Util::abs($this->overflow);
return $return;
}
/**
* @param bool $bool
* @return self
*/
public function setUnsignedInt($bool = false)
{
$this->unsignedInt = !empty($bool);
return $this;
}
/**
* @return string
* @throws TypeError
*/
public function toString()
{
return ParagonIE_Sodium_Core32_Util::intToChr(($this->limbs[0] >> 8) & 0xff) .
ParagonIE_Sodium_Core32_Util::intToChr($this->limbs[0] & 0xff) .
ParagonIE_Sodium_Core32_Util::intToChr(($this->limbs[1] >> 8) & 0xff) .
ParagonIE_Sodium_Core32_Util::intToChr($this->limbs[1] & 0xff) .
ParagonIE_Sodium_Core32_Util::intToChr(($this->limbs[2] >> 8) & 0xff) .
ParagonIE_Sodium_Core32_Util::intToChr($this->limbs[2] & 0xff) .
ParagonIE_Sodium_Core32_Util::intToChr(($this->limbs[3] >> 8) & 0xff) .
ParagonIE_Sodium_Core32_Util::intToChr($this->limbs[3] & 0xff);
}
/**
* @return string
* @throws TypeError
*/
public function toReverseString()
{
return ParagonIE_Sodium_Core32_Util::intToChr($this->limbs[3] & 0xff) .
ParagonIE_Sodium_Core32_Util::intToChr(($this->limbs[3] >> 8) & 0xff) .
ParagonIE_Sodium_Core32_Util::intToChr($this->limbs[2] & 0xff) .
ParagonIE_Sodium_Core32_Util::intToChr(($this->limbs[2] >> 8) & 0xff) .
ParagonIE_Sodium_Core32_Util::intToChr($this->limbs[1] & 0xff) .
ParagonIE_Sodium_Core32_Util::intToChr(($this->limbs[1] >> 8) & 0xff) .
ParagonIE_Sodium_Core32_Util::intToChr($this->limbs[0] & 0xff) .
ParagonIE_Sodium_Core32_Util::intToChr(($this->limbs[0] >> 8) & 0xff);
}
/**
* @return string
*/
public function __toString()
{
try {
return $this->toString();
} catch (TypeError $ex) {
// PHP engine can't handle exceptions from __toString()
return '';
}
}
}
Core32/HSalsa20.php 0000604 00000015435 15133021213 0007622 0 ustar 00 <?php
if (class_exists('ParagonIE_Sodium_Core32_HSalsa20', false)) {
return;
}
/**
* Class ParagonIE_Sodium_Core32_HSalsa20
*/
abstract class ParagonIE_Sodium_Core32_HSalsa20 extends ParagonIE_Sodium_Core32_Salsa20
{
/**
* Calculate an hsalsa20 hash of a single block
*
* HSalsa20 doesn't have a counter and will never be used for more than
* one block (used to derive a subkey for xsalsa20).
*
* @internal You should not use this directly from another application
*
* @param string $in
* @param string $k
* @param string|null $c
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function hsalsa20($in, $k, $c = null)
{
/**
* @var ParagonIE_Sodium_Core32_Int32 $x0
* @var ParagonIE_Sodium_Core32_Int32 $x1
* @var ParagonIE_Sodium_Core32_Int32 $x2
* @var ParagonIE_Sodium_Core32_Int32 $x3
* @var ParagonIE_Sodium_Core32_Int32 $x4
* @var ParagonIE_Sodium_Core32_Int32 $x5
* @var ParagonIE_Sodium_Core32_Int32 $x6
* @var ParagonIE_Sodium_Core32_Int32 $x7
* @var ParagonIE_Sodium_Core32_Int32 $x8
* @var ParagonIE_Sodium_Core32_Int32 $x9
* @var ParagonIE_Sodium_Core32_Int32 $x10
* @var ParagonIE_Sodium_Core32_Int32 $x11
* @var ParagonIE_Sodium_Core32_Int32 $x12
* @var ParagonIE_Sodium_Core32_Int32 $x13
* @var ParagonIE_Sodium_Core32_Int32 $x14
* @var ParagonIE_Sodium_Core32_Int32 $x15
* @var ParagonIE_Sodium_Core32_Int32 $j0
* @var ParagonIE_Sodium_Core32_Int32 $j1
* @var ParagonIE_Sodium_Core32_Int32 $j2
* @var ParagonIE_Sodium_Core32_Int32 $j3
* @var ParagonIE_Sodium_Core32_Int32 $j4
* @var ParagonIE_Sodium_Core32_Int32 $j5
* @var ParagonIE_Sodium_Core32_Int32 $j6
* @var ParagonIE_Sodium_Core32_Int32 $j7
* @var ParagonIE_Sodium_Core32_Int32 $j8
* @var ParagonIE_Sodium_Core32_Int32 $j9
* @var ParagonIE_Sodium_Core32_Int32 $j10
* @var ParagonIE_Sodium_Core32_Int32 $j11
* @var ParagonIE_Sodium_Core32_Int32 $j12
* @var ParagonIE_Sodium_Core32_Int32 $j13
* @var ParagonIE_Sodium_Core32_Int32 $j14
* @var ParagonIE_Sodium_Core32_Int32 $j15
*/
if (self::strlen($k) < 32) {
throw new RangeException('Key must be 32 bytes long');
}
if ($c === null) {
$x0 = new ParagonIE_Sodium_Core32_Int32(array(0x6170, 0x7865));
$x5 = new ParagonIE_Sodium_Core32_Int32(array(0x3320, 0x646e));
$x10 = new ParagonIE_Sodium_Core32_Int32(array(0x7962, 0x2d32));
$x15 = new ParagonIE_Sodium_Core32_Int32(array(0x6b20, 0x6574));
} else {
$x0 = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($c, 0, 4));
$x5 = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($c, 4, 4));
$x10 = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($c, 8, 4));
$x15 = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($c, 12, 4));
}
$x1 = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($k, 0, 4));
$x2 = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($k, 4, 4));
$x3 = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($k, 8, 4));
$x4 = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($k, 12, 4));
$x6 = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($in, 0, 4));
$x7 = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($in, 4, 4));
$x8 = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($in, 8, 4));
$x9 = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($in, 12, 4));
$x11 = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($k, 16, 4));
$x12 = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($k, 20, 4));
$x13 = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($k, 24, 4));
$x14 = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($k, 28, 4));
for ($i = self::ROUNDS; $i > 0; $i -= 2) {
$x4 = $x4->xorInt32($x0->addInt32($x12)->rotateLeft(7));
$x8 = $x8->xorInt32($x4->addInt32($x0)->rotateLeft(9));
$x12 = $x12->xorInt32($x8->addInt32($x4)->rotateLeft(13));
$x0 = $x0->xorInt32($x12->addInt32($x8)->rotateLeft(18));
$x9 = $x9->xorInt32($x5->addInt32($x1)->rotateLeft(7));
$x13 = $x13->xorInt32($x9->addInt32($x5)->rotateLeft(9));
$x1 = $x1->xorInt32($x13->addInt32($x9)->rotateLeft(13));
$x5 = $x5->xorInt32($x1->addInt32($x13)->rotateLeft(18));
$x14 = $x14->xorInt32($x10->addInt32($x6)->rotateLeft(7));
$x2 = $x2->xorInt32($x14->addInt32($x10)->rotateLeft(9));
$x6 = $x6->xorInt32($x2->addInt32($x14)->rotateLeft(13));
$x10 = $x10->xorInt32($x6->addInt32($x2)->rotateLeft(18));
$x3 = $x3->xorInt32($x15->addInt32($x11)->rotateLeft(7));
$x7 = $x7->xorInt32($x3->addInt32($x15)->rotateLeft(9));
$x11 = $x11->xorInt32($x7->addInt32($x3)->rotateLeft(13));
$x15 = $x15->xorInt32($x11->addInt32($x7)->rotateLeft(18));
$x1 = $x1->xorInt32($x0->addInt32($x3)->rotateLeft(7));
$x2 = $x2->xorInt32($x1->addInt32($x0)->rotateLeft(9));
$x3 = $x3->xorInt32($x2->addInt32($x1)->rotateLeft(13));
$x0 = $x0->xorInt32($x3->addInt32($x2)->rotateLeft(18));
$x6 = $x6->xorInt32($x5->addInt32($x4)->rotateLeft(7));
$x7 = $x7->xorInt32($x6->addInt32($x5)->rotateLeft(9));
$x4 = $x4->xorInt32($x7->addInt32($x6)->rotateLeft(13));
$x5 = $x5->xorInt32($x4->addInt32($x7)->rotateLeft(18));
$x11 = $x11->xorInt32($x10->addInt32($x9)->rotateLeft(7));
$x8 = $x8->xorInt32($x11->addInt32($x10)->rotateLeft(9));
$x9 = $x9->xorInt32($x8->addInt32($x11)->rotateLeft(13));
$x10 = $x10->xorInt32($x9->addInt32($x8)->rotateLeft(18));
$x12 = $x12->xorInt32($x15->addInt32($x14)->rotateLeft(7));
$x13 = $x13->xorInt32($x12->addInt32($x15)->rotateLeft(9));
$x14 = $x14->xorInt32($x13->addInt32($x12)->rotateLeft(13));
$x15 = $x15->xorInt32($x14->addInt32($x13)->rotateLeft(18));
}
return $x0->toReverseString() .
$x5->toReverseString() .
$x10->toReverseString() .
$x15->toReverseString() .
$x6->toReverseString() .
$x7->toReverseString() .
$x8->toReverseString() .
$x9->toReverseString();
}
}
Core32/XChaCha20.php 0000644 00000004626 15133021213 0007712 0 ustar 00 <?php
if (class_exists('ParagonIE_Sodium_Core32_XChaCha20', false)) {
return;
}
/**
* Class ParagonIE_Sodium_Core32_XChaCha20
*/
class ParagonIE_Sodium_Core32_XChaCha20 extends ParagonIE_Sodium_Core32_HChaCha20
{
/**
* @internal You should not use this directly from another application
*
* @param int $len
* @param string $nonce
* @param string $key
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function stream($len = 64, $nonce = '', $key = '')
{
if (self::strlen($nonce) !== 24) {
throw new SodiumException('Nonce must be 24 bytes long');
}
return self::encryptBytes(
new ParagonIE_Sodium_Core32_ChaCha20_Ctx(
self::hChaCha20(
self::substr($nonce, 0, 16),
$key
),
self::substr($nonce, 16, 8)
),
str_repeat("\x00", $len)
);
}
/**
* @internal You should not use this directly from another application
*
* @param string $message
* @param string $nonce
* @param string $key
* @param string $ic
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function streamXorIc($message, $nonce = '', $key = '', $ic = '')
{
if (self::strlen($nonce) !== 24) {
throw new SodiumException('Nonce must be 24 bytes long');
}
return self::encryptBytes(
new ParagonIE_Sodium_Core32_ChaCha20_Ctx(
self::hChaCha20(self::substr($nonce, 0, 16), $key),
self::substr($nonce, 16, 8),
$ic
),
$message
);
}
/**
* @internal You should not use this directly from another application
*
* @param string $message
* @param string $nonce
* @param string $key
* @param string $ic
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function ietfStreamXorIc($message, $nonce = '', $key = '', $ic = '')
{
return self::encryptBytes(
new ParagonIE_Sodium_Core32_ChaCha20_IetfCtx(
self::hChaCha20(self::substr($nonce, 0, 16), $key),
"\x00\x00\x00\x00" . self::substr($nonce, 16, 8),
$ic
),
$message
);
}
}
Core32/ChaCha20.php 0000604 00000034257 15133021213 0007561 0 ustar 00 <?php
if (class_exists('ParagonIE_Sodium_Core32_ChaCha20', false)) {
return;
}
/**
* Class ParagonIE_Sodium_Core32_ChaCha20
*/
class ParagonIE_Sodium_Core32_ChaCha20 extends ParagonIE_Sodium_Core32_Util
{
/**
* The ChaCha20 quarter round function. Works on four 32-bit integers.
*
* @internal You should not use this directly from another application
*
* @param ParagonIE_Sodium_Core32_Int32 $a
* @param ParagonIE_Sodium_Core32_Int32 $b
* @param ParagonIE_Sodium_Core32_Int32 $c
* @param ParagonIE_Sodium_Core32_Int32 $d
* @return array<int, ParagonIE_Sodium_Core32_Int32>
* @throws SodiumException
* @throws TypeError
*/
protected static function quarterRound(
ParagonIE_Sodium_Core32_Int32 $a,
ParagonIE_Sodium_Core32_Int32 $b,
ParagonIE_Sodium_Core32_Int32 $c,
ParagonIE_Sodium_Core32_Int32 $d
) {
/** @var ParagonIE_Sodium_Core32_Int32 $a */
/** @var ParagonIE_Sodium_Core32_Int32 $b */
/** @var ParagonIE_Sodium_Core32_Int32 $c */
/** @var ParagonIE_Sodium_Core32_Int32 $d */
# a = PLUS(a,b); d = ROTATE(XOR(d,a),16);
$a = $a->addInt32($b);
$d = $d->xorInt32($a)->rotateLeft(16);
# c = PLUS(c,d); b = ROTATE(XOR(b,c),12);
$c = $c->addInt32($d);
$b = $b->xorInt32($c)->rotateLeft(12);
# a = PLUS(a,b); d = ROTATE(XOR(d,a), 8);
$a = $a->addInt32($b);
$d = $d->xorInt32($a)->rotateLeft(8);
# c = PLUS(c,d); b = ROTATE(XOR(b,c), 7);
$c = $c->addInt32($d);
$b = $b->xorInt32($c)->rotateLeft(7);
return array($a, $b, $c, $d);
}
/**
* @internal You should not use this directly from another application
*
* @param ParagonIE_Sodium_Core32_ChaCha20_Ctx $ctx
* @param string $message
*
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function encryptBytes(
ParagonIE_Sodium_Core32_ChaCha20_Ctx $ctx,
$message = ''
) {
$bytes = self::strlen($message);
/** @var ParagonIE_Sodium_Core32_Int32 $x0 */
/** @var ParagonIE_Sodium_Core32_Int32 $x1 */
/** @var ParagonIE_Sodium_Core32_Int32 $x2 */
/** @var ParagonIE_Sodium_Core32_Int32 $x3 */
/** @var ParagonIE_Sodium_Core32_Int32 $x4 */
/** @var ParagonIE_Sodium_Core32_Int32 $x5 */
/** @var ParagonIE_Sodium_Core32_Int32 $x6 */
/** @var ParagonIE_Sodium_Core32_Int32 $x7 */
/** @var ParagonIE_Sodium_Core32_Int32 $x8 */
/** @var ParagonIE_Sodium_Core32_Int32 $x9 */
/** @var ParagonIE_Sodium_Core32_Int32 $x10 */
/** @var ParagonIE_Sodium_Core32_Int32 $x11 */
/** @var ParagonIE_Sodium_Core32_Int32 $x12 */
/** @var ParagonIE_Sodium_Core32_Int32 $x13 */
/** @var ParagonIE_Sodium_Core32_Int32 $x14 */
/** @var ParagonIE_Sodium_Core32_Int32 $x15 */
/*
j0 = ctx->input[0];
j1 = ctx->input[1];
j2 = ctx->input[2];
j3 = ctx->input[3];
j4 = ctx->input[4];
j5 = ctx->input[5];
j6 = ctx->input[6];
j7 = ctx->input[7];
j8 = ctx->input[8];
j9 = ctx->input[9];
j10 = ctx->input[10];
j11 = ctx->input[11];
j12 = ctx->input[12];
j13 = ctx->input[13];
j14 = ctx->input[14];
j15 = ctx->input[15];
*/
/** @var ParagonIE_Sodium_Core32_Int32 $j0 */
$j0 = $ctx[0];
/** @var ParagonIE_Sodium_Core32_Int32 $j1 */
$j1 = $ctx[1];
/** @var ParagonIE_Sodium_Core32_Int32 $j2 */
$j2 = $ctx[2];
/** @var ParagonIE_Sodium_Core32_Int32 $j3 */
$j3 = $ctx[3];
/** @var ParagonIE_Sodium_Core32_Int32 $j4 */
$j4 = $ctx[4];
/** @var ParagonIE_Sodium_Core32_Int32 $j5 */
$j5 = $ctx[5];
/** @var ParagonIE_Sodium_Core32_Int32 $j6 */
$j6 = $ctx[6];
/** @var ParagonIE_Sodium_Core32_Int32 $j7 */
$j7 = $ctx[7];
/** @var ParagonIE_Sodium_Core32_Int32 $j8 */
$j8 = $ctx[8];
/** @var ParagonIE_Sodium_Core32_Int32 $j9 */
$j9 = $ctx[9];
/** @var ParagonIE_Sodium_Core32_Int32 $j10 */
$j10 = $ctx[10];
/** @var ParagonIE_Sodium_Core32_Int32 $j11 */
$j11 = $ctx[11];
/** @var ParagonIE_Sodium_Core32_Int32 $j12 */
$j12 = $ctx[12];
/** @var ParagonIE_Sodium_Core32_Int32 $j13 */
$j13 = $ctx[13];
/** @var ParagonIE_Sodium_Core32_Int32 $j14 */
$j14 = $ctx[14];
/** @var ParagonIE_Sodium_Core32_Int32 $j15 */
$j15 = $ctx[15];
$c = '';
for (;;) {
if ($bytes < 64) {
$message .= str_repeat("\x00", 64 - $bytes);
}
$x0 = clone $j0;
$x1 = clone $j1;
$x2 = clone $j2;
$x3 = clone $j3;
$x4 = clone $j4;
$x5 = clone $j5;
$x6 = clone $j6;
$x7 = clone $j7;
$x8 = clone $j8;
$x9 = clone $j9;
$x10 = clone $j10;
$x11 = clone $j11;
$x12 = clone $j12;
$x13 = clone $j13;
$x14 = clone $j14;
$x15 = clone $j15;
# for (i = 20; i > 0; i -= 2) {
for ($i = 20; $i > 0; $i -= 2) {
# QUARTERROUND( x0, x4, x8, x12)
list($x0, $x4, $x8, $x12) = self::quarterRound($x0, $x4, $x8, $x12);
# QUARTERROUND( x1, x5, x9, x13)
list($x1, $x5, $x9, $x13) = self::quarterRound($x1, $x5, $x9, $x13);
# QUARTERROUND( x2, x6, x10, x14)
list($x2, $x6, $x10, $x14) = self::quarterRound($x2, $x6, $x10, $x14);
# QUARTERROUND( x3, x7, x11, x15)
list($x3, $x7, $x11, $x15) = self::quarterRound($x3, $x7, $x11, $x15);
# QUARTERROUND( x0, x5, x10, x15)
list($x0, $x5, $x10, $x15) = self::quarterRound($x0, $x5, $x10, $x15);
# QUARTERROUND( x1, x6, x11, x12)
list($x1, $x6, $x11, $x12) = self::quarterRound($x1, $x6, $x11, $x12);
# QUARTERROUND( x2, x7, x8, x13)
list($x2, $x7, $x8, $x13) = self::quarterRound($x2, $x7, $x8, $x13);
# QUARTERROUND( x3, x4, x9, x14)
list($x3, $x4, $x9, $x14) = self::quarterRound($x3, $x4, $x9, $x14);
}
/*
x0 = PLUS(x0, j0);
x1 = PLUS(x1, j1);
x2 = PLUS(x2, j2);
x3 = PLUS(x3, j3);
x4 = PLUS(x4, j4);
x5 = PLUS(x5, j5);
x6 = PLUS(x6, j6);
x7 = PLUS(x7, j7);
x8 = PLUS(x8, j8);
x9 = PLUS(x9, j9);
x10 = PLUS(x10, j10);
x11 = PLUS(x11, j11);
x12 = PLUS(x12, j12);
x13 = PLUS(x13, j13);
x14 = PLUS(x14, j14);
x15 = PLUS(x15, j15);
*/
$x0 = $x0->addInt32($j0);
$x1 = $x1->addInt32($j1);
$x2 = $x2->addInt32($j2);
$x3 = $x3->addInt32($j3);
$x4 = $x4->addInt32($j4);
$x5 = $x5->addInt32($j5);
$x6 = $x6->addInt32($j6);
$x7 = $x7->addInt32($j7);
$x8 = $x8->addInt32($j8);
$x9 = $x9->addInt32($j9);
$x10 = $x10->addInt32($j10);
$x11 = $x11->addInt32($j11);
$x12 = $x12->addInt32($j12);
$x13 = $x13->addInt32($j13);
$x14 = $x14->addInt32($j14);
$x15 = $x15->addInt32($j15);
/*
x0 = XOR(x0, LOAD32_LE(m + 0));
x1 = XOR(x1, LOAD32_LE(m + 4));
x2 = XOR(x2, LOAD32_LE(m + 8));
x3 = XOR(x3, LOAD32_LE(m + 12));
x4 = XOR(x4, LOAD32_LE(m + 16));
x5 = XOR(x5, LOAD32_LE(m + 20));
x6 = XOR(x6, LOAD32_LE(m + 24));
x7 = XOR(x7, LOAD32_LE(m + 28));
x8 = XOR(x8, LOAD32_LE(m + 32));
x9 = XOR(x9, LOAD32_LE(m + 36));
x10 = XOR(x10, LOAD32_LE(m + 40));
x11 = XOR(x11, LOAD32_LE(m + 44));
x12 = XOR(x12, LOAD32_LE(m + 48));
x13 = XOR(x13, LOAD32_LE(m + 52));
x14 = XOR(x14, LOAD32_LE(m + 56));
x15 = XOR(x15, LOAD32_LE(m + 60));
*/
$x0 = $x0->xorInt32(ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($message, 0, 4)));
$x1 = $x1->xorInt32(ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($message, 4, 4)));
$x2 = $x2->xorInt32(ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($message, 8, 4)));
$x3 = $x3->xorInt32(ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($message, 12, 4)));
$x4 = $x4->xorInt32(ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($message, 16, 4)));
$x5 = $x5->xorInt32(ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($message, 20, 4)));
$x6 = $x6->xorInt32(ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($message, 24, 4)));
$x7 = $x7->xorInt32(ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($message, 28, 4)));
$x8 = $x8->xorInt32(ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($message, 32, 4)));
$x9 = $x9->xorInt32(ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($message, 36, 4)));
$x10 = $x10->xorInt32(ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($message, 40, 4)));
$x11 = $x11->xorInt32(ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($message, 44, 4)));
$x12 = $x12->xorInt32(ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($message, 48, 4)));
$x13 = $x13->xorInt32(ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($message, 52, 4)));
$x14 = $x14->xorInt32(ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($message, 56, 4)));
$x15 = $x15->xorInt32(ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($message, 60, 4)));
/*
j12 = PLUSONE(j12);
if (!j12) {
j13 = PLUSONE(j13);
}
*/
/** @var ParagonIE_Sodium_Core32_Int32 $j12 */
$j12 = $j12->addInt(1);
if ($j12->limbs[0] === 0 && $j12->limbs[1] === 0) {
$j13 = $j13->addInt(1);
}
/*
STORE32_LE(c + 0, x0);
STORE32_LE(c + 4, x1);
STORE32_LE(c + 8, x2);
STORE32_LE(c + 12, x3);
STORE32_LE(c + 16, x4);
STORE32_LE(c + 20, x5);
STORE32_LE(c + 24, x6);
STORE32_LE(c + 28, x7);
STORE32_LE(c + 32, x8);
STORE32_LE(c + 36, x9);
STORE32_LE(c + 40, x10);
STORE32_LE(c + 44, x11);
STORE32_LE(c + 48, x12);
STORE32_LE(c + 52, x13);
STORE32_LE(c + 56, x14);
STORE32_LE(c + 60, x15);
*/
$block = $x0->toReverseString() .
$x1->toReverseString() .
$x2->toReverseString() .
$x3->toReverseString() .
$x4->toReverseString() .
$x5->toReverseString() .
$x6->toReverseString() .
$x7->toReverseString() .
$x8->toReverseString() .
$x9->toReverseString() .
$x10->toReverseString() .
$x11->toReverseString() .
$x12->toReverseString() .
$x13->toReverseString() .
$x14->toReverseString() .
$x15->toReverseString();
/* Partial block */
if ($bytes < 64) {
$c .= self::substr($block, 0, $bytes);
break;
}
/* Full block */
$c .= $block;
$bytes -= 64;
if ($bytes <= 0) {
break;
}
$message = self::substr($message, 64);
}
/* end for(;;) loop */
$ctx[12] = $j12;
$ctx[13] = $j13;
return $c;
}
/**
* @internal You should not use this directly from another application
*
* @param int $len
* @param string $nonce
* @param string $key
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function stream($len = 64, $nonce = '', $key = '')
{
return self::encryptBytes(
new ParagonIE_Sodium_Core32_ChaCha20_Ctx($key, $nonce),
str_repeat("\x00", $len)
);
}
/**
* @internal You should not use this directly from another application
*
* @param int $len
* @param string $nonce
* @param string $key
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function ietfStream($len, $nonce = '', $key = '')
{
return self::encryptBytes(
new ParagonIE_Sodium_Core32_ChaCha20_IetfCtx($key, $nonce),
str_repeat("\x00", $len)
);
}
/**
* @internal You should not use this directly from another application
*
* @param string $message
* @param string $nonce
* @param string $key
* @param string $ic
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function ietfStreamXorIc($message, $nonce = '', $key = '', $ic = '')
{
return self::encryptBytes(
new ParagonIE_Sodium_Core32_ChaCha20_IetfCtx($key, $nonce, $ic),
$message
);
}
/**
* @internal You should not use this directly from another application
*
* @param string $message
* @param string $nonce
* @param string $key
* @param string $ic
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function streamXorIc($message, $nonce = '', $key = '', $ic = '')
{
return self::encryptBytes(
new ParagonIE_Sodium_Core32_ChaCha20_Ctx($key, $nonce, $ic),
$message
);
}
}
Core32/Int32.php 0000644 00000060004 15133021213 0007200 0 ustar 00 <?php
/**
* Class ParagonIE_Sodium_Core32_Int32
*
* Encapsulates a 32-bit integer.
*
* These are immutable. It always returns a new instance.
*/
class ParagonIE_Sodium_Core32_Int32
{
/**
* @var array<int, int> - two 16-bit integers
*
* 0 is the higher 16 bits
* 1 is the lower 16 bits
*/
public $limbs = array(0, 0);
/**
* @var int
*/
public $overflow = 0;
/**
* @var bool
*/
public $unsignedInt = false;
/**
* ParagonIE_Sodium_Core32_Int32 constructor.
* @param array $array
* @param bool $unsignedInt
*/
public function __construct($array = array(0, 0), $unsignedInt = false)
{
$this->limbs = array(
(int) $array[0],
(int) $array[1]
);
$this->overflow = 0;
$this->unsignedInt = $unsignedInt;
}
/**
* Adds two int32 objects
*
* @param ParagonIE_Sodium_Core32_Int32 $addend
* @return ParagonIE_Sodium_Core32_Int32
*/
public function addInt32(ParagonIE_Sodium_Core32_Int32 $addend)
{
$i0 = $this->limbs[0];
$i1 = $this->limbs[1];
$j0 = $addend->limbs[0];
$j1 = $addend->limbs[1];
$r1 = $i1 + ($j1 & 0xffff);
$carry = $r1 >> 16;
$r0 = $i0 + ($j0 & 0xffff) + $carry;
$carry = $r0 >> 16;
$r0 &= 0xffff;
$r1 &= 0xffff;
$return = new ParagonIE_Sodium_Core32_Int32(
array($r0, $r1)
);
$return->overflow = $carry;
$return->unsignedInt = $this->unsignedInt;
return $return;
}
/**
* Adds a normal integer to an int32 object
*
* @param int $int
* @return ParagonIE_Sodium_Core32_Int32
* @throws SodiumException
* @throws TypeError
*/
public function addInt($int)
{
ParagonIE_Sodium_Core32_Util::declareScalarType($int, 'int', 1);
/** @var int $int */
$int = (int) $int;
$int = (int) $int;
$i0 = $this->limbs[0];
$i1 = $this->limbs[1];
$r1 = $i1 + ($int & 0xffff);
$carry = $r1 >> 16;
$r0 = $i0 + (($int >> 16) & 0xffff) + $carry;
$carry = $r0 >> 16;
$r0 &= 0xffff;
$r1 &= 0xffff;
$return = new ParagonIE_Sodium_Core32_Int32(
array($r0, $r1)
);
$return->overflow = $carry;
$return->unsignedInt = $this->unsignedInt;
return $return;
}
/**
* @param int $b
* @return int
*/
public function compareInt($b = 0)
{
$gt = 0;
$eq = 1;
$i = 2;
$j = 0;
while ($i > 0) {
--$i;
/** @var int $x1 */
$x1 = $this->limbs[$i];
/** @var int $x2 */
$x2 = ($b >> ($j << 4)) & 0xffff;
/** @var int $gt */
$gt |= (($x2 - $x1) >> 8) & $eq;
/** @var int $eq */
$eq &= (($x2 ^ $x1) - 1) >> 8;
}
return ($gt + $gt - $eq) + 1;
}
/**
* @param int $m
* @return ParagonIE_Sodium_Core32_Int32
*/
public function mask($m = 0)
{
/** @var int $hi */
$hi = ((int) $m >> 16);
$hi &= 0xffff;
/** @var int $lo */
$lo = ((int) $m) & 0xffff;
return new ParagonIE_Sodium_Core32_Int32(
array(
(int) ($this->limbs[0] & $hi),
(int) ($this->limbs[1] & $lo)
),
$this->unsignedInt
);
}
/**
* @param array<int, int> $a
* @param array<int, int> $b
* @param int $baseLog2
* @return array<int, int>
*/
public function multiplyLong(array $a, array $b, $baseLog2 = 16)
{
$a_l = count($a);
$b_l = count($b);
/** @var array<int, int> $r */
$r = array_fill(0, $a_l + $b_l + 1, 0);
$base = 1 << $baseLog2;
for ($i = 0; $i < $a_l; ++$i) {
$a_i = $a[$i];
for ($j = 0; $j < $a_l; ++$j) {
$b_j = $b[$j];
$product = ($a_i * $b_j) + $r[$i + $j];
$carry = ((int) $product >> $baseLog2 & 0xffff);
$r[$i + $j] = ((int) $product - (int) ($carry * $base)) & 0xffff;
$r[$i + $j + 1] += $carry;
}
}
return array_slice($r, 0, 5);
}
/**
* @param int $int
* @return ParagonIE_Sodium_Core32_Int32
*/
public function mulIntFast($int)
{
// Handle negative numbers
$aNeg = ($this->limbs[0] >> 15) & 1;
$bNeg = ($int >> 31) & 1;
$a = array_reverse($this->limbs);
$b = array(
$int & 0xffff,
($int >> 16) & 0xffff
);
if ($aNeg) {
for ($i = 0; $i < 2; ++$i) {
$a[$i] = ($a[$i] ^ 0xffff) & 0xffff;
}
++$a[0];
}
if ($bNeg) {
for ($i = 0; $i < 2; ++$i) {
$b[$i] = ($b[$i] ^ 0xffff) & 0xffff;
}
++$b[0];
}
// Multiply
$res = $this->multiplyLong($a, $b);
// Re-apply negation to results
if ($aNeg !== $bNeg) {
for ($i = 0; $i < 2; ++$i) {
$res[$i] = (0xffff ^ $res[$i]) & 0xffff;
}
// Handle integer overflow
$c = 1;
for ($i = 0; $i < 2; ++$i) {
$res[$i] += $c;
$c = $res[$i] >> 16;
$res[$i] &= 0xffff;
}
}
// Return our values
$return = new ParagonIE_Sodium_Core32_Int32();
$return->limbs = array(
$res[1] & 0xffff,
$res[0] & 0xffff
);
if (count($res) > 2) {
$return->overflow = $res[2] & 0xffff;
}
$return->unsignedInt = $this->unsignedInt;
return $return;
}
/**
* @param ParagonIE_Sodium_Core32_Int32 $right
* @return ParagonIE_Sodium_Core32_Int32
*/
public function mulInt32Fast(ParagonIE_Sodium_Core32_Int32 $right)
{
$aNeg = ($this->limbs[0] >> 15) & 1;
$bNeg = ($right->limbs[0] >> 15) & 1;
$a = array_reverse($this->limbs);
$b = array_reverse($right->limbs);
if ($aNeg) {
for ($i = 0; $i < 2; ++$i) {
$a[$i] = ($a[$i] ^ 0xffff) & 0xffff;
}
++$a[0];
}
if ($bNeg) {
for ($i = 0; $i < 2; ++$i) {
$b[$i] = ($b[$i] ^ 0xffff) & 0xffff;
}
++$b[0];
}
$res = $this->multiplyLong($a, $b);
if ($aNeg !== $bNeg) {
if ($aNeg !== $bNeg) {
for ($i = 0; $i < 2; ++$i) {
$res[$i] = ($res[$i] ^ 0xffff) & 0xffff;
}
$c = 1;
for ($i = 0; $i < 2; ++$i) {
$res[$i] += $c;
$c = $res[$i] >> 16;
$res[$i] &= 0xffff;
}
}
}
$return = new ParagonIE_Sodium_Core32_Int32();
$return->limbs = array(
$res[1] & 0xffff,
$res[0] & 0xffff
);
if (count($res) > 2) {
$return->overflow = $res[2];
}
return $return;
}
/**
* @param int $int
* @param int $size
* @return ParagonIE_Sodium_Core32_Int32
* @throws SodiumException
* @throws TypeError
*/
public function mulInt($int = 0, $size = 0)
{
ParagonIE_Sodium_Core32_Util::declareScalarType($int, 'int', 1);
ParagonIE_Sodium_Core32_Util::declareScalarType($size, 'int', 2);
if (ParagonIE_Sodium_Compat::$fastMult) {
return $this->mulIntFast((int) $int);
}
/** @var int $int */
$int = (int) $int;
/** @var int $size */
$size = (int) $size;
if (!$size) {
$size = 31;
}
/** @var int $size */
$a = clone $this;
$return = new ParagonIE_Sodium_Core32_Int32();
$return->unsignedInt = $this->unsignedInt;
// Initialize:
$ret0 = 0;
$ret1 = 0;
$a0 = $a->limbs[0];
$a1 = $a->limbs[1];
/** @var int $size */
/** @var int $i */
for ($i = $size; $i >= 0; --$i) {
$m = (int) (-($int & 1));
$x0 = $a0 & $m;
$x1 = $a1 & $m;
$ret1 += $x1;
$c = $ret1 >> 16;
$ret0 += $x0 + $c;
$ret0 &= 0xffff;
$ret1 &= 0xffff;
$a1 = ($a1 << 1);
$x1 = $a1 >> 16;
$a0 = ($a0 << 1) | $x1;
$a0 &= 0xffff;
$a1 &= 0xffff;
$int >>= 1;
}
$return->limbs[0] = $ret0;
$return->limbs[1] = $ret1;
return $return;
}
/**
* @param ParagonIE_Sodium_Core32_Int32 $int
* @param int $size
* @return ParagonIE_Sodium_Core32_Int32
* @throws SodiumException
* @throws TypeError
*/
public function mulInt32(ParagonIE_Sodium_Core32_Int32 $int, $size = 0)
{
ParagonIE_Sodium_Core32_Util::declareScalarType($size, 'int', 2);
if (ParagonIE_Sodium_Compat::$fastMult) {
return $this->mulInt32Fast($int);
}
if (!$size) {
$size = 31;
}
/** @var int $size */
$a = clone $this;
$b = clone $int;
$return = new ParagonIE_Sodium_Core32_Int32();
$return->unsignedInt = $this->unsignedInt;
// Initialize:
$ret0 = 0;
$ret1 = 0;
$a0 = $a->limbs[0];
$a1 = $a->limbs[1];
$b0 = $b->limbs[0];
$b1 = $b->limbs[1];
/** @var int $size */
/** @var int $i */
for ($i = $size; $i >= 0; --$i) {
$m = (int) (-($b1 & 1));
$x0 = $a0 & $m;
$x1 = $a1 & $m;
$ret1 += $x1;
$c = $ret1 >> 16;
$ret0 += $x0 + $c;
$ret0 &= 0xffff;
$ret1 &= 0xffff;
$a1 = ($a1 << 1);
$x1 = $a1 >> 16;
$a0 = ($a0 << 1) | $x1;
$a0 &= 0xffff;
$a1 &= 0xffff;
$x0 = ($b0 & 1) << 16;
$b0 = ($b0 >> 1);
$b1 = (($b1 | $x0) >> 1);
$b0 &= 0xffff;
$b1 &= 0xffff;
}
$return->limbs[0] = $ret0;
$return->limbs[1] = $ret1;
return $return;
}
/**
* OR this 32-bit integer with another.
*
* @param ParagonIE_Sodium_Core32_Int32 $b
* @return ParagonIE_Sodium_Core32_Int32
*/
public function orInt32(ParagonIE_Sodium_Core32_Int32 $b)
{
$return = new ParagonIE_Sodium_Core32_Int32();
$return->unsignedInt = $this->unsignedInt;
$return->limbs = array(
(int) ($this->limbs[0] | $b->limbs[0]),
(int) ($this->limbs[1] | $b->limbs[1])
);
/** @var int overflow */
$return->overflow = $this->overflow | $b->overflow;
return $return;
}
/**
* @param int $b
* @return bool
*/
public function isGreaterThan($b = 0)
{
return $this->compareInt($b) > 0;
}
/**
* @param int $b
* @return bool
*/
public function isLessThanInt($b = 0)
{
return $this->compareInt($b) < 0;
}
/**
* @param int $c
* @return ParagonIE_Sodium_Core32_Int32
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArrayAccess
*/
public function rotateLeft($c = 0)
{
ParagonIE_Sodium_Core32_Util::declareScalarType($c, 'int', 1);
/** @var int $c */
$c = (int) $c;
$return = new ParagonIE_Sodium_Core32_Int32();
$return->unsignedInt = $this->unsignedInt;
$c &= 31;
if ($c === 0) {
// NOP, but we want a copy.
$return->limbs = $this->limbs;
} else {
/** @var int $c */
/** @var int $idx_shift */
$idx_shift = ($c >> 4) & 1;
/** @var int $sub_shift */
$sub_shift = $c & 15;
/** @var array<int, int> $limbs */
$limbs =& $return->limbs;
/** @var array<int, int> $myLimbs */
$myLimbs =& $this->limbs;
for ($i = 1; $i >= 0; --$i) {
/** @var int $j */
$j = ($i + $idx_shift) & 1;
/** @var int $k */
$k = ($i + $idx_shift + 1) & 1;
$limbs[$i] = (int) (
(
((int) ($myLimbs[$j]) << $sub_shift)
|
((int) ($myLimbs[$k]) >> (16 - $sub_shift))
) & 0xffff
);
}
}
return $return;
}
/**
* Rotate to the right
*
* @param int $c
* @return ParagonIE_Sodium_Core32_Int32
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArrayAccess
*/
public function rotateRight($c = 0)
{
ParagonIE_Sodium_Core32_Util::declareScalarType($c, 'int', 1);
/** @var int $c */
$c = (int) $c;
$return = new ParagonIE_Sodium_Core32_Int32();
$return->unsignedInt = $this->unsignedInt;
$c &= 31;
/** @var int $c */
if ($c === 0) {
// NOP, but we want a copy.
$return->limbs = $this->limbs;
} else {
/** @var int $c */
/** @var int $idx_shift */
$idx_shift = ($c >> 4) & 1;
/** @var int $sub_shift */
$sub_shift = $c & 15;
/** @var array<int, int> $limbs */
$limbs =& $return->limbs;
/** @var array<int, int> $myLimbs */
$myLimbs =& $this->limbs;
for ($i = 1; $i >= 0; --$i) {
/** @var int $j */
$j = ($i - $idx_shift) & 1;
/** @var int $k */
$k = ($i - $idx_shift - 1) & 1;
$limbs[$i] = (int) (
(
((int) ($myLimbs[$j]) >> (int) ($sub_shift))
|
((int) ($myLimbs[$k]) << (16 - (int) ($sub_shift)))
) & 0xffff
);
}
}
return $return;
}
/**
* @param bool $bool
* @return self
*/
public function setUnsignedInt($bool = false)
{
$this->unsignedInt = !empty($bool);
return $this;
}
/**
* @param int $c
* @return ParagonIE_Sodium_Core32_Int32
* @throws SodiumException
* @throws TypeError
*/
public function shiftLeft($c = 0)
{
ParagonIE_Sodium_Core32_Util::declareScalarType($c, 'int', 1);
/** @var int $c */
$c = (int) $c;
$return = new ParagonIE_Sodium_Core32_Int32();
$return->unsignedInt = $this->unsignedInt;
$c &= 63;
/** @var int $c */
if ($c === 0) {
$return->limbs = $this->limbs;
} elseif ($c < 0) {
/** @var int $c */
return $this->shiftRight(-$c);
} else {
/** @var int $c */
/** @var int $tmp */
$tmp = $this->limbs[1] << $c;
$return->limbs[1] = (int)($tmp & 0xffff);
/** @var int $carry */
$carry = $tmp >> 16;
/** @var int $tmp */
$tmp = ($this->limbs[0] << $c) | ($carry & 0xffff);
$return->limbs[0] = (int) ($tmp & 0xffff);
}
return $return;
}
/**
* @param int $c
* @return ParagonIE_Sodium_Core32_Int32
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedAssignment
* @psalm-suppress MixedOperand
*/
public function shiftRight($c = 0)
{
ParagonIE_Sodium_Core32_Util::declareScalarType($c, 'int', 1);
/** @var int $c */
$c = (int) $c;
$return = new ParagonIE_Sodium_Core32_Int32();
$return->unsignedInt = $this->unsignedInt;
$c &= 63;
/** @var int $c */
if ($c >= 16) {
$return->limbs = array(
(int) ($this->overflow & 0xffff),
(int) ($this->limbs[0])
);
$return->overflow = $this->overflow >> 16;
return $return->shiftRight($c & 15);
}
if ($c === 0) {
$return->limbs = $this->limbs;
} elseif ($c < 0) {
/** @var int $c */
return $this->shiftLeft(-$c);
} else {
if (!is_int($c)) {
throw new TypeError();
}
/** @var int $c */
// $return->limbs[0] = (int) (($this->limbs[0] >> $c) & 0xffff);
$carryLeft = (int) ($this->overflow & ((1 << ($c + 1)) - 1));
$return->limbs[0] = (int) ((($this->limbs[0] >> $c) | ($carryLeft << (16 - $c))) & 0xffff);
$carryRight = (int) ($this->limbs[0] & ((1 << ($c + 1)) - 1));
$return->limbs[1] = (int) ((($this->limbs[1] >> $c) | ($carryRight << (16 - $c))) & 0xffff);
$return->overflow >>= $c;
}
return $return;
}
/**
* Subtract a normal integer from an int32 object.
*
* @param int $int
* @return ParagonIE_Sodium_Core32_Int32
* @throws SodiumException
* @throws TypeError
*/
public function subInt($int)
{
ParagonIE_Sodium_Core32_Util::declareScalarType($int, 'int', 1);
/** @var int $int */
$int = (int) $int;
$return = new ParagonIE_Sodium_Core32_Int32();
$return->unsignedInt = $this->unsignedInt;
/** @var int $tmp */
$tmp = $this->limbs[1] - ($int & 0xffff);
/** @var int $carry */
$carry = $tmp >> 16;
$return->limbs[1] = (int) ($tmp & 0xffff);
/** @var int $tmp */
$tmp = $this->limbs[0] - (($int >> 16) & 0xffff) + $carry;
$return->limbs[0] = (int) ($tmp & 0xffff);
return $return;
}
/**
* Subtract two int32 objects from each other
*
* @param ParagonIE_Sodium_Core32_Int32 $b
* @return ParagonIE_Sodium_Core32_Int32
*/
public function subInt32(ParagonIE_Sodium_Core32_Int32 $b)
{
$return = new ParagonIE_Sodium_Core32_Int32();
$return->unsignedInt = $this->unsignedInt;
/** @var int $tmp */
$tmp = $this->limbs[1] - ($b->limbs[1] & 0xffff);
/** @var int $carry */
$carry = $tmp >> 16;
$return->limbs[1] = (int) ($tmp & 0xffff);
/** @var int $tmp */
$tmp = $this->limbs[0] - ($b->limbs[0] & 0xffff) + $carry;
$return->limbs[0] = (int) ($tmp & 0xffff);
return $return;
}
/**
* XOR this 32-bit integer with another.
*
* @param ParagonIE_Sodium_Core32_Int32 $b
* @return ParagonIE_Sodium_Core32_Int32
*/
public function xorInt32(ParagonIE_Sodium_Core32_Int32 $b)
{
$return = new ParagonIE_Sodium_Core32_Int32();
$return->unsignedInt = $this->unsignedInt;
$return->limbs = array(
(int) ($this->limbs[0] ^ $b->limbs[0]),
(int) ($this->limbs[1] ^ $b->limbs[1])
);
return $return;
}
/**
* @param int $signed
* @return self
* @throws SodiumException
* @throws TypeError
*/
public static function fromInt($signed)
{
ParagonIE_Sodium_Core32_Util::declareScalarType($signed, 'int', 1);;
/** @var int $signed */
$signed = (int) $signed;
return new ParagonIE_Sodium_Core32_Int32(
array(
(int) (($signed >> 16) & 0xffff),
(int) ($signed & 0xffff)
)
);
}
/**
* @param string $string
* @return self
* @throws SodiumException
* @throws TypeError
*/
public static function fromString($string)
{
ParagonIE_Sodium_Core32_Util::declareScalarType($string, 'string', 1);
$string = (string) $string;
if (ParagonIE_Sodium_Core32_Util::strlen($string) !== 4) {
throw new RangeException(
'String must be 4 bytes; ' . ParagonIE_Sodium_Core32_Util::strlen($string) . ' given.'
);
}
$return = new ParagonIE_Sodium_Core32_Int32();
$return->limbs[0] = (int) ((ParagonIE_Sodium_Core32_Util::chrToInt($string[0]) & 0xff) << 8);
$return->limbs[0] |= (ParagonIE_Sodium_Core32_Util::chrToInt($string[1]) & 0xff);
$return->limbs[1] = (int) ((ParagonIE_Sodium_Core32_Util::chrToInt($string[2]) & 0xff) << 8);
$return->limbs[1] |= (ParagonIE_Sodium_Core32_Util::chrToInt($string[3]) & 0xff);
return $return;
}
/**
* @param string $string
* @return self
* @throws SodiumException
* @throws TypeError
*/
public static function fromReverseString($string)
{
ParagonIE_Sodium_Core32_Util::declareScalarType($string, 'string', 1);
$string = (string) $string;
if (ParagonIE_Sodium_Core32_Util::strlen($string) !== 4) {
throw new RangeException(
'String must be 4 bytes; ' . ParagonIE_Sodium_Core32_Util::strlen($string) . ' given.'
);
}
$return = new ParagonIE_Sodium_Core32_Int32();
$return->limbs[0] = (int) ((ParagonIE_Sodium_Core32_Util::chrToInt($string[3]) & 0xff) << 8);
$return->limbs[0] |= (ParagonIE_Sodium_Core32_Util::chrToInt($string[2]) & 0xff);
$return->limbs[1] = (int) ((ParagonIE_Sodium_Core32_Util::chrToInt($string[1]) & 0xff) << 8);
$return->limbs[1] |= (ParagonIE_Sodium_Core32_Util::chrToInt($string[0]) & 0xff);
return $return;
}
/**
* @return array<int, int>
*/
public function toArray()
{
return array((int) ($this->limbs[0] << 16 | $this->limbs[1]));
}
/**
* @return string
* @throws TypeError
*/
public function toString()
{
return
ParagonIE_Sodium_Core32_Util::intToChr(($this->limbs[0] >> 8) & 0xff) .
ParagonIE_Sodium_Core32_Util::intToChr($this->limbs[0] & 0xff) .
ParagonIE_Sodium_Core32_Util::intToChr(($this->limbs[1] >> 8) & 0xff) .
ParagonIE_Sodium_Core32_Util::intToChr($this->limbs[1] & 0xff);
}
/**
* @return int
*/
public function toInt()
{
return (int) (
(($this->limbs[0] & 0xffff) << 16)
|
($this->limbs[1] & 0xffff)
);
}
/**
* @return ParagonIE_Sodium_Core32_Int32
*/
public function toInt32()
{
$return = new ParagonIE_Sodium_Core32_Int32();
$return->limbs[0] = (int) ($this->limbs[0] & 0xffff);
$return->limbs[1] = (int) ($this->limbs[1] & 0xffff);
$return->unsignedInt = $this->unsignedInt;
$return->overflow = (int) ($this->overflow & 0x7fffffff);
return $return;
}
/**
* @return ParagonIE_Sodium_Core32_Int64
*/
public function toInt64()
{
$return = new ParagonIE_Sodium_Core32_Int64();
$return->unsignedInt = $this->unsignedInt;
if ($this->unsignedInt) {
$return->limbs[0] += (($this->overflow >> 16) & 0xffff);
$return->limbs[1] += (($this->overflow) & 0xffff);
} else {
$neg = -(($this->limbs[0] >> 15) & 1);
$return->limbs[0] = (int)($neg & 0xffff);
$return->limbs[1] = (int)($neg & 0xffff);
}
$return->limbs[2] = (int) ($this->limbs[0] & 0xffff);
$return->limbs[3] = (int) ($this->limbs[1] & 0xffff);
return $return;
}
/**
* @return string
* @throws TypeError
*/
public function toReverseString()
{
return ParagonIE_Sodium_Core32_Util::intToChr($this->limbs[1] & 0xff) .
ParagonIE_Sodium_Core32_Util::intToChr(($this->limbs[1] >> 8) & 0xff) .
ParagonIE_Sodium_Core32_Util::intToChr($this->limbs[0] & 0xff) .
ParagonIE_Sodium_Core32_Util::intToChr(($this->limbs[0] >> 8) & 0xff);
}
/**
* @return string
*/
public function __toString()
{
try {
return $this->toString();
} catch (TypeError $ex) {
// PHP engine can't handle exceptions from __toString()
return '';
}
}
}
Core32/ChaCha20/IetfCtx.php 0000604 00000002737 15133021213 0011125 0 ustar 00 <?php
if (class_exists('ParagonIE_Sodium_Core_ChaCha20_IetfCtx', false)) {
return;
}
/**
* Class ParagonIE_Sodium_Core32_ChaCha20_IetfCtx
*/
class ParagonIE_Sodium_Core32_ChaCha20_IetfCtx extends ParagonIE_Sodium_Core32_ChaCha20_Ctx
{
/**
* ParagonIE_Sodium_Core_ChaCha20_IetfCtx constructor.
*
* @internal You should not use this directly from another application
*
* @param string $key ChaCha20 key.
* @param string $iv Initialization Vector (a.k.a. nonce).
* @param string $counter The initial counter value.
* Defaults to 4 0x00 bytes.
* @throws InvalidArgumentException
* @throws SodiumException
* @throws TypeError
*/
public function __construct($key = '', $iv = '', $counter = '')
{
if (self::strlen($iv) !== 12) {
throw new InvalidArgumentException('ChaCha20 expects a 96-bit nonce in IETF mode.');
}
parent::__construct($key, self::substr($iv, 0, 8), $counter);
if (!empty($counter)) {
$this->container[12] = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($counter, 0, 4));
}
$this->container[13] = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($iv, 0, 4));
$this->container[14] = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($iv, 4, 4));
$this->container[15] = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($iv, 8, 4));
}
}
Core32/ChaCha20/Ctx.php 0000644 00000011450 15133021213 0010311 0 ustar 00 <?php
if (class_exists('ParagonIE_Sodium_Core_ChaCha20_Ctx', false)) {
return;
}
/**
* Class ParagonIE_Sodium_Core32_ChaCha20_Ctx
*/
class ParagonIE_Sodium_Core32_ChaCha20_Ctx extends ParagonIE_Sodium_Core32_Util implements ArrayAccess
{
/**
* @var SplFixedArray internally, <int, ParagonIE_Sodium_Core32_Int32>
*/
protected $container;
/**
* ParagonIE_Sodium_Core_ChaCha20_Ctx constructor.
*
* @internal You should not use this directly from another application
*
* @param string $key ChaCha20 key.
* @param string $iv Initialization Vector (a.k.a. nonce).
* @param string $counter The initial counter value.
* Defaults to 8 0x00 bytes.
* @throws InvalidArgumentException
* @throws SodiumException
* @throws TypeError
*/
public function __construct($key = '', $iv = '', $counter = '')
{
if (self::strlen($key) !== 32) {
throw new InvalidArgumentException('ChaCha20 expects a 256-bit key.');
}
if (self::strlen($iv) !== 8) {
throw new InvalidArgumentException('ChaCha20 expects a 64-bit nonce.');
}
$this->container = new SplFixedArray(16);
/* "expand 32-byte k" as per ChaCha20 spec */
$this->container[0] = new ParagonIE_Sodium_Core32_Int32(array(0x6170, 0x7865));
$this->container[1] = new ParagonIE_Sodium_Core32_Int32(array(0x3320, 0x646e));
$this->container[2] = new ParagonIE_Sodium_Core32_Int32(array(0x7962, 0x2d32));
$this->container[3] = new ParagonIE_Sodium_Core32_Int32(array(0x6b20, 0x6574));
$this->container[4] = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($key, 0, 4));
$this->container[5] = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($key, 4, 4));
$this->container[6] = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($key, 8, 4));
$this->container[7] = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($key, 12, 4));
$this->container[8] = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($key, 16, 4));
$this->container[9] = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($key, 20, 4));
$this->container[10] = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($key, 24, 4));
$this->container[11] = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($key, 28, 4));
if (empty($counter)) {
$this->container[12] = new ParagonIE_Sodium_Core32_Int32();
$this->container[13] = new ParagonIE_Sodium_Core32_Int32();
} else {
$this->container[12] = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($counter, 0, 4));
$this->container[13] = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($counter, 4, 4));
}
$this->container[14] = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($iv, 0, 4));
$this->container[15] = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($iv, 4, 4));
}
/**
* @internal You should not use this directly from another application
*
* @param int $offset
* @param int|ParagonIE_Sodium_Core32_Int32 $value
* @return void
*/
#[ReturnTypeWillChange]
public function offsetSet($offset, $value)
{
if (!is_int($offset)) {
throw new InvalidArgumentException('Expected an integer');
}
if ($value instanceof ParagonIE_Sodium_Core32_Int32) {
/*
} elseif (is_int($value)) {
$value = ParagonIE_Sodium_Core32_Int32::fromInt($value);
*/
} else {
throw new InvalidArgumentException('Expected an integer');
}
$this->container[$offset] = $value;
}
/**
* @internal You should not use this directly from another application
*
* @param int $offset
* @return bool
* @psalm-suppress MixedArrayOffset
*/
#[ReturnTypeWillChange]
public function offsetExists($offset)
{
return isset($this->container[$offset]);
}
/**
* @internal You should not use this directly from another application
*
* @param int $offset
* @return void
* @psalm-suppress MixedArrayOffset
*/
#[ReturnTypeWillChange]
public function offsetUnset($offset)
{
unset($this->container[$offset]);
}
/**
* @internal You should not use this directly from another application
*
* @param int $offset
* @return mixed|null
* @psalm-suppress MixedArrayOffset
*/
#[ReturnTypeWillChange]
public function offsetGet($offset)
{
return isset($this->container[$offset])
? $this->container[$offset]
: null;
}
}
Core32/SipHash.php 0000604 00000014725 15133021213 0007645 0 ustar 00 <?php
if (class_exists('ParagonIE_Sodium_Core32_SipHash', false)) {
return;
}
/**
* Class ParagonIE_SodiumCompat_Core32_SipHash
*
* Only uses 32-bit arithmetic, while the original SipHash used 64-bit integers
*/
class ParagonIE_Sodium_Core32_SipHash extends ParagonIE_Sodium_Core32_Util
{
/**
* @internal You should not use this directly from another application
*
* @param array<int, ParagonIE_Sodium_Core32_Int64> $v
* @return array<int, ParagonIE_Sodium_Core32_Int64>
*/
public static function sipRound(array $v)
{
# v0 += v1;
$v[0] = $v[0]->addInt64($v[1]);
# v1 = ROTL(v1, 13);
$v[1] = $v[1]->rotateLeft(13);
# v1 ^= v0;
$v[1] = $v[1]->xorInt64($v[0]);
# v0=ROTL(v0,32);
$v[0] = $v[0]->rotateLeft(32);
# v2 += v3;
$v[2] = $v[2]->addInt64($v[3]);
# v3=ROTL(v3,16);
$v[3] = $v[3]->rotateLeft(16);
# v3 ^= v2;
$v[3] = $v[3]->xorInt64($v[2]);
# v0 += v3;
$v[0] = $v[0]->addInt64($v[3]);
# v3=ROTL(v3,21);
$v[3] = $v[3]->rotateLeft(21);
# v3 ^= v0;
$v[3] = $v[3]->xorInt64($v[0]);
# v2 += v1;
$v[2] = $v[2]->addInt64($v[1]);
# v1=ROTL(v1,17);
$v[1] = $v[1]->rotateLeft(17);
# v1 ^= v2;
$v[1] = $v[1]->xorInt64($v[2]);
# v2=ROTL(v2,32)
$v[2] = $v[2]->rotateLeft(32);
return $v;
}
/**
* @internal You should not use this directly from another application
*
* @param string $in
* @param string $key
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function sipHash24($in, $key)
{
$inlen = self::strlen($in);
# /* "somepseudorandomlygeneratedbytes" */
# u64 v0 = 0x736f6d6570736575ULL;
# u64 v1 = 0x646f72616e646f6dULL;
# u64 v2 = 0x6c7967656e657261ULL;
# u64 v3 = 0x7465646279746573ULL;
$v = array(
new ParagonIE_Sodium_Core32_Int64(
array(0x736f, 0x6d65, 0x7073, 0x6575)
),
new ParagonIE_Sodium_Core32_Int64(
array(0x646f, 0x7261, 0x6e64, 0x6f6d)
),
new ParagonIE_Sodium_Core32_Int64(
array(0x6c79, 0x6765, 0x6e65, 0x7261)
),
new ParagonIE_Sodium_Core32_Int64(
array(0x7465, 0x6462, 0x7974, 0x6573)
)
);
# u64 k0 = LOAD64_LE( k );
# u64 k1 = LOAD64_LE( k + 8 );
$k = array(
ParagonIE_Sodium_Core32_Int64::fromReverseString(
self::substr($key, 0, 8)
),
ParagonIE_Sodium_Core32_Int64::fromReverseString(
self::substr($key, 8, 8)
)
);
# b = ( ( u64 )inlen ) << 56;
$b = new ParagonIE_Sodium_Core32_Int64(
array(($inlen << 8) & 0xffff, 0, 0, 0)
);
# v3 ^= k1;
$v[3] = $v[3]->xorInt64($k[1]);
# v2 ^= k0;
$v[2] = $v[2]->xorInt64($k[0]);
# v1 ^= k1;
$v[1] = $v[1]->xorInt64($k[1]);
# v0 ^= k0;
$v[0] = $v[0]->xorInt64($k[0]);
$left = $inlen;
# for ( ; in != end; in += 8 )
while ($left >= 8) {
# m = LOAD64_LE( in );
$m = ParagonIE_Sodium_Core32_Int64::fromReverseString(
self::substr($in, 0, 8)
);
# v3 ^= m;
$v[3] = $v[3]->xorInt64($m);
# SIPROUND;
# SIPROUND;
$v = self::sipRound($v);
$v = self::sipRound($v);
# v0 ^= m;
$v[0] = $v[0]->xorInt64($m);
$in = self::substr($in, 8);
$left -= 8;
}
# switch( left )
# {
# case 7: b |= ( ( u64 )in[ 6] ) << 48;
# case 6: b |= ( ( u64 )in[ 5] ) << 40;
# case 5: b |= ( ( u64 )in[ 4] ) << 32;
# case 4: b |= ( ( u64 )in[ 3] ) << 24;
# case 3: b |= ( ( u64 )in[ 2] ) << 16;
# case 2: b |= ( ( u64 )in[ 1] ) << 8;
# case 1: b |= ( ( u64 )in[ 0] ); break;
# case 0: break;
# }
switch ($left) {
case 7:
$b = $b->orInt64(
ParagonIE_Sodium_Core32_Int64::fromInts(
0, self::chrToInt($in[6]) << 16
)
);
case 6:
$b = $b->orInt64(
ParagonIE_Sodium_Core32_Int64::fromInts(
0, self::chrToInt($in[5]) << 8
)
);
case 5:
$b = $b->orInt64(
ParagonIE_Sodium_Core32_Int64::fromInts(
0, self::chrToInt($in[4])
)
);
case 4:
$b = $b->orInt64(
ParagonIE_Sodium_Core32_Int64::fromInts(
self::chrToInt($in[3]) << 24, 0
)
);
case 3:
$b = $b->orInt64(
ParagonIE_Sodium_Core32_Int64::fromInts(
self::chrToInt($in[2]) << 16, 0
)
);
case 2:
$b = $b->orInt64(
ParagonIE_Sodium_Core32_Int64::fromInts(
self::chrToInt($in[1]) << 8, 0
)
);
case 1:
$b = $b->orInt64(
ParagonIE_Sodium_Core32_Int64::fromInts(
self::chrToInt($in[0]), 0
)
);
case 0:
break;
}
# v3 ^= b;
$v[3] = $v[3]->xorInt64($b);
# SIPROUND;
# SIPROUND;
$v = self::sipRound($v);
$v = self::sipRound($v);
# v0 ^= b;
$v[0] = $v[0]->xorInt64($b);
// Flip the lower 8 bits of v2 which is ($v[4], $v[5]) in our implementation
# v2 ^= 0xff;
$v[2]->limbs[3] ^= 0xff;
# SIPROUND;
# SIPROUND;
# SIPROUND;
# SIPROUND;
$v = self::sipRound($v);
$v = self::sipRound($v);
$v = self::sipRound($v);
$v = self::sipRound($v);
# b = v0 ^ v1 ^ v2 ^ v3;
# STORE64_LE( out, b );
return $v[0]
->xorInt64($v[1])
->xorInt64($v[2])
->xorInt64($v[3])
->toReverseString();
}
}
Core32/Salsa20.php 0000604 00000026362 15133021213 0007513 0 ustar 00 <?php
if (class_exists('ParagonIE_Sodium_Core32_Salsa20', false)) {
return;
}
/**
* Class ParagonIE_Sodium_Core32_Salsa20
*/
abstract class ParagonIE_Sodium_Core32_Salsa20 extends ParagonIE_Sodium_Core32_Util
{
const ROUNDS = 20;
/**
* Calculate an salsa20 hash of a single block
*
* @internal You should not use this directly from another application
*
* @param string $in
* @param string $k
* @param string|null $c
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function core_salsa20($in, $k, $c = null)
{
/**
* @var ParagonIE_Sodium_Core32_Int32 $x0
* @var ParagonIE_Sodium_Core32_Int32 $x1
* @var ParagonIE_Sodium_Core32_Int32 $x2
* @var ParagonIE_Sodium_Core32_Int32 $x3
* @var ParagonIE_Sodium_Core32_Int32 $x4
* @var ParagonIE_Sodium_Core32_Int32 $x5
* @var ParagonIE_Sodium_Core32_Int32 $x6
* @var ParagonIE_Sodium_Core32_Int32 $x7
* @var ParagonIE_Sodium_Core32_Int32 $x8
* @var ParagonIE_Sodium_Core32_Int32 $x9
* @var ParagonIE_Sodium_Core32_Int32 $x10
* @var ParagonIE_Sodium_Core32_Int32 $x11
* @var ParagonIE_Sodium_Core32_Int32 $x12
* @var ParagonIE_Sodium_Core32_Int32 $x13
* @var ParagonIE_Sodium_Core32_Int32 $x14
* @var ParagonIE_Sodium_Core32_Int32 $x15
* @var ParagonIE_Sodium_Core32_Int32 $j0
* @var ParagonIE_Sodium_Core32_Int32 $j1
* @var ParagonIE_Sodium_Core32_Int32 $j2
* @var ParagonIE_Sodium_Core32_Int32 $j3
* @var ParagonIE_Sodium_Core32_Int32 $j4
* @var ParagonIE_Sodium_Core32_Int32 $j5
* @var ParagonIE_Sodium_Core32_Int32 $j6
* @var ParagonIE_Sodium_Core32_Int32 $j7
* @var ParagonIE_Sodium_Core32_Int32 $j8
* @var ParagonIE_Sodium_Core32_Int32 $j9
* @var ParagonIE_Sodium_Core32_Int32 $j10
* @var ParagonIE_Sodium_Core32_Int32 $j11
* @var ParagonIE_Sodium_Core32_Int32 $j12
* @var ParagonIE_Sodium_Core32_Int32 $j13
* @var ParagonIE_Sodium_Core32_Int32 $j14
* @var ParagonIE_Sodium_Core32_Int32 $j15
*/
if (self::strlen($k) < 32) {
throw new RangeException('Key must be 32 bytes long');
}
if ($c === null) {
$x0 = new ParagonIE_Sodium_Core32_Int32(array(0x6170, 0x7865));
$x5 = new ParagonIE_Sodium_Core32_Int32(array(0x3320, 0x646e));
$x10 = new ParagonIE_Sodium_Core32_Int32(array(0x7962, 0x2d32));
$x15 = new ParagonIE_Sodium_Core32_Int32(array(0x6b20, 0x6574));
} else {
$x0 = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($c, 0, 4));
$x5 = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($c, 4, 4));
$x10 = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($c, 8, 4));
$x15 = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($c, 12, 4));
}
$x1 = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($k, 0, 4));
$x2 = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($k, 4, 4));
$x3 = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($k, 8, 4));
$x4 = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($k, 12, 4));
$x6 = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($in, 0, 4));
$x7 = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($in, 4, 4));
$x8 = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($in, 8, 4));
$x9 = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($in, 12, 4));
$x11 = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($k, 16, 4));
$x12 = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($k, 20, 4));
$x13 = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($k, 24, 4));
$x14 = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($k, 28, 4));
$j0 = clone $x0;
$j1 = clone $x1;
$j2 = clone $x2;
$j3 = clone $x3;
$j4 = clone $x4;
$j5 = clone $x5;
$j6 = clone $x6;
$j7 = clone $x7;
$j8 = clone $x8;
$j9 = clone $x9;
$j10 = clone $x10;
$j11 = clone $x11;
$j12 = clone $x12;
$j13 = clone $x13;
$j14 = clone $x14;
$j15 = clone $x15;
for ($i = self::ROUNDS; $i > 0; $i -= 2) {
$x4 = $x4->xorInt32($x0->addInt32($x12)->rotateLeft(7));
$x8 = $x8->xorInt32($x4->addInt32($x0)->rotateLeft(9));
$x12 = $x12->xorInt32($x8->addInt32($x4)->rotateLeft(13));
$x0 = $x0->xorInt32($x12->addInt32($x8)->rotateLeft(18));
$x9 = $x9->xorInt32($x5->addInt32($x1)->rotateLeft(7));
$x13 = $x13->xorInt32($x9->addInt32($x5)->rotateLeft(9));
$x1 = $x1->xorInt32($x13->addInt32($x9)->rotateLeft(13));
$x5 = $x5->xorInt32($x1->addInt32($x13)->rotateLeft(18));
$x14 = $x14->xorInt32($x10->addInt32($x6)->rotateLeft(7));
$x2 = $x2->xorInt32($x14->addInt32($x10)->rotateLeft(9));
$x6 = $x6->xorInt32($x2->addInt32($x14)->rotateLeft(13));
$x10 = $x10->xorInt32($x6->addInt32($x2)->rotateLeft(18));
$x3 = $x3->xorInt32($x15->addInt32($x11)->rotateLeft(7));
$x7 = $x7->xorInt32($x3->addInt32($x15)->rotateLeft(9));
$x11 = $x11->xorInt32($x7->addInt32($x3)->rotateLeft(13));
$x15 = $x15->xorInt32($x11->addInt32($x7)->rotateLeft(18));
$x1 = $x1->xorInt32($x0->addInt32($x3)->rotateLeft(7));
$x2 = $x2->xorInt32($x1->addInt32($x0)->rotateLeft(9));
$x3 = $x3->xorInt32($x2->addInt32($x1)->rotateLeft(13));
$x0 = $x0->xorInt32($x3->addInt32($x2)->rotateLeft(18));
$x6 = $x6->xorInt32($x5->addInt32($x4)->rotateLeft(7));
$x7 = $x7->xorInt32($x6->addInt32($x5)->rotateLeft(9));
$x4 = $x4->xorInt32($x7->addInt32($x6)->rotateLeft(13));
$x5 = $x5->xorInt32($x4->addInt32($x7)->rotateLeft(18));
$x11 = $x11->xorInt32($x10->addInt32($x9)->rotateLeft(7));
$x8 = $x8->xorInt32($x11->addInt32($x10)->rotateLeft(9));
$x9 = $x9->xorInt32($x8->addInt32($x11)->rotateLeft(13));
$x10 = $x10->xorInt32($x9->addInt32($x8)->rotateLeft(18));
$x12 = $x12->xorInt32($x15->addInt32($x14)->rotateLeft(7));
$x13 = $x13->xorInt32($x12->addInt32($x15)->rotateLeft(9));
$x14 = $x14->xorInt32($x13->addInt32($x12)->rotateLeft(13));
$x15 = $x15->xorInt32($x14->addInt32($x13)->rotateLeft(18));
}
$x0 = $x0->addInt32($j0);
$x1 = $x1->addInt32($j1);
$x2 = $x2->addInt32($j2);
$x3 = $x3->addInt32($j3);
$x4 = $x4->addInt32($j4);
$x5 = $x5->addInt32($j5);
$x6 = $x6->addInt32($j6);
$x7 = $x7->addInt32($j7);
$x8 = $x8->addInt32($j8);
$x9 = $x9->addInt32($j9);
$x10 = $x10->addInt32($j10);
$x11 = $x11->addInt32($j11);
$x12 = $x12->addInt32($j12);
$x13 = $x13->addInt32($j13);
$x14 = $x14->addInt32($j14);
$x15 = $x15->addInt32($j15);
return $x0->toReverseString() .
$x1->toReverseString() .
$x2->toReverseString() .
$x3->toReverseString() .
$x4->toReverseString() .
$x5->toReverseString() .
$x6->toReverseString() .
$x7->toReverseString() .
$x8->toReverseString() .
$x9->toReverseString() .
$x10->toReverseString() .
$x11->toReverseString() .
$x12->toReverseString() .
$x13->toReverseString() .
$x14->toReverseString() .
$x15->toReverseString();
}
/**
* @internal You should not use this directly from another application
*
* @param int $len
* @param string $nonce
* @param string $key
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function salsa20($len, $nonce, $key)
{
if (self::strlen($key) !== 32) {
throw new RangeException('Key must be 32 bytes long');
}
$kcopy = '' . $key;
$in = self::substr($nonce, 0, 8) . str_repeat("\0", 8);
$c = '';
while ($len >= 64) {
$c .= self::core_salsa20($in, $kcopy, null);
$u = 1;
// Internal counter.
for ($i = 8; $i < 16; ++$i) {
$u += self::chrToInt($in[$i]);
$in[$i] = self::intToChr($u & 0xff);
$u >>= 8;
}
$len -= 64;
}
if ($len > 0) {
$c .= self::substr(
self::core_salsa20($in, $kcopy, null),
0,
$len
);
}
try {
ParagonIE_Sodium_Compat::memzero($kcopy);
} catch (SodiumException $ex) {
$kcopy = null;
}
return $c;
}
/**
* @internal You should not use this directly from another application
*
* @param string $m
* @param string $n
* @param int $ic
* @param string $k
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function salsa20_xor_ic($m, $n, $ic, $k)
{
$mlen = self::strlen($m);
if ($mlen < 1) {
return '';
}
$kcopy = self::substr($k, 0, 32);
$in = self::substr($n, 0, 8);
// Initialize the counter
$in .= ParagonIE_Sodium_Core32_Util::store64_le($ic);
$c = '';
while ($mlen >= 64) {
$block = self::core_salsa20($in, $kcopy, null);
$c .= self::xorStrings(
self::substr($m, 0, 64),
self::substr($block, 0, 64)
);
$u = 1;
for ($i = 8; $i < 16; ++$i) {
$u += self::chrToInt($in[$i]);
$in[$i] = self::intToChr($u & 0xff);
$u >>= 8;
}
$mlen -= 64;
$m = self::substr($m, 64);
}
if ($mlen) {
$block = self::core_salsa20($in, $kcopy, null);
$c .= self::xorStrings(
self::substr($m, 0, $mlen),
self::substr($block, 0, $mlen)
);
}
try {
ParagonIE_Sodium_Compat::memzero($block);
ParagonIE_Sodium_Compat::memzero($kcopy);
} catch (SodiumException $ex) {
$block = null;
$kcopy = null;
}
return $c;
}
/**
* @internal You should not use this directly from another application
*
* @param string $message
* @param string $nonce
* @param string $key
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function salsa20_xor($message, $nonce, $key)
{
return self::xorStrings(
$message,
self::salsa20(
self::strlen($message),
$nonce,
$key
)
);
}
}
Core32/BLAKE2b.php 0000604 00000053464 15133021213 0007353 0 ustar 00 <?php
if (class_exists('ParagonIE_Sodium_Core_BLAKE2b', false)) {
return;
}
/**
* Class ParagonIE_Sodium_Core_BLAKE2b
*
* Based on the work of Devi Mandiri in devi/salt.
*/
abstract class ParagonIE_Sodium_Core32_BLAKE2b extends ParagonIE_Sodium_Core_Util
{
/**
* @var SplFixedArray
*/
public static $iv;
/**
* @var array<int, array<int, int>>
*/
public static $sigma = array(
array( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15),
array( 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3),
array( 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4),
array( 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8),
array( 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13),
array( 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9),
array( 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11),
array( 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10),
array( 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5),
array( 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0),
array( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15),
array( 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3)
);
const BLOCKBYTES = 128;
const OUTBYTES = 64;
const KEYBYTES = 64;
/**
* Turn two 32-bit integers into a fixed array representing a 64-bit integer.
*
* @internal You should not use this directly from another application
*
* @param int $high
* @param int $low
* @return ParagonIE_Sodium_Core32_Int64
* @throws SodiumException
* @throws TypeError
*/
public static function new64($high, $low)
{
return ParagonIE_Sodium_Core32_Int64::fromInts($low, $high);
}
/**
* Convert an arbitrary number into an SplFixedArray of two 32-bit integers
* that represents a 64-bit integer.
*
* @internal You should not use this directly from another application
*
* @param int $num
* @return ParagonIE_Sodium_Core32_Int64
* @throws SodiumException
* @throws TypeError
*/
protected static function to64($num)
{
list($hi, $lo) = self::numericTo64BitInteger($num);
return self::new64($hi, $lo);
}
/**
* Adds two 64-bit integers together, returning their sum as a SplFixedArray
* containing two 32-bit integers (representing a 64-bit integer).
*
* @internal You should not use this directly from another application
*
* @param ParagonIE_Sodium_Core32_Int64 $x
* @param ParagonIE_Sodium_Core32_Int64 $y
* @return ParagonIE_Sodium_Core32_Int64
*/
protected static function add64($x, $y)
{
return $x->addInt64($y);
}
/**
* @internal You should not use this directly from another application
*
* @param ParagonIE_Sodium_Core32_Int64 $x
* @param ParagonIE_Sodium_Core32_Int64 $y
* @param ParagonIE_Sodium_Core32_Int64 $z
* @return ParagonIE_Sodium_Core32_Int64
*/
public static function add364($x, $y, $z)
{
return $x->addInt64($y)->addInt64($z);
}
/**
* @internal You should not use this directly from another application
*
* @param ParagonIE_Sodium_Core32_Int64 $x
* @param ParagonIE_Sodium_Core32_Int64 $y
* @return ParagonIE_Sodium_Core32_Int64
* @throws TypeError
*/
public static function xor64(ParagonIE_Sodium_Core32_Int64 $x, ParagonIE_Sodium_Core32_Int64 $y)
{
return $x->xorInt64($y);
}
/**
* @internal You should not use this directly from another application
*
* @param ParagonIE_Sodium_Core32_Int64 $x
* @param int $c
* @return ParagonIE_Sodium_Core32_Int64
* @throws SodiumException
* @throws TypeError
*/
public static function rotr64(ParagonIE_Sodium_Core32_Int64 $x, $c)
{
return $x->rotateRight($c);
}
/**
* @internal You should not use this directly from another application
*
* @param SplFixedArray $x
* @param int $i
* @return ParagonIE_Sodium_Core32_Int64
* @throws SodiumException
* @throws TypeError
*/
public static function load64($x, $i)
{
/** @var int $l */
$l = (int) ($x[$i])
| ((int) ($x[$i+1]) << 8)
| ((int) ($x[$i+2]) << 16)
| ((int) ($x[$i+3]) << 24);
/** @var int $h */
$h = (int) ($x[$i+4])
| ((int) ($x[$i+5]) << 8)
| ((int) ($x[$i+6]) << 16)
| ((int) ($x[$i+7]) << 24);
return self::new64($h, $l);
}
/**
* @internal You should not use this directly from another application
*
* @param SplFixedArray $x
* @param int $i
* @param ParagonIE_Sodium_Core32_Int64 $u
* @return void
* @throws TypeError
* @psalm-suppress MixedArgument
* @psalm-suppress MixedAssignment
* @psalm-suppress MixedArrayAccess
* @psalm-suppress MixedArrayAssignment
* @psalm-suppress MixedArrayOffset
*/
public static function store64(SplFixedArray $x, $i, ParagonIE_Sodium_Core32_Int64 $u)
{
$v = clone $u;
$maxLength = $x->getSize() - 1;
for ($j = 0; $j < 8; ++$j) {
$k = 3 - ($j >> 1);
$x[$i] = $v->limbs[$k] & 0xff;
if (++$i > $maxLength) {
return;
}
$v->limbs[$k] >>= 8;
}
}
/**
* This just sets the $iv static variable.
*
* @internal You should not use this directly from another application
*
* @return void
* @throws SodiumException
* @throws TypeError
*/
public static function pseudoConstructor()
{
static $called = false;
if ($called) {
return;
}
self::$iv = new SplFixedArray(8);
self::$iv[0] = self::new64(0x6a09e667, 0xf3bcc908);
self::$iv[1] = self::new64(0xbb67ae85, 0x84caa73b);
self::$iv[2] = self::new64(0x3c6ef372, 0xfe94f82b);
self::$iv[3] = self::new64(0xa54ff53a, 0x5f1d36f1);
self::$iv[4] = self::new64(0x510e527f, 0xade682d1);
self::$iv[5] = self::new64(0x9b05688c, 0x2b3e6c1f);
self::$iv[6] = self::new64(0x1f83d9ab, 0xfb41bd6b);
self::$iv[7] = self::new64(0x5be0cd19, 0x137e2179);
$called = true;
}
/**
* Returns a fresh BLAKE2 context.
*
* @internal You should not use this directly from another application
*
* @return SplFixedArray
* @throws TypeError
* @psalm-suppress MixedArgument
* @psalm-suppress MixedAssignment
* @psalm-suppress MixedArrayAccess
* @psalm-suppress MixedArrayAssignment
* @psalm-suppress MixedArrayOffset
* @throws SodiumException
* @throws TypeError
*/
protected static function context()
{
$ctx = new SplFixedArray(6);
$ctx[0] = new SplFixedArray(8); // h
$ctx[1] = new SplFixedArray(2); // t
$ctx[2] = new SplFixedArray(2); // f
$ctx[3] = new SplFixedArray(256); // buf
$ctx[4] = 0; // buflen
$ctx[5] = 0; // last_node (uint8_t)
for ($i = 8; $i--;) {
$ctx[0][$i] = self::$iv[$i];
}
for ($i = 256; $i--;) {
$ctx[3][$i] = 0;
}
$zero = self::new64(0, 0);
$ctx[1][0] = $zero;
$ctx[1][1] = $zero;
$ctx[2][0] = $zero;
$ctx[2][1] = $zero;
return $ctx;
}
/**
* @internal You should not use this directly from another application
*
* @param SplFixedArray $ctx
* @param SplFixedArray $buf
* @return void
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArgument
* @psalm-suppress MixedArrayAccess
* @psalm-suppress MixedArrayAssignment
* @psalm-suppress MixedAssignment
*/
protected static function compress(SplFixedArray $ctx, SplFixedArray $buf)
{
$m = new SplFixedArray(16);
$v = new SplFixedArray(16);
for ($i = 16; $i--;) {
$m[$i] = self::load64($buf, $i << 3);
}
for ($i = 8; $i--;) {
$v[$i] = $ctx[0][$i];
}
$v[ 8] = self::$iv[0];
$v[ 9] = self::$iv[1];
$v[10] = self::$iv[2];
$v[11] = self::$iv[3];
$v[12] = self::xor64($ctx[1][0], self::$iv[4]);
$v[13] = self::xor64($ctx[1][1], self::$iv[5]);
$v[14] = self::xor64($ctx[2][0], self::$iv[6]);
$v[15] = self::xor64($ctx[2][1], self::$iv[7]);
for ($r = 0; $r < 12; ++$r) {
$v = self::G($r, 0, 0, 4, 8, 12, $v, $m);
$v = self::G($r, 1, 1, 5, 9, 13, $v, $m);
$v = self::G($r, 2, 2, 6, 10, 14, $v, $m);
$v = self::G($r, 3, 3, 7, 11, 15, $v, $m);
$v = self::G($r, 4, 0, 5, 10, 15, $v, $m);
$v = self::G($r, 5, 1, 6, 11, 12, $v, $m);
$v = self::G($r, 6, 2, 7, 8, 13, $v, $m);
$v = self::G($r, 7, 3, 4, 9, 14, $v, $m);
}
for ($i = 8; $i--;) {
$ctx[0][$i] = self::xor64(
$ctx[0][$i], self::xor64($v[$i], $v[$i+8])
);
}
}
/**
* @internal You should not use this directly from another application
*
* @param int $r
* @param int $i
* @param int $a
* @param int $b
* @param int $c
* @param int $d
* @param SplFixedArray $v
* @param SplFixedArray $m
* @return SplFixedArray
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArgument
* @psalm-suppress MixedArrayOffset
*/
public static function G($r, $i, $a, $b, $c, $d, SplFixedArray $v, SplFixedArray $m)
{
$v[$a] = self::add364($v[$a], $v[$b], $m[self::$sigma[$r][$i << 1]]);
$v[$d] = self::rotr64(self::xor64($v[$d], $v[$a]), 32);
$v[$c] = self::add64($v[$c], $v[$d]);
$v[$b] = self::rotr64(self::xor64($v[$b], $v[$c]), 24);
$v[$a] = self::add364($v[$a], $v[$b], $m[self::$sigma[$r][($i << 1) + 1]]);
$v[$d] = self::rotr64(self::xor64($v[$d], $v[$a]), 16);
$v[$c] = self::add64($v[$c], $v[$d]);
$v[$b] = self::rotr64(self::xor64($v[$b], $v[$c]), 63);
return $v;
}
/**
* @internal You should not use this directly from another application
*
* @param SplFixedArray $ctx
* @param int $inc
* @return void
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArgument
* @psalm-suppress MixedArrayAccess
* @psalm-suppress MixedArrayAssignment
*/
public static function increment_counter($ctx, $inc)
{
if ($inc < 0) {
throw new SodiumException('Increasing by a negative number makes no sense.');
}
$t = self::to64($inc);
# S->t is $ctx[1] in our implementation
# S->t[0] = ( uint64_t )( t >> 0 );
$ctx[1][0] = self::add64($ctx[1][0], $t);
# S->t[1] += ( S->t[0] < inc );
if (!($ctx[1][0] instanceof ParagonIE_Sodium_Core32_Int64)) {
throw new TypeError('Not an int64');
}
/** @var ParagonIE_Sodium_Core32_Int64 $c*/
$c = $ctx[1][0];
if ($c->isLessThanInt($inc)) {
$ctx[1][1] = self::add64($ctx[1][1], self::to64(1));
}
}
/**
* @internal You should not use this directly from another application
*
* @param SplFixedArray $ctx
* @param SplFixedArray $p
* @param int $plen
* @return void
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArgument
* @psalm-suppress MixedAssignment
* @psalm-suppress MixedArrayAccess
* @psalm-suppress MixedArrayAssignment
* @psalm-suppress MixedArrayOffset
* @psalm-suppress MixedMethodCall
* @psalm-suppress MixedOperand
*/
public static function update(SplFixedArray $ctx, SplFixedArray $p, $plen)
{
self::pseudoConstructor();
$offset = 0;
while ($plen > 0) {
$left = $ctx[4];
$fill = 256 - $left;
if ($plen > $fill) {
# memcpy( S->buf + left, in, fill ); /* Fill buffer */
for ($i = $fill; $i--;) {
$ctx[3][$i + $left] = $p[$i + $offset];
}
# S->buflen += fill;
$ctx[4] += $fill;
# blake2b_increment_counter( S, BLAKE2B_BLOCKBYTES );
self::increment_counter($ctx, 128);
# blake2b_compress( S, S->buf ); /* Compress */
self::compress($ctx, $ctx[3]);
# memcpy( S->buf, S->buf + BLAKE2B_BLOCKBYTES, BLAKE2B_BLOCKBYTES ); /* Shift buffer left */
for ($i = 128; $i--;) {
$ctx[3][$i] = $ctx[3][$i + 128];
}
# S->buflen -= BLAKE2B_BLOCKBYTES;
$ctx[4] -= 128;
# in += fill;
$offset += $fill;
# inlen -= fill;
$plen -= $fill;
} else {
for ($i = $plen; $i--;) {
$ctx[3][$i + $left] = $p[$i + $offset];
}
$ctx[4] += $plen;
$offset += $plen;
$plen -= $plen;
}
}
}
/**
* @internal You should not use this directly from another application
*
* @param SplFixedArray $ctx
* @param SplFixedArray $out
* @return SplFixedArray
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArgument
* @psalm-suppress MixedAssignment
* @psalm-suppress MixedArrayAccess
* @psalm-suppress MixedArrayAssignment
* @psalm-suppress MixedArrayOffset
* @psalm-suppress MixedMethodCall
* @psalm-suppress MixedOperand
*/
public static function finish(SplFixedArray $ctx, SplFixedArray $out)
{
self::pseudoConstructor();
if ($ctx[4] > 128) {
self::increment_counter($ctx, 128);
self::compress($ctx, $ctx[3]);
$ctx[4] -= 128;
if ($ctx[4] > 128) {
throw new SodiumException('Failed to assert that buflen <= 128 bytes');
}
for ($i = $ctx[4]; $i--;) {
$ctx[3][$i] = $ctx[3][$i + 128];
}
}
self::increment_counter($ctx, $ctx[4]);
$ctx[2][0] = self::new64(0xffffffff, 0xffffffff);
for ($i = 256 - $ctx[4]; $i--;) {
/** @var int $i */
$ctx[3][$i + $ctx[4]] = 0;
}
self::compress($ctx, $ctx[3]);
$i = (int) (($out->getSize() - 1) / 8);
for (; $i >= 0; --$i) {
self::store64($out, $i << 3, $ctx[0][$i]);
}
return $out;
}
/**
* @internal You should not use this directly from another application
*
* @param SplFixedArray|null $key
* @param int $outlen
* @param SplFixedArray|null $salt
* @param SplFixedArray|null $personal
* @return SplFixedArray
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArgument
* @psalm-suppress MixedAssignment
* @psalm-suppress MixedArrayAccess
* @psalm-suppress MixedArrayAssignment
* @psalm-suppress MixedMethodCall
*/
public static function init(
$key = null,
$outlen = 64,
$salt = null,
$personal = null
) {
self::pseudoConstructor();
$klen = 0;
if ($key !== null) {
if (count($key) > 64) {
throw new SodiumException('Invalid key size');
}
$klen = count($key);
}
if ($outlen > 64) {
throw new SodiumException('Invalid output size');
}
$ctx = self::context();
$p = new SplFixedArray(64);
// Zero our param buffer...
for ($i = 64; --$i;) {
$p[$i] = 0;
}
$p[0] = $outlen; // digest_length
$p[1] = $klen; // key_length
$p[2] = 1; // fanout
$p[3] = 1; // depth
if ($salt instanceof SplFixedArray) {
// salt: [32] through [47]
for ($i = 0; $i < 16; ++$i) {
$p[32 + $i] = (int) $salt[$i];
}
}
if ($personal instanceof SplFixedArray) {
// personal: [48] through [63]
for ($i = 0; $i < 16; ++$i) {
$p[48 + $i] = (int) $personal[$i];
}
}
$ctx[0][0] = self::xor64(
$ctx[0][0],
self::load64($p, 0)
);
if ($salt instanceof SplFixedArray || $personal instanceof SplFixedArray) {
// We need to do what blake2b_init_param() does:
for ($i = 1; $i < 8; ++$i) {
$ctx[0][$i] = self::xor64(
$ctx[0][$i],
self::load64($p, $i << 3)
);
}
}
if ($klen > 0 && $key instanceof SplFixedArray) {
$block = new SplFixedArray(128);
for ($i = 128; $i--;) {
$block[$i] = 0;
}
for ($i = $klen; $i--;) {
$block[$i] = $key[$i];
}
self::update($ctx, $block, 128);
$ctx[4] = 128;
}
return $ctx;
}
/**
* Convert a string into an SplFixedArray of integers
*
* @internal You should not use this directly from another application
*
* @param string $str
* @return SplFixedArray
* @psalm-suppress MixedArgumentTypeCoercion
*/
public static function stringToSplFixedArray($str = '')
{
$values = unpack('C*', $str);
return SplFixedArray::fromArray(array_values($values));
}
/**
* Convert an SplFixedArray of integers into a string
*
* @internal You should not use this directly from another application
*
* @param SplFixedArray $a
* @return string
*/
public static function SplFixedArrayToString(SplFixedArray $a)
{
/**
* @var array<int, string|int>
*/
$arr = $a->toArray();
$c = $a->count();
array_unshift($arr, str_repeat('C', $c));
return (string) (call_user_func_array('pack', $arr));
}
/**
* @internal You should not use this directly from another application
*
* @param SplFixedArray $ctx
* @return string
* @throws TypeError
* @psalm-suppress MixedArgument
* @psalm-suppress MixedArrayAccess
* @psalm-suppress MixedArrayAssignment
* @psalm-suppress MixedMethodCall
*/
public static function contextToString(SplFixedArray $ctx)
{
$str = '';
/** @var array<int, ParagonIE_Sodium_Core32_Int64> $ctxA */
$ctxA = $ctx[0]->toArray();
# uint64_t h[8];
for ($i = 0; $i < 8; ++$i) {
if (!($ctxA[$i] instanceof ParagonIE_Sodium_Core32_Int64)) {
throw new TypeError('Not an instance of Int64');
}
/** @var ParagonIE_Sodium_Core32_Int64 $ctxAi */
$ctxAi = $ctxA[$i];
$str .= $ctxAi->toReverseString();
}
# uint64_t t[2];
# uint64_t f[2];
for ($i = 1; $i < 3; ++$i) {
/** @var array<int, ParagonIE_Sodium_Core32_Int64> $ctxA */
$ctxA = $ctx[$i]->toArray();
/** @var ParagonIE_Sodium_Core32_Int64 $ctxA1 */
$ctxA1 = $ctxA[0];
/** @var ParagonIE_Sodium_Core32_Int64 $ctxA2 */
$ctxA2 = $ctxA[1];
$str .= $ctxA1->toReverseString();
$str .= $ctxA2->toReverseString();
}
# uint8_t buf[2 * 128];
$str .= self::SplFixedArrayToString($ctx[3]);
/** @var int $ctx4 */
$ctx4 = $ctx[4];
# size_t buflen;
$str .= implode('', array(
self::intToChr($ctx4 & 0xff),
self::intToChr(($ctx4 >> 8) & 0xff),
self::intToChr(($ctx4 >> 16) & 0xff),
self::intToChr(($ctx4 >> 24) & 0xff),
"\x00\x00\x00\x00"
/*
self::intToChr(($ctx4 >> 32) & 0xff),
self::intToChr(($ctx4 >> 40) & 0xff),
self::intToChr(($ctx4 >> 48) & 0xff),
self::intToChr(($ctx4 >> 56) & 0xff)
*/
));
# uint8_t last_node;
return $str . self::intToChr($ctx[5]) . str_repeat("\x00", 23);
}
/**
* Creates an SplFixedArray containing other SplFixedArray elements, from
* a string (compatible with \Sodium\crypto_generichash_{init, update, final})
*
* @internal You should not use this directly from another application
*
* @param string $string
* @return SplFixedArray
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArrayAccess
* @psalm-suppress MixedArrayAssignment
*/
public static function stringToContext($string)
{
$ctx = self::context();
# uint64_t h[8];
for ($i = 0; $i < 8; ++$i) {
$ctx[0][$i] = ParagonIE_Sodium_Core32_Int64::fromReverseString(
self::substr($string, (($i << 3) + 0), 8)
);
}
# uint64_t t[2];
# uint64_t f[2];
for ($i = 1; $i < 3; ++$i) {
$ctx[$i][1] = ParagonIE_Sodium_Core32_Int64::fromReverseString(
self::substr($string, 72 + (($i - 1) << 4), 8)
);
$ctx[$i][0] = ParagonIE_Sodium_Core32_Int64::fromReverseString(
self::substr($string, 64 + (($i - 1) << 4), 8)
);
}
# uint8_t buf[2 * 128];
$ctx[3] = self::stringToSplFixedArray(self::substr($string, 96, 256));
# uint8_t buf[2 * 128];
$int = 0;
for ($i = 0; $i < 8; ++$i) {
$int |= self::chrToInt($string[352 + $i]) << ($i << 3);
}
$ctx[4] = $int;
return $ctx;
}
}
Core32/Curve25519/README.md 0000604 00000000332 15133021213 0010533 0 ustar 00 # Curve25519 Data Structures
These are PHP implementation of the [structs used in the ref10 curve25519 code](https://github.com/jedisct1/libsodium/blob/master/src/libsodium/include/sodium/private/curve25519_ref10.h).
Core32/Curve25519/Ge/P3.php 0000604 00000003242 15133021213 0010605 0 ustar 00 <?php
if (class_exists('ParagonIE_Sodium_Core32_Curve25519_Ge_P3', false)) {
return;
}
/**
* Class ParagonIE_Sodium_Core32_Curve25519_Ge_P3
*/
class ParagonIE_Sodium_Core32_Curve25519_Ge_P3
{
/**
* @var ParagonIE_Sodium_Core32_Curve25519_Fe
*/
public $X;
/**
* @var ParagonIE_Sodium_Core32_Curve25519_Fe
*/
public $Y;
/**
* @var ParagonIE_Sodium_Core32_Curve25519_Fe
*/
public $Z;
/**
* @var ParagonIE_Sodium_Core32_Curve25519_Fe
*/
public $T;
/**
* ParagonIE_Sodium_Core32_Curve25519_Ge_P3 constructor.
*
* @internal You should not use this directly from another application
*
* @param ParagonIE_Sodium_Core32_Curve25519_Fe|null $x
* @param ParagonIE_Sodium_Core32_Curve25519_Fe|null $y
* @param ParagonIE_Sodium_Core32_Curve25519_Fe|null $z
* @param ParagonIE_Sodium_Core32_Curve25519_Fe|null $t
*/
public function __construct(
ParagonIE_Sodium_Core32_Curve25519_Fe $x = null,
ParagonIE_Sodium_Core32_Curve25519_Fe $y = null,
ParagonIE_Sodium_Core32_Curve25519_Fe $z = null,
ParagonIE_Sodium_Core32_Curve25519_Fe $t = null
) {
if ($x === null) {
$x = new ParagonIE_Sodium_Core32_Curve25519_Fe();
}
$this->X = $x;
if ($y === null) {
$y = new ParagonIE_Sodium_Core32_Curve25519_Fe();
}
$this->Y = $y;
if ($z === null) {
$z = new ParagonIE_Sodium_Core32_Curve25519_Fe();
}
$this->Z = $z;
if ($t === null) {
$t = new ParagonIE_Sodium_Core32_Curve25519_Fe();
}
$this->T = $t;
}
}
Core32/Curve25519/Ge/P2.php 0000604 00000002541 15133021213 0010605 0 ustar 00 <?php
if (class_exists('ParagonIE_Sodium_Core32_Curve25519_Ge_P2', false)) {
return;
}
/**
* Class ParagonIE_Sodium_Core32_Curve25519_Ge_P2
*/
class ParagonIE_Sodium_Core32_Curve25519_Ge_P2
{
/**
* @var ParagonIE_Sodium_Core32_Curve25519_Fe
*/
public $X;
/**
* @var ParagonIE_Sodium_Core32_Curve25519_Fe
*/
public $Y;
/**
* @var ParagonIE_Sodium_Core32_Curve25519_Fe
*/
public $Z;
/**
* ParagonIE_Sodium_Core32_Curve25519_Ge_P2 constructor.
*
* @internal You should not use this directly from another application
*
* @param ParagonIE_Sodium_Core32_Curve25519_Fe|null $x
* @param ParagonIE_Sodium_Core32_Curve25519_Fe|null $y
* @param ParagonIE_Sodium_Core32_Curve25519_Fe|null $z
*/
public function __construct(
ParagonIE_Sodium_Core32_Curve25519_Fe $x = null,
ParagonIE_Sodium_Core32_Curve25519_Fe $y = null,
ParagonIE_Sodium_Core32_Curve25519_Fe $z = null
) {
if ($x === null) {
$x = new ParagonIE_Sodium_Core32_Curve25519_Fe();
}
$this->X = $x;
if ($y === null) {
$y = new ParagonIE_Sodium_Core32_Curve25519_Fe();
}
$this->Y = $y;
if ($z === null) {
$z = new ParagonIE_Sodium_Core32_Curve25519_Fe();
}
$this->Z = $z;
}
}
Core32/Curve25519/Ge/P1p1.php 0000604 00000003344 15133021213 0011047 0 ustar 00 <?php
if (class_exists('ParagonIE_Sodium_Core32_Curve25519_Ge_P1p1', false)) {
return;
}
/**
* Class ParagonIE_Sodium_Core32_Curve25519_Ge_P1p1
*/
class ParagonIE_Sodium_Core32_Curve25519_Ge_P1p1
{
/**
* @var ParagonIE_Sodium_Core32_Curve25519_Fe
*/
public $X;
/**
* @var ParagonIE_Sodium_Core32_Curve25519_Fe
*/
public $Y;
/**
* @var ParagonIE_Sodium_Core32_Curve25519_Fe
*/
public $Z;
/**
* @var ParagonIE_Sodium_Core32_Curve25519_Fe
*/
public $T;
/**
* ParagonIE_Sodium_Core32_Curve25519_Ge_P1p1 constructor.
*
* @internal You should not use this directly from another application
*
* @param ParagonIE_Sodium_Core32_Curve25519_Fe|null $x
* @param ParagonIE_Sodium_Core32_Curve25519_Fe|null $y
* @param ParagonIE_Sodium_Core32_Curve25519_Fe|null $z
* @param ParagonIE_Sodium_Core32_Curve25519_Fe|null $t
*
* @throws SodiumException
* @throws TypeError
*/
public function __construct(
ParagonIE_Sodium_Core32_Curve25519_Fe $x = null,
ParagonIE_Sodium_Core32_Curve25519_Fe $y = null,
ParagonIE_Sodium_Core32_Curve25519_Fe $z = null,
ParagonIE_Sodium_Core32_Curve25519_Fe $t = null
) {
if ($x === null) {
$x = ParagonIE_Sodium_Core32_Curve25519::fe_0();
}
$this->X = $x;
if ($y === null) {
$y = ParagonIE_Sodium_Core32_Curve25519::fe_0();
}
$this->Y = $y;
if ($z === null) {
$z = ParagonIE_Sodium_Core32_Curve25519::fe_0();
}
$this->Z = $z;
if ($t === null) {
$t = ParagonIE_Sodium_Core32_Curve25519::fe_0();
}
$this->T = $t;
}
}
Core32/Curve25519/Ge/Precomp.php 0000604 00000002775 15133021213 0011742 0 ustar 00 <?php
if (class_exists('ParagonIE_Sodium_Core32_Curve25519_Ge_Precomp', false)) {
return;
}
/**
* Class ParagonIE_Sodium_Core32_Curve25519_Ge_Precomp
*/
class ParagonIE_Sodium_Core32_Curve25519_Ge_Precomp
{
/**
* @var ParagonIE_Sodium_Core32_Curve25519_Fe
*/
public $yplusx;
/**
* @var ParagonIE_Sodium_Core32_Curve25519_Fe
*/
public $yminusx;
/**
* @var ParagonIE_Sodium_Core32_Curve25519_Fe
*/
public $xy2d;
/**
* ParagonIE_Sodium_Core32_Curve25519_Ge_Precomp constructor.
*
* @internal You should not use this directly from another application
*
* @param ParagonIE_Sodium_Core32_Curve25519_Fe $yplusx
* @param ParagonIE_Sodium_Core32_Curve25519_Fe $yminusx
* @param ParagonIE_Sodium_Core32_Curve25519_Fe $xy2d
* @throws SodiumException
* @throws TypeError
*/
public function __construct(
ParagonIE_Sodium_Core32_Curve25519_Fe $yplusx = null,
ParagonIE_Sodium_Core32_Curve25519_Fe $yminusx = null,
ParagonIE_Sodium_Core32_Curve25519_Fe $xy2d = null
) {
if ($yplusx === null) {
$yplusx = ParagonIE_Sodium_Core32_Curve25519::fe_0();
}
$this->yplusx = $yplusx;
if ($yminusx === null) {
$yminusx = ParagonIE_Sodium_Core32_Curve25519::fe_0();
}
$this->yminusx = $yminusx;
if ($xy2d === null) {
$xy2d = ParagonIE_Sodium_Core32_Curve25519::fe_0();
}
$this->xy2d = $xy2d;
}
}
Core32/Curve25519/Ge/Cached.php 0000604 00000003415 15133021213 0011474 0 ustar 00 <?php
if (class_exists('ParagonIE_Sodium_Core32_Curve25519_Ge_Cached', false)) {
return;
}
/**
* Class ParagonIE_Sodium_Core32_Curve25519_Ge_Cached
*/
class ParagonIE_Sodium_Core32_Curve25519_Ge_Cached
{
/**
* @var ParagonIE_Sodium_Core32_Curve25519_Fe
*/
public $YplusX;
/**
* @var ParagonIE_Sodium_Core32_Curve25519_Fe
*/
public $YminusX;
/**
* @var ParagonIE_Sodium_Core32_Curve25519_Fe
*/
public $Z;
/**
* @var ParagonIE_Sodium_Core32_Curve25519_Fe
*/
public $T2d;
/**
* ParagonIE_Sodium_Core32_Curve25519_Ge_Cached constructor.
*
* @internal You should not use this directly from another application
*
* @param ParagonIE_Sodium_Core32_Curve25519_Fe|null $YplusX
* @param ParagonIE_Sodium_Core32_Curve25519_Fe|null $YminusX
* @param ParagonIE_Sodium_Core32_Curve25519_Fe|null $Z
* @param ParagonIE_Sodium_Core32_Curve25519_Fe|null $T2d
*/
public function __construct(
ParagonIE_Sodium_Core32_Curve25519_Fe $YplusX = null,
ParagonIE_Sodium_Core32_Curve25519_Fe $YminusX = null,
ParagonIE_Sodium_Core32_Curve25519_Fe $Z = null,
ParagonIE_Sodium_Core32_Curve25519_Fe $T2d = null
) {
if ($YplusX === null) {
$YplusX = new ParagonIE_Sodium_Core32_Curve25519_Fe();
}
$this->YplusX = $YplusX;
if ($YminusX === null) {
$YminusX = new ParagonIE_Sodium_Core32_Curve25519_Fe();
}
$this->YminusX = $YminusX;
if ($Z === null) {
$Z = new ParagonIE_Sodium_Core32_Curve25519_Fe();
}
$this->Z = $Z;
if ($T2d === null) {
$T2d = new ParagonIE_Sodium_Core32_Curve25519_Fe();
}
$this->T2d = $T2d;
}
}
Core32/Curve25519/H.php 0000604 00000324375 15133021213 0010174 0 ustar 00 <?php
if (class_exists('ParagonIE_Sodium_Core32_Curve25519_H', false)) {
return;
}
/**
* Class ParagonIE_Sodium_Core32_Curve25519_H
*
* This just contains the constants in the ref10/base.h file
*/
class ParagonIE_Sodium_Core32_Curve25519_H extends ParagonIE_Sodium_Core32_Util
{
/**
* See: libsodium's crypto_core/curve25519/ref10/base.h
*
* @var array<int, array<int, array<int, array<int, int>>>> Basically, int[32][8][3][10]
*/
protected static $base = array(
array(
array(
array(25967493, -14356035, 29566456, 3660896, -12694345, 4014787, 27544626, -11754271, -6079156, 2047605),
array(-12545711, 934262, -2722910, 3049990, -727428, 9406986, 12720692, 5043384, 19500929, -15469378),
array(-8738181, 4489570, 9688441, -14785194, 10184609, -12363380, 29287919, 11864899, -24514362, -4438546),
),
array(
array(-12815894, -12976347, -21581243, 11784320, -25355658, -2750717, -11717903, -3814571, -358445, -10211303),
array(-21703237, 6903825, 27185491, 6451973, -29577724, -9554005, -15616551, 11189268, -26829678, -5319081),
array(26966642, 11152617, 32442495, 15396054, 14353839, -12752335, -3128826, -9541118, -15472047, -4166697),
),
array(
array(15636291, -9688557, 24204773, -7912398, 616977, -16685262, 27787600, -14772189, 28944400, -1550024),
array(16568933, 4717097, -11556148, -1102322, 15682896, -11807043, 16354577, -11775962, 7689662, 11199574),
array(30464156, -5976125, -11779434, -15670865, 23220365, 15915852, 7512774, 10017326, -17749093, -9920357),
),
array(
array(-17036878, 13921892, 10945806, -6033431, 27105052, -16084379, -28926210, 15006023, 3284568, -6276540),
array(23599295, -8306047, -11193664, -7687416, 13236774, 10506355, 7464579, 9656445, 13059162, 10374397),
array(7798556, 16710257, 3033922, 2874086, 28997861, 2835604, 32406664, -3839045, -641708, -101325),
),
array(
array(10861363, 11473154, 27284546, 1981175, -30064349, 12577861, 32867885, 14515107, -15438304, 10819380),
array(4708026, 6336745, 20377586, 9066809, -11272109, 6594696, -25653668, 12483688, -12668491, 5581306),
array(19563160, 16186464, -29386857, 4097519, 10237984, -4348115, 28542350, 13850243, -23678021, -15815942),
),
array(
array(-15371964, -12862754, 32573250, 4720197, -26436522, 5875511, -19188627, -15224819, -9818940, -12085777),
array(-8549212, 109983, 15149363, 2178705, 22900618, 4543417, 3044240, -15689887, 1762328, 14866737),
array(-18199695, -15951423, -10473290, 1707278, -17185920, 3916101, -28236412, 3959421, 27914454, 4383652),
),
array(
array(5153746, 9909285, 1723747, -2777874, 30523605, 5516873, 19480852, 5230134, -23952439, -15175766),
array(-30269007, -3463509, 7665486, 10083793, 28475525, 1649722, 20654025, 16520125, 30598449, 7715701),
array(28881845, 14381568, 9657904, 3680757, -20181635, 7843316, -31400660, 1370708, 29794553, -1409300),
),
array(
array(14499471, -2729599, -33191113, -4254652, 28494862, 14271267, 30290735, 10876454, -33154098, 2381726),
array(-7195431, -2655363, -14730155, 462251, -27724326, 3941372, -6236617, 3696005, -32300832, 15351955),
array(27431194, 8222322, 16448760, -3907995, -18707002, 11938355, -32961401, -2970515, 29551813, 10109425),
),
),
array(
array(
array(-13657040, -13155431, -31283750, 11777098, 21447386, 6519384, -2378284, -1627556, 10092783, -4764171),
array(27939166, 14210322, 4677035, 16277044, -22964462, -12398139, -32508754, 12005538, -17810127, 12803510),
array(17228999, -15661624, -1233527, 300140, -1224870, -11714777, 30364213, -9038194, 18016357, 4397660),
),
array(
array(-10958843, -7690207, 4776341, -14954238, 27850028, -15602212, -26619106, 14544525, -17477504, 982639),
array(29253598, 15796703, -2863982, -9908884, 10057023, 3163536, 7332899, -4120128, -21047696, 9934963),
array(5793303, 16271923, -24131614, -10116404, 29188560, 1206517, -14747930, 4559895, -30123922, -10897950),
),
array(
array(-27643952, -11493006, 16282657, -11036493, 28414021, -15012264, 24191034, 4541697, -13338309, 5500568),
array(12650548, -1497113, 9052871, 11355358, -17680037, -8400164, -17430592, 12264343, 10874051, 13524335),
array(25556948, -3045990, 714651, 2510400, 23394682, -10415330, 33119038, 5080568, -22528059, 5376628),
),
array(
array(-26088264, -4011052, -17013699, -3537628, -6726793, 1920897, -22321305, -9447443, 4535768, 1569007),
array(-2255422, 14606630, -21692440, -8039818, 28430649, 8775819, -30494562, 3044290, 31848280, 12543772),
array(-22028579, 2943893, -31857513, 6777306, 13784462, -4292203, -27377195, -2062731, 7718482, 14474653),
),
array(
array(2385315, 2454213, -22631320, 46603, -4437935, -15680415, 656965, -7236665, 24316168, -5253567),
array(13741529, 10911568, -33233417, -8603737, -20177830, -1033297, 33040651, -13424532, -20729456, 8321686),
array(21060490, -2212744, 15712757, -4336099, 1639040, 10656336, 23845965, -11874838, -9984458, 608372),
),
array(
array(-13672732, -15087586, -10889693, -7557059, -6036909, 11305547, 1123968, -6780577, 27229399, 23887),
array(-23244140, -294205, -11744728, 14712571, -29465699, -2029617, 12797024, -6440308, -1633405, 16678954),
array(-29500620, 4770662, -16054387, 14001338, 7830047, 9564805, -1508144, -4795045, -17169265, 4904953),
),
array(
array(24059557, 14617003, 19037157, -15039908, 19766093, -14906429, 5169211, 16191880, 2128236, -4326833),
array(-16981152, 4124966, -8540610, -10653797, 30336522, -14105247, -29806336, 916033, -6882542, -2986532),
array(-22630907, 12419372, -7134229, -7473371, -16478904, 16739175, 285431, 2763829, 15736322, 4143876),
),
array(
array(2379352, 11839345, -4110402, -5988665, 11274298, 794957, 212801, -14594663, 23527084, -16458268),
array(33431127, -11130478, -17838966, -15626900, 8909499, 8376530, -32625340, 4087881, -15188911, -14416214),
array(1767683, 7197987, -13205226, -2022635, -13091350, 448826, 5799055, 4357868, -4774191, -16323038),
),
),
array(
array(
array(6721966, 13833823, -23523388, -1551314, 26354293, -11863321, 23365147, -3949732, 7390890, 2759800),
array(4409041, 2052381, 23373853, 10530217, 7676779, -12885954, 21302353, -4264057, 1244380, -12919645),
array(-4421239, 7169619, 4982368, -2957590, 30256825, -2777540, 14086413, 9208236, 15886429, 16489664),
),
array(
array(1996075, 10375649, 14346367, 13311202, -6874135, -16438411, -13693198, 398369, -30606455, -712933),
array(-25307465, 9795880, -2777414, 14878809, -33531835, 14780363, 13348553, 12076947, -30836462, 5113182),
array(-17770784, 11797796, 31950843, 13929123, -25888302, 12288344, -30341101, -7336386, 13847711, 5387222),
),
array(
array(-18582163, -3416217, 17824843, -2340966, 22744343, -10442611, 8763061, 3617786, -19600662, 10370991),
array(20246567, -14369378, 22358229, -543712, 18507283, -10413996, 14554437, -8746092, 32232924, 16763880),
array(9648505, 10094563, 26416693, 14745928, -30374318, -6472621, 11094161, 15689506, 3140038, -16510092),
),
array(
array(-16160072, 5472695, 31895588, 4744994, 8823515, 10365685, -27224800, 9448613, -28774454, 366295),
array(19153450, 11523972, -11096490, -6503142, -24647631, 5420647, 28344573, 8041113, 719605, 11671788),
array(8678025, 2694440, -6808014, 2517372, 4964326, 11152271, -15432916, -15266516, 27000813, -10195553),
),
array(
array(-15157904, 7134312, 8639287, -2814877, -7235688, 10421742, 564065, 5336097, 6750977, -14521026),
array(11836410, -3979488, 26297894, 16080799, 23455045, 15735944, 1695823, -8819122, 8169720, 16220347),
array(-18115838, 8653647, 17578566, -6092619, -8025777, -16012763, -11144307, -2627664, -5990708, -14166033),
),
array(
array(-23308498, -10968312, 15213228, -10081214, -30853605, -11050004, 27884329, 2847284, 2655861, 1738395),
array(-27537433, -14253021, -25336301, -8002780, -9370762, 8129821, 21651608, -3239336, -19087449, -11005278),
array(1533110, 3437855, 23735889, 459276, 29970501, 11335377, 26030092, 5821408, 10478196, 8544890),
),
array(
array(32173121, -16129311, 24896207, 3921497, 22579056, -3410854, 19270449, 12217473, 17789017, -3395995),
array(-30552961, -2228401, -15578829, -10147201, 13243889, 517024, 15479401, -3853233, 30460520, 1052596),
array(-11614875, 13323618, 32618793, 8175907, -15230173, 12596687, 27491595, -4612359, 3179268, -9478891),
),
array(
array(31947069, -14366651, -4640583, -15339921, -15125977, -6039709, -14756777, -16411740, 19072640, -9511060),
array(11685058, 11822410, 3158003, -13952594, 33402194, -4165066, 5977896, -5215017, 473099, 5040608),
array(-20290863, 8198642, -27410132, 11602123, 1290375, -2799760, 28326862, 1721092, -19558642, -3131606),
),
),
array(
array(
array(7881532, 10687937, 7578723, 7738378, -18951012, -2553952, 21820786, 8076149, -27868496, 11538389),
array(-19935666, 3899861, 18283497, -6801568, -15728660, -11249211, 8754525, 7446702, -5676054, 5797016),
array(-11295600, -3793569, -15782110, -7964573, 12708869, -8456199, 2014099, -9050574, -2369172, -5877341),
),
array(
array(-22472376, -11568741, -27682020, 1146375, 18956691, 16640559, 1192730, -3714199, 15123619, 10811505),
array(14352098, -3419715, -18942044, 10822655, 32750596, 4699007, -70363, 15776356, -28886779, -11974553),
array(-28241164, -8072475, -4978962, -5315317, 29416931, 1847569, -20654173, -16484855, 4714547, -9600655),
),
array(
array(15200332, 8368572, 19679101, 15970074, -31872674, 1959451, 24611599, -4543832, -11745876, 12340220),
array(12876937, -10480056, 33134381, 6590940, -6307776, 14872440, 9613953, 8241152, 15370987, 9608631),
array(-4143277, -12014408, 8446281, -391603, 4407738, 13629032, -7724868, 15866074, -28210621, -8814099),
),
array(
array(26660628, -15677655, 8393734, 358047, -7401291, 992988, -23904233, 858697, 20571223, 8420556),
array(14620715, 13067227, -15447274, 8264467, 14106269, 15080814, 33531827, 12516406, -21574435, -12476749),
array(236881, 10476226, 57258, -14677024, 6472998, 2466984, 17258519, 7256740, 8791136, 15069930),
),
array(
array(1276410, -9371918, 22949635, -16322807, -23493039, -5702186, 14711875, 4874229, -30663140, -2331391),
array(5855666, 4990204, -13711848, 7294284, -7804282, 1924647, -1423175, -7912378, -33069337, 9234253),
array(20590503, -9018988, 31529744, -7352666, -2706834, 10650548, 31559055, -11609587, 18979186, 13396066),
),
array(
array(24474287, 4968103, 22267082, 4407354, 24063882, -8325180, -18816887, 13594782, 33514650, 7021958),
array(-11566906, -6565505, -21365085, 15928892, -26158305, 4315421, -25948728, -3916677, -21480480, 12868082),
array(-28635013, 13504661, 19988037, -2132761, 21078225, 6443208, -21446107, 2244500, -12455797, -8089383),
),
array(
array(-30595528, 13793479, -5852820, 319136, -25723172, -6263899, 33086546, 8957937, -15233648, 5540521),
array(-11630176, -11503902, -8119500, -7643073, 2620056, 1022908, -23710744, -1568984, -16128528, -14962807),
array(23152971, 775386, 27395463, 14006635, -9701118, 4649512, 1689819, 892185, -11513277, -15205948),
),
array(
array(9770129, 9586738, 26496094, 4324120, 1556511, -3550024, 27453819, 4763127, -19179614, 5867134),
array(-32765025, 1927590, 31726409, -4753295, 23962434, -16019500, 27846559, 5931263, -29749703, -16108455),
array(27461885, -2977536, 22380810, 1815854, -23033753, -3031938, 7283490, -15148073, -19526700, 7734629),
),
),
array(
array(
array(-8010264, -9590817, -11120403, 6196038, 29344158, -13430885, 7585295, -3176626, 18549497, 15302069),
array(-32658337, -6171222, -7672793, -11051681, 6258878, 13504381, 10458790, -6418461, -8872242, 8424746),
array(24687205, 8613276, -30667046, -3233545, 1863892, -1830544, 19206234, 7134917, -11284482, -828919),
),
array(
array(11334899, -9218022, 8025293, 12707519, 17523892, -10476071, 10243738, -14685461, -5066034, 16498837),
array(8911542, 6887158, -9584260, -6958590, 11145641, -9543680, 17303925, -14124238, 6536641, 10543906),
array(-28946384, 15479763, -17466835, 568876, -1497683, 11223454, -2669190, -16625574, -27235709, 8876771),
),
array(
array(-25742899, -12566864, -15649966, -846607, -33026686, -796288, -33481822, 15824474, -604426, -9039817),
array(10330056, 70051, 7957388, -9002667, 9764902, 15609756, 27698697, -4890037, 1657394, 3084098),
array(10477963, -7470260, 12119566, -13250805, 29016247, -5365589, 31280319, 14396151, -30233575, 15272409),
),
array(
array(-12288309, 3169463, 28813183, 16658753, 25116432, -5630466, -25173957, -12636138, -25014757, 1950504),
array(-26180358, 9489187, 11053416, -14746161, -31053720, 5825630, -8384306, -8767532, 15341279, 8373727),
array(28685821, 7759505, -14378516, -12002860, -31971820, 4079242, 298136, -10232602, -2878207, 15190420),
),
array(
array(-32932876, 13806336, -14337485, -15794431, -24004620, 10940928, 8669718, 2742393, -26033313, -6875003),
array(-1580388, -11729417, -25979658, -11445023, -17411874, -10912854, 9291594, -16247779, -12154742, 6048605),
array(-30305315, 14843444, 1539301, 11864366, 20201677, 1900163, 13934231, 5128323, 11213262, 9168384),
),
array(
array(-26280513, 11007847, 19408960, -940758, -18592965, -4328580, -5088060, -11105150, 20470157, -16398701),
array(-23136053, 9282192, 14855179, -15390078, -7362815, -14408560, -22783952, 14461608, 14042978, 5230683),
array(29969567, -2741594, -16711867, -8552442, 9175486, -2468974, 21556951, 3506042, -5933891, -12449708),
),
array(
array(-3144746, 8744661, 19704003, 4581278, -20430686, 6830683, -21284170, 8971513, -28539189, 15326563),
array(-19464629, 10110288, -17262528, -3503892, -23500387, 1355669, -15523050, 15300988, -20514118, 9168260),
array(-5353335, 4488613, -23803248, 16314347, 7780487, -15638939, -28948358, 9601605, 33087103, -9011387),
),
array(
array(-19443170, -15512900, -20797467, -12445323, -29824447, 10229461, -27444329, -15000531, -5996870, 15664672),
array(23294591, -16632613, -22650781, -8470978, 27844204, 11461195, 13099750, -2460356, 18151676, 13417686),
array(-24722913, -4176517, -31150679, 5988919, -26858785, 6685065, 1661597, -12551441, 15271676, -15452665),
),
),
array(
array(
array(11433042, -13228665, 8239631, -5279517, -1985436, -725718, -18698764, 2167544, -6921301, -13440182),
array(-31436171, 15575146, 30436815, 12192228, -22463353, 9395379, -9917708, -8638997, 12215110, 12028277),
array(14098400, 6555944, 23007258, 5757252, -15427832, -12950502, 30123440, 4617780, -16900089, -655628),
),
array(
array(-4026201, -15240835, 11893168, 13718664, -14809462, 1847385, -15819999, 10154009, 23973261, -12684474),
array(-26531820, -3695990, -1908898, 2534301, -31870557, -16550355, 18341390, -11419951, 32013174, -10103539),
array(-25479301, 10876443, -11771086, -14625140, -12369567, 1838104, 21911214, 6354752, 4425632, -837822),
),
array(
array(-10433389, -14612966, 22229858, -3091047, -13191166, 776729, -17415375, -12020462, 4725005, 14044970),
array(19268650, -7304421, 1555349, 8692754, -21474059, -9910664, 6347390, -1411784, -19522291, -16109756),
array(-24864089, 12986008, -10898878, -5558584, -11312371, -148526, 19541418, 8180106, 9282262, 10282508),
),
array(
array(-26205082, 4428547, -8661196, -13194263, 4098402, -14165257, 15522535, 8372215, 5542595, -10702683),
array(-10562541, 14895633, 26814552, -16673850, -17480754, -2489360, -2781891, 6993761, -18093885, 10114655),
array(-20107055, -929418, 31422704, 10427861, -7110749, 6150669, -29091755, -11529146, 25953725, -106158),
),
array(
array(-4234397, -8039292, -9119125, 3046000, 2101609, -12607294, 19390020, 6094296, -3315279, 12831125),
array(-15998678, 7578152, 5310217, 14408357, -33548620, -224739, 31575954, 6326196, 7381791, -2421839),
array(-20902779, 3296811, 24736065, -16328389, 18374254, 7318640, 6295303, 8082724, -15362489, 12339664),
),
array(
array(27724736, 2291157, 6088201, -14184798, 1792727, 5857634, 13848414, 15768922, 25091167, 14856294),
array(-18866652, 8331043, 24373479, 8541013, -701998, -9269457, 12927300, -12695493, -22182473, -9012899),
array(-11423429, -5421590, 11632845, 3405020, 30536730, -11674039, -27260765, 13866390, 30146206, 9142070),
),
array(
array(3924129, -15307516, -13817122, -10054960, 12291820, -668366, -27702774, 9326384, -8237858, 4171294),
array(-15921940, 16037937, 6713787, 16606682, -21612135, 2790944, 26396185, 3731949, 345228, -5462949),
array(-21327538, 13448259, 25284571, 1143661, 20614966, -8849387, 2031539, -12391231, -16253183, -13582083),
),
array(
array(31016211, -16722429, 26371392, -14451233, -5027349, 14854137, 17477601, 3842657, 28012650, -16405420),
array(-5075835, 9368966, -8562079, -4600902, -15249953, 6970560, -9189873, 16292057, -8867157, 3507940),
array(29439664, 3537914, 23333589, 6997794, -17555561, -11018068, -15209202, -15051267, -9164929, 6580396),
),
),
array(
array(
array(-12185861, -7679788, 16438269, 10826160, -8696817, -6235611, 17860444, -9273846, -2095802, 9304567),
array(20714564, -4336911, 29088195, 7406487, 11426967, -5095705, 14792667, -14608617, 5289421, -477127),
array(-16665533, -10650790, -6160345, -13305760, 9192020, -1802462, 17271490, 12349094, 26939669, -3752294),
),
array(
array(-12889898, 9373458, 31595848, 16374215, 21471720, 13221525, -27283495, -12348559, -3698806, 117887),
array(22263325, -6560050, 3984570, -11174646, -15114008, -566785, 28311253, 5358056, -23319780, 541964),
array(16259219, 3261970, 2309254, -15534474, -16885711, -4581916, 24134070, -16705829, -13337066, -13552195),
),
array(
array(9378160, -13140186, -22845982, -12745264, 28198281, -7244098, -2399684, -717351, 690426, 14876244),
array(24977353, -314384, -8223969, -13465086, 28432343, -1176353, -13068804, -12297348, -22380984, 6618999),
array(-1538174, 11685646, 12944378, 13682314, -24389511, -14413193, 8044829, -13817328, 32239829, -5652762),
),
array(
array(-18603066, 4762990, -926250, 8885304, -28412480, -3187315, 9781647, -10350059, 32779359, 5095274),
array(-33008130, -5214506, -32264887, -3685216, 9460461, -9327423, -24601656, 14506724, 21639561, -2630236),
array(-16400943, -13112215, 25239338, 15531969, 3987758, -4499318, -1289502, -6863535, 17874574, 558605),
),
array(
array(-13600129, 10240081, 9171883, 16131053, -20869254, 9599700, 33499487, 5080151, 2085892, 5119761),
array(-22205145, -2519528, -16381601, 414691, -25019550, 2170430, 30634760, -8363614, -31999993, -5759884),
array(-6845704, 15791202, 8550074, -1312654, 29928809, -12092256, 27534430, -7192145, -22351378, 12961482),
),
array(
array(-24492060, -9570771, 10368194, 11582341, -23397293, -2245287, 16533930, 8206996, -30194652, -5159638),
array(-11121496, -3382234, 2307366, 6362031, -135455, 8868177, -16835630, 7031275, 7589640, 8945490),
array(-32152748, 8917967, 6661220, -11677616, -1192060, -15793393, 7251489, -11182180, 24099109, -14456170),
),
array(
array(5019558, -7907470, 4244127, -14714356, -26933272, 6453165, -19118182, -13289025, -6231896, -10280736),
array(10853594, 10721687, 26480089, 5861829, -22995819, 1972175, -1866647, -10557898, -3363451, -6441124),
array(-17002408, 5906790, 221599, -6563147, 7828208, -13248918, 24362661, -2008168, -13866408, 7421392),
),
array(
array(8139927, -6546497, 32257646, -5890546, 30375719, 1886181, -21175108, 15441252, 28826358, -4123029),
array(6267086, 9695052, 7709135, -16603597, -32869068, -1886135, 14795160, -7840124, 13746021, -1742048),
array(28584902, 7787108, -6732942, -15050729, 22846041, -7571236, -3181936, -363524, 4771362, -8419958),
),
),
array(
array(
array(24949256, 6376279, -27466481, -8174608, -18646154, -9930606, 33543569, -12141695, 3569627, 11342593),
array(26514989, 4740088, 27912651, 3697550, 19331575, -11472339, 6809886, 4608608, 7325975, -14801071),
array(-11618399, -14554430, -24321212, 7655128, -1369274, 5214312, -27400540, 10258390, -17646694, -8186692),
),
array(
array(11431204, 15823007, 26570245, 14329124, 18029990, 4796082, -31446179, 15580664, 9280358, -3973687),
array(-160783, -10326257, -22855316, -4304997, -20861367, -13621002, -32810901, -11181622, -15545091, 4387441),
array(-20799378, 12194512, 3937617, -5805892, -27154820, 9340370, -24513992, 8548137, 20617071, -7482001),
),
array(
array(-938825, -3930586, -8714311, 16124718, 24603125, -6225393, -13775352, -11875822, 24345683, 10325460),
array(-19855277, -1568885, -22202708, 8714034, 14007766, 6928528, 16318175, -1010689, 4766743, 3552007),
array(-21751364, -16730916, 1351763, -803421, -4009670, 3950935, 3217514, 14481909, 10988822, -3994762),
),
array(
array(15564307, -14311570, 3101243, 5684148, 30446780, -8051356, 12677127, -6505343, -8295852, 13296005),
array(-9442290, 6624296, -30298964, -11913677, -4670981, -2057379, 31521204, 9614054, -30000824, 12074674),
array(4771191, -135239, 14290749, -13089852, 27992298, 14998318, -1413936, -1556716, 29832613, -16391035),
),
array(
array(7064884, -7541174, -19161962, -5067537, -18891269, -2912736, 25825242, 5293297, -27122660, 13101590),
array(-2298563, 2439670, -7466610, 1719965, -27267541, -16328445, 32512469, -5317593, -30356070, -4190957),
array(-30006540, 10162316, -33180176, 3981723, -16482138, -13070044, 14413974, 9515896, 19568978, 9628812),
),
array(
array(33053803, 199357, 15894591, 1583059, 27380243, -4580435, -17838894, -6106839, -6291786, 3437740),
array(-18978877, 3884493, 19469877, 12726490, 15913552, 13614290, -22961733, 70104, 7463304, 4176122),
array(-27124001, 10659917, 11482427, -16070381, 12771467, -6635117, -32719404, -5322751, 24216882, 5944158),
),
array(
array(8894125, 7450974, -2664149, -9765752, -28080517, -12389115, 19345746, 14680796, 11632993, 5847885),
array(26942781, -2315317, 9129564, -4906607, 26024105, 11769399, -11518837, 6367194, -9727230, 4782140),
array(19916461, -4828410, -22910704, -11414391, 25606324, -5972441, 33253853, 8220911, 6358847, -1873857),
),
array(
array(801428, -2081702, 16569428, 11065167, 29875704, 96627, 7908388, -4480480, -13538503, 1387155),
array(19646058, 5720633, -11416706, 12814209, 11607948, 12749789, 14147075, 15156355, -21866831, 11835260),
array(19299512, 1155910, 28703737, 14890794, 2925026, 7269399, 26121523, 15467869, -26560550, 5052483),
),
),
array(
array(
array(-3017432, 10058206, 1980837, 3964243, 22160966, 12322533, -6431123, -12618185, 12228557, -7003677),
array(32944382, 14922211, -22844894, 5188528, 21913450, -8719943, 4001465, 13238564, -6114803, 8653815),
array(22865569, -4652735, 27603668, -12545395, 14348958, 8234005, 24808405, 5719875, 28483275, 2841751),
),
array(
array(-16420968, -1113305, -327719, -12107856, 21886282, -15552774, -1887966, -315658, 19932058, -12739203),
array(-11656086, 10087521, -8864888, -5536143, -19278573, -3055912, 3999228, 13239134, -4777469, -13910208),
array(1382174, -11694719, 17266790, 9194690, -13324356, 9720081, 20403944, 11284705, -14013818, 3093230),
),
array(
array(16650921, -11037932, -1064178, 1570629, -8329746, 7352753, -302424, 16271225, -24049421, -6691850),
array(-21911077, -5927941, -4611316, -5560156, -31744103, -10785293, 24123614, 15193618, -21652117, -16739389),
array(-9935934, -4289447, -25279823, 4372842, 2087473, 10399484, 31870908, 14690798, 17361620, 11864968),
),
array(
array(-11307610, 6210372, 13206574, 5806320, -29017692, -13967200, -12331205, -7486601, -25578460, -16240689),
array(14668462, -12270235, 26039039, 15305210, 25515617, 4542480, 10453892, 6577524, 9145645, -6443880),
array(5974874, 3053895, -9433049, -10385191, -31865124, 3225009, -7972642, 3936128, -5652273, -3050304),
),
array(
array(30625386, -4729400, -25555961, -12792866, -20484575, 7695099, 17097188, -16303496, -27999779, 1803632),
array(-3553091, 9865099, -5228566, 4272701, -5673832, -16689700, 14911344, 12196514, -21405489, 7047412),
array(20093277, 9920966, -11138194, -5343857, 13161587, 12044805, -32856851, 4124601, -32343828, -10257566),
),
array(
array(-20788824, 14084654, -13531713, 7842147, 19119038, -13822605, 4752377, -8714640, -21679658, 2288038),
array(-26819236, -3283715, 29965059, 3039786, -14473765, 2540457, 29457502, 14625692, -24819617, 12570232),
array(-1063558, -11551823, 16920318, 12494842, 1278292, -5869109, -21159943, -3498680, -11974704, 4724943),
),
array(
array(17960970, -11775534, -4140968, -9702530, -8876562, -1410617, -12907383, -8659932, -29576300, 1903856),
array(23134274, -14279132, -10681997, -1611936, 20684485, 15770816, -12989750, 3190296, 26955097, 14109738),
array(15308788, 5320727, -30113809, -14318877, 22902008, 7767164, 29425325, -11277562, 31960942, 11934971),
),
array(
array(-27395711, 8435796, 4109644, 12222639, -24627868, 14818669, 20638173, 4875028, 10491392, 1379718),
array(-13159415, 9197841, 3875503, -8936108, -1383712, -5879801, 33518459, 16176658, 21432314, 12180697),
array(-11787308, 11500838, 13787581, -13832590, -22430679, 10140205, 1465425, 12689540, -10301319, -13872883),
),
),
array(
array(
array(5414091, -15386041, -21007664, 9643570, 12834970, 1186149, -2622916, -1342231, 26128231, 6032912),
array(-26337395, -13766162, 32496025, -13653919, 17847801, -12669156, 3604025, 8316894, -25875034, -10437358),
array(3296484, 6223048, 24680646, -12246460, -23052020, 5903205, -8862297, -4639164, 12376617, 3188849),
),
array(
array(29190488, -14659046, 27549113, -1183516, 3520066, -10697301, 32049515, -7309113, -16109234, -9852307),
array(-14744486, -9309156, 735818, -598978, -20407687, -5057904, 25246078, -15795669, 18640741, -960977),
array(-6928835, -16430795, 10361374, 5642961, 4910474, 12345252, -31638386, -494430, 10530747, 1053335),
),
array(
array(-29265967, -14186805, -13538216, -12117373, -19457059, -10655384, -31462369, -2948985, 24018831, 15026644),
array(-22592535, -3145277, -2289276, 5953843, -13440189, 9425631, 25310643, 13003497, -2314791, -15145616),
array(-27419985, -603321, -8043984, -1669117, -26092265, 13987819, -27297622, 187899, -23166419, -2531735),
),
array(
array(-21744398, -13810475, 1844840, 5021428, -10434399, -15911473, 9716667, 16266922, -5070217, 726099),
array(29370922, -6053998, 7334071, -15342259, 9385287, 2247707, -13661962, -4839461, 30007388, -15823341),
array(-936379, 16086691, 23751945, -543318, -1167538, -5189036, 9137109, 730663, 9835848, 4555336),
),
array(
array(-23376435, 1410446, -22253753, -12899614, 30867635, 15826977, 17693930, 544696, -11985298, 12422646),
array(31117226, -12215734, -13502838, 6561947, -9876867, -12757670, -5118685, -4096706, 29120153, 13924425),
array(-17400879, -14233209, 19675799, -2734756, -11006962, -5858820, -9383939, -11317700, 7240931, -237388),
),
array(
array(-31361739, -11346780, -15007447, -5856218, -22453340, -12152771, 1222336, 4389483, 3293637, -15551743),
array(-16684801, -14444245, 11038544, 11054958, -13801175, -3338533, -24319580, 7733547, 12796905, -6335822),
array(-8759414, -10817836, -25418864, 10783769, -30615557, -9746811, -28253339, 3647836, 3222231, -11160462),
),
array(
array(18606113, 1693100, -25448386, -15170272, 4112353, 10045021, 23603893, -2048234, -7550776, 2484985),
array(9255317, -3131197, -12156162, -1004256, 13098013, -9214866, 16377220, -2102812, -19802075, -3034702),
array(-22729289, 7496160, -5742199, 11329249, 19991973, -3347502, -31718148, 9936966, -30097688, -10618797),
),
array(
array(21878590, -5001297, 4338336, 13643897, -3036865, 13160960, 19708896, 5415497, -7360503, -4109293),
array(27736861, 10103576, 12500508, 8502413, -3413016, -9633558, 10436918, -1550276, -23659143, -8132100),
array(19492550, -12104365, -29681976, -852630, -3208171, 12403437, 30066266, 8367329, 13243957, 8709688),
),
),
array(
array(
array(12015105, 2801261, 28198131, 10151021, 24818120, -4743133, -11194191, -5645734, 5150968, 7274186),
array(2831366, -12492146, 1478975, 6122054, 23825128, -12733586, 31097299, 6083058, 31021603, -9793610),
array(-2529932, -2229646, 445613, 10720828, -13849527, -11505937, -23507731, 16354465, 15067285, -14147707),
),
array(
array(7840942, 14037873, -33364863, 15934016, -728213, -3642706, 21403988, 1057586, -19379462, -12403220),
array(915865, -16469274, 15608285, -8789130, -24357026, 6060030, -17371319, 8410997, -7220461, 16527025),
array(32922597, -556987, 20336074, -16184568, 10903705, -5384487, 16957574, 52992, 23834301, 6588044),
),
array(
array(32752030, 11232950, 3381995, -8714866, 22652988, -10744103, 17159699, 16689107, -20314580, -1305992),
array(-4689649, 9166776, -25710296, -10847306, 11576752, 12733943, 7924251, -2752281, 1976123, -7249027),
array(21251222, 16309901, -2983015, -6783122, 30810597, 12967303, 156041, -3371252, 12331345, -8237197),
),
array(
array(8651614, -4477032, -16085636, -4996994, 13002507, 2950805, 29054427, -5106970, 10008136, -4667901),
array(31486080, 15114593, -14261250, 12951354, 14369431, -7387845, 16347321, -13662089, 8684155, -10532952),
array(19443825, 11385320, 24468943, -9659068, -23919258, 2187569, -26263207, -6086921, 31316348, 14219878),
),
array(
array(-28594490, 1193785, 32245219, 11392485, 31092169, 15722801, 27146014, 6992409, 29126555, 9207390),
array(32382935, 1110093, 18477781, 11028262, -27411763, -7548111, -4980517, 10843782, -7957600, -14435730),
array(2814918, 7836403, 27519878, -7868156, -20894015, -11553689, -21494559, 8550130, 28346258, 1994730),
),
array(
array(-19578299, 8085545, -14000519, -3948622, 2785838, -16231307, -19516951, 7174894, 22628102, 8115180),
array(-30405132, 955511, -11133838, -15078069, -32447087, -13278079, -25651578, 3317160, -9943017, 930272),
array(-15303681, -6833769, 28856490, 1357446, 23421993, 1057177, 24091212, -1388970, -22765376, -10650715),
),
array(
array(-22751231, -5303997, -12907607, -12768866, -15811511, -7797053, -14839018, -16554220, -1867018, 8398970),
array(-31969310, 2106403, -4736360, 1362501, 12813763, 16200670, 22981545, -6291273, 18009408, -15772772),
array(-17220923, -9545221, -27784654, 14166835, 29815394, 7444469, 29551787, -3727419, 19288549, 1325865),
),
array(
array(15100157, -15835752, -23923978, -1005098, -26450192, 15509408, 12376730, -3479146, 33166107, -8042750),
array(20909231, 13023121, -9209752, 16251778, -5778415, -8094914, 12412151, 10018715, 2213263, -13878373),
array(32529814, -11074689, 30361439, -16689753, -9135940, 1513226, 22922121, 6382134, -5766928, 8371348),
),
),
array(
array(
array(9923462, 11271500, 12616794, 3544722, -29998368, -1721626, 12891687, -8193132, -26442943, 10486144),
array(-22597207, -7012665, 8587003, -8257861, 4084309, -12970062, 361726, 2610596, -23921530, -11455195),
array(5408411, -1136691, -4969122, 10561668, 24145918, 14240566, 31319731, -4235541, 19985175, -3436086),
),
array(
array(-13994457, 16616821, 14549246, 3341099, 32155958, 13648976, -17577068, 8849297, 65030, 8370684),
array(-8320926, -12049626, 31204563, 5839400, -20627288, -1057277, -19442942, 6922164, 12743482, -9800518),
array(-2361371, 12678785, 28815050, 4759974, -23893047, 4884717, 23783145, 11038569, 18800704, 255233),
),
array(
array(-5269658, -1773886, 13957886, 7990715, 23132995, 728773, 13393847, 9066957, 19258688, -14753793),
array(-2936654, -10827535, -10432089, 14516793, -3640786, 4372541, -31934921, 2209390, -1524053, 2055794),
array(580882, 16705327, 5468415, -2683018, -30926419, -14696000, -7203346, -8994389, -30021019, 7394435),
),
array(
array(23838809, 1822728, -15738443, 15242727, 8318092, -3733104, -21672180, -3492205, -4821741, 14799921),
array(13345610, 9759151, 3371034, -16137791, 16353039, 8577942, 31129804, 13496856, -9056018, 7402518),
array(2286874, -4435931, -20042458, -2008336, -13696227, 5038122, 11006906, -15760352, 8205061, 1607563),
),
array(
array(14414086, -8002132, 3331830, -3208217, 22249151, -5594188, 18364661, -2906958, 30019587, -9029278),
array(-27688051, 1585953, -10775053, 931069, -29120221, -11002319, -14410829, 12029093, 9944378, 8024),
array(4368715, -3709630, 29874200, -15022983, -20230386, -11410704, -16114594, -999085, -8142388, 5640030),
),
array(
array(10299610, 13746483, 11661824, 16234854, 7630238, 5998374, 9809887, -16694564, 15219798, -14327783),
array(27425505, -5719081, 3055006, 10660664, 23458024, 595578, -15398605, -1173195, -18342183, 9742717),
array(6744077, 2427284, 26042789, 2720740, -847906, 1118974, 32324614, 7406442, 12420155, 1994844),
),
array(
array(14012521, -5024720, -18384453, -9578469, -26485342, -3936439, -13033478, -10909803, 24319929, -6446333),
array(16412690, -4507367, 10772641, 15929391, -17068788, -4658621, 10555945, -10484049, -30102368, -4739048),
array(22397382, -7767684, -9293161, -12792868, 17166287, -9755136, -27333065, 6199366, 21880021, -12250760),
),
array(
array(-4283307, 5368523, -31117018, 8163389, -30323063, 3209128, 16557151, 8890729, 8840445, 4957760),
array(-15447727, 709327, -6919446, -10870178, -29777922, 6522332, -21720181, 12130072, -14796503, 5005757),
array(-2114751, -14308128, 23019042, 15765735, -25269683, 6002752, 10183197, -13239326, -16395286, -2176112),
),
),
array(
array(
array(-19025756, 1632005, 13466291, -7995100, -23640451, 16573537, -32013908, -3057104, 22208662, 2000468),
array(3065073, -1412761, -25598674, -361432, -17683065, -5703415, -8164212, 11248527, -3691214, -7414184),
array(10379208, -6045554, 8877319, 1473647, -29291284, -12507580, 16690915, 2553332, -3132688, 16400289),
),
array(
array(15716668, 1254266, -18472690, 7446274, -8448918, 6344164, -22097271, -7285580, 26894937, 9132066),
array(24158887, 12938817, 11085297, -8177598, -28063478, -4457083, -30576463, 64452, -6817084, -2692882),
array(13488534, 7794716, 22236231, 5989356, 25426474, -12578208, 2350710, -3418511, -4688006, 2364226),
),
array(
array(16335052, 9132434, 25640582, 6678888, 1725628, 8517937, -11807024, -11697457, 15445875, -7798101),
array(29004207, -7867081, 28661402, -640412, -12794003, -7943086, 31863255, -4135540, -278050, -15759279),
array(-6122061, -14866665, -28614905, 14569919, -10857999, -3591829, 10343412, -6976290, -29828287, -10815811),
),
array(
array(27081650, 3463984, 14099042, -4517604, 1616303, -6205604, 29542636, 15372179, 17293797, 960709),
array(20263915, 11434237, -5765435, 11236810, 13505955, -10857102, -16111345, 6493122, -19384511, 7639714),
array(-2830798, -14839232, 25403038, -8215196, -8317012, -16173699, 18006287, -16043750, 29994677, -15808121),
),
array(
array(9769828, 5202651, -24157398, -13631392, -28051003, -11561624, -24613141, -13860782, -31184575, 709464),
array(12286395, 13076066, -21775189, -1176622, -25003198, 4057652, -32018128, -8890874, 16102007, 13205847),
array(13733362, 5599946, 10557076, 3195751, -5557991, 8536970, -25540170, 8525972, 10151379, 10394400),
),
array(
array(4024660, -16137551, 22436262, 12276534, -9099015, -2686099, 19698229, 11743039, -33302334, 8934414),
array(-15879800, -4525240, -8580747, -2934061, 14634845, -698278, -9449077, 3137094, -11536886, 11721158),
array(17555939, -5013938, 8268606, 2331751, -22738815, 9761013, 9319229, 8835153, -9205489, -1280045),
),
array(
array(-461409, -7830014, 20614118, 16688288, -7514766, -4807119, 22300304, 505429, 6108462, -6183415),
array(-5070281, 12367917, -30663534, 3234473, 32617080, -8422642, 29880583, -13483331, -26898490, -7867459),
array(-31975283, 5726539, 26934134, 10237677, -3173717, -605053, 24199304, 3795095, 7592688, -14992079),
),
array(
array(21594432, -14964228, 17466408, -4077222, 32537084, 2739898, 6407723, 12018833, -28256052, 4298412),
array(-20650503, -11961496, -27236275, 570498, 3767144, -1717540, 13891942, -1569194, 13717174, 10805743),
array(-14676630, -15644296, 15287174, 11927123, 24177847, -8175568, -796431, 14860609, -26938930, -5863836),
),
),
array(
array(
array(12962541, 5311799, -10060768, 11658280, 18855286, -7954201, 13286263, -12808704, -4381056, 9882022),
array(18512079, 11319350, -20123124, 15090309, 18818594, 5271736, -22727904, 3666879, -23967430, -3299429),
array(-6789020, -3146043, 16192429, 13241070, 15898607, -14206114, -10084880, -6661110, -2403099, 5276065),
),
array(
array(30169808, -5317648, 26306206, -11750859, 27814964, 7069267, 7152851, 3684982, 1449224, 13082861),
array(10342826, 3098505, 2119311, 193222, 25702612, 12233820, 23697382, 15056736, -21016438, -8202000),
array(-33150110, 3261608, 22745853, 7948688, 19370557, -15177665, -26171976, 6482814, -10300080, -11060101),
),
array(
array(32869458, -5408545, 25609743, 15678670, -10687769, -15471071, 26112421, 2521008, -22664288, 6904815),
array(29506923, 4457497, 3377935, -9796444, -30510046, 12935080, 1561737, 3841096, -29003639, -6657642),
array(10340844, -6630377, -18656632, -2278430, 12621151, -13339055, 30878497, -11824370, -25584551, 5181966),
),
array(
array(25940115, -12658025, 17324188, -10307374, -8671468, 15029094, 24396252, -16450922, -2322852, -12388574),
array(-21765684, 9916823, -1300409, 4079498, -1028346, 11909559, 1782390, 12641087, 20603771, -6561742),
array(-18882287, -11673380, 24849422, 11501709, 13161720, -4768874, 1925523, 11914390, 4662781, 7820689),
),
array(
array(12241050, -425982, 8132691, 9393934, 32846760, -1599620, 29749456, 12172924, 16136752, 15264020),
array(-10349955, -14680563, -8211979, 2330220, -17662549, -14545780, 10658213, 6671822, 19012087, 3772772),
array(3753511, -3421066, 10617074, 2028709, 14841030, -6721664, 28718732, -15762884, 20527771, 12988982),
),
array(
array(-14822485, -5797269, -3707987, 12689773, -898983, -10914866, -24183046, -10564943, 3299665, -12424953),
array(-16777703, -15253301, -9642417, 4978983, 3308785, 8755439, 6943197, 6461331, -25583147, 8991218),
array(-17226263, 1816362, -1673288, -6086439, 31783888, -8175991, -32948145, 7417950, -30242287, 1507265),
),
array(
array(29692663, 6829891, -10498800, 4334896, 20945975, -11906496, -28887608, 8209391, 14606362, -10647073),
array(-3481570, 8707081, 32188102, 5672294, 22096700, 1711240, -33020695, 9761487, 4170404, -2085325),
array(-11587470, 14855945, -4127778, -1531857, -26649089, 15084046, 22186522, 16002000, -14276837, -8400798),
),
array(
array(-4811456, 13761029, -31703877, -2483919, -3312471, 7869047, -7113572, -9620092, 13240845, 10965870),
array(-7742563, -8256762, -14768334, -13656260, -23232383, 12387166, 4498947, 14147411, 29514390, 4302863),
array(-13413405, -12407859, 20757302, -13801832, 14785143, 8976368, -5061276, -2144373, 17846988, -13971927),
),
),
array(
array(
array(-2244452, -754728, -4597030, -1066309, -6247172, 1455299, -21647728, -9214789, -5222701, 12650267),
array(-9906797, -16070310, 21134160, 12198166, -27064575, 708126, 387813, 13770293, -19134326, 10958663),
array(22470984, 12369526, 23446014, -5441109, -21520802, -9698723, -11772496, -11574455, -25083830, 4271862),
),
array(
array(-25169565, -10053642, -19909332, 15361595, -5984358, 2159192, 75375, -4278529, -32526221, 8469673),
array(15854970, 4148314, -8893890, 7259002, 11666551, 13824734, -30531198, 2697372, 24154791, -9460943),
array(15446137, -15806644, 29759747, 14019369, 30811221, -9610191, -31582008, 12840104, 24913809, 9815020),
),
array(
array(-4709286, -5614269, -31841498, -12288893, -14443537, 10799414, -9103676, 13438769, 18735128, 9466238),
array(11933045, 9281483, 5081055, -5183824, -2628162, -4905629, -7727821, -10896103, -22728655, 16199064),
array(14576810, 379472, -26786533, -8317236, -29426508, -10812974, -102766, 1876699, 30801119, 2164795),
),
array(
array(15995086, 3199873, 13672555, 13712240, -19378835, -4647646, -13081610, -15496269, -13492807, 1268052),
array(-10290614, -3659039, -3286592, 10948818, 23037027, 3794475, -3470338, -12600221, -17055369, 3565904),
array(29210088, -9419337, -5919792, -4952785, 10834811, -13327726, -16512102, -10820713, -27162222, -14030531),
),
array(
array(-13161890, 15508588, 16663704, -8156150, -28349942, 9019123, -29183421, -3769423, 2244111, -14001979),
array(-5152875, -3800936, -9306475, -6071583, 16243069, 14684434, -25673088, -16180800, 13491506, 4641841),
array(10813417, 643330, -19188515, -728916, 30292062, -16600078, 27548447, -7721242, 14476989, -12767431),
),
array(
array(10292079, 9984945, 6481436, 8279905, -7251514, 7032743, 27282937, -1644259, -27912810, 12651324),
array(-31185513, -813383, 22271204, 11835308, 10201545, 15351028, 17099662, 3988035, 21721536, -3148940),
array(10202177, -6545839, -31373232, -9574638, -32150642, -8119683, -12906320, 3852694, 13216206, 14842320),
),
array(
array(-15815640, -10601066, -6538952, -7258995, -6984659, -6581778, -31500847, 13765824, -27434397, 9900184),
array(14465505, -13833331, -32133984, -14738873, -27443187, 12990492, 33046193, 15796406, -7051866, -8040114),
array(30924417, -8279620, 6359016, -12816335, 16508377, 9071735, -25488601, 15413635, 9524356, -7018878),
),
array(
array(12274201, -13175547, 32627641, -1785326, 6736625, 13267305, 5237659, -5109483, 15663516, 4035784),
array(-2951309, 8903985, 17349946, 601635, -16432815, -4612556, -13732739, -15889334, -22258478, 4659091),
array(-16916263, -4952973, -30393711, -15158821, 20774812, 15897498, 5736189, 15026997, -2178256, -13455585),
),
),
array(
array(
array(-8858980, -2219056, 28571666, -10155518, -474467, -10105698, -3801496, 278095, 23440562, -290208),
array(10226241, -5928702, 15139956, 120818, -14867693, 5218603, 32937275, 11551483, -16571960, -7442864),
array(17932739, -12437276, -24039557, 10749060, 11316803, 7535897, 22503767, 5561594, -3646624, 3898661),
),
array(
array(7749907, -969567, -16339731, -16464, -25018111, 15122143, -1573531, 7152530, 21831162, 1245233),
array(26958459, -14658026, 4314586, 8346991, -5677764, 11960072, -32589295, -620035, -30402091, -16716212),
array(-12165896, 9166947, 33491384, 13673479, 29787085, 13096535, 6280834, 14587357, -22338025, 13987525),
),
array(
array(-24349909, 7778775, 21116000, 15572597, -4833266, -5357778, -4300898, -5124639, -7469781, -2858068),
array(9681908, -6737123, -31951644, 13591838, -6883821, 386950, 31622781, 6439245, -14581012, 4091397),
array(-8426427, 1470727, -28109679, -1596990, 3978627, -5123623, -19622683, 12092163, 29077877, -14741988),
),
array(
array(5269168, -6859726, -13230211, -8020715, 25932563, 1763552, -5606110, -5505881, -20017847, 2357889),
array(32264008, -15407652, -5387735, -1160093, -2091322, -3946900, 23104804, -12869908, 5727338, 189038),
array(14609123, -8954470, -6000566, -16622781, -14577387, -7743898, -26745169, 10942115, -25888931, -14884697),
),
array(
array(20513500, 5557931, -15604613, 7829531, 26413943, -2019404, -21378968, 7471781, 13913677, -5137875),
array(-25574376, 11967826, 29233242, 12948236, -6754465, 4713227, -8940970, 14059180, 12878652, 8511905),
array(-25656801, 3393631, -2955415, -7075526, -2250709, 9366908, -30223418, 6812974, 5568676, -3127656),
),
array(
array(11630004, 12144454, 2116339, 13606037, 27378885, 15676917, -17408753, -13504373, -14395196, 8070818),
array(27117696, -10007378, -31282771, -5570088, 1127282, 12772488, -29845906, 10483306, -11552749, -1028714),
array(10637467, -5688064, 5674781, 1072708, -26343588, -6982302, -1683975, 9177853, -27493162, 15431203),
),
array(
array(20525145, 10892566, -12742472, 12779443, -29493034, 16150075, -28240519, 14943142, -15056790, -7935931),
array(-30024462, 5626926, -551567, -9981087, 753598, 11981191, 25244767, -3239766, -3356550, 9594024),
array(-23752644, 2636870, -5163910, -10103818, 585134, 7877383, 11345683, -6492290, 13352335, -10977084),
),
array(
array(-1931799, -5407458, 3304649, -12884869, 17015806, -4877091, -29783850, -7752482, -13215537, -319204),
array(20239939, 6607058, 6203985, 3483793, -18386976, -779229, -20723742, 15077870, -22750759, 14523817),
array(27406042, -6041657, 27423596, -4497394, 4996214, 10002360, -28842031, -4545494, -30172742, -4805667),
),
),
array(
array(
array(11374242, 12660715, 17861383, -12540833, 10935568, 1099227, -13886076, -9091740, -27727044, 11358504),
array(-12730809, 10311867, 1510375, 10778093, -2119455, -9145702, 32676003, 11149336, -26123651, 4985768),
array(-19096303, 341147, -6197485, -239033, 15756973, -8796662, -983043, 13794114, -19414307, -15621255),
),
array(
array(6490081, 11940286, 25495923, -7726360, 8668373, -8751316, 3367603, 6970005, -1691065, -9004790),
array(1656497, 13457317, 15370807, 6364910, 13605745, 8362338, -19174622, -5475723, -16796596, -5031438),
array(-22273315, -13524424, -64685, -4334223, -18605636, -10921968, -20571065, -7007978, -99853, -10237333),
),
array(
array(17747465, 10039260, 19368299, -4050591, -20630635, -16041286, 31992683, -15857976, -29260363, -5511971),
array(31932027, -4986141, -19612382, 16366580, 22023614, 88450, 11371999, -3744247, 4882242, -10626905),
array(29796507, 37186, 19818052, 10115756, -11829032, 3352736, 18551198, 3272828, -5190932, -4162409),
),
array(
array(12501286, 4044383, -8612957, -13392385, -32430052, 5136599, -19230378, -3529697, 330070, -3659409),
array(6384877, 2899513, 17807477, 7663917, -2358888, 12363165, 25366522, -8573892, -271295, 12071499),
array(-8365515, -4042521, 25133448, -4517355, -6211027, 2265927, -32769618, 1936675, -5159697, 3829363),
),
array(
array(28425966, -5835433, -577090, -4697198, -14217555, 6870930, 7921550, -6567787, 26333140, 14267664),
array(-11067219, 11871231, 27385719, -10559544, -4585914, -11189312, 10004786, -8709488, -21761224, 8930324),
array(-21197785, -16396035, 25654216, -1725397, 12282012, 11008919, 1541940, 4757911, -26491501, -16408940),
),
array(
array(13537262, -7759490, -20604840, 10961927, -5922820, -13218065, -13156584, 6217254, -15943699, 13814990),
array(-17422573, 15157790, 18705543, 29619, 24409717, -260476, 27361681, 9257833, -1956526, -1776914),
array(-25045300, -10191966, 15366585, 15166509, -13105086, 8423556, -29171540, 12361135, -18685978, 4578290),
),
array(
array(24579768, 3711570, 1342322, -11180126, -27005135, 14124956, -22544529, 14074919, 21964432, 8235257),
array(-6528613, -2411497, 9442966, -5925588, 12025640, -1487420, -2981514, -1669206, 13006806, 2355433),
array(-16304899, -13605259, -6632427, -5142349, 16974359, -10911083, 27202044, 1719366, 1141648, -12796236),
),
array(
array(-12863944, -13219986, -8318266, -11018091, -6810145, -4843894, 13475066, -3133972, 32674895, 13715045),
array(11423335, -5468059, 32344216, 8962751, 24989809, 9241752, -13265253, 16086212, -28740881, -15642093),
array(-1409668, 12530728, -6368726, 10847387, 19531186, -14132160, -11709148, 7791794, -27245943, 4383347),
),
),
array(
array(
array(-28970898, 5271447, -1266009, -9736989, -12455236, 16732599, -4862407, -4906449, 27193557, 6245191),
array(-15193956, 5362278, -1783893, 2695834, 4960227, 12840725, 23061898, 3260492, 22510453, 8577507),
array(-12632451, 11257346, -32692994, 13548177, -721004, 10879011, 31168030, 13952092, -29571492, -3635906),
),
array(
array(3877321, -9572739, 32416692, 5405324, -11004407, -13656635, 3759769, 11935320, 5611860, 8164018),
array(-16275802, 14667797, 15906460, 12155291, -22111149, -9039718, 32003002, -8832289, 5773085, -8422109),
array(-23788118, -8254300, 1950875, 8937633, 18686727, 16459170, -905725, 12376320, 31632953, 190926),
),
array(
array(-24593607, -16138885, -8423991, 13378746, 14162407, 6901328, -8288749, 4508564, -25341555, -3627528),
array(8884438, -5884009, 6023974, 10104341, -6881569, -4941533, 18722941, -14786005, -1672488, 827625),
array(-32720583, -16289296, -32503547, 7101210, 13354605, 2659080, -1800575, -14108036, -24878478, 1541286),
),
array(
array(2901347, -1117687, 3880376, -10059388, -17620940, -3612781, -21802117, -3567481, 20456845, -1885033),
array(27019610, 12299467, -13658288, -1603234, -12861660, -4861471, -19540150, -5016058, 29439641, 15138866),
array(21536104, -6626420, -32447818, -10690208, -22408077, 5175814, -5420040, -16361163, 7779328, 109896),
),
array(
array(30279744, 14648750, -8044871, 6425558, 13639621, -743509, 28698390, 12180118, 23177719, -554075),
array(26572847, 3405927, -31701700, 12890905, -19265668, 5335866, -6493768, 2378492, 4439158, -13279347),
array(-22716706, 3489070, -9225266, -332753, 18875722, -1140095, 14819434, -12731527, -17717757, -5461437),
),
array(
array(-5056483, 16566551, 15953661, 3767752, -10436499, 15627060, -820954, 2177225, 8550082, -15114165),
array(-18473302, 16596775, -381660, 15663611, 22860960, 15585581, -27844109, -3582739, -23260460, -8428588),
array(-32480551, 15707275, -8205912, -5652081, 29464558, 2713815, -22725137, 15860482, -21902570, 1494193),
),
array(
array(-19562091, -14087393, -25583872, -9299552, 13127842, 759709, 21923482, 16529112, 8742704, 12967017),
array(-28464899, 1553205, 32536856, -10473729, -24691605, -406174, -8914625, -2933896, -29903758, 15553883),
array(21877909, 3230008, 9881174, 10539357, -4797115, 2841332, 11543572, 14513274, 19375923, -12647961),
),
array(
array(8832269, -14495485, 13253511, 5137575, 5037871, 4078777, 24880818, -6222716, 2862653, 9455043),
array(29306751, 5123106, 20245049, -14149889, 9592566, 8447059, -2077124, -2990080, 15511449, 4789663),
array(-20679756, 7004547, 8824831, -9434977, -4045704, -3750736, -5754762, 108893, 23513200, 16652362),
),
),
array(
array(
array(-33256173, 4144782, -4476029, -6579123, 10770039, -7155542, -6650416, -12936300, -18319198, 10212860),
array(2756081, 8598110, 7383731, -6859892, 22312759, -1105012, 21179801, 2600940, -9988298, -12506466),
array(-24645692, 13317462, -30449259, -15653928, 21365574, -10869657, 11344424, 864440, -2499677, -16710063),
),
array(
array(-26432803, 6148329, -17184412, -14474154, 18782929, -275997, -22561534, 211300, 2719757, 4940997),
array(-1323882, 3911313, -6948744, 14759765, -30027150, 7851207, 21690126, 8518463, 26699843, 5276295),
array(-13149873, -6429067, 9396249, 365013, 24703301, -10488939, 1321586, 149635, -15452774, 7159369),
),
array(
array(9987780, -3404759, 17507962, 9505530, 9731535, -2165514, 22356009, 8312176, 22477218, -8403385),
array(18155857, -16504990, 19744716, 9006923, 15154154, -10538976, 24256460, -4864995, -22548173, 9334109),
array(2986088, -4911893, 10776628, -3473844, 10620590, -7083203, -21413845, 14253545, -22587149, 536906),
),
array(
array(4377756, 8115836, 24567078, 15495314, 11625074, 13064599, 7390551, 10589625, 10838060, -15420424),
array(-19342404, 867880, 9277171, -3218459, -14431572, -1986443, 19295826, -15796950, 6378260, 699185),
array(7895026, 4057113, -7081772, -13077756, -17886831, -323126, -716039, 15693155, -5045064, -13373962),
),
array(
array(-7737563, -5869402, -14566319, -7406919, 11385654, 13201616, 31730678, -10962840, -3918636, -9669325),
array(10188286, -15770834, -7336361, 13427543, 22223443, 14896287, 30743455, 7116568, -21786507, 5427593),
array(696102, 13206899, 27047647, -10632082, 15285305, -9853179, 10798490, -4578720, 19236243, 12477404),
),
array(
array(-11229439, 11243796, -17054270, -8040865, -788228, -8167967, -3897669, 11180504, -23169516, 7733644),
array(17800790, -14036179, -27000429, -11766671, 23887827, 3149671, 23466177, -10538171, 10322027, 15313801),
array(26246234, 11968874, 32263343, -5468728, 6830755, -13323031, -15794704, -101982, -24449242, 10890804),
),
array(
array(-31365647, 10271363, -12660625, -6267268, 16690207, -13062544, -14982212, 16484931, 25180797, -5334884),
array(-586574, 10376444, -32586414, -11286356, 19801893, 10997610, 2276632, 9482883, 316878, 13820577),
array(-9882808, -4510367, -2115506, 16457136, -11100081, 11674996, 30756178, -7515054, 30696930, -3712849),
),
array(
array(32988917, -9603412, 12499366, 7910787, -10617257, -11931514, -7342816, -9985397, -32349517, 7392473),
array(-8855661, 15927861, 9866406, -3649411, -2396914, -16655781, -30409476, -9134995, 25112947, -2926644),
array(-2504044, -436966, 25621774, -5678772, 15085042, -5479877, -24884878, -13526194, 5537438, -13914319),
),
),
array(
array(
array(-11225584, 2320285, -9584280, 10149187, -33444663, 5808648, -14876251, -1729667, 31234590, 6090599),
array(-9633316, 116426, 26083934, 2897444, -6364437, -2688086, 609721, 15878753, -6970405, -9034768),
array(-27757857, 247744, -15194774, -9002551, 23288161, -10011936, -23869595, 6503646, 20650474, 1804084),
),
array(
array(-27589786, 15456424, 8972517, 8469608, 15640622, 4439847, 3121995, -10329713, 27842616, -202328),
array(-15306973, 2839644, 22530074, 10026331, 4602058, 5048462, 28248656, 5031932, -11375082, 12714369),
array(20807691, -7270825, 29286141, 11421711, -27876523, -13868230, -21227475, 1035546, -19733229, 12796920),
),
array(
array(12076899, -14301286, -8785001, -11848922, -25012791, 16400684, -17591495, -12899438, 3480665, -15182815),
array(-32361549, 5457597, 28548107, 7833186, 7303070, -11953545, -24363064, -15921875, -33374054, 2771025),
array(-21389266, 421932, 26597266, 6860826, 22486084, -6737172, -17137485, -4210226, -24552282, 15673397),
),
array(
array(-20184622, 2338216, 19788685, -9620956, -4001265, -8740893, -20271184, 4733254, 3727144, -12934448),
array(6120119, 814863, -11794402, -622716, 6812205, -15747771, 2019594, 7975683, 31123697, -10958981),
array(30069250, -11435332, 30434654, 2958439, 18399564, -976289, 12296869, 9204260, -16432438, 9648165),
),
array(
array(32705432, -1550977, 30705658, 7451065, -11805606, 9631813, 3305266, 5248604, -26008332, -11377501),
array(17219865, 2375039, -31570947, -5575615, -19459679, 9219903, 294711, 15298639, 2662509, -16297073),
array(-1172927, -7558695, -4366770, -4287744, -21346413, -8434326, 32087529, -1222777, 32247248, -14389861),
),
array(
array(14312628, 1221556, 17395390, -8700143, -4945741, -8684635, -28197744, -9637817, -16027623, -13378845),
array(-1428825, -9678990, -9235681, 6549687, -7383069, -468664, 23046502, 9803137, 17597934, 2346211),
array(18510800, 15337574, 26171504, 981392, -22241552, 7827556, -23491134, -11323352, 3059833, -11782870),
),
array(
array(10141598, 6082907, 17829293, -1947643, 9830092, 13613136, -25556636, -5544586, -33502212, 3592096),
array(33114168, -15889352, -26525686, -13343397, 33076705, 8716171, 1151462, 1521897, -982665, -6837803),
array(-32939165, -4255815, 23947181, -324178, -33072974, -12305637, -16637686, 3891704, 26353178, 693168),
),
array(
array(30374239, 1595580, -16884039, 13186931, 4600344, 406904, 9585294, -400668, 31375464, 14369965),
array(-14370654, -7772529, 1510301, 6434173, -18784789, -6262728, 32732230, -13108839, 17901441, 16011505),
array(18171223, -11934626, -12500402, 15197122, -11038147, -15230035, -19172240, -16046376, 8764035, 12309598),
),
),
array(
array(
array(5975908, -5243188, -19459362, -9681747, -11541277, 14015782, -23665757, 1228319, 17544096, -10593782),
array(5811932, -1715293, 3442887, -2269310, -18367348, -8359541, -18044043, -15410127, -5565381, 12348900),
array(-31399660, 11407555, 25755363, 6891399, -3256938, 14872274, -24849353, 8141295, -10632534, -585479),
),
array(
array(-12675304, 694026, -5076145, 13300344, 14015258, -14451394, -9698672, -11329050, 30944593, 1130208),
array(8247766, -6710942, -26562381, -7709309, -14401939, -14648910, 4652152, 2488540, 23550156, -271232),
array(17294316, -3788438, 7026748, 15626851, 22990044, 113481, 2267737, -5908146, -408818, -137719),
),
array(
array(16091085, -16253926, 18599252, 7340678, 2137637, -1221657, -3364161, 14550936, 3260525, -7166271),
array(-4910104, -13332887, 18550887, 10864893, -16459325, -7291596, -23028869, -13204905, -12748722, 2701326),
array(-8574695, 16099415, 4629974, -16340524, -20786213, -6005432, -10018363, 9276971, 11329923, 1862132),
),
array(
array(14763076, -15903608, -30918270, 3689867, 3511892, 10313526, -21951088, 12219231, -9037963, -940300),
array(8894987, -3446094, 6150753, 3013931, 301220, 15693451, -31981216, -2909717, -15438168, 11595570),
array(15214962, 3537601, -26238722, -14058872, 4418657, -15230761, 13947276, 10730794, -13489462, -4363670),
),
array(
array(-2538306, 7682793, 32759013, 263109, -29984731, -7955452, -22332124, -10188635, 977108, 699994),
array(-12466472, 4195084, -9211532, 550904, -15565337, 12917920, 19118110, -439841, -30534533, -14337913),
array(31788461, -14507657, 4799989, 7372237, 8808585, -14747943, 9408237, -10051775, 12493932, -5409317),
),
array(
array(-25680606, 5260744, -19235809, -6284470, -3695942, 16566087, 27218280, 2607121, 29375955, 6024730),
array(842132, -2794693, -4763381, -8722815, 26332018, -12405641, 11831880, 6985184, -9940361, 2854096),
array(-4847262, -7969331, 2516242, -5847713, 9695691, -7221186, 16512645, 960770, 12121869, 16648078),
),
array(
array(-15218652, 14667096, -13336229, 2013717, 30598287, -464137, -31504922, -7882064, 20237806, 2838411),
array(-19288047, 4453152, 15298546, -16178388, 22115043, -15972604, 12544294, -13470457, 1068881, -12499905),
array(-9558883, -16518835, 33238498, 13506958, 30505848, -1114596, -8486907, -2630053, 12521378, 4845654),
),
array(
array(-28198521, 10744108, -2958380, 10199664, 7759311, -13088600, 3409348, -873400, -6482306, -12885870),
array(-23561822, 6230156, -20382013, 10655314, -24040585, -11621172, 10477734, -1240216, -3113227, 13974498),
array(12966261, 15550616, -32038948, -1615346, 21025980, -629444, 5642325, 7188737, 18895762, 12629579),
),
),
array(
array(
array(14741879, -14946887, 22177208, -11721237, 1279741, 8058600, 11758140, 789443, 32195181, 3895677),
array(10758205, 15755439, -4509950, 9243698, -4879422, 6879879, -2204575, -3566119, -8982069, 4429647),
array(-2453894, 15725973, -20436342, -10410672, -5803908, -11040220, -7135870, -11642895, 18047436, -15281743),
),
array(
array(-25173001, -11307165, 29759956, 11776784, -22262383, -15820455, 10993114, -12850837, -17620701, -9408468),
array(21987233, 700364, -24505048, 14972008, -7774265, -5718395, 32155026, 2581431, -29958985, 8773375),
array(-25568350, 454463, -13211935, 16126715, 25240068, 8594567, 20656846, 12017935, -7874389, -13920155),
),
array(
array(6028182, 6263078, -31011806, -11301710, -818919, 2461772, -31841174, -5468042, -1721788, -2776725),
array(-12278994, 16624277, 987579, -5922598, 32908203, 1248608, 7719845, -4166698, 28408820, 6816612),
array(-10358094, -8237829, 19549651, -12169222, 22082623, 16147817, 20613181, 13982702, -10339570, 5067943),
),
array(
array(-30505967, -3821767, 12074681, 13582412, -19877972, 2443951, -19719286, 12746132, 5331210, -10105944),
array(30528811, 3601899, -1957090, 4619785, -27361822, -15436388, 24180793, -12570394, 27679908, -1648928),
array(9402404, -13957065, 32834043, 10838634, -26580150, -13237195, 26653274, -8685565, 22611444, -12715406),
),
array(
array(22190590, 1118029, 22736441, 15130463, -30460692, -5991321, 19189625, -4648942, 4854859, 6622139),
array(-8310738, -2953450, -8262579, -3388049, -10401731, -271929, 13424426, -3567227, 26404409, 13001963),
array(-31241838, -15415700, -2994250, 8939346, 11562230, -12840670, -26064365, -11621720, -15405155, 11020693),
),
array(
array(1866042, -7949489, -7898649, -10301010, 12483315, 13477547, 3175636, -12424163, 28761762, 1406734),
array(-448555, -1777666, 13018551, 3194501, -9580420, -11161737, 24760585, -4347088, 25577411, -13378680),
array(-24290378, 4759345, -690653, -1852816, 2066747, 10693769, -29595790, 9884936, -9368926, 4745410),
),
array(
array(-9141284, 6049714, -19531061, -4341411, -31260798, 9944276, -15462008, -11311852, 10931924, -11931931),
array(-16561513, 14112680, -8012645, 4817318, -8040464, -11414606, -22853429, 10856641, -20470770, 13434654),
array(22759489, -10073434, -16766264, -1871422, 13637442, -10168091, 1765144, -12654326, 28445307, -5364710),
),
array(
array(29875063, 12493613, 2795536, -3786330, 1710620, 15181182, -10195717, -8788675, 9074234, 1167180),
array(-26205683, 11014233, -9842651, -2635485, -26908120, 7532294, -18716888, -9535498, 3843903, 9367684),
array(-10969595, -6403711, 9591134, 9582310, 11349256, 108879, 16235123, 8601684, -139197, 4242895),
),
),
array(
array(
array(22092954, -13191123, -2042793, -11968512, 32186753, -11517388, -6574341, 2470660, -27417366, 16625501),
array(-11057722, 3042016, 13770083, -9257922, 584236, -544855, -7770857, 2602725, -27351616, 14247413),
array(6314175, -10264892, -32772502, 15957557, -10157730, 168750, -8618807, 14290061, 27108877, -1180880),
),
array(
array(-8586597, -7170966, 13241782, 10960156, -32991015, -13794596, 33547976, -11058889, -27148451, 981874),
array(22833440, 9293594, -32649448, -13618667, -9136966, 14756819, -22928859, -13970780, -10479804, -16197962),
array(-7768587, 3326786, -28111797, 10783824, 19178761, 14905060, 22680049, 13906969, -15933690, 3797899),
),
array(
array(21721356, -4212746, -12206123, 9310182, -3882239, -13653110, 23740224, -2709232, 20491983, -8042152),
array(9209270, -15135055, -13256557, -6167798, -731016, 15289673, 25947805, 15286587, 30997318, -6703063),
array(7392032, 16618386, 23946583, -8039892, -13265164, -1533858, -14197445, -2321576, 17649998, -250080),
),
array(
array(-9301088, -14193827, 30609526, -3049543, -25175069, -1283752, -15241566, -9525724, -2233253, 7662146),
array(-17558673, 1763594, -33114336, 15908610, -30040870, -12174295, 7335080, -8472199, -3174674, 3440183),
array(-19889700, -5977008, -24111293, -9688870, 10799743, -16571957, 40450, -4431835, 4862400, 1133),
),
array(
array(-32856209, -7873957, -5422389, 14860950, -16319031, 7956142, 7258061, 311861, -30594991, -7379421),
array(-3773428, -1565936, 28985340, 7499440, 24445838, 9325937, 29727763, 16527196, 18278453, 15405622),
array(-4381906, 8508652, -19898366, -3674424, -5984453, 15149970, -13313598, 843523, -21875062, 13626197),
),
array(
array(2281448, -13487055, -10915418, -2609910, 1879358, 16164207, -10783882, 3953792, 13340839, 15928663),
array(31727126, -7179855, -18437503, -8283652, 2875793, -16390330, -25269894, -7014826, -23452306, 5964753),
array(4100420, -5959452, -17179337, 6017714, -18705837, 12227141, -26684835, 11344144, 2538215, -7570755),
),
array(
array(-9433605, 6123113, 11159803, -2156608, 30016280, 14966241, -20474983, 1485421, -629256, -15958862),
array(-26804558, 4260919, 11851389, 9658551, -32017107, 16367492, -20205425, -13191288, 11659922, -11115118),
array(26180396, 10015009, -30844224, -8581293, 5418197, 9480663, 2231568, -10170080, 33100372, -1306171),
),
array(
array(15121113, -5201871, -10389905, 15427821, -27509937, -15992507, 21670947, 4486675, -5931810, -14466380),
array(16166486, -9483733, -11104130, 6023908, -31926798, -1364923, 2340060, -16254968, -10735770, -10039824),
array(28042865, -3557089, -12126526, 12259706, -3717498, -6945899, 6766453, -8689599, 18036436, 5803270),
),
),
array(
array(
array(-817581, 6763912, 11803561, 1585585, 10958447, -2671165, 23855391, 4598332, -6159431, -14117438),
array(-31031306, -14256194, 17332029, -2383520, 31312682, -5967183, 696309, 50292, -20095739, 11763584),
array(-594563, -2514283, -32234153, 12643980, 12650761, 14811489, 665117, -12613632, -19773211, -10713562),
),
array(
array(30464590, -11262872, -4127476, -12734478, 19835327, -7105613, -24396175, 2075773, -17020157, 992471),
array(18357185, -6994433, 7766382, 16342475, -29324918, 411174, 14578841, 8080033, -11574335, -10601610),
array(19598397, 10334610, 12555054, 2555664, 18821899, -10339780, 21873263, 16014234, 26224780, 16452269),
),
array(
array(-30223925, 5145196, 5944548, 16385966, 3976735, 2009897, -11377804, -7618186, -20533829, 3698650),
array(14187449, 3448569, -10636236, -10810935, -22663880, -3433596, 7268410, -10890444, 27394301, 12015369),
array(19695761, 16087646, 28032085, 12999827, 6817792, 11427614, 20244189, -1312777, -13259127, -3402461),
),
array(
array(30860103, 12735208, -1888245, -4699734, -16974906, 2256940, -8166013, 12298312, -8550524, -10393462),
array(-5719826, -11245325, -1910649, 15569035, 26642876, -7587760, -5789354, -15118654, -4976164, 12651793),
array(-2848395, 9953421, 11531313, -5282879, 26895123, -12697089, -13118820, -16517902, 9768698, -2533218),
),
array(
array(-24719459, 1894651, -287698, -4704085, 15348719, -8156530, 32767513, 12765450, 4940095, 10678226),
array(18860224, 15980149, -18987240, -1562570, -26233012, -11071856, -7843882, 13944024, -24372348, 16582019),
array(-15504260, 4970268, -29893044, 4175593, -20993212, -2199756, -11704054, 15444560, -11003761, 7989037),
),
array(
array(31490452, 5568061, -2412803, 2182383, -32336847, 4531686, -32078269, 6200206, -19686113, -14800171),
array(-17308668, -15879940, -31522777, -2831, -32887382, 16375549, 8680158, -16371713, 28550068, -6857132),
array(-28126887, -5688091, 16837845, -1820458, -6850681, 12700016, -30039981, 4364038, 1155602, 5988841),
),
array(
array(21890435, -13272907, -12624011, 12154349, -7831873, 15300496, 23148983, -4470481, 24618407, 8283181),
array(-33136107, -10512751, 9975416, 6841041, -31559793, 16356536, 3070187, -7025928, 1466169, 10740210),
array(-1509399, -15488185, -13503385, -10655916, 32799044, 909394, -13938903, -5779719, -32164649, -15327040),
),
array(
array(3960823, -14267803, -28026090, -15918051, -19404858, 13146868, 15567327, 951507, -3260321, -573935),
array(24740841, 5052253, -30094131, 8961361, 25877428, 6165135, -24368180, 14397372, -7380369, -6144105),
array(-28888365, 3510803, -28103278, -1158478, -11238128, -10631454, -15441463, -14453128, -1625486, -6494814),
),
),
array(
array(
array(793299, -9230478, 8836302, -6235707, -27360908, -2369593, 33152843, -4885251, -9906200, -621852),
array(5666233, 525582, 20782575, -8038419, -24538499, 14657740, 16099374, 1468826, -6171428, -15186581),
array(-4859255, -3779343, -2917758, -6748019, 7778750, 11688288, -30404353, -9871238, -1558923, -9863646),
),
array(
array(10896332, -7719704, 824275, 472601, -19460308, 3009587, 25248958, 14783338, -30581476, -15757844),
array(10566929, 12612572, -31944212, 11118703, -12633376, 12362879, 21752402, 8822496, 24003793, 14264025),
array(27713862, -7355973, -11008240, 9227530, 27050101, 2504721, 23886875, -13117525, 13958495, -5732453),
),
array(
array(-23481610, 4867226, -27247128, 3900521, 29838369, -8212291, -31889399, -10041781, 7340521, -15410068),
array(4646514, -8011124, -22766023, -11532654, 23184553, 8566613, 31366726, -1381061, -15066784, -10375192),
array(-17270517, 12723032, -16993061, 14878794, 21619651, -6197576, 27584817, 3093888, -8843694, 3849921),
),
array(
array(-9064912, 2103172, 25561640, -15125738, -5239824, 9582958, 32477045, -9017955, 5002294, -15550259),
array(-12057553, -11177906, 21115585, -13365155, 8808712, -12030708, 16489530, 13378448, -25845716, 12741426),
array(-5946367, 10645103, -30911586, 15390284, -3286982, -7118677, 24306472, 15852464, 28834118, -7646072),
),
array(
array(-17335748, -9107057, -24531279, 9434953, -8472084, -583362, -13090771, 455841, 20461858, 5491305),
array(13669248, -16095482, -12481974, -10203039, -14569770, -11893198, -24995986, 11293807, -28588204, -9421832),
array(28497928, 6272777, -33022994, 14470570, 8906179, -1225630, 18504674, -14165166, 29867745, -8795943),
),
array(
array(-16207023, 13517196, -27799630, -13697798, 24009064, -6373891, -6367600, -13175392, 22853429, -4012011),
array(24191378, 16712145, -13931797, 15217831, 14542237, 1646131, 18603514, -11037887, 12876623, -2112447),
array(17902668, 4518229, -411702, -2829247, 26878217, 5258055, -12860753, 608397, 16031844, 3723494),
),
array(
array(-28632773, 12763728, -20446446, 7577504, 33001348, -13017745, 17558842, -7872890, 23896954, -4314245),
array(-20005381, -12011952, 31520464, 605201, 2543521, 5991821, -2945064, 7229064, -9919646, -8826859),
array(28816045, 298879, -28165016, -15920938, 19000928, -1665890, -12680833, -2949325, -18051778, -2082915),
),
array(
array(16000882, -344896, 3493092, -11447198, -29504595, -13159789, 12577740, 16041268, -19715240, 7847707),
array(10151868, 10572098, 27312476, 7922682, 14825339, 4723128, -32855931, -6519018, -10020567, 3852848),
array(-11430470, 15697596, -21121557, -4420647, 5386314, 15063598, 16514493, -15932110, 29330899, -15076224),
),
),
array(
array(
array(-25499735, -4378794, -15222908, -6901211, 16615731, 2051784, 3303702, 15490, -27548796, 12314391),
array(15683520, -6003043, 18109120, -9980648, 15337968, -5997823, -16717435, 15921866, 16103996, -3731215),
array(-23169824, -10781249, 13588192, -1628807, -3798557, -1074929, -19273607, 5402699, -29815713, -9841101),
),
array(
array(23190676, 2384583, -32714340, 3462154, -29903655, -1529132, -11266856, 8911517, -25205859, 2739713),
array(21374101, -3554250, -33524649, 9874411, 15377179, 11831242, -33529904, 6134907, 4931255, 11987849),
array(-7732, -2978858, -16223486, 7277597, 105524, -322051, -31480539, 13861388, -30076310, 10117930),
),
array(
array(-29501170, -10744872, -26163768, 13051539, -25625564, 5089643, -6325503, 6704079, 12890019, 15728940),
array(-21972360, -11771379, -951059, -4418840, 14704840, 2695116, 903376, -10428139, 12885167, 8311031),
array(-17516482, 5352194, 10384213, -13811658, 7506451, 13453191, 26423267, 4384730, 1888765, -5435404),
),
array(
array(-25817338, -3107312, -13494599, -3182506, 30896459, -13921729, -32251644, -12707869, -19464434, -3340243),
array(-23607977, -2665774, -526091, 4651136, 5765089, 4618330, 6092245, 14845197, 17151279, -9854116),
array(-24830458, -12733720, -15165978, 10367250, -29530908, -265356, 22825805, -7087279, -16866484, 16176525),
),
array(
array(-23583256, 6564961, 20063689, 3798228, -4740178, 7359225, 2006182, -10363426, -28746253, -10197509),
array(-10626600, -4486402, -13320562, -5125317, 3432136, -6393229, 23632037, -1940610, 32808310, 1099883),
array(15030977, 5768825, -27451236, -2887299, -6427378, -15361371, -15277896, -6809350, 2051441, -15225865),
),
array(
array(-3362323, -7239372, 7517890, 9824992, 23555850, 295369, 5148398, -14154188, -22686354, 16633660),
array(4577086, -16752288, 13249841, -15304328, 19958763, -14537274, 18559670, -10759549, 8402478, -9864273),
array(-28406330, -1051581, -26790155, -907698, -17212414, -11030789, 9453451, -14980072, 17983010, 9967138),
),
array(
array(-25762494, 6524722, 26585488, 9969270, 24709298, 1220360, -1677990, 7806337, 17507396, 3651560),
array(-10420457, -4118111, 14584639, 15971087, -15768321, 8861010, 26556809, -5574557, -18553322, -11357135),
array(2839101, 14284142, 4029895, 3472686, 14402957, 12689363, -26642121, 8459447, -5605463, -7621941),
),
array(
array(-4839289, -3535444, 9744961, 2871048, 25113978, 3187018, -25110813, -849066, 17258084, -7977739),
array(18164541, -10595176, -17154882, -1542417, 19237078, -9745295, 23357533, -15217008, 26908270, 12150756),
array(-30264870, -7647865, 5112249, -7036672, -1499807, -6974257, 43168, -5537701, -32302074, 16215819),
),
),
array(
array(
array(-6898905, 9824394, -12304779, -4401089, -31397141, -6276835, 32574489, 12532905, -7503072, -8675347),
array(-27343522, -16515468, -27151524, -10722951, 946346, 16291093, 254968, 7168080, 21676107, -1943028),
array(21260961, -8424752, -16831886, -11920822, -23677961, 3968121, -3651949, -6215466, -3556191, -7913075),
),
array(
array(16544754, 13250366, -16804428, 15546242, -4583003, 12757258, -2462308, -8680336, -18907032, -9662799),
array(-2415239, -15577728, 18312303, 4964443, -15272530, -12653564, 26820651, 16690659, 25459437, -4564609),
array(-25144690, 11425020, 28423002, -11020557, -6144921, -15826224, 9142795, -2391602, -6432418, -1644817),
),
array(
array(-23104652, 6253476, 16964147, -3768872, -25113972, -12296437, -27457225, -16344658, 6335692, 7249989),
array(-30333227, 13979675, 7503222, -12368314, -11956721, -4621693, -30272269, 2682242, 25993170, -12478523),
array(4364628, 5930691, 32304656, -10044554, -8054781, 15091131, 22857016, -10598955, 31820368, 15075278),
),
array(
array(31879134, -8918693, 17258761, 90626, -8041836, -4917709, 24162788, -9650886, -17970238, 12833045),
array(19073683, 14851414, -24403169, -11860168, 7625278, 11091125, -19619190, 2074449, -9413939, 14905377),
array(24483667, -11935567, -2518866, -11547418, -1553130, 15355506, -25282080, 9253129, 27628530, -7555480),
),
array(
array(17597607, 8340603, 19355617, 552187, 26198470, -3176583, 4593324, -9157582, -14110875, 15297016),
array(510886, 14337390, -31785257, 16638632, 6328095, 2713355, -20217417, -11864220, 8683221, 2921426),
array(18606791, 11874196, 27155355, -5281482, -24031742, 6265446, -25178240, -1278924, 4674690, 13890525),
),
array(
array(13609624, 13069022, -27372361, -13055908, 24360586, 9592974, 14977157, 9835105, 4389687, 288396),
array(9922506, -519394, 13613107, 5883594, -18758345, -434263, -12304062, 8317628, 23388070, 16052080),
array(12720016, 11937594, -31970060, -5028689, 26900120, 8561328, -20155687, -11632979, -14754271, -10812892),
),
array(
array(15961858, 14150409, 26716931, -665832, -22794328, 13603569, 11829573, 7467844, -28822128, 929275),
array(11038231, -11582396, -27310482, -7316562, -10498527, -16307831, -23479533, -9371869, -21393143, 2465074),
array(20017163, -4323226, 27915242, 1529148, 12396362, 15675764, 13817261, -9658066, 2463391, -4622140),
),
array(
array(-16358878, -12663911, -12065183, 4996454, -1256422, 1073572, 9583558, 12851107, 4003896, 12673717),
array(-1731589, -15155870, -3262930, 16143082, 19294135, 13385325, 14741514, -9103726, 7903886, 2348101),
array(24536016, -16515207, 12715592, -3862155, 1511293, 10047386, -3842346, -7129159, -28377538, 10048127),
),
),
array(
array(
array(-12622226, -6204820, 30718825, 2591312, -10617028, 12192840, 18873298, -7297090, -32297756, 15221632),
array(-26478122, -11103864, 11546244, -1852483, 9180880, 7656409, -21343950, 2095755, 29769758, 6593415),
array(-31994208, -2907461, 4176912, 3264766, 12538965, -868111, 26312345, -6118678, 30958054, 8292160),
),
array(
array(31429822, -13959116, 29173532, 15632448, 12174511, -2760094, 32808831, 3977186, 26143136, -3148876),
array(22648901, 1402143, -22799984, 13746059, 7936347, 365344, -8668633, -1674433, -3758243, -2304625),
array(-15491917, 8012313, -2514730, -12702462, -23965846, -10254029, -1612713, -1535569, -16664475, 8194478),
),
array(
array(27338066, -7507420, -7414224, 10140405, -19026427, -6589889, 27277191, 8855376, 28572286, 3005164),
array(26287124, 4821776, 25476601, -4145903, -3764513, -15788984, -18008582, 1182479, -26094821, -13079595),
array(-7171154, 3178080, 23970071, 6201893, -17195577, -4489192, -21876275, -13982627, 32208683, -1198248),
),
array(
array(-16657702, 2817643, -10286362, 14811298, 6024667, 13349505, -27315504, -10497842, -27672585, -11539858),
array(15941029, -9405932, -21367050, 8062055, 31876073, -238629, -15278393, -1444429, 15397331, -4130193),
array(8934485, -13485467, -23286397, -13423241, -32446090, 14047986, 31170398, -1441021, -27505566, 15087184),
),
array(
array(-18357243, -2156491, 24524913, -16677868, 15520427, -6360776, -15502406, 11461896, 16788528, -5868942),
array(-1947386, 16013773, 21750665, 3714552, -17401782, -16055433, -3770287, -10323320, 31322514, -11615635),
array(21426655, -5650218, -13648287, -5347537, -28812189, -4920970, -18275391, -14621414, 13040862, -12112948),
),
array(
array(11293895, 12478086, -27136401, 15083750, -29307421, 14748872, 14555558, -13417103, 1613711, 4896935),
array(-25894883, 15323294, -8489791, -8057900, 25967126, -13425460, 2825960, -4897045, -23971776, -11267415),
array(-15924766, -5229880, -17443532, 6410664, 3622847, 10243618, 20615400, 12405433, -23753030, -8436416),
),
array(
array(-7091295, 12556208, -20191352, 9025187, -17072479, 4333801, 4378436, 2432030, 23097949, -566018),
array(4565804, -16025654, 20084412, -7842817, 1724999, 189254, 24767264, 10103221, -18512313, 2424778),
array(366633, -11976806, 8173090, -6890119, 30788634, 5745705, -7168678, 1344109, -3642553, 12412659),
),
array(
array(-24001791, 7690286, 14929416, -168257, -32210835, -13412986, 24162697, -15326504, -3141501, 11179385),
array(18289522, -14724954, 8056945, 16430056, -21729724, 7842514, -6001441, -1486897, -18684645, -11443503),
array(476239, 6601091, -6152790, -9723375, 17503545, -4863900, 27672959, 13403813, 11052904, 5219329),
),
),
array(
array(
array(20678546, -8375738, -32671898, 8849123, -5009758, 14574752, 31186971, -3973730, 9014762, -8579056),
array(-13644050, -10350239, -15962508, 5075808, -1514661, -11534600, -33102500, 9160280, 8473550, -3256838),
array(24900749, 14435722, 17209120, -15292541, -22592275, 9878983, -7689309, -16335821, -24568481, 11788948),
),
array(
array(-3118155, -11395194, -13802089, 14797441, 9652448, -6845904, -20037437, 10410733, -24568470, -1458691),
array(-15659161, 16736706, -22467150, 10215878, -9097177, 7563911, 11871841, -12505194, -18513325, 8464118),
array(-23400612, 8348507, -14585951, -861714, -3950205, -6373419, 14325289, 8628612, 33313881, -8370517),
),
array(
array(-20186973, -4967935, 22367356, 5271547, -1097117, -4788838, -24805667, -10236854, -8940735, -5818269),
array(-6948785, -1795212, -32625683, -16021179, 32635414, -7374245, 15989197, -12838188, 28358192, -4253904),
array(-23561781, -2799059, -32351682, -1661963, -9147719, 10429267, -16637684, 4072016, -5351664, 5596589),
),
array(
array(-28236598, -3390048, 12312896, 6213178, 3117142, 16078565, 29266239, 2557221, 1768301, 15373193),
array(-7243358, -3246960, -4593467, -7553353, -127927, -912245, -1090902, -4504991, -24660491, 3442910),
array(-30210571, 5124043, 14181784, 8197961, 18964734, -11939093, 22597931, 7176455, -18585478, 13365930),
),
array(
array(-7877390, -1499958, 8324673, 4690079, 6261860, 890446, 24538107, -8570186, -9689599, -3031667),
array(25008904, -10771599, -4305031, -9638010, 16265036, 15721635, 683793, -11823784, 15723479, -15163481),
array(-9660625, 12374379, -27006999, -7026148, -7724114, -12314514, 11879682, 5400171, 519526, -1235876),
),
array(
array(22258397, -16332233, -7869817, 14613016, -22520255, -2950923, -20353881, 7315967, 16648397, 7605640),
array(-8081308, -8464597, -8223311, 9719710, 19259459, -15348212, 23994942, -5281555, -9468848, 4763278),
array(-21699244, 9220969, -15730624, 1084137, -25476107, -2852390, 31088447, -7764523, -11356529, 728112),
),
array(
array(26047220, -11751471, -6900323, -16521798, 24092068, 9158119, -4273545, -12555558, -29365436, -5498272),
array(17510331, -322857, 5854289, 8403524, 17133918, -3112612, -28111007, 12327945, 10750447, 10014012),
array(-10312768, 3936952, 9156313, -8897683, 16498692, -994647, -27481051, -666732, 3424691, 7540221),
),
array(
array(30322361, -6964110, 11361005, -4143317, 7433304, 4989748, -7071422, -16317219, -9244265, 15258046),
array(13054562, -2779497, 19155474, 469045, -12482797, 4566042, 5631406, 2711395, 1062915, -5136345),
array(-19240248, -11254599, -29509029, -7499965, -5835763, 13005411, -6066489, 12194497, 32960380, 1459310),
),
),
array(
array(
array(19852034, 7027924, 23669353, 10020366, 8586503, -6657907, 394197, -6101885, 18638003, -11174937),
array(31395534, 15098109, 26581030, 8030562, -16527914, -5007134, 9012486, -7584354, -6643087, -5442636),
array(-9192165, -2347377, -1997099, 4529534, 25766844, 607986, -13222, 9677543, -32294889, -6456008),
),
array(
array(-2444496, -149937, 29348902, 8186665, 1873760, 12489863, -30934579, -7839692, -7852844, -8138429),
array(-15236356, -15433509, 7766470, 746860, 26346930, -10221762, -27333451, 10754588, -9431476, 5203576),
array(31834314, 14135496, -770007, 5159118, 20917671, -16768096, -7467973, -7337524, 31809243, 7347066),
),
array(
array(-9606723, -11874240, 20414459, 13033986, 13716524, -11691881, 19797970, -12211255, 15192876, -2087490),
array(-12663563, -2181719, 1168162, -3804809, 26747877, -14138091, 10609330, 12694420, 33473243, -13382104),
array(33184999, 11180355, 15832085, -11385430, -1633671, 225884, 15089336, -11023903, -6135662, 14480053),
),
array(
array(31308717, -5619998, 31030840, -1897099, 15674547, -6582883, 5496208, 13685227, 27595050, 8737275),
array(-20318852, -15150239, 10933843, -16178022, 8335352, -7546022, -31008351, -12610604, 26498114, 66511),
array(22644454, -8761729, -16671776, 4884562, -3105614, -13559366, 30540766, -4286747, -13327787, -7515095),
),
array(
array(-28017847, 9834845, 18617207, -2681312, -3401956, -13307506, 8205540, 13585437, -17127465, 15115439),
array(23711543, -672915, 31206561, -8362711, 6164647, -9709987, -33535882, -1426096, 8236921, 16492939),
array(-23910559, -13515526, -26299483, -4503841, 25005590, -7687270, 19574902, 10071562, 6708380, -6222424),
),
array(
array(2101391, -4930054, 19702731, 2367575, -15427167, 1047675, 5301017, 9328700, 29955601, -11678310),
array(3096359, 9271816, -21620864, -15521844, -14847996, -7592937, -25892142, -12635595, -9917575, 6216608),
array(-32615849, 338663, -25195611, 2510422, -29213566, -13820213, 24822830, -6146567, -26767480, 7525079),
),
array(
array(-23066649, -13985623, 16133487, -7896178, -3389565, 778788, -910336, -2782495, -19386633, 11994101),
array(21691500, -13624626, -641331, -14367021, 3285881, -3483596, -25064666, 9718258, -7477437, 13381418),
array(18445390, -4202236, 14979846, 11622458, -1727110, -3582980, 23111648, -6375247, 28535282, 15779576),
),
array(
array(30098053, 3089662, -9234387, 16662135, -21306940, 11308411, -14068454, 12021730, 9955285, -16303356),
array(9734894, -14576830, -7473633, -9138735, 2060392, 11313496, -18426029, 9924399, 20194861, 13380996),
array(-26378102, -7965207, -22167821, 15789297, -18055342, -6168792, -1984914, 15707771, 26342023, 10146099),
),
),
array(
array(
array(-26016874, -219943, 21339191, -41388, 19745256, -2878700, -29637280, 2227040, 21612326, -545728),
array(-13077387, 1184228, 23562814, -5970442, -20351244, -6348714, 25764461, 12243797, -20856566, 11649658),
array(-10031494, 11262626, 27384172, 2271902, 26947504, -15997771, 39944, 6114064, 33514190, 2333242),
),
array(
array(-21433588, -12421821, 8119782, 7219913, -21830522, -9016134, -6679750, -12670638, 24350578, -13450001),
array(-4116307, -11271533, -23886186, 4843615, -30088339, 690623, -31536088, -10406836, 8317860, 12352766),
array(18200138, -14475911, -33087759, -2696619, -23702521, -9102511, -23552096, -2287550, 20712163, 6719373),
),
array(
array(26656208, 6075253, -7858556, 1886072, -28344043, 4262326, 11117530, -3763210, 26224235, -3297458),
array(-17168938, -14854097, -3395676, -16369877, -19954045, 14050420, 21728352, 9493610, 18620611, -16428628),
array(-13323321, 13325349, 11432106, 5964811, 18609221, 6062965, -5269471, -9725556, -30701573, -16479657),
),
array(
array(-23860538, -11233159, 26961357, 1640861, -32413112, -16737940, 12248509, -5240639, 13735342, 1934062),
array(25089769, 6742589, 17081145, -13406266, 21909293, -16067981, -15136294, -3765346, -21277997, 5473616),
array(31883677, -7961101, 1083432, -11572403, 22828471, 13290673, -7125085, 12469656, 29111212, -5451014),
),
array(
array(24244947, -15050407, -26262976, 2791540, -14997599, 16666678, 24367466, 6388839, -10295587, 452383),
array(-25640782, -3417841, 5217916, 16224624, 19987036, -4082269, -24236251, -5915248, 15766062, 8407814),
array(-20406999, 13990231, 15495425, 16395525, 5377168, 15166495, -8917023, -4388953, -8067909, 2276718),
),
array(
array(30157918, 12924066, -17712050, 9245753, 19895028, 3368142, -23827587, 5096219, 22740376, -7303417),
array(2041139, -14256350, 7783687, 13876377, -25946985, -13352459, 24051124, 13742383, -15637599, 13295222),
array(33338237, -8505733, 12532113, 7977527, 9106186, -1715251, -17720195, -4612972, -4451357, -14669444),
),
array(
array(-20045281, 5454097, -14346548, 6447146, 28862071, 1883651, -2469266, -4141880, 7770569, 9620597),
array(23208068, 7979712, 33071466, 8149229, 1758231, -10834995, 30945528, -1694323, -33502340, -14767970),
array(1439958, -16270480, -1079989, -793782, 4625402, 10647766, -5043801, 1220118, 30494170, -11440799),
),
array(
array(-5037580, -13028295, -2970559, -3061767, 15640974, -6701666, -26739026, 926050, -1684339, -13333647),
array(13908495, -3549272, 30919928, -6273825, -21521863, 7989039, 9021034, 9078865, 3353509, 4033511),
array(-29663431, -15113610, 32259991, -344482, 24295849, -12912123, 23161163, 8839127, 27485041, 7356032),
),
),
array(
array(
array(9661027, 705443, 11980065, -5370154, -1628543, 14661173, -6346142, 2625015, 28431036, -16771834),
array(-23839233, -8311415, -25945511, 7480958, -17681669, -8354183, -22545972, 14150565, 15970762, 4099461),
array(29262576, 16756590, 26350592, -8793563, 8529671, -11208050, 13617293, -9937143, 11465739, 8317062),
),
array(
array(-25493081, -6962928, 32500200, -9419051, -23038724, -2302222, 14898637, 3848455, 20969334, -5157516),
array(-20384450, -14347713, -18336405, 13884722, -33039454, 2842114, -21610826, -3649888, 11177095, 14989547),
array(-24496721, -11716016, 16959896, 2278463, 12066309, 10137771, 13515641, 2581286, -28487508, 9930240),
),
array(
array(-17751622, -2097826, 16544300, -13009300, -15914807, -14949081, 18345767, -13403753, 16291481, -5314038),
array(-33229194, 2553288, 32678213, 9875984, 8534129, 6889387, -9676774, 6957617, 4368891, 9788741),
array(16660756, 7281060, -10830758, 12911820, 20108584, -8101676, -21722536, -8613148, 16250552, -11111103),
),
array(
array(-19765507, 2390526, -16551031, 14161980, 1905286, 6414907, 4689584, 10604807, -30190403, 4782747),
array(-1354539, 14736941, -7367442, -13292886, 7710542, -14155590, -9981571, 4383045, 22546403, 437323),
array(31665577, -12180464, -16186830, 1491339, -18368625, 3294682, 27343084, 2786261, -30633590, -14097016),
),
array(
array(-14467279, -683715, -33374107, 7448552, 19294360, 14334329, -19690631, 2355319, -19284671, -6114373),
array(15121312, -15796162, 6377020, -6031361, -10798111, -12957845, 18952177, 15496498, -29380133, 11754228),
array(-2637277, -13483075, 8488727, -14303896, 12728761, -1622493, 7141596, 11724556, 22761615, -10134141),
),
array(
array(16918416, 11729663, -18083579, 3022987, -31015732, -13339659, -28741185, -12227393, 32851222, 11717399),
array(11166634, 7338049, -6722523, 4531520, -29468672, -7302055, 31474879, 3483633, -1193175, -4030831),
array(-185635, 9921305, 31456609, -13536438, -12013818, 13348923, 33142652, 6546660, -19985279, -3948376),
),
array(
array(-32460596, 11266712, -11197107, -7899103, 31703694, 3855903, -8537131, -12833048, -30772034, -15486313),
array(-18006477, 12709068, 3991746, -6479188, -21491523, -10550425, -31135347, -16049879, 10928917, 3011958),
array(-6957757, -15594337, 31696059, 334240, 29576716, 14796075, -30831056, -12805180, 18008031, 10258577),
),
array(
array(-22448644, 15655569, 7018479, -4410003, -30314266, -1201591, -1853465, 1367120, 25127874, 6671743),
array(29701166, -14373934, -10878120, 9279288, -17568, 13127210, 21382910, 11042292, 25838796, 4642684),
array(-20430234, 14955537, -24126347, 8124619, -5369288, -5990470, 30468147, -13900640, 18423289, 4177476),
),
)
);
/**
* See: libsodium's crypto_core/curve25519/ref10/base2.h
*
* @var array<int, array<int, array<int, int>>> basically int[8][3]
*/
protected static $base2 = array(
array(
array(25967493, -14356035, 29566456, 3660896, -12694345, 4014787, 27544626, -11754271, -6079156, 2047605),
array(-12545711, 934262, -2722910, 3049990, -727428, 9406986, 12720692, 5043384, 19500929, -15469378),
array(-8738181, 4489570, 9688441, -14785194, 10184609, -12363380, 29287919, 11864899, -24514362, -4438546),
),
array(
array(15636291, -9688557, 24204773, -7912398, 616977, -16685262, 27787600, -14772189, 28944400, -1550024),
array(16568933, 4717097, -11556148, -1102322, 15682896, -11807043, 16354577, -11775962, 7689662, 11199574),
array(30464156, -5976125, -11779434, -15670865, 23220365, 15915852, 7512774, 10017326, -17749093, -9920357),
),
array(
array(10861363, 11473154, 27284546, 1981175, -30064349, 12577861, 32867885, 14515107, -15438304, 10819380),
array(4708026, 6336745, 20377586, 9066809, -11272109, 6594696, -25653668, 12483688, -12668491, 5581306),
array(19563160, 16186464, -29386857, 4097519, 10237984, -4348115, 28542350, 13850243, -23678021, -15815942),
),
array(
array(5153746, 9909285, 1723747, -2777874, 30523605, 5516873, 19480852, 5230134, -23952439, -15175766),
array(-30269007, -3463509, 7665486, 10083793, 28475525, 1649722, 20654025, 16520125, 30598449, 7715701),
array(28881845, 14381568, 9657904, 3680757, -20181635, 7843316, -31400660, 1370708, 29794553, -1409300),
),
array(
array(-22518993, -6692182, 14201702, -8745502, -23510406, 8844726, 18474211, -1361450, -13062696, 13821877),
array(-6455177, -7839871, 3374702, -4740862, -27098617, -10571707, 31655028, -7212327, 18853322, -14220951),
array(4566830, -12963868, -28974889, -12240689, -7602672, -2830569, -8514358, -10431137, 2207753, -3209784),
),
array(
array(-25154831, -4185821, 29681144, 7868801, -6854661, -9423865, -12437364, -663000, -31111463, -16132436),
array(25576264, -2703214, 7349804, -11814844, 16472782, 9300885, 3844789, 15725684, 171356, 6466918),
array(23103977, 13316479, 9739013, -16149481, 817875, -15038942, 8965339, -14088058, -30714912, 16193877),
),
array(
array(-33521811, 3180713, -2394130, 14003687, -16903474, -16270840, 17238398, 4729455, -18074513, 9256800),
array(-25182317, -4174131, 32336398, 5036987, -21236817, 11360617, 22616405, 9761698, -19827198, 630305),
array(-13720693, 2639453, -24237460, -7406481, 9494427, -5774029, -6554551, -15960994, -2449256, -14291300),
),
array(
array(-3151181, -5046075, 9282714, 6866145, -31907062, -863023, -18940575, 15033784, 25105118, -7894876),
array(-24326370, 15950226, -31801215, -14592823, -11662737, -5090925, 1573892, -2625887, 2198790, -15804619),
array(-3099351, 10324967, -2241613, 7453183, -5446979, -2735503, -13812022, -16236442, -32461234, -12290683),
)
);
/**
* 37095705934669439343138083508754565189542113879843219016388785533085940283555
*
* @var array<int, int>
*/
protected static $d = array(
-10913610,
13857413,
-15372611,
6949391,
114729,
-8787816,
-6275908,
-3247719,
-18696448,
-12055116
);
/**
* 2 * d = 16295367250680780974490674513165176452449235426866156013048779062215315747161
*
* @var array<int, int>
*/
protected static $d2 = array(
-21827239,
-5839606,
-30745221,
13898782,
229458,
15978800,
-12551817,
-6495438,
29715968,
9444199
);
/**
* sqrt(-1)
*
* @var array<int, int>
*/
protected static $sqrtm1 = array(
-32595792,
-7943725,
9377950,
3500415,
12389472,
-272473,
-25146209,
-2005654,
326686,
11406482
);
}
Core32/Curve25519/Fe.php 0000644 00000012572 15133021213 0010334 0 ustar 00 <?php
if (class_exists('ParagonIE_Sodium_Core32_Curve25519_Fe', false)) {
return;
}
/**
* Class ParagonIE_Sodium_Core32_Curve25519_Fe
*
* This represents a Field Element
*/
class ParagonIE_Sodium_Core32_Curve25519_Fe implements ArrayAccess
{
/**
* @var array<int, ParagonIE_Sodium_Core32_Int32>
*/
protected $container = array();
/**
* @var int
*/
protected $size = 10;
/**
* @internal You should not use this directly from another application
*
* @param array<int, ParagonIE_Sodium_Core32_Int32> $array
* @param bool $save_indexes
* @return self
* @throws SodiumException
* @throws TypeError
*/
public static function fromArray($array, $save_indexes = null)
{
$count = count($array);
if ($save_indexes) {
$keys = array_keys($array);
} else {
$keys = range(0, $count - 1);
}
$array = array_values($array);
$obj = new ParagonIE_Sodium_Core32_Curve25519_Fe();
if ($save_indexes) {
for ($i = 0; $i < $count; ++$i) {
$array[$i]->overflow = 0;
$obj->offsetSet($keys[$i], $array[$i]);
}
} else {
for ($i = 0; $i < $count; ++$i) {
if (!($array[$i] instanceof ParagonIE_Sodium_Core32_Int32)) {
throw new TypeError('Expected ParagonIE_Sodium_Core32_Int32');
}
$array[$i]->overflow = 0;
$obj->offsetSet($i, $array[$i]);
}
}
return $obj;
}
/**
* @internal You should not use this directly from another application
*
* @param array<int, int> $array
* @param bool $save_indexes
* @return self
* @throws SodiumException
* @throws TypeError
*/
public static function fromIntArray($array, $save_indexes = null)
{
$count = count($array);
if ($save_indexes) {
$keys = array_keys($array);
} else {
$keys = range(0, $count - 1);
}
$array = array_values($array);
$set = array();
/** @var int $i */
/** @var int $v */
foreach ($array as $i => $v) {
$set[$i] = ParagonIE_Sodium_Core32_Int32::fromInt($v);
}
$obj = new ParagonIE_Sodium_Core32_Curve25519_Fe();
if ($save_indexes) {
for ($i = 0; $i < $count; ++$i) {
$set[$i]->overflow = 0;
$obj->offsetSet($keys[$i], $set[$i]);
}
} else {
for ($i = 0; $i < $count; ++$i) {
$set[$i]->overflow = 0;
$obj->offsetSet($i, $set[$i]);
}
}
return $obj;
}
/**
* @internal You should not use this directly from another application
*
* @param mixed $offset
* @param mixed $value
* @return void
* @throws SodiumException
* @throws TypeError
*/
#[ReturnTypeWillChange]
public function offsetSet($offset, $value)
{
if (!($value instanceof ParagonIE_Sodium_Core32_Int32)) {
throw new InvalidArgumentException('Expected an instance of ParagonIE_Sodium_Core32_Int32');
}
if (is_null($offset)) {
$this->container[] = $value;
} else {
ParagonIE_Sodium_Core32_Util::declareScalarType($offset, 'int', 1);
$this->container[(int) $offset] = $value;
}
}
/**
* @internal You should not use this directly from another application
*
* @param mixed $offset
* @return bool
* @psalm-suppress MixedArrayOffset
*/
#[ReturnTypeWillChange]
public function offsetExists($offset)
{
return isset($this->container[$offset]);
}
/**
* @internal You should not use this directly from another application
*
* @param mixed $offset
* @return void
* @psalm-suppress MixedArrayOffset
*/
#[ReturnTypeWillChange]
public function offsetUnset($offset)
{
unset($this->container[$offset]);
}
/**
* @internal You should not use this directly from another application
*
* @param mixed $offset
* @return ParagonIE_Sodium_Core32_Int32
* @psalm-suppress MixedArrayOffset
*/
#[ReturnTypeWillChange]
public function offsetGet($offset)
{
if (!isset($this->container[$offset])) {
$this->container[(int) $offset] = new ParagonIE_Sodium_Core32_Int32();
}
/** @var ParagonIE_Sodium_Core32_Int32 $get */
$get = $this->container[$offset];
return $get;
}
/**
* @internal You should not use this directly from another application
*
* @return array
*/
public function __debugInfo()
{
if (empty($this->container)) {
return array();
}
$c = array(
(int) ($this->container[0]->toInt()),
(int) ($this->container[1]->toInt()),
(int) ($this->container[2]->toInt()),
(int) ($this->container[3]->toInt()),
(int) ($this->container[4]->toInt()),
(int) ($this->container[5]->toInt()),
(int) ($this->container[6]->toInt()),
(int) ($this->container[7]->toInt()),
(int) ($this->container[8]->toInt()),
(int) ($this->container[9]->toInt())
);
return array(implode(', ', $c));
}
}
Core32/Curve25519.php 0000644 00000403556 15133021213 0010010 0 ustar 00 <?php
if (class_exists('ParagonIE_Sodium_Core32_Curve25519', false)) {
return;
}
/**
* Class ParagonIE_Sodium_Core32_Curve25519
*
* Implements Curve25519 core functions
*
* Based on the ref10 curve25519 code provided by libsodium
*
* @ref https://github.com/jedisct1/libsodium/blob/master/src/libsodium/crypto_core/curve25519/ref10/curve25519_ref10.c
*/
abstract class ParagonIE_Sodium_Core32_Curve25519 extends ParagonIE_Sodium_Core32_Curve25519_H
{
/**
* Get a field element of size 10 with a value of 0
*
* @internal You should not use this directly from another application
*
* @return ParagonIE_Sodium_Core32_Curve25519_Fe
* @throws SodiumException
* @throws TypeError
*/
public static function fe_0()
{
return ParagonIE_Sodium_Core32_Curve25519_Fe::fromArray(
array(
new ParagonIE_Sodium_Core32_Int32(),
new ParagonIE_Sodium_Core32_Int32(),
new ParagonIE_Sodium_Core32_Int32(),
new ParagonIE_Sodium_Core32_Int32(),
new ParagonIE_Sodium_Core32_Int32(),
new ParagonIE_Sodium_Core32_Int32(),
new ParagonIE_Sodium_Core32_Int32(),
new ParagonIE_Sodium_Core32_Int32(),
new ParagonIE_Sodium_Core32_Int32(),
new ParagonIE_Sodium_Core32_Int32()
)
);
}
/**
* Get a field element of size 10 with a value of 1
*
* @internal You should not use this directly from another application
*
* @return ParagonIE_Sodium_Core32_Curve25519_Fe
* @throws SodiumException
* @throws TypeError
*/
public static function fe_1()
{
return ParagonIE_Sodium_Core32_Curve25519_Fe::fromArray(
array(
ParagonIE_Sodium_Core32_Int32::fromInt(1),
new ParagonIE_Sodium_Core32_Int32(),
new ParagonIE_Sodium_Core32_Int32(),
new ParagonIE_Sodium_Core32_Int32(),
new ParagonIE_Sodium_Core32_Int32(),
new ParagonIE_Sodium_Core32_Int32(),
new ParagonIE_Sodium_Core32_Int32(),
new ParagonIE_Sodium_Core32_Int32(),
new ParagonIE_Sodium_Core32_Int32(),
new ParagonIE_Sodium_Core32_Int32()
)
);
}
/**
* Add two field elements.
*
* @internal You should not use this directly from another application
*
* @param ParagonIE_Sodium_Core32_Curve25519_Fe $f
* @param ParagonIE_Sodium_Core32_Curve25519_Fe $g
* @return ParagonIE_Sodium_Core32_Curve25519_Fe
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedAssignment
* @psalm-suppress MixedMethodCall
*/
public static function fe_add(
ParagonIE_Sodium_Core32_Curve25519_Fe $f,
ParagonIE_Sodium_Core32_Curve25519_Fe $g
) {
$arr = array();
for ($i = 0; $i < 10; ++$i) {
$arr[$i] = $f[$i]->addInt32($g[$i]);
}
/** @var array<int, ParagonIE_Sodium_Core32_Int32> $arr */
return ParagonIE_Sodium_Core32_Curve25519_Fe::fromArray($arr);
}
/**
* Constant-time conditional move.
*
* @internal You should not use this directly from another application
*
* @param ParagonIE_Sodium_Core32_Curve25519_Fe $f
* @param ParagonIE_Sodium_Core32_Curve25519_Fe $g
* @param int $b
* @return ParagonIE_Sodium_Core32_Curve25519_Fe
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedAssignment
* @psalm-suppress MixedMethodCall
*/
public static function fe_cmov(
ParagonIE_Sodium_Core32_Curve25519_Fe $f,
ParagonIE_Sodium_Core32_Curve25519_Fe $g,
$b = 0
) {
/** @var array<int, ParagonIE_Sodium_Core32_Int32> $h */
$h = array();
for ($i = 0; $i < 10; ++$i) {
if (!($f[$i] instanceof ParagonIE_Sodium_Core32_Int32)) {
throw new TypeError('Expected Int32');
}
if (!($g[$i] instanceof ParagonIE_Sodium_Core32_Int32)) {
throw new TypeError('Expected Int32');
}
$h[$i] = $f[$i]->xorInt32(
$f[$i]->xorInt32($g[$i])->mask($b)
);
}
/** @var array<int, ParagonIE_Sodium_Core32_Int32> $h */
return ParagonIE_Sodium_Core32_Curve25519_Fe::fromArray($h);
}
/**
* Create a copy of a field element.
*
* @internal You should not use this directly from another application
*
* @param ParagonIE_Sodium_Core32_Curve25519_Fe $f
* @return ParagonIE_Sodium_Core32_Curve25519_Fe
*/
public static function fe_copy(ParagonIE_Sodium_Core32_Curve25519_Fe $f)
{
$h = clone $f;
return $h;
}
/**
* Give: 32-byte string.
* Receive: A field element object to use for internal calculations.
*
* @internal You should not use this directly from another application
*
* @param string $s
* @return ParagonIE_Sodium_Core32_Curve25519_Fe
* @throws RangeException
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedMethodCall
*/
public static function fe_frombytes($s)
{
if (self::strlen($s) !== 32) {
throw new RangeException('Expected a 32-byte string.');
}
/** @var ParagonIE_Sodium_Core32_Int32 $h0 */
$h0 = ParagonIE_Sodium_Core32_Int32::fromInt(
self::load_4($s)
);
/** @var ParagonIE_Sodium_Core32_Int32 $h1 */
$h1 = ParagonIE_Sodium_Core32_Int32::fromInt(
self::load_3(self::substr($s, 4, 3)) << 6
);
/** @var ParagonIE_Sodium_Core32_Int32 $h2 */
$h2 = ParagonIE_Sodium_Core32_Int32::fromInt(
self::load_3(self::substr($s, 7, 3)) << 5
);
/** @var ParagonIE_Sodium_Core32_Int32 $h3 */
$h3 = ParagonIE_Sodium_Core32_Int32::fromInt(
self::load_3(self::substr($s, 10, 3)) << 3
);
/** @var ParagonIE_Sodium_Core32_Int32 $h4 */
$h4 = ParagonIE_Sodium_Core32_Int32::fromInt(
self::load_3(self::substr($s, 13, 3)) << 2
);
/** @var ParagonIE_Sodium_Core32_Int32 $h5 */
$h5 = ParagonIE_Sodium_Core32_Int32::fromInt(
self::load_4(self::substr($s, 16, 4))
);
/** @var ParagonIE_Sodium_Core32_Int32 $h6 */
$h6 = ParagonIE_Sodium_Core32_Int32::fromInt(
self::load_3(self::substr($s, 20, 3)) << 7
);
/** @var ParagonIE_Sodium_Core32_Int32 $h7 */
$h7 = ParagonIE_Sodium_Core32_Int32::fromInt(
self::load_3(self::substr($s, 23, 3)) << 5
);
/** @var ParagonIE_Sodium_Core32_Int32 $h8 */
$h8 = ParagonIE_Sodium_Core32_Int32::fromInt(
self::load_3(self::substr($s, 26, 3)) << 4
);
/** @var ParagonIE_Sodium_Core32_Int32 $h9 */
$h9 = ParagonIE_Sodium_Core32_Int32::fromInt(
(self::load_3(self::substr($s, 29, 3)) & 8388607) << 2
);
$carry9 = $h9->addInt(1 << 24)->shiftRight(25);
$h0 = $h0->addInt32($carry9->mulInt(19, 5));
$h9 = $h9->subInt32($carry9->shiftLeft(25));
$carry1 = $h1->addInt(1 << 24)->shiftRight(25);
$h2 = $h2->addInt32($carry1);
$h1 = $h1->subInt32($carry1->shiftLeft(25));
$carry3 = $h3->addInt(1 << 24)->shiftRight(25);
$h4 = $h4->addInt32($carry3);
$h3 = $h3->subInt32($carry3->shiftLeft(25));
$carry5 = $h5->addInt(1 << 24)->shiftRight(25);
$h6 = $h6->addInt32($carry5);
$h5 = $h5->subInt32($carry5->shiftLeft(25));
$carry7 = $h7->addInt(1 << 24)->shiftRight(25);
$h8 = $h8->addInt32($carry7);
$h7 = $h7->subInt32($carry7->shiftLeft(25));
$carry0 = $h0->addInt(1 << 25)->shiftRight(26);
$h1 = $h1->addInt32($carry0);
$h0 = $h0->subInt32($carry0->shiftLeft(26));
$carry2 = $h2->addInt(1 << 25)->shiftRight(26);
$h3 = $h3->addInt32($carry2);
$h2 = $h2->subInt32($carry2->shiftLeft(26));
$carry4 = $h4->addInt(1 << 25)->shiftRight(26);
$h5 = $h5->addInt32($carry4);
$h4 = $h4->subInt32($carry4->shiftLeft(26));
$carry6 = $h6->addInt(1 << 25)->shiftRight(26);
$h7 = $h7->addInt32($carry6);
$h6 = $h6->subInt32($carry6->shiftLeft(26));
$carry8 = $h8->addInt(1 << 25)->shiftRight(26);
$h9 = $h9->addInt32($carry8);
$h8 = $h8->subInt32($carry8->shiftLeft(26));
return ParagonIE_Sodium_Core32_Curve25519_Fe::fromArray(
array($h0, $h1, $h2,$h3, $h4, $h5, $h6, $h7, $h8, $h9)
);
}
/**
* Convert a field element to a byte string.
*
* @internal You should not use this directly from another application
*
* @param ParagonIE_Sodium_Core32_Curve25519_Fe $h
* @return string
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedAssignment
* @psalm-suppress MixedMethodCall
*/
public static function fe_tobytes(ParagonIE_Sodium_Core32_Curve25519_Fe $h)
{
/**
* @var ParagonIE_Sodium_Core32_Int64[] $f
* @var ParagonIE_Sodium_Core32_Int64 $q
*/
$f = array();
for ($i = 0; $i < 10; ++$i) {
$f[$i] = $h[$i]->toInt64();
}
$q = $f[9]->mulInt(19, 5)->addInt(1 << 14)->shiftRight(25)
->addInt64($f[0])->shiftRight(26)
->addInt64($f[1])->shiftRight(25)
->addInt64($f[2])->shiftRight(26)
->addInt64($f[3])->shiftRight(25)
->addInt64($f[4])->shiftRight(26)
->addInt64($f[5])->shiftRight(25)
->addInt64($f[6])->shiftRight(26)
->addInt64($f[7])->shiftRight(25)
->addInt64($f[8])->shiftRight(26)
->addInt64($f[9])->shiftRight(25);
$f[0] = $f[0]->addInt64($q->mulInt(19, 5));
$carry0 = $f[0]->shiftRight(26);
$f[1] = $f[1]->addInt64($carry0);
$f[0] = $f[0]->subInt64($carry0->shiftLeft(26));
$carry1 = $f[1]->shiftRight(25);
$f[2] = $f[2]->addInt64($carry1);
$f[1] = $f[1]->subInt64($carry1->shiftLeft(25));
$carry2 = $f[2]->shiftRight(26);
$f[3] = $f[3]->addInt64($carry2);
$f[2] = $f[2]->subInt64($carry2->shiftLeft(26));
$carry3 = $f[3]->shiftRight(25);
$f[4] = $f[4]->addInt64($carry3);
$f[3] = $f[3]->subInt64($carry3->shiftLeft(25));
$carry4 = $f[4]->shiftRight(26);
$f[5] = $f[5]->addInt64($carry4);
$f[4] = $f[4]->subInt64($carry4->shiftLeft(26));
$carry5 = $f[5]->shiftRight(25);
$f[6] = $f[6]->addInt64($carry5);
$f[5] = $f[5]->subInt64($carry5->shiftLeft(25));
$carry6 = $f[6]->shiftRight(26);
$f[7] = $f[7]->addInt64($carry6);
$f[6] = $f[6]->subInt64($carry6->shiftLeft(26));
$carry7 = $f[7]->shiftRight(25);
$f[8] = $f[8]->addInt64($carry7);
$f[7] = $f[7]->subInt64($carry7->shiftLeft(25));
$carry8 = $f[8]->shiftRight(26);
$f[9] = $f[9]->addInt64($carry8);
$f[8] = $f[8]->subInt64($carry8->shiftLeft(26));
$carry9 = $f[9]->shiftRight(25);
$f[9] = $f[9]->subInt64($carry9->shiftLeft(25));
$h0 = $f[0]->toInt32()->toInt();
$h1 = $f[1]->toInt32()->toInt();
$h2 = $f[2]->toInt32()->toInt();
$h3 = $f[3]->toInt32()->toInt();
$h4 = $f[4]->toInt32()->toInt();
$h5 = $f[5]->toInt32()->toInt();
$h6 = $f[6]->toInt32()->toInt();
$h7 = $f[7]->toInt32()->toInt();
$h8 = $f[8]->toInt32()->toInt();
$h9 = $f[9]->toInt32()->toInt();
/**
* @var array<int, int>
*/
$s = array(
(int) (($h0 >> 0) & 0xff),
(int) (($h0 >> 8) & 0xff),
(int) (($h0 >> 16) & 0xff),
(int) ((($h0 >> 24) | ($h1 << 2)) & 0xff),
(int) (($h1 >> 6) & 0xff),
(int) (($h1 >> 14) & 0xff),
(int) ((($h1 >> 22) | ($h2 << 3)) & 0xff),
(int) (($h2 >> 5) & 0xff),
(int) (($h2 >> 13) & 0xff),
(int) ((($h2 >> 21) | ($h3 << 5)) & 0xff),
(int) (($h3 >> 3) & 0xff),
(int) (($h3 >> 11) & 0xff),
(int) ((($h3 >> 19) | ($h4 << 6)) & 0xff),
(int) (($h4 >> 2) & 0xff),
(int) (($h4 >> 10) & 0xff),
(int) (($h4 >> 18) & 0xff),
(int) (($h5 >> 0) & 0xff),
(int) (($h5 >> 8) & 0xff),
(int) (($h5 >> 16) & 0xff),
(int) ((($h5 >> 24) | ($h6 << 1)) & 0xff),
(int) (($h6 >> 7) & 0xff),
(int) (($h6 >> 15) & 0xff),
(int) ((($h6 >> 23) | ($h7 << 3)) & 0xff),
(int) (($h7 >> 5) & 0xff),
(int) (($h7 >> 13) & 0xff),
(int) ((($h7 >> 21) | ($h8 << 4)) & 0xff),
(int) (($h8 >> 4) & 0xff),
(int) (($h8 >> 12) & 0xff),
(int) ((($h8 >> 20) | ($h9 << 6)) & 0xff),
(int) (($h9 >> 2) & 0xff),
(int) (($h9 >> 10) & 0xff),
(int) (($h9 >> 18) & 0xff)
);
return self::intArrayToString($s);
}
/**
* Is a field element negative? (1 = yes, 0 = no. Used in calculations.)
*
* @internal You should not use this directly from another application
*
* @param ParagonIE_Sodium_Core32_Curve25519_Fe $f
* @return int
* @throws SodiumException
* @throws TypeError
*/
public static function fe_isnegative(ParagonIE_Sodium_Core32_Curve25519_Fe $f)
{
$str = self::fe_tobytes($f);
return (int) (self::chrToInt($str[0]) & 1);
}
/**
* Returns 0 if this field element results in all NUL bytes.
*
* @internal You should not use this directly from another application
*
* @param ParagonIE_Sodium_Core32_Curve25519_Fe $f
* @return bool
* @throws SodiumException
* @throws TypeError
*/
public static function fe_isnonzero(ParagonIE_Sodium_Core32_Curve25519_Fe $f)
{
static $zero;
if ($zero === null) {
$zero = str_repeat("\x00", 32);
}
$str = self::fe_tobytes($f);
/** @var string $zero */
return !self::verify_32($str, $zero);
}
/**
* Multiply two field elements
*
* h = f * g
*
* @internal You should not use this directly from another application
*
* @security Is multiplication a source of timing leaks? If so, can we do
* anything to prevent that from happening?
*
* @param ParagonIE_Sodium_Core32_Curve25519_Fe $f
* @param ParagonIE_Sodium_Core32_Curve25519_Fe $g
* @return ParagonIE_Sodium_Core32_Curve25519_Fe
* @throws SodiumException
* @throws TypeError
*/
public static function fe_mul(
ParagonIE_Sodium_Core32_Curve25519_Fe $f,
ParagonIE_Sodium_Core32_Curve25519_Fe $g
) {
/**
* @var ParagonIE_Sodium_Core32_Int32[] $f
* @var ParagonIE_Sodium_Core32_Int32[] $g
* @var ParagonIE_Sodium_Core32_Int64 $f0
* @var ParagonIE_Sodium_Core32_Int64 $f1
* @var ParagonIE_Sodium_Core32_Int64 $f2
* @var ParagonIE_Sodium_Core32_Int64 $f3
* @var ParagonIE_Sodium_Core32_Int64 $f4
* @var ParagonIE_Sodium_Core32_Int64 $f5
* @var ParagonIE_Sodium_Core32_Int64 $f6
* @var ParagonIE_Sodium_Core32_Int64 $f7
* @var ParagonIE_Sodium_Core32_Int64 $f8
* @var ParagonIE_Sodium_Core32_Int64 $f9
* @var ParagonIE_Sodium_Core32_Int64 $g0
* @var ParagonIE_Sodium_Core32_Int64 $g1
* @var ParagonIE_Sodium_Core32_Int64 $g2
* @var ParagonIE_Sodium_Core32_Int64 $g3
* @var ParagonIE_Sodium_Core32_Int64 $g4
* @var ParagonIE_Sodium_Core32_Int64 $g5
* @var ParagonIE_Sodium_Core32_Int64 $g6
* @var ParagonIE_Sodium_Core32_Int64 $g7
* @var ParagonIE_Sodium_Core32_Int64 $g8
* @var ParagonIE_Sodium_Core32_Int64 $g9
*/
$f0 = $f[0]->toInt64();
$f1 = $f[1]->toInt64();
$f2 = $f[2]->toInt64();
$f3 = $f[3]->toInt64();
$f4 = $f[4]->toInt64();
$f5 = $f[5]->toInt64();
$f6 = $f[6]->toInt64();
$f7 = $f[7]->toInt64();
$f8 = $f[8]->toInt64();
$f9 = $f[9]->toInt64();
$g0 = $g[0]->toInt64();
$g1 = $g[1]->toInt64();
$g2 = $g[2]->toInt64();
$g3 = $g[3]->toInt64();
$g4 = $g[4]->toInt64();
$g5 = $g[5]->toInt64();
$g6 = $g[6]->toInt64();
$g7 = $g[7]->toInt64();
$g8 = $g[8]->toInt64();
$g9 = $g[9]->toInt64();
$g1_19 = $g1->mulInt(19, 5); /* 2^4 <= 19 <= 2^5, but we only want 5 bits */
$g2_19 = $g2->mulInt(19, 5);
$g3_19 = $g3->mulInt(19, 5);
$g4_19 = $g4->mulInt(19, 5);
$g5_19 = $g5->mulInt(19, 5);
$g6_19 = $g6->mulInt(19, 5);
$g7_19 = $g7->mulInt(19, 5);
$g8_19 = $g8->mulInt(19, 5);
$g9_19 = $g9->mulInt(19, 5);
$f1_2 = $f1->shiftLeft(1);
$f3_2 = $f3->shiftLeft(1);
$f5_2 = $f5->shiftLeft(1);
$f7_2 = $f7->shiftLeft(1);
$f9_2 = $f9->shiftLeft(1);
$f0g0 = $f0->mulInt64($g0, 27);
$f0g1 = $f0->mulInt64($g1, 27);
$f0g2 = $f0->mulInt64($g2, 27);
$f0g3 = $f0->mulInt64($g3, 27);
$f0g4 = $f0->mulInt64($g4, 27);
$f0g5 = $f0->mulInt64($g5, 27);
$f0g6 = $f0->mulInt64($g6, 27);
$f0g7 = $f0->mulInt64($g7, 27);
$f0g8 = $f0->mulInt64($g8, 27);
$f0g9 = $f0->mulInt64($g9, 27);
$f1g0 = $f1->mulInt64($g0, 27);
$f1g1_2 = $f1_2->mulInt64($g1, 27);
$f1g2 = $f1->mulInt64($g2, 27);
$f1g3_2 = $f1_2->mulInt64($g3, 27);
$f1g4 = $f1->mulInt64($g4, 30);
$f1g5_2 = $f1_2->mulInt64($g5, 30);
$f1g6 = $f1->mulInt64($g6, 30);
$f1g7_2 = $f1_2->mulInt64($g7, 30);
$f1g8 = $f1->mulInt64($g8, 30);
$f1g9_38 = $g9_19->mulInt64($f1_2, 30);
$f2g0 = $f2->mulInt64($g0, 30);
$f2g1 = $f2->mulInt64($g1, 29);
$f2g2 = $f2->mulInt64($g2, 30);
$f2g3 = $f2->mulInt64($g3, 29);
$f2g4 = $f2->mulInt64($g4, 30);
$f2g5 = $f2->mulInt64($g5, 29);
$f2g6 = $f2->mulInt64($g6, 30);
$f2g7 = $f2->mulInt64($g7, 29);
$f2g8_19 = $g8_19->mulInt64($f2, 30);
$f2g9_19 = $g9_19->mulInt64($f2, 30);
$f3g0 = $f3->mulInt64($g0, 30);
$f3g1_2 = $f3_2->mulInt64($g1, 30);
$f3g2 = $f3->mulInt64($g2, 30);
$f3g3_2 = $f3_2->mulInt64($g3, 30);
$f3g4 = $f3->mulInt64($g4, 30);
$f3g5_2 = $f3_2->mulInt64($g5, 30);
$f3g6 = $f3->mulInt64($g6, 30);
$f3g7_38 = $g7_19->mulInt64($f3_2, 30);
$f3g8_19 = $g8_19->mulInt64($f3, 30);
$f3g9_38 = $g9_19->mulInt64($f3_2, 30);
$f4g0 = $f4->mulInt64($g0, 30);
$f4g1 = $f4->mulInt64($g1, 30);
$f4g2 = $f4->mulInt64($g2, 30);
$f4g3 = $f4->mulInt64($g3, 30);
$f4g4 = $f4->mulInt64($g4, 30);
$f4g5 = $f4->mulInt64($g5, 30);
$f4g6_19 = $g6_19->mulInt64($f4, 30);
$f4g7_19 = $g7_19->mulInt64($f4, 30);
$f4g8_19 = $g8_19->mulInt64($f4, 30);
$f4g9_19 = $g9_19->mulInt64($f4, 30);
$f5g0 = $f5->mulInt64($g0, 30);
$f5g1_2 = $f5_2->mulInt64($g1, 30);
$f5g2 = $f5->mulInt64($g2, 30);
$f5g3_2 = $f5_2->mulInt64($g3, 30);
$f5g4 = $f5->mulInt64($g4, 30);
$f5g5_38 = $g5_19->mulInt64($f5_2, 30);
$f5g6_19 = $g6_19->mulInt64($f5, 30);
$f5g7_38 = $g7_19->mulInt64($f5_2, 30);
$f5g8_19 = $g8_19->mulInt64($f5, 30);
$f5g9_38 = $g9_19->mulInt64($f5_2, 30);
$f6g0 = $f6->mulInt64($g0, 30);
$f6g1 = $f6->mulInt64($g1, 30);
$f6g2 = $f6->mulInt64($g2, 30);
$f6g3 = $f6->mulInt64($g3, 30);
$f6g4_19 = $g4_19->mulInt64($f6, 30);
$f6g5_19 = $g5_19->mulInt64($f6, 30);
$f6g6_19 = $g6_19->mulInt64($f6, 30);
$f6g7_19 = $g7_19->mulInt64($f6, 30);
$f6g8_19 = $g8_19->mulInt64($f6, 30);
$f6g9_19 = $g9_19->mulInt64($f6, 30);
$f7g0 = $f7->mulInt64($g0, 30);
$f7g1_2 = $g1->mulInt64($f7_2, 30);
$f7g2 = $f7->mulInt64($g2, 30);
$f7g3_38 = $g3_19->mulInt64($f7_2, 30);
$f7g4_19 = $g4_19->mulInt64($f7, 30);
$f7g5_38 = $g5_19->mulInt64($f7_2, 30);
$f7g6_19 = $g6_19->mulInt64($f7, 30);
$f7g7_38 = $g7_19->mulInt64($f7_2, 30);
$f7g8_19 = $g8_19->mulInt64($f7, 30);
$f7g9_38 = $g9_19->mulInt64($f7_2, 30);
$f8g0 = $f8->mulInt64($g0, 30);
$f8g1 = $f8->mulInt64($g1, 29);
$f8g2_19 = $g2_19->mulInt64($f8, 30);
$f8g3_19 = $g3_19->mulInt64($f8, 30);
$f8g4_19 = $g4_19->mulInt64($f8, 30);
$f8g5_19 = $g5_19->mulInt64($f8, 30);
$f8g6_19 = $g6_19->mulInt64($f8, 30);
$f8g7_19 = $g7_19->mulInt64($f8, 30);
$f8g8_19 = $g8_19->mulInt64($f8, 30);
$f8g9_19 = $g9_19->mulInt64($f8, 30);
$f9g0 = $f9->mulInt64($g0, 30);
$f9g1_38 = $g1_19->mulInt64($f9_2, 30);
$f9g2_19 = $g2_19->mulInt64($f9, 30);
$f9g3_38 = $g3_19->mulInt64($f9_2, 30);
$f9g4_19 = $g4_19->mulInt64($f9, 30);
$f9g5_38 = $g5_19->mulInt64($f9_2, 30);
$f9g6_19 = $g6_19->mulInt64($f9, 30);
$f9g7_38 = $g7_19->mulInt64($f9_2, 30);
$f9g8_19 = $g8_19->mulInt64($f9, 30);
$f9g9_38 = $g9_19->mulInt64($f9_2, 30);
// $h0 = $f0g0 + $f1g9_38 + $f2g8_19 + $f3g7_38 + $f4g6_19 + $f5g5_38 + $f6g4_19 + $f7g3_38 + $f8g2_19 + $f9g1_38;
$h0 = $f0g0->addInt64($f1g9_38)->addInt64($f2g8_19)->addInt64($f3g7_38)
->addInt64($f4g6_19)->addInt64($f5g5_38)->addInt64($f6g4_19)
->addInt64($f7g3_38)->addInt64($f8g2_19)->addInt64($f9g1_38);
// $h1 = $f0g1 + $f1g0 + $f2g9_19 + $f3g8_19 + $f4g7_19 + $f5g6_19 + $f6g5_19 + $f7g4_19 + $f8g3_19 + $f9g2_19;
$h1 = $f0g1->addInt64($f1g0)->addInt64($f2g9_19)->addInt64($f3g8_19)
->addInt64($f4g7_19)->addInt64($f5g6_19)->addInt64($f6g5_19)
->addInt64($f7g4_19)->addInt64($f8g3_19)->addInt64($f9g2_19);
// $h2 = $f0g2 + $f1g1_2 + $f2g0 + $f3g9_38 + $f4g8_19 + $f5g7_38 + $f6g6_19 + $f7g5_38 + $f8g4_19 + $f9g3_38;
$h2 = $f0g2->addInt64($f1g1_2)->addInt64($f2g0)->addInt64($f3g9_38)
->addInt64($f4g8_19)->addInt64($f5g7_38)->addInt64($f6g6_19)
->addInt64($f7g5_38)->addInt64($f8g4_19)->addInt64($f9g3_38);
// $h3 = $f0g3 + $f1g2 + $f2g1 + $f3g0 + $f4g9_19 + $f5g8_19 + $f6g7_19 + $f7g6_19 + $f8g5_19 + $f9g4_19;
$h3 = $f0g3->addInt64($f1g2)->addInt64($f2g1)->addInt64($f3g0)
->addInt64($f4g9_19)->addInt64($f5g8_19)->addInt64($f6g7_19)
->addInt64($f7g6_19)->addInt64($f8g5_19)->addInt64($f9g4_19);
// $h4 = $f0g4 + $f1g3_2 + $f2g2 + $f3g1_2 + $f4g0 + $f5g9_38 + $f6g8_19 + $f7g7_38 + $f8g6_19 + $f9g5_38;
$h4 = $f0g4->addInt64($f1g3_2)->addInt64($f2g2)->addInt64($f3g1_2)
->addInt64($f4g0)->addInt64($f5g9_38)->addInt64($f6g8_19)
->addInt64($f7g7_38)->addInt64($f8g6_19)->addInt64($f9g5_38);
// $h5 = $f0g5 + $f1g4 + $f2g3 + $f3g2 + $f4g1 + $f5g0 + $f6g9_19 + $f7g8_19 + $f8g7_19 + $f9g6_19;
$h5 = $f0g5->addInt64($f1g4)->addInt64($f2g3)->addInt64($f3g2)
->addInt64($f4g1)->addInt64($f5g0)->addInt64($f6g9_19)
->addInt64($f7g8_19)->addInt64($f8g7_19)->addInt64($f9g6_19);
// $h6 = $f0g6 + $f1g5_2 + $f2g4 + $f3g3_2 + $f4g2 + $f5g1_2 + $f6g0 + $f7g9_38 + $f8g8_19 + $f9g7_38;
$h6 = $f0g6->addInt64($f1g5_2)->addInt64($f2g4)->addInt64($f3g3_2)
->addInt64($f4g2)->addInt64($f5g1_2)->addInt64($f6g0)
->addInt64($f7g9_38)->addInt64($f8g8_19)->addInt64($f9g7_38);
// $h7 = $f0g7 + $f1g6 + $f2g5 + $f3g4 + $f4g3 + $f5g2 + $f6g1 + $f7g0 + $f8g9_19 + $f9g8_19;
$h7 = $f0g7->addInt64($f1g6)->addInt64($f2g5)->addInt64($f3g4)
->addInt64($f4g3)->addInt64($f5g2)->addInt64($f6g1)
->addInt64($f7g0)->addInt64($f8g9_19)->addInt64($f9g8_19);
// $h8 = $f0g8 + $f1g7_2 + $f2g6 + $f3g5_2 + $f4g4 + $f5g3_2 + $f6g2 + $f7g1_2 + $f8g0 + $f9g9_38;
$h8 = $f0g8->addInt64($f1g7_2)->addInt64($f2g6)->addInt64($f3g5_2)
->addInt64($f4g4)->addInt64($f5g3_2)->addInt64($f6g2)
->addInt64($f7g1_2)->addInt64($f8g0)->addInt64($f9g9_38);
// $h9 = $f0g9 + $f1g8 + $f2g7 + $f3g6 + $f4g5 + $f5g4 + $f6g3 + $f7g2 + $f8g1 + $f9g0 ;
$h9 = $f0g9->addInt64($f1g8)->addInt64($f2g7)->addInt64($f3g6)
->addInt64($f4g5)->addInt64($f5g4)->addInt64($f6g3)
->addInt64($f7g2)->addInt64($f8g1)->addInt64($f9g0);
/**
* @var ParagonIE_Sodium_Core32_Int64 $h0
* @var ParagonIE_Sodium_Core32_Int64 $h1
* @var ParagonIE_Sodium_Core32_Int64 $h2
* @var ParagonIE_Sodium_Core32_Int64 $h3
* @var ParagonIE_Sodium_Core32_Int64 $h4
* @var ParagonIE_Sodium_Core32_Int64 $h5
* @var ParagonIE_Sodium_Core32_Int64 $h6
* @var ParagonIE_Sodium_Core32_Int64 $h7
* @var ParagonIE_Sodium_Core32_Int64 $h8
* @var ParagonIE_Sodium_Core32_Int64 $h9
* @var ParagonIE_Sodium_Core32_Int64 $carry0
* @var ParagonIE_Sodium_Core32_Int64 $carry1
* @var ParagonIE_Sodium_Core32_Int64 $carry2
* @var ParagonIE_Sodium_Core32_Int64 $carry3
* @var ParagonIE_Sodium_Core32_Int64 $carry4
* @var ParagonIE_Sodium_Core32_Int64 $carry5
* @var ParagonIE_Sodium_Core32_Int64 $carry6
* @var ParagonIE_Sodium_Core32_Int64 $carry7
* @var ParagonIE_Sodium_Core32_Int64 $carry8
* @var ParagonIE_Sodium_Core32_Int64 $carry9
*/
$carry0 = $h0->addInt(1 << 25)->shiftRight(26);
$h1 = $h1->addInt64($carry0);
$h0 = $h0->subInt64($carry0->shiftLeft(26));
$carry4 = $h4->addInt(1 << 25)->shiftRight(26);
$h5 = $h5->addInt64($carry4);
$h4 = $h4->subInt64($carry4->shiftLeft(26));
$carry1 = $h1->addInt(1 << 24)->shiftRight(25);
$h2 = $h2->addInt64($carry1);
$h1 = $h1->subInt64($carry1->shiftLeft(25));
$carry5 = $h5->addInt(1 << 24)->shiftRight(25);
$h6 = $h6->addInt64($carry5);
$h5 = $h5->subInt64($carry5->shiftLeft(25));
$carry2 = $h2->addInt(1 << 25)->shiftRight(26);
$h3 = $h3->addInt64($carry2);
$h2 = $h2->subInt64($carry2->shiftLeft(26));
$carry6 = $h6->addInt(1 << 25)->shiftRight(26);
$h7 = $h7->addInt64($carry6);
$h6 = $h6->subInt64($carry6->shiftLeft(26));
$carry3 = $h3->addInt(1 << 24)->shiftRight(25);
$h4 = $h4->addInt64($carry3);
$h3 = $h3->subInt64($carry3->shiftLeft(25));
$carry7 = $h7->addInt(1 << 24)->shiftRight(25);
$h8 = $h8->addInt64($carry7);
$h7 = $h7->subInt64($carry7->shiftLeft(25));
$carry4 = $h4->addInt(1 << 25)->shiftRight(26);
$h5 = $h5->addInt64($carry4);
$h4 = $h4->subInt64($carry4->shiftLeft(26));
$carry8 = $h8->addInt(1 << 25)->shiftRight(26);
$h9 = $h9->addInt64($carry8);
$h8 = $h8->subInt64($carry8->shiftLeft(26));
$carry9 = $h9->addInt(1 << 24)->shiftRight(25);
$h0 = $h0->addInt64($carry9->mulInt(19, 5));
$h9 = $h9->subInt64($carry9->shiftLeft(25));
$carry0 = $h0->addInt(1 << 25)->shiftRight(26);
$h1 = $h1->addInt64($carry0);
$h0 = $h0->subInt64($carry0->shiftLeft(26));
return ParagonIE_Sodium_Core32_Curve25519_Fe::fromArray(
array(
$h0->toInt32(),
$h1->toInt32(),
$h2->toInt32(),
$h3->toInt32(),
$h4->toInt32(),
$h5->toInt32(),
$h6->toInt32(),
$h7->toInt32(),
$h8->toInt32(),
$h9->toInt32()
)
);
}
/**
* Get the negative values for each piece of the field element.
*
* h = -f
*
* @internal You should not use this directly from another application
*
* @param ParagonIE_Sodium_Core32_Curve25519_Fe $f
* @return ParagonIE_Sodium_Core32_Curve25519_Fe
* @psalm-suppress MixedAssignment
* @psalm-suppress MixedMethodCall
*/
public static function fe_neg(ParagonIE_Sodium_Core32_Curve25519_Fe $f)
{
$h = new ParagonIE_Sodium_Core32_Curve25519_Fe();
for ($i = 0; $i < 10; ++$i) {
$h[$i] = $h[$i]->subInt32($f[$i]);
}
return $h;
}
/**
* Square a field element
*
* h = f * f
*
* @internal You should not use this directly from another application
*
* @param ParagonIE_Sodium_Core32_Curve25519_Fe $f
* @return ParagonIE_Sodium_Core32_Curve25519_Fe
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedMethodCall
*/
public static function fe_sq(ParagonIE_Sodium_Core32_Curve25519_Fe $f)
{
$f0 = $f[0]->toInt64();
$f1 = $f[1]->toInt64();
$f2 = $f[2]->toInt64();
$f3 = $f[3]->toInt64();
$f4 = $f[4]->toInt64();
$f5 = $f[5]->toInt64();
$f6 = $f[6]->toInt64();
$f7 = $f[7]->toInt64();
$f8 = $f[8]->toInt64();
$f9 = $f[9]->toInt64();
$f0_2 = $f0->shiftLeft(1);
$f1_2 = $f1->shiftLeft(1);
$f2_2 = $f2->shiftLeft(1);
$f3_2 = $f3->shiftLeft(1);
$f4_2 = $f4->shiftLeft(1);
$f5_2 = $f5->shiftLeft(1);
$f6_2 = $f6->shiftLeft(1);
$f7_2 = $f7->shiftLeft(1);
$f5_38 = $f5->mulInt(38, 6);
$f6_19 = $f6->mulInt(19, 5);
$f7_38 = $f7->mulInt(38, 6);
$f8_19 = $f8->mulInt(19, 5);
$f9_38 = $f9->mulInt(38, 6);
$f0f0 = $f0->mulInt64($f0, 28);
$f0f1_2 = $f0_2->mulInt64($f1, 28);
$f0f2_2 = $f0_2->mulInt64($f2, 28);
$f0f3_2 = $f0_2->mulInt64($f3, 28);
$f0f4_2 = $f0_2->mulInt64($f4, 28);
$f0f5_2 = $f0_2->mulInt64($f5, 28);
$f0f6_2 = $f0_2->mulInt64($f6, 28);
$f0f7_2 = $f0_2->mulInt64($f7, 28);
$f0f8_2 = $f0_2->mulInt64($f8, 28);
$f0f9_2 = $f0_2->mulInt64($f9, 28);
$f1f1_2 = $f1_2->mulInt64($f1, 28);
$f1f2_2 = $f1_2->mulInt64($f2, 28);
$f1f3_4 = $f1_2->mulInt64($f3_2, 28);
$f1f4_2 = $f1_2->mulInt64($f4, 28);
$f1f5_4 = $f1_2->mulInt64($f5_2, 30);
$f1f6_2 = $f1_2->mulInt64($f6, 28);
$f1f7_4 = $f1_2->mulInt64($f7_2, 28);
$f1f8_2 = $f1_2->mulInt64($f8, 28);
$f1f9_76 = $f9_38->mulInt64($f1_2, 30);
$f2f2 = $f2->mulInt64($f2, 28);
$f2f3_2 = $f2_2->mulInt64($f3, 28);
$f2f4_2 = $f2_2->mulInt64($f4, 28);
$f2f5_2 = $f2_2->mulInt64($f5, 28);
$f2f6_2 = $f2_2->mulInt64($f6, 28);
$f2f7_2 = $f2_2->mulInt64($f7, 28);
$f2f8_38 = $f8_19->mulInt64($f2_2, 30);
$f2f9_38 = $f9_38->mulInt64($f2, 30);
$f3f3_2 = $f3_2->mulInt64($f3, 28);
$f3f4_2 = $f3_2->mulInt64($f4, 28);
$f3f5_4 = $f3_2->mulInt64($f5_2, 30);
$f3f6_2 = $f3_2->mulInt64($f6, 28);
$f3f7_76 = $f7_38->mulInt64($f3_2, 30);
$f3f8_38 = $f8_19->mulInt64($f3_2, 30);
$f3f9_76 = $f9_38->mulInt64($f3_2, 30);
$f4f4 = $f4->mulInt64($f4, 28);
$f4f5_2 = $f4_2->mulInt64($f5, 28);
$f4f6_38 = $f6_19->mulInt64($f4_2, 30);
$f4f7_38 = $f7_38->mulInt64($f4, 30);
$f4f8_38 = $f8_19->mulInt64($f4_2, 30);
$f4f9_38 = $f9_38->mulInt64($f4, 30);
$f5f5_38 = $f5_38->mulInt64($f5, 30);
$f5f6_38 = $f6_19->mulInt64($f5_2, 30);
$f5f7_76 = $f7_38->mulInt64($f5_2, 30);
$f5f8_38 = $f8_19->mulInt64($f5_2, 30);
$f5f9_76 = $f9_38->mulInt64($f5_2, 30);
$f6f6_19 = $f6_19->mulInt64($f6, 30);
$f6f7_38 = $f7_38->mulInt64($f6, 30);
$f6f8_38 = $f8_19->mulInt64($f6_2, 30);
$f6f9_38 = $f9_38->mulInt64($f6, 30);
$f7f7_38 = $f7_38->mulInt64($f7, 28);
$f7f8_38 = $f8_19->mulInt64($f7_2, 30);
$f7f9_76 = $f9_38->mulInt64($f7_2, 30);
$f8f8_19 = $f8_19->mulInt64($f8, 30);
$f8f9_38 = $f9_38->mulInt64($f8, 30);
$f9f9_38 = $f9_38->mulInt64($f9, 28);
$h0 = $f0f0->addInt64($f1f9_76)->addInt64($f2f8_38)->addInt64($f3f7_76)->addInt64($f4f6_38)->addInt64($f5f5_38);
$h1 = $f0f1_2->addInt64($f2f9_38)->addInt64($f3f8_38)->addInt64($f4f7_38)->addInt64($f5f6_38);
$h2 = $f0f2_2->addInt64($f1f1_2)->addInt64($f3f9_76)->addInt64($f4f8_38)->addInt64($f5f7_76)->addInt64($f6f6_19);
$h3 = $f0f3_2->addInt64($f1f2_2)->addInt64($f4f9_38)->addInt64($f5f8_38)->addInt64($f6f7_38);
$h4 = $f0f4_2->addInt64($f1f3_4)->addInt64($f2f2)->addInt64($f5f9_76)->addInt64($f6f8_38)->addInt64($f7f7_38);
$h5 = $f0f5_2->addInt64($f1f4_2)->addInt64($f2f3_2)->addInt64($f6f9_38)->addInt64($f7f8_38);
$h6 = $f0f6_2->addInt64($f1f5_4)->addInt64($f2f4_2)->addInt64($f3f3_2)->addInt64($f7f9_76)->addInt64($f8f8_19);
$h7 = $f0f7_2->addInt64($f1f6_2)->addInt64($f2f5_2)->addInt64($f3f4_2)->addInt64($f8f9_38);
$h8 = $f0f8_2->addInt64($f1f7_4)->addInt64($f2f6_2)->addInt64($f3f5_4)->addInt64($f4f4)->addInt64($f9f9_38);
$h9 = $f0f9_2->addInt64($f1f8_2)->addInt64($f2f7_2)->addInt64($f3f6_2)->addInt64($f4f5_2);
/**
* @var ParagonIE_Sodium_Core32_Int64 $h0
* @var ParagonIE_Sodium_Core32_Int64 $h1
* @var ParagonIE_Sodium_Core32_Int64 $h2
* @var ParagonIE_Sodium_Core32_Int64 $h3
* @var ParagonIE_Sodium_Core32_Int64 $h4
* @var ParagonIE_Sodium_Core32_Int64 $h5
* @var ParagonIE_Sodium_Core32_Int64 $h6
* @var ParagonIE_Sodium_Core32_Int64 $h7
* @var ParagonIE_Sodium_Core32_Int64 $h8
* @var ParagonIE_Sodium_Core32_Int64 $h9
*/
$carry0 = $h0->addInt(1 << 25)->shiftRight(26);
$h1 = $h1->addInt64($carry0);
$h0 = $h0->subInt64($carry0->shiftLeft(26));
$carry4 = $h4->addInt(1 << 25)->shiftRight(26);
$h5 = $h5->addInt64($carry4);
$h4 = $h4->subInt64($carry4->shiftLeft(26));
$carry1 = $h1->addInt(1 << 24)->shiftRight(25);
$h2 = $h2->addInt64($carry1);
$h1 = $h1->subInt64($carry1->shiftLeft(25));
$carry5 = $h5->addInt(1 << 24)->shiftRight(25);
$h6 = $h6->addInt64($carry5);
$h5 = $h5->subInt64($carry5->shiftLeft(25));
$carry2 = $h2->addInt(1 << 25)->shiftRight(26);
$h3 = $h3->addInt64($carry2);
$h2 = $h2->subInt64($carry2->shiftLeft(26));
$carry6 = $h6->addInt(1 << 25)->shiftRight(26);
$h7 = $h7->addInt64($carry6);
$h6 = $h6->subInt64($carry6->shiftLeft(26));
$carry3 = $h3->addInt(1 << 24)->shiftRight(25);
$h4 = $h4->addInt64($carry3);
$h3 = $h3->subInt64($carry3->shiftLeft(25));
$carry7 = $h7->addInt(1 << 24)->shiftRight(25);
$h8 = $h8->addInt64($carry7);
$h7 = $h7->subInt64($carry7->shiftLeft(25));
$carry4 = $h4->addInt(1 << 25)->shiftRight(26);
$h5 = $h5->addInt64($carry4);
$h4 = $h4->subInt64($carry4->shiftLeft(26));
$carry8 = $h8->addInt(1 << 25)->shiftRight(26);
$h9 = $h9->addInt64($carry8);
$h8 = $h8->subInt64($carry8->shiftLeft(26));
$carry9 = $h9->addInt(1 << 24)->shiftRight(25);
$h0 = $h0->addInt64($carry9->mulInt(19, 5));
$h9 = $h9->subInt64($carry9->shiftLeft(25));
$carry0 = $h0->addInt(1 << 25)->shiftRight(26);
$h1 = $h1->addInt64($carry0);
$h0 = $h0->subInt64($carry0->shiftLeft(26));
return ParagonIE_Sodium_Core32_Curve25519_Fe::fromArray(
array(
$h0->toInt32(),
$h1->toInt32(),
$h2->toInt32(),
$h3->toInt32(),
$h4->toInt32(),
$h5->toInt32(),
$h6->toInt32(),
$h7->toInt32(),
$h8->toInt32(),
$h9->toInt32()
)
);
}
/**
* Square and double a field element
*
* h = 2 * f * f
*
* @internal You should not use this directly from another application
*
* @param ParagonIE_Sodium_Core32_Curve25519_Fe $f
* @return ParagonIE_Sodium_Core32_Curve25519_Fe
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedMethodCall
*/
public static function fe_sq2(ParagonIE_Sodium_Core32_Curve25519_Fe $f)
{
$f0 = $f[0]->toInt64();
$f1 = $f[1]->toInt64();
$f2 = $f[2]->toInt64();
$f3 = $f[3]->toInt64();
$f4 = $f[4]->toInt64();
$f5 = $f[5]->toInt64();
$f6 = $f[6]->toInt64();
$f7 = $f[7]->toInt64();
$f8 = $f[8]->toInt64();
$f9 = $f[9]->toInt64();
$f0_2 = $f0->shiftLeft(1);
$f1_2 = $f1->shiftLeft(1);
$f2_2 = $f2->shiftLeft(1);
$f3_2 = $f3->shiftLeft(1);
$f4_2 = $f4->shiftLeft(1);
$f5_2 = $f5->shiftLeft(1);
$f6_2 = $f6->shiftLeft(1);
$f7_2 = $f7->shiftLeft(1);
$f5_38 = $f5->mulInt(38, 6); /* 1.959375*2^30 */
$f6_19 = $f6->mulInt(19, 5); /* 1.959375*2^30 */
$f7_38 = $f7->mulInt(38, 6); /* 1.959375*2^30 */
$f8_19 = $f8->mulInt(19, 5); /* 1.959375*2^30 */
$f9_38 = $f9->mulInt(38, 6); /* 1.959375*2^30 */
$f0f0 = $f0->mulInt64($f0, 28);
$f0f1_2 = $f0_2->mulInt64($f1, 28);
$f0f2_2 = $f0_2->mulInt64($f2, 28);
$f0f3_2 = $f0_2->mulInt64($f3, 28);
$f0f4_2 = $f0_2->mulInt64($f4, 28);
$f0f5_2 = $f0_2->mulInt64($f5, 28);
$f0f6_2 = $f0_2->mulInt64($f6, 28);
$f0f7_2 = $f0_2->mulInt64($f7, 28);
$f0f8_2 = $f0_2->mulInt64($f8, 28);
$f0f9_2 = $f0_2->mulInt64($f9, 28);
$f1f1_2 = $f1_2->mulInt64($f1, 28);
$f1f2_2 = $f1_2->mulInt64($f2, 28);
$f1f3_4 = $f1_2->mulInt64($f3_2, 29);
$f1f4_2 = $f1_2->mulInt64($f4, 28);
$f1f5_4 = $f1_2->mulInt64($f5_2, 29);
$f1f6_2 = $f1_2->mulInt64($f6, 28);
$f1f7_4 = $f1_2->mulInt64($f7_2, 29);
$f1f8_2 = $f1_2->mulInt64($f8, 28);
$f1f9_76 = $f9_38->mulInt64($f1_2, 29);
$f2f2 = $f2->mulInt64($f2, 28);
$f2f3_2 = $f2_2->mulInt64($f3, 28);
$f2f4_2 = $f2_2->mulInt64($f4, 28);
$f2f5_2 = $f2_2->mulInt64($f5, 28);
$f2f6_2 = $f2_2->mulInt64($f6, 28);
$f2f7_2 = $f2_2->mulInt64($f7, 28);
$f2f8_38 = $f8_19->mulInt64($f2_2, 29);
$f2f9_38 = $f9_38->mulInt64($f2, 29);
$f3f3_2 = $f3_2->mulInt64($f3, 28);
$f3f4_2 = $f3_2->mulInt64($f4, 28);
$f3f5_4 = $f3_2->mulInt64($f5_2, 28);
$f3f6_2 = $f3_2->mulInt64($f6, 28);
$f3f7_76 = $f7_38->mulInt64($f3_2, 29);
$f3f8_38 = $f8_19->mulInt64($f3_2, 29);
$f3f9_76 = $f9_38->mulInt64($f3_2, 29);
$f4f4 = $f4->mulInt64($f4, 28);
$f4f5_2 = $f4_2->mulInt64($f5, 28);
$f4f6_38 = $f6_19->mulInt64($f4_2, 29);
$f4f7_38 = $f7_38->mulInt64($f4, 29);
$f4f8_38 = $f8_19->mulInt64($f4_2, 29);
$f4f9_38 = $f9_38->mulInt64($f4, 29);
$f5f5_38 = $f5_38->mulInt64($f5, 29);
$f5f6_38 = $f6_19->mulInt64($f5_2, 29);
$f5f7_76 = $f7_38->mulInt64($f5_2, 29);
$f5f8_38 = $f8_19->mulInt64($f5_2, 29);
$f5f9_76 = $f9_38->mulInt64($f5_2, 29);
$f6f6_19 = $f6_19->mulInt64($f6, 29);
$f6f7_38 = $f7_38->mulInt64($f6, 29);
$f6f8_38 = $f8_19->mulInt64($f6_2, 29);
$f6f9_38 = $f9_38->mulInt64($f6, 29);
$f7f7_38 = $f7_38->mulInt64($f7, 29);
$f7f8_38 = $f8_19->mulInt64($f7_2, 29);
$f7f9_76 = $f9_38->mulInt64($f7_2, 29);
$f8f8_19 = $f8_19->mulInt64($f8, 29);
$f8f9_38 = $f9_38->mulInt64($f8, 29);
$f9f9_38 = $f9_38->mulInt64($f9, 29);
$h0 = $f0f0->addInt64($f1f9_76)->addInt64($f2f8_38)->addInt64($f3f7_76)->addInt64($f4f6_38)->addInt64($f5f5_38);
$h1 = $f0f1_2->addInt64($f2f9_38)->addInt64($f3f8_38)->addInt64($f4f7_38)->addInt64($f5f6_38);
$h2 = $f0f2_2->addInt64($f1f1_2)->addInt64($f3f9_76)->addInt64($f4f8_38)->addInt64($f5f7_76)->addInt64($f6f6_19);
$h3 = $f0f3_2->addInt64($f1f2_2)->addInt64($f4f9_38)->addInt64($f5f8_38)->addInt64($f6f7_38);
$h4 = $f0f4_2->addInt64($f1f3_4)->addInt64($f2f2)->addInt64($f5f9_76)->addInt64($f6f8_38)->addInt64($f7f7_38);
$h5 = $f0f5_2->addInt64($f1f4_2)->addInt64($f2f3_2)->addInt64($f6f9_38)->addInt64($f7f8_38);
$h6 = $f0f6_2->addInt64($f1f5_4)->addInt64($f2f4_2)->addInt64($f3f3_2)->addInt64($f7f9_76)->addInt64($f8f8_19);
$h7 = $f0f7_2->addInt64($f1f6_2)->addInt64($f2f5_2)->addInt64($f3f4_2)->addInt64($f8f9_38);
$h8 = $f0f8_2->addInt64($f1f7_4)->addInt64($f2f6_2)->addInt64($f3f5_4)->addInt64($f4f4)->addInt64($f9f9_38);
$h9 = $f0f9_2->addInt64($f1f8_2)->addInt64($f2f7_2)->addInt64($f3f6_2)->addInt64($f4f5_2);
/**
* @var ParagonIE_Sodium_Core32_Int64 $h0
* @var ParagonIE_Sodium_Core32_Int64 $h1
* @var ParagonIE_Sodium_Core32_Int64 $h2
* @var ParagonIE_Sodium_Core32_Int64 $h3
* @var ParagonIE_Sodium_Core32_Int64 $h4
* @var ParagonIE_Sodium_Core32_Int64 $h5
* @var ParagonIE_Sodium_Core32_Int64 $h6
* @var ParagonIE_Sodium_Core32_Int64 $h7
* @var ParagonIE_Sodium_Core32_Int64 $h8
* @var ParagonIE_Sodium_Core32_Int64 $h9
*/
$h0 = $h0->shiftLeft(1);
$h1 = $h1->shiftLeft(1);
$h2 = $h2->shiftLeft(1);
$h3 = $h3->shiftLeft(1);
$h4 = $h4->shiftLeft(1);
$h5 = $h5->shiftLeft(1);
$h6 = $h6->shiftLeft(1);
$h7 = $h7->shiftLeft(1);
$h8 = $h8->shiftLeft(1);
$h9 = $h9->shiftLeft(1);
$carry0 = $h0->addInt(1 << 25)->shiftRight(26);
$h1 = $h1->addInt64($carry0);
$h0 = $h0->subInt64($carry0->shiftLeft(26));
$carry4 = $h4->addInt(1 << 25)->shiftRight(26);
$h5 = $h5->addInt64($carry4);
$h4 = $h4->subInt64($carry4->shiftLeft(26));
$carry1 = $h1->addInt(1 << 24)->shiftRight(25);
$h2 = $h2->addInt64($carry1);
$h1 = $h1->subInt64($carry1->shiftLeft(25));
$carry5 = $h5->addInt(1 << 24)->shiftRight(25);
$h6 = $h6->addInt64($carry5);
$h5 = $h5->subInt64($carry5->shiftLeft(25));
$carry2 = $h2->addInt(1 << 25)->shiftRight(26);
$h3 = $h3->addInt64($carry2);
$h2 = $h2->subInt64($carry2->shiftLeft(26));
$carry6 = $h6->addInt(1 << 25)->shiftRight(26);
$h7 = $h7->addInt64($carry6);
$h6 = $h6->subInt64($carry6->shiftLeft(26));
$carry3 = $h3->addInt(1 << 24)->shiftRight(25);
$h4 = $h4->addInt64($carry3);
$h3 = $h3->subInt64($carry3->shiftLeft(25));
$carry7 = $h7->addInt(1 << 24)->shiftRight(25);
$h8 = $h8->addInt64($carry7);
$h7 = $h7->subInt64($carry7->shiftLeft(25));
$carry4 = $h4->addInt(1 << 25)->shiftRight(26);
$h5 = $h5->addInt64($carry4);
$h4 = $h4->subInt64($carry4->shiftLeft(26));
$carry8 = $h8->addInt(1 << 25)->shiftRight(26);
$h9 = $h9->addInt64($carry8);
$h8 = $h8->subInt64($carry8->shiftLeft(26));
$carry9 = $h9->addInt(1 << 24)->shiftRight(25);
$h0 = $h0->addInt64($carry9->mulInt(19, 5));
$h9 = $h9->subInt64($carry9->shiftLeft(25));
$carry0 = $h0->addInt(1 << 25)->shiftRight(26);
$h1 = $h1->addInt64($carry0);
$h0 = $h0->subInt64($carry0->shiftLeft(26));
return ParagonIE_Sodium_Core32_Curve25519_Fe::fromArray(
array(
$h0->toInt32(),
$h1->toInt32(),
$h2->toInt32(),
$h3->toInt32(),
$h4->toInt32(),
$h5->toInt32(),
$h6->toInt32(),
$h7->toInt32(),
$h8->toInt32(),
$h9->toInt32()
)
);
}
/**
* @internal You should not use this directly from another application
*
* @param ParagonIE_Sodium_Core32_Curve25519_Fe $Z
* @return ParagonIE_Sodium_Core32_Curve25519_Fe
* @throws SodiumException
* @throws TypeError
*/
public static function fe_invert(ParagonIE_Sodium_Core32_Curve25519_Fe $Z)
{
$z = clone $Z;
$t0 = self::fe_sq($z);
$t1 = self::fe_sq($t0);
$t1 = self::fe_sq($t1);
$t1 = self::fe_mul($z, $t1);
$t0 = self::fe_mul($t0, $t1);
$t2 = self::fe_sq($t0);
$t1 = self::fe_mul($t1, $t2);
$t2 = self::fe_sq($t1);
for ($i = 1; $i < 5; ++$i) {
$t2 = self::fe_sq($t2);
}
$t1 = self::fe_mul($t2, $t1);
$t2 = self::fe_sq($t1);
for ($i = 1; $i < 10; ++$i) {
$t2 = self::fe_sq($t2);
}
$t2 = self::fe_mul($t2, $t1);
$t3 = self::fe_sq($t2);
for ($i = 1; $i < 20; ++$i) {
$t3 = self::fe_sq($t3);
}
$t2 = self::fe_mul($t3, $t2);
$t2 = self::fe_sq($t2);
for ($i = 1; $i < 10; ++$i) {
$t2 = self::fe_sq($t2);
}
$t1 = self::fe_mul($t2, $t1);
$t2 = self::fe_sq($t1);
for ($i = 1; $i < 50; ++$i) {
$t2 = self::fe_sq($t2);
}
$t2 = self::fe_mul($t2, $t1);
$t3 = self::fe_sq($t2);
for ($i = 1; $i < 100; ++$i) {
$t3 = self::fe_sq($t3);
}
$t2 = self::fe_mul($t3, $t2);
$t2 = self::fe_sq($t2);
for ($i = 1; $i < 50; ++$i) {
$t2 = self::fe_sq($t2);
}
$t1 = self::fe_mul($t2, $t1);
$t1 = self::fe_sq($t1);
for ($i = 1; $i < 5; ++$i) {
$t1 = self::fe_sq($t1);
}
return self::fe_mul($t1, $t0);
}
/**
* @internal You should not use this directly from another application
*
* @ref https://github.com/jedisct1/libsodium/blob/68564326e1e9dc57ef03746f85734232d20ca6fb/src/libsodium/crypto_core/curve25519/ref10/curve25519_ref10.c#L1054-L1106
*
* @param ParagonIE_Sodium_Core32_Curve25519_Fe $z
* @return ParagonIE_Sodium_Core32_Curve25519_Fe
* @throws SodiumException
* @throws TypeError
*/
public static function fe_pow22523(ParagonIE_Sodium_Core32_Curve25519_Fe $z)
{
# fe_sq(t0, z);
# fe_sq(t1, t0);
# fe_sq(t1, t1);
# fe_mul(t1, z, t1);
# fe_mul(t0, t0, t1);
# fe_sq(t0, t0);
# fe_mul(t0, t1, t0);
# fe_sq(t1, t0);
$t0 = self::fe_sq($z);
$t1 = self::fe_sq($t0);
$t1 = self::fe_sq($t1);
$t1 = self::fe_mul($z, $t1);
$t0 = self::fe_mul($t0, $t1);
$t0 = self::fe_sq($t0);
$t0 = self::fe_mul($t1, $t0);
$t1 = self::fe_sq($t0);
# for (i = 1; i < 5; ++i) {
# fe_sq(t1, t1);
# }
for ($i = 1; $i < 5; ++$i) {
$t1 = self::fe_sq($t1);
}
# fe_mul(t0, t1, t0);
# fe_sq(t1, t0);
$t0 = self::fe_mul($t1, $t0);
$t1 = self::fe_sq($t0);
# for (i = 1; i < 10; ++i) {
# fe_sq(t1, t1);
# }
for ($i = 1; $i < 10; ++$i) {
$t1 = self::fe_sq($t1);
}
# fe_mul(t1, t1, t0);
# fe_sq(t2, t1);
$t1 = self::fe_mul($t1, $t0);
$t2 = self::fe_sq($t1);
# for (i = 1; i < 20; ++i) {
# fe_sq(t2, t2);
# }
for ($i = 1; $i < 20; ++$i) {
$t2 = self::fe_sq($t2);
}
# fe_mul(t1, t2, t1);
# fe_sq(t1, t1);
$t1 = self::fe_mul($t2, $t1);
$t1 = self::fe_sq($t1);
# for (i = 1; i < 10; ++i) {
# fe_sq(t1, t1);
# }
for ($i = 1; $i < 10; ++$i) {
$t1 = self::fe_sq($t1);
}
# fe_mul(t0, t1, t0);
# fe_sq(t1, t0);
$t0 = self::fe_mul($t1, $t0);
$t1 = self::fe_sq($t0);
# for (i = 1; i < 50; ++i) {
# fe_sq(t1, t1);
# }
for ($i = 1; $i < 50; ++$i) {
$t1 = self::fe_sq($t1);
}
# fe_mul(t1, t1, t0);
# fe_sq(t2, t1);
$t1 = self::fe_mul($t1, $t0);
$t2 = self::fe_sq($t1);
# for (i = 1; i < 100; ++i) {
# fe_sq(t2, t2);
# }
for ($i = 1; $i < 100; ++$i) {
$t2 = self::fe_sq($t2);
}
# fe_mul(t1, t2, t1);
# fe_sq(t1, t1);
$t1 = self::fe_mul($t2, $t1);
$t1 = self::fe_sq($t1);
# for (i = 1; i < 50; ++i) {
# fe_sq(t1, t1);
# }
for ($i = 1; $i < 50; ++$i) {
$t1 = self::fe_sq($t1);
}
# fe_mul(t0, t1, t0);
# fe_sq(t0, t0);
# fe_sq(t0, t0);
# fe_mul(out, t0, z);
$t0 = self::fe_mul($t1, $t0);
$t0 = self::fe_sq($t0);
$t0 = self::fe_sq($t0);
return self::fe_mul($t0, $z);
}
/**
* Subtract two field elements.
*
* h = f - g
*
* Preconditions:
* |f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
* |g| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
*
* Postconditions:
* |h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
*
* @internal You should not use this directly from another application
*
* @param ParagonIE_Sodium_Core32_Curve25519_Fe $f
* @param ParagonIE_Sodium_Core32_Curve25519_Fe $g
* @return ParagonIE_Sodium_Core32_Curve25519_Fe
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedMethodCall
* @psalm-suppress MixedTypeCoercion
*/
public static function fe_sub(ParagonIE_Sodium_Core32_Curve25519_Fe $f, ParagonIE_Sodium_Core32_Curve25519_Fe $g)
{
return ParagonIE_Sodium_Core32_Curve25519_Fe::fromArray(
array(
$f[0]->subInt32($g[0]),
$f[1]->subInt32($g[1]),
$f[2]->subInt32($g[2]),
$f[3]->subInt32($g[3]),
$f[4]->subInt32($g[4]),
$f[5]->subInt32($g[5]),
$f[6]->subInt32($g[6]),
$f[7]->subInt32($g[7]),
$f[8]->subInt32($g[8]),
$f[9]->subInt32($g[9])
)
);
}
/**
* Add two group elements.
*
* r = p + q
*
* @internal You should not use this directly from another application
*
* @param ParagonIE_Sodium_Core32_Curve25519_Ge_P3 $p
* @param ParagonIE_Sodium_Core32_Curve25519_Ge_Cached $q
* @return ParagonIE_Sodium_Core32_Curve25519_Ge_P1p1
* @throws SodiumException
* @throws TypeError
*/
public static function ge_add(
ParagonIE_Sodium_Core32_Curve25519_Ge_P3 $p,
ParagonIE_Sodium_Core32_Curve25519_Ge_Cached $q
) {
$r = new ParagonIE_Sodium_Core32_Curve25519_Ge_P1p1();
$r->X = self::fe_add($p->Y, $p->X);
$r->Y = self::fe_sub($p->Y, $p->X);
$r->Z = self::fe_mul($r->X, $q->YplusX);
$r->Y = self::fe_mul($r->Y, $q->YminusX);
$r->T = self::fe_mul($q->T2d, $p->T);
$r->X = self::fe_mul($p->Z, $q->Z);
$t0 = self::fe_add($r->X, $r->X);
$r->X = self::fe_sub($r->Z, $r->Y);
$r->Y = self::fe_add($r->Z, $r->Y);
$r->Z = self::fe_add($t0, $r->T);
$r->T = self::fe_sub($t0, $r->T);
return $r;
}
/**
* @internal You should not use this directly from another application
*
* @ref https://github.com/jedisct1/libsodium/blob/157c4a80c13b117608aeae12178b2d38825f9f8f/src/libsodium/crypto_core/curve25519/ref10/curve25519_ref10.c#L1185-L1215
* @param string $a
* @return array<int, mixed>
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArrayOffset
*/
public static function slide($a)
{
if (self::strlen($a) < 256) {
if (self::strlen($a) < 16) {
$a = str_pad($a, 256, '0', STR_PAD_RIGHT);
}
}
/** @var array<int, int> $r */
$r = array();
for ($i = 0; $i < 256; ++$i) {
$r[$i] = (int) (1 &
(
self::chrToInt($a[$i >> 3])
>>
($i & 7)
)
);
}
for ($i = 0;$i < 256;++$i) {
if ($r[$i]) {
for ($b = 1;$b <= 6 && $i + $b < 256;++$b) {
if ($r[$i + $b]) {
if ($r[$i] + ($r[$i + $b] << $b) <= 15) {
$r[$i] += $r[$i + $b] << $b;
$r[$i + $b] = 0;
} elseif ($r[$i] - ($r[$i + $b] << $b) >= -15) {
$r[$i] -= $r[$i + $b] << $b;
for ($k = $i + $b; $k < 256; ++$k) {
if (!$r[$k]) {
$r[$k] = 1;
break;
}
$r[$k] = 0;
}
} else {
break;
}
}
}
}
}
return $r;
}
/**
* @internal You should not use this directly from another application
*
* @param string $s
* @return ParagonIE_Sodium_Core32_Curve25519_Ge_P3
* @throws SodiumException
* @throws TypeError
*/
public static function ge_frombytes_negate_vartime($s)
{
static $d = null;
if (!$d) {
$d = ParagonIE_Sodium_Core32_Curve25519_Fe::fromArray(
array(
ParagonIE_Sodium_Core32_Int32::fromInt(self::$d[0]),
ParagonIE_Sodium_Core32_Int32::fromInt(self::$d[1]),
ParagonIE_Sodium_Core32_Int32::fromInt(self::$d[2]),
ParagonIE_Sodium_Core32_Int32::fromInt(self::$d[3]),
ParagonIE_Sodium_Core32_Int32::fromInt(self::$d[4]),
ParagonIE_Sodium_Core32_Int32::fromInt(self::$d[5]),
ParagonIE_Sodium_Core32_Int32::fromInt(self::$d[6]),
ParagonIE_Sodium_Core32_Int32::fromInt(self::$d[7]),
ParagonIE_Sodium_Core32_Int32::fromInt(self::$d[8]),
ParagonIE_Sodium_Core32_Int32::fromInt(self::$d[9])
)
);
}
/** @var ParagonIE_Sodium_Core32_Curve25519_Fe $d */
# fe_frombytes(h->Y,s);
# fe_1(h->Z);
$h = new ParagonIE_Sodium_Core32_Curve25519_Ge_P3(
self::fe_0(),
self::fe_frombytes($s),
self::fe_1()
);
# fe_sq(u,h->Y);
# fe_mul(v,u,d);
# fe_sub(u,u,h->Z); /* u = y^2-1 */
# fe_add(v,v,h->Z); /* v = dy^2+1 */
$u = self::fe_sq($h->Y);
/** @var ParagonIE_Sodium_Core32_Curve25519_Fe $d */
$v = self::fe_mul($u, $d);
$u = self::fe_sub($u, $h->Z); /* u = y^2 - 1 */
$v = self::fe_add($v, $h->Z); /* v = dy^2 + 1 */
# fe_sq(v3,v);
# fe_mul(v3,v3,v); /* v3 = v^3 */
# fe_sq(h->X,v3);
# fe_mul(h->X,h->X,v);
# fe_mul(h->X,h->X,u); /* x = uv^7 */
$v3 = self::fe_sq($v);
$v3 = self::fe_mul($v3, $v); /* v3 = v^3 */
$h->X = self::fe_sq($v3);
$h->X = self::fe_mul($h->X, $v);
$h->X = self::fe_mul($h->X, $u); /* x = uv^7 */
# fe_pow22523(h->X,h->X); /* x = (uv^7)^((q-5)/8) */
# fe_mul(h->X,h->X,v3);
# fe_mul(h->X,h->X,u); /* x = uv^3(uv^7)^((q-5)/8) */
$h->X = self::fe_pow22523($h->X); /* x = (uv^7)^((q-5)/8) */
$h->X = self::fe_mul($h->X, $v3);
$h->X = self::fe_mul($h->X, $u); /* x = uv^3(uv^7)^((q-5)/8) */
# fe_sq(vxx,h->X);
# fe_mul(vxx,vxx,v);
# fe_sub(check,vxx,u); /* vx^2-u */
$vxx = self::fe_sq($h->X);
$vxx = self::fe_mul($vxx, $v);
$check = self::fe_sub($vxx, $u); /* vx^2 - u */
# if (fe_isnonzero(check)) {
# fe_add(check,vxx,u); /* vx^2+u */
# if (fe_isnonzero(check)) {
# return -1;
# }
# fe_mul(h->X,h->X,sqrtm1);
# }
if (self::fe_isnonzero($check)) {
$check = self::fe_add($vxx, $u); /* vx^2 + u */
if (self::fe_isnonzero($check)) {
throw new RangeException('Internal check failed.');
}
$h->X = self::fe_mul(
$h->X,
ParagonIE_Sodium_Core32_Curve25519_Fe::fromIntArray(self::$sqrtm1)
);
}
# if (fe_isnegative(h->X) == (s[31] >> 7)) {
# fe_neg(h->X,h->X);
# }
$i = self::chrToInt($s[31]);
if (self::fe_isnegative($h->X) === ($i >> 7)) {
$h->X = self::fe_neg($h->X);
}
# fe_mul(h->T,h->X,h->Y);
$h->T = self::fe_mul($h->X, $h->Y);
return $h;
}
/**
* @internal You should not use this directly from another application
*
* @param ParagonIE_Sodium_Core32_Curve25519_Ge_P1p1 $R
* @param ParagonIE_Sodium_Core32_Curve25519_Ge_P3 $p
* @param ParagonIE_Sodium_Core32_Curve25519_Ge_Precomp $q
* @return ParagonIE_Sodium_Core32_Curve25519_Ge_P1p1
* @throws SodiumException
* @throws TypeError
*/
public static function ge_madd(
ParagonIE_Sodium_Core32_Curve25519_Ge_P1p1 $R,
ParagonIE_Sodium_Core32_Curve25519_Ge_P3 $p,
ParagonIE_Sodium_Core32_Curve25519_Ge_Precomp $q
) {
$r = clone $R;
$r->X = self::fe_add($p->Y, $p->X);
$r->Y = self::fe_sub($p->Y, $p->X);
$r->Z = self::fe_mul($r->X, $q->yplusx);
$r->Y = self::fe_mul($r->Y, $q->yminusx);
$r->T = self::fe_mul($q->xy2d, $p->T);
$t0 = self::fe_add(clone $p->Z, clone $p->Z);
$r->X = self::fe_sub($r->Z, $r->Y);
$r->Y = self::fe_add($r->Z, $r->Y);
$r->Z = self::fe_add($t0, $r->T);
$r->T = self::fe_sub($t0, $r->T);
return $r;
}
/**
* @internal You should not use this directly from another application
*
* @param ParagonIE_Sodium_Core32_Curve25519_Ge_P1p1 $R
* @param ParagonIE_Sodium_Core32_Curve25519_Ge_P3 $p
* @param ParagonIE_Sodium_Core32_Curve25519_Ge_Precomp $q
* @return ParagonIE_Sodium_Core32_Curve25519_Ge_P1p1
* @throws SodiumException
* @throws TypeError
*/
public static function ge_msub(
ParagonIE_Sodium_Core32_Curve25519_Ge_P1p1 $R,
ParagonIE_Sodium_Core32_Curve25519_Ge_P3 $p,
ParagonIE_Sodium_Core32_Curve25519_Ge_Precomp $q
) {
$r = clone $R;
$r->X = self::fe_add($p->Y, $p->X);
$r->Y = self::fe_sub($p->Y, $p->X);
$r->Z = self::fe_mul($r->X, $q->yminusx);
$r->Y = self::fe_mul($r->Y, $q->yplusx);
$r->T = self::fe_mul($q->xy2d, $p->T);
$t0 = self::fe_add($p->Z, $p->Z);
$r->X = self::fe_sub($r->Z, $r->Y);
$r->Y = self::fe_add($r->Z, $r->Y);
$r->Z = self::fe_sub($t0, $r->T);
$r->T = self::fe_add($t0, $r->T);
return $r;
}
/**
* @internal You should not use this directly from another application
*
* @param ParagonIE_Sodium_Core32_Curve25519_Ge_P1p1 $p
* @return ParagonIE_Sodium_Core32_Curve25519_Ge_P2
* @throws SodiumException
* @throws TypeError
*/
public static function ge_p1p1_to_p2(ParagonIE_Sodium_Core32_Curve25519_Ge_P1p1 $p)
{
$r = new ParagonIE_Sodium_Core32_Curve25519_Ge_P2();
$r->X = self::fe_mul($p->X, $p->T);
$r->Y = self::fe_mul($p->Y, $p->Z);
$r->Z = self::fe_mul($p->Z, $p->T);
return $r;
}
/**
* @internal You should not use this directly from another application
*
* @param ParagonIE_Sodium_Core32_Curve25519_Ge_P1p1 $p
* @return ParagonIE_Sodium_Core32_Curve25519_Ge_P3
* @throws SodiumException
* @throws TypeError
*/
public static function ge_p1p1_to_p3(ParagonIE_Sodium_Core32_Curve25519_Ge_P1p1 $p)
{
$r = new ParagonIE_Sodium_Core32_Curve25519_Ge_P3();
$r->X = self::fe_mul($p->X, $p->T);
$r->Y = self::fe_mul($p->Y, $p->Z);
$r->Z = self::fe_mul($p->Z, $p->T);
$r->T = self::fe_mul($p->X, $p->Y);
return $r;
}
/**
* @internal You should not use this directly from another application
*
* @return ParagonIE_Sodium_Core32_Curve25519_Ge_P2
* @throws SodiumException
* @throws TypeError
*/
public static function ge_p2_0()
{
return new ParagonIE_Sodium_Core32_Curve25519_Ge_P2(
self::fe_0(),
self::fe_1(),
self::fe_1()
);
}
/**
* @internal You should not use this directly from another application
*
* @param ParagonIE_Sodium_Core32_Curve25519_Ge_P2 $p
* @return ParagonIE_Sodium_Core32_Curve25519_Ge_P1p1
* @throws SodiumException
* @throws TypeError
*/
public static function ge_p2_dbl(ParagonIE_Sodium_Core32_Curve25519_Ge_P2 $p)
{
$r = new ParagonIE_Sodium_Core32_Curve25519_Ge_P1p1();
$r->X = self::fe_sq($p->X);
$r->Z = self::fe_sq($p->Y);
$r->T = self::fe_sq2($p->Z);
$r->Y = self::fe_add($p->X, $p->Y);
$t0 = self::fe_sq($r->Y);
$r->Y = self::fe_add($r->Z, $r->X);
$r->Z = self::fe_sub($r->Z, $r->X);
$r->X = self::fe_sub($t0, $r->Y);
$r->T = self::fe_sub($r->T, $r->Z);
return $r;
}
/**
* @internal You should not use this directly from another application
*
* @return ParagonIE_Sodium_Core32_Curve25519_Ge_P3
* @throws SodiumException
* @throws TypeError
*/
public static function ge_p3_0()
{
return new ParagonIE_Sodium_Core32_Curve25519_Ge_P3(
self::fe_0(),
self::fe_1(),
self::fe_1(),
self::fe_0()
);
}
/**
* @internal You should not use this directly from another application
*
* @param ParagonIE_Sodium_Core32_Curve25519_Ge_P3 $p
* @return ParagonIE_Sodium_Core32_Curve25519_Ge_Cached
* @throws SodiumException
* @throws TypeError
*/
public static function ge_p3_to_cached(ParagonIE_Sodium_Core32_Curve25519_Ge_P3 $p)
{
static $d2 = null;
if ($d2 === null) {
$d2 = ParagonIE_Sodium_Core32_Curve25519_Fe::fromArray(
array(
ParagonIE_Sodium_Core32_Int32::fromInt(self::$d2[0]),
ParagonIE_Sodium_Core32_Int32::fromInt(self::$d2[1]),
ParagonIE_Sodium_Core32_Int32::fromInt(self::$d2[2]),
ParagonIE_Sodium_Core32_Int32::fromInt(self::$d2[3]),
ParagonIE_Sodium_Core32_Int32::fromInt(self::$d2[4]),
ParagonIE_Sodium_Core32_Int32::fromInt(self::$d2[5]),
ParagonIE_Sodium_Core32_Int32::fromInt(self::$d2[6]),
ParagonIE_Sodium_Core32_Int32::fromInt(self::$d2[7]),
ParagonIE_Sodium_Core32_Int32::fromInt(self::$d2[8]),
ParagonIE_Sodium_Core32_Int32::fromInt(self::$d2[9])
)
);
}
/** @var ParagonIE_Sodium_Core32_Curve25519_Fe $d2 */
$r = new ParagonIE_Sodium_Core32_Curve25519_Ge_Cached();
$r->YplusX = self::fe_add($p->Y, $p->X);
$r->YminusX = self::fe_sub($p->Y, $p->X);
$r->Z = self::fe_copy($p->Z);
$r->T2d = self::fe_mul($p->T, $d2);
return $r;
}
/**
* @internal You should not use this directly from another application
*
* @param ParagonIE_Sodium_Core32_Curve25519_Ge_P3 $p
* @return ParagonIE_Sodium_Core32_Curve25519_Ge_P2
*/
public static function ge_p3_to_p2(ParagonIE_Sodium_Core32_Curve25519_Ge_P3 $p)
{
return new ParagonIE_Sodium_Core32_Curve25519_Ge_P2(
$p->X,
$p->Y,
$p->Z
);
}
/**
* @internal You should not use this directly from another application
*
* @param ParagonIE_Sodium_Core32_Curve25519_Ge_P3 $h
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function ge_p3_tobytes(ParagonIE_Sodium_Core32_Curve25519_Ge_P3 $h)
{
$recip = self::fe_invert($h->Z);
$x = self::fe_mul($h->X, $recip);
$y = self::fe_mul($h->Y, $recip);
$s = self::fe_tobytes($y);
$s[31] = self::intToChr(
self::chrToInt($s[31]) ^ (self::fe_isnegative($x) << 7)
);
return $s;
}
/**
* @internal You should not use this directly from another application
*
* @param ParagonIE_Sodium_Core32_Curve25519_Ge_P3 $p
* @return ParagonIE_Sodium_Core32_Curve25519_Ge_P1p1
* @throws SodiumException
* @throws TypeError
*/
public static function ge_p3_dbl(ParagonIE_Sodium_Core32_Curve25519_Ge_P3 $p)
{
$q = self::ge_p3_to_p2($p);
return self::ge_p2_dbl($q);
}
/**
* @return ParagonIE_Sodium_Core32_Curve25519_Ge_Precomp
* @throws SodiumException
* @throws TypeError
*/
public static function ge_precomp_0()
{
return new ParagonIE_Sodium_Core32_Curve25519_Ge_Precomp(
self::fe_1(),
self::fe_1(),
self::fe_0()
);
}
/**
* @internal You should not use this directly from another application
*
* @param int $b
* @param int $c
* @return int
* @psalm-suppress MixedReturnStatement
*/
public static function equal($b, $c)
{
$b0 = $b & 0xffff;
$b1 = ($b >> 16) & 0xffff;
$c0 = $c & 0xffff;
$c1 = ($c >> 16) & 0xffff;
$d0 = (($b0 ^ $c0) - 1) >> 31;
$d1 = (($b1 ^ $c1) - 1) >> 31;
return ($d0 & $d1) & 1;
}
/**
* @internal You should not use this directly from another application
*
* @param string|int $char
* @return int (1 = yes, 0 = no)
* @throws SodiumException
* @throws TypeError
*/
public static function negative($char)
{
if (is_int($char)) {
return $char < 0 ? 1 : 0;
}
/** @var string $char */
$x = self::chrToInt(self::substr($char, 0, 1));
return (int) ($x >> 31);
}
/**
* Conditional move
*
* @internal You should not use this directly from another application
*
* @param ParagonIE_Sodium_Core32_Curve25519_Ge_Precomp $t
* @param ParagonIE_Sodium_Core32_Curve25519_Ge_Precomp $u
* @param int $b
* @return ParagonIE_Sodium_Core32_Curve25519_Ge_Precomp
* @throws SodiumException
* @throws TypeError
*/
public static function cmov(
ParagonIE_Sodium_Core32_Curve25519_Ge_Precomp $t,
ParagonIE_Sodium_Core32_Curve25519_Ge_Precomp $u,
$b
) {
if (!is_int($b)) {
throw new InvalidArgumentException('Expected an integer.');
}
return new ParagonIE_Sodium_Core32_Curve25519_Ge_Precomp(
self::fe_cmov($t->yplusx, $u->yplusx, $b),
self::fe_cmov($t->yminusx, $u->yminusx, $b),
self::fe_cmov($t->xy2d, $u->xy2d, $b)
);
}
/**
* @internal You should not use this directly from another application
*
* @param int $pos
* @param int $b
* @return ParagonIE_Sodium_Core32_Curve25519_Ge_Precomp
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArrayAccess
* @psalm-suppress MixedArrayOffset
* @psalm-suppress MixedArgument
*/
public static function ge_select($pos = 0, $b = 0)
{
static $base = null;
if ($base === null) {
$base = array();
foreach (self::$base as $i => $bas) {
for ($j = 0; $j < 8; ++$j) {
$base[$i][$j] = new ParagonIE_Sodium_Core32_Curve25519_Ge_Precomp(
ParagonIE_Sodium_Core32_Curve25519_Fe::fromArray(
array(
ParagonIE_Sodium_Core32_Int32::fromInt($bas[$j][0][0]),
ParagonIE_Sodium_Core32_Int32::fromInt($bas[$j][0][1]),
ParagonIE_Sodium_Core32_Int32::fromInt($bas[$j][0][2]),
ParagonIE_Sodium_Core32_Int32::fromInt($bas[$j][0][3]),
ParagonIE_Sodium_Core32_Int32::fromInt($bas[$j][0][4]),
ParagonIE_Sodium_Core32_Int32::fromInt($bas[$j][0][5]),
ParagonIE_Sodium_Core32_Int32::fromInt($bas[$j][0][6]),
ParagonIE_Sodium_Core32_Int32::fromInt($bas[$j][0][7]),
ParagonIE_Sodium_Core32_Int32::fromInt($bas[$j][0][8]),
ParagonIE_Sodium_Core32_Int32::fromInt($bas[$j][0][9])
)
),
ParagonIE_Sodium_Core32_Curve25519_Fe::fromArray(
array(
ParagonIE_Sodium_Core32_Int32::fromInt($bas[$j][1][0]),
ParagonIE_Sodium_Core32_Int32::fromInt($bas[$j][1][1]),
ParagonIE_Sodium_Core32_Int32::fromInt($bas[$j][1][2]),
ParagonIE_Sodium_Core32_Int32::fromInt($bas[$j][1][3]),
ParagonIE_Sodium_Core32_Int32::fromInt($bas[$j][1][4]),
ParagonIE_Sodium_Core32_Int32::fromInt($bas[$j][1][5]),
ParagonIE_Sodium_Core32_Int32::fromInt($bas[$j][1][6]),
ParagonIE_Sodium_Core32_Int32::fromInt($bas[$j][1][7]),
ParagonIE_Sodium_Core32_Int32::fromInt($bas[$j][1][8]),
ParagonIE_Sodium_Core32_Int32::fromInt($bas[$j][1][9])
)
),
ParagonIE_Sodium_Core32_Curve25519_Fe::fromArray(
array(
ParagonIE_Sodium_Core32_Int32::fromInt($bas[$j][2][0]),
ParagonIE_Sodium_Core32_Int32::fromInt($bas[$j][2][1]),
ParagonIE_Sodium_Core32_Int32::fromInt($bas[$j][2][2]),
ParagonIE_Sodium_Core32_Int32::fromInt($bas[$j][2][3]),
ParagonIE_Sodium_Core32_Int32::fromInt($bas[$j][2][4]),
ParagonIE_Sodium_Core32_Int32::fromInt($bas[$j][2][5]),
ParagonIE_Sodium_Core32_Int32::fromInt($bas[$j][2][6]),
ParagonIE_Sodium_Core32_Int32::fromInt($bas[$j][2][7]),
ParagonIE_Sodium_Core32_Int32::fromInt($bas[$j][2][8]),
ParagonIE_Sodium_Core32_Int32::fromInt($bas[$j][2][9])
)
)
);
}
}
}
if (!is_int($pos)) {
throw new InvalidArgumentException('Position must be an integer');
}
if ($pos < 0 || $pos > 31) {
throw new RangeException('Position is out of range [0, 31]');
}
$bnegative = self::negative($b);
$babs = $b - (((-$bnegative) & $b) << 1);
$t = self::ge_precomp_0();
for ($i = 0; $i < 8; ++$i) {
$t = self::cmov(
$t,
$base[$pos][$i],
-self::equal($babs, $i + 1)
);
}
$minusT = new ParagonIE_Sodium_Core32_Curve25519_Ge_Precomp(
self::fe_copy($t->yminusx),
self::fe_copy($t->yplusx),
self::fe_neg($t->xy2d)
);
return self::cmov($t, $minusT, -$bnegative);
}
/**
* Subtract two group elements.
*
* r = p - q
*
* @internal You should not use this directly from another application
*
* @param ParagonIE_Sodium_Core32_Curve25519_Ge_P3 $p
* @param ParagonIE_Sodium_Core32_Curve25519_Ge_Cached $q
* @return ParagonIE_Sodium_Core32_Curve25519_Ge_P1p1
* @throws SodiumException
* @throws TypeError
*/
public static function ge_sub(
ParagonIE_Sodium_Core32_Curve25519_Ge_P3 $p,
ParagonIE_Sodium_Core32_Curve25519_Ge_Cached $q
) {
$r = new ParagonIE_Sodium_Core32_Curve25519_Ge_P1p1();
$r->X = self::fe_add($p->Y, $p->X);
$r->Y = self::fe_sub($p->Y, $p->X);
$r->Z = self::fe_mul($r->X, $q->YminusX);
$r->Y = self::fe_mul($r->Y, $q->YplusX);
$r->T = self::fe_mul($q->T2d, $p->T);
$r->X = self::fe_mul($p->Z, $q->Z);
$t0 = self::fe_add($r->X, $r->X);
$r->X = self::fe_sub($r->Z, $r->Y);
$r->Y = self::fe_add($r->Z, $r->Y);
$r->Z = self::fe_sub($t0, $r->T);
$r->T = self::fe_add($t0, $r->T);
return $r;
}
/**
* Convert a group element to a byte string.
*
* @param ParagonIE_Sodium_Core32_Curve25519_Ge_P2 $h
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function ge_tobytes(ParagonIE_Sodium_Core32_Curve25519_Ge_P2 $h)
{
$recip = self::fe_invert($h->Z);
$x = self::fe_mul($h->X, $recip);
$y = self::fe_mul($h->Y, $recip);
$s = self::fe_tobytes($y);
$s[31] = self::intToChr(
self::chrToInt($s[31]) ^ (self::fe_isnegative($x) << 7)
);
return $s;
}
/**
* @internal You should not use this directly from another application
*
* @param string $a
* @param ParagonIE_Sodium_Core32_Curve25519_Ge_P3 $A
* @param string $b
* @return ParagonIE_Sodium_Core32_Curve25519_Ge_P2
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArrayAccess
*/
public static function ge_double_scalarmult_vartime(
$a,
ParagonIE_Sodium_Core32_Curve25519_Ge_P3 $A,
$b
) {
/** @var array<int, ParagonIE_Sodium_Core32_Curve25519_Ge_Cached> $Ai */
$Ai = array();
static $Bi = array();
/** @var array<int, ParagonIE_Sodium_Core32_Curve25519_Ge_Precomp> $Bi */
if (!$Bi) {
for ($i = 0; $i < 8; ++$i) {
$Bi[$i] = new ParagonIE_Sodium_Core32_Curve25519_Ge_Precomp(
ParagonIE_Sodium_Core32_Curve25519_Fe::fromArray(
array(
ParagonIE_Sodium_Core32_Int32::fromInt(self::$base2[$i][0][0]),
ParagonIE_Sodium_Core32_Int32::fromInt(self::$base2[$i][0][1]),
ParagonIE_Sodium_Core32_Int32::fromInt(self::$base2[$i][0][2]),
ParagonIE_Sodium_Core32_Int32::fromInt(self::$base2[$i][0][3]),
ParagonIE_Sodium_Core32_Int32::fromInt(self::$base2[$i][0][4]),
ParagonIE_Sodium_Core32_Int32::fromInt(self::$base2[$i][0][5]),
ParagonIE_Sodium_Core32_Int32::fromInt(self::$base2[$i][0][6]),
ParagonIE_Sodium_Core32_Int32::fromInt(self::$base2[$i][0][7]),
ParagonIE_Sodium_Core32_Int32::fromInt(self::$base2[$i][0][8]),
ParagonIE_Sodium_Core32_Int32::fromInt(self::$base2[$i][0][9])
)
),
ParagonIE_Sodium_Core32_Curve25519_Fe::fromArray(
array(
ParagonIE_Sodium_Core32_Int32::fromInt(self::$base2[$i][1][0]),
ParagonIE_Sodium_Core32_Int32::fromInt(self::$base2[$i][1][1]),
ParagonIE_Sodium_Core32_Int32::fromInt(self::$base2[$i][1][2]),
ParagonIE_Sodium_Core32_Int32::fromInt(self::$base2[$i][1][3]),
ParagonIE_Sodium_Core32_Int32::fromInt(self::$base2[$i][1][4]),
ParagonIE_Sodium_Core32_Int32::fromInt(self::$base2[$i][1][5]),
ParagonIE_Sodium_Core32_Int32::fromInt(self::$base2[$i][1][6]),
ParagonIE_Sodium_Core32_Int32::fromInt(self::$base2[$i][1][7]),
ParagonIE_Sodium_Core32_Int32::fromInt(self::$base2[$i][1][8]),
ParagonIE_Sodium_Core32_Int32::fromInt(self::$base2[$i][1][9])
)
),
ParagonIE_Sodium_Core32_Curve25519_Fe::fromArray(
array(
ParagonIE_Sodium_Core32_Int32::fromInt(self::$base2[$i][2][0]),
ParagonIE_Sodium_Core32_Int32::fromInt(self::$base2[$i][2][1]),
ParagonIE_Sodium_Core32_Int32::fromInt(self::$base2[$i][2][2]),
ParagonIE_Sodium_Core32_Int32::fromInt(self::$base2[$i][2][3]),
ParagonIE_Sodium_Core32_Int32::fromInt(self::$base2[$i][2][4]),
ParagonIE_Sodium_Core32_Int32::fromInt(self::$base2[$i][2][5]),
ParagonIE_Sodium_Core32_Int32::fromInt(self::$base2[$i][2][6]),
ParagonIE_Sodium_Core32_Int32::fromInt(self::$base2[$i][2][7]),
ParagonIE_Sodium_Core32_Int32::fromInt(self::$base2[$i][2][8]),
ParagonIE_Sodium_Core32_Int32::fromInt(self::$base2[$i][2][9])
)
)
);
}
}
for ($i = 0; $i < 8; ++$i) {
$Ai[$i] = new ParagonIE_Sodium_Core32_Curve25519_Ge_Cached(
self::fe_0(),
self::fe_0(),
self::fe_0(),
self::fe_0()
);
}
/** @var array<int, ParagonIE_Sodium_Core32_Curve25519_Ge_Cached> $Ai */
# slide(aslide,a);
# slide(bslide,b);
/** @var array<int, int> $aslide */
$aslide = self::slide($a);
/** @var array<int, int> $bslide */
$bslide = self::slide($b);
# ge_p3_to_cached(&Ai[0],A);
# ge_p3_dbl(&t,A); ge_p1p1_to_p3(&A2,&t);
$Ai[0] = self::ge_p3_to_cached($A);
$t = self::ge_p3_dbl($A);
$A2 = self::ge_p1p1_to_p3($t);
# ge_add(&t,&A2,&Ai[0]); ge_p1p1_to_p3(&u,&t); ge_p3_to_cached(&Ai[1],&u);
# ge_add(&t,&A2,&Ai[1]); ge_p1p1_to_p3(&u,&t); ge_p3_to_cached(&Ai[2],&u);
# ge_add(&t,&A2,&Ai[2]); ge_p1p1_to_p3(&u,&t); ge_p3_to_cached(&Ai[3],&u);
# ge_add(&t,&A2,&Ai[3]); ge_p1p1_to_p3(&u,&t); ge_p3_to_cached(&Ai[4],&u);
# ge_add(&t,&A2,&Ai[4]); ge_p1p1_to_p3(&u,&t); ge_p3_to_cached(&Ai[5],&u);
# ge_add(&t,&A2,&Ai[5]); ge_p1p1_to_p3(&u,&t); ge_p3_to_cached(&Ai[6],&u);
# ge_add(&t,&A2,&Ai[6]); ge_p1p1_to_p3(&u,&t); ge_p3_to_cached(&Ai[7],&u);
for ($i = 0; $i < 7; ++$i) {
$t = self::ge_add($A2, $Ai[$i]);
$u = self::ge_p1p1_to_p3($t);
$Ai[$i + 1] = self::ge_p3_to_cached($u);
}
# ge_p2_0(r);
$r = self::ge_p2_0();
# for (i = 255;i >= 0;--i) {
# if (aslide[i] || bslide[i]) break;
# }
$i = 255;
for (; $i >= 0; --$i) {
if ($aslide[$i] || $bslide[$i]) {
break;
}
}
# for (;i >= 0;--i) {
for (; $i >= 0; --$i) {
# ge_p2_dbl(&t,r);
$t = self::ge_p2_dbl($r);
# if (aslide[i] > 0) {
if ($aslide[$i] > 0) {
# ge_p1p1_to_p3(&u,&t);
# ge_add(&t,&u,&Ai[aslide[i]/2]);
$u = self::ge_p1p1_to_p3($t);
$t = self::ge_add(
$u,
$Ai[(int) floor($aslide[$i] / 2)]
);
# } else if (aslide[i] < 0) {
} elseif ($aslide[$i] < 0) {
# ge_p1p1_to_p3(&u,&t);
# ge_sub(&t,&u,&Ai[(-aslide[i])/2]);
$u = self::ge_p1p1_to_p3($t);
$t = self::ge_sub(
$u,
$Ai[(int) floor(-$aslide[$i] / 2)]
);
}
/** @var array<int, ParagonIE_Sodium_Core32_Curve25519_Ge_Precomp> $Bi */
# if (bslide[i] > 0) {
if ($bslide[$i] > 0) {
# ge_p1p1_to_p3(&u,&t);
# ge_madd(&t,&u,&Bi[bslide[i]/2]);
$u = self::ge_p1p1_to_p3($t);
/** @var int $index */
$index = (int) floor($bslide[$i] / 2);
/** @var ParagonIE_Sodium_Core32_Curve25519_Ge_Precomp $thisB */
$thisB = $Bi[$index];
$t = self::ge_madd($t, $u, $thisB);
# } else if (bslide[i] < 0) {
} elseif ($bslide[$i] < 0) {
# ge_p1p1_to_p3(&u,&t);
# ge_msub(&t,&u,&Bi[(-bslide[i])/2]);
$u = self::ge_p1p1_to_p3($t);
/** @var int $index */
$index = (int) floor(-$bslide[$i] / 2);
/** @var ParagonIE_Sodium_Core32_Curve25519_Ge_Precomp $thisB */
$thisB = $Bi[$index];
$t = self::ge_msub($t, $u, $thisB);
}
# ge_p1p1_to_p2(r,&t);
$r = self::ge_p1p1_to_p2($t);
}
return $r;
}
/**
* @internal You should not use this directly from another application
*
* @param string $a
* @return ParagonIE_Sodium_Core32_Curve25519_Ge_P3
* @psalm-suppress MixedAssignment
* @psalm-suppress MixedOperand
* @throws SodiumException
* @throws TypeError
*/
public static function ge_scalarmult_base($a)
{
/** @var array<int, int> $e */
$e = array();
$r = new ParagonIE_Sodium_Core32_Curve25519_Ge_P1p1();
for ($i = 0; $i < 32; ++$i) {
/** @var int $dbl */
$dbl = (int) $i << 1;
$e[$dbl] = (int) self::chrToInt($a[$i]) & 15;
$e[$dbl + 1] = (int) (self::chrToInt($a[$i]) >> 4) & 15;
}
/** @var int $carry */
$carry = 0;
for ($i = 0; $i < 63; ++$i) {
$e[$i] += $carry;
$carry = $e[$i] + 8;
$carry >>= 4;
$e[$i] -= $carry << 4;
}
/** @var array<int, int> $e */
$e[63] += (int) $carry;
$h = self::ge_p3_0();
for ($i = 1; $i < 64; $i += 2) {
$t = self::ge_select((int) floor($i / 2), (int) $e[$i]);
$r = self::ge_madd($r, $h, $t);
$h = self::ge_p1p1_to_p3($r);
}
$r = self::ge_p3_dbl($h);
$s = self::ge_p1p1_to_p2($r);
$r = self::ge_p2_dbl($s);
$s = self::ge_p1p1_to_p2($r);
$r = self::ge_p2_dbl($s);
$s = self::ge_p1p1_to_p2($r);
$r = self::ge_p2_dbl($s);
$h = self::ge_p1p1_to_p3($r);
for ($i = 0; $i < 64; $i += 2) {
$t = self::ge_select($i >> 1, (int) $e[$i]);
$r = self::ge_madd($r, $h, $t);
$h = self::ge_p1p1_to_p3($r);
}
return $h;
}
/**
* Calculates (ab + c) mod l
* where l = 2^252 + 27742317777372353535851937790883648493
*
* @internal You should not use this directly from another application
*
* @param string $a
* @param string $b
* @param string $c
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function sc_muladd($a, $b, $c)
{
$a0 = ParagonIE_Sodium_Core32_Int64::fromInt(2097151 & self::load_3(self::substr($a, 0, 3)));
$a1 = ParagonIE_Sodium_Core32_Int64::fromInt(2097151 & (self::load_4(self::substr($a, 2, 4)) >> 5));
$a2 = ParagonIE_Sodium_Core32_Int64::fromInt(2097151 & (self::load_3(self::substr($a, 5, 3)) >> 2));
$a3 = ParagonIE_Sodium_Core32_Int64::fromInt(2097151 & (self::load_4(self::substr($a, 7, 4)) >> 7));
$a4 = ParagonIE_Sodium_Core32_Int64::fromInt(2097151 & (self::load_4(self::substr($a, 10, 4)) >> 4));
$a5 = ParagonIE_Sodium_Core32_Int64::fromInt(2097151 & (self::load_3(self::substr($a, 13, 3)) >> 1));
$a6 = ParagonIE_Sodium_Core32_Int64::fromInt(2097151 & (self::load_4(self::substr($a, 15, 4)) >> 6));
$a7 = ParagonIE_Sodium_Core32_Int64::fromInt(2097151 & (self::load_3(self::substr($a, 18, 3)) >> 3));
$a8 = ParagonIE_Sodium_Core32_Int64::fromInt(2097151 & self::load_3(self::substr($a, 21, 3)));
$a9 = ParagonIE_Sodium_Core32_Int64::fromInt(2097151 & (self::load_4(self::substr($a, 23, 4)) >> 5));
$a10 = ParagonIE_Sodium_Core32_Int64::fromInt(2097151 & (self::load_3(self::substr($a, 26, 3)) >> 2));
$a11 = ParagonIE_Sodium_Core32_Int64::fromInt(0x1fffffff & (self::load_4(self::substr($a, 28, 4)) >> 7));
$b0 = ParagonIE_Sodium_Core32_Int64::fromInt(2097151 & self::load_3(self::substr($b, 0, 3)));
$b1 = ParagonIE_Sodium_Core32_Int64::fromInt(2097151 & (self::load_4(self::substr($b, 2, 4)) >> 5));
$b2 = ParagonIE_Sodium_Core32_Int64::fromInt(2097151 & (self::load_3(self::substr($b, 5, 3)) >> 2));
$b3 = ParagonIE_Sodium_Core32_Int64::fromInt(2097151 & (self::load_4(self::substr($b, 7, 4)) >> 7));
$b4 = ParagonIE_Sodium_Core32_Int64::fromInt(2097151 & (self::load_4(self::substr($b, 10, 4)) >> 4));
$b5 = ParagonIE_Sodium_Core32_Int64::fromInt(2097151 & (self::load_3(self::substr($b, 13, 3)) >> 1));
$b6 = ParagonIE_Sodium_Core32_Int64::fromInt(2097151 & (self::load_4(self::substr($b, 15, 4)) >> 6));
$b7 = ParagonIE_Sodium_Core32_Int64::fromInt(2097151 & (self::load_3(self::substr($b, 18, 3)) >> 3));
$b8 = ParagonIE_Sodium_Core32_Int64::fromInt(2097151 & self::load_3(self::substr($b, 21, 3)));
$b9 = ParagonIE_Sodium_Core32_Int64::fromInt(2097151 & (self::load_4(self::substr($b, 23, 4)) >> 5));
$b10 = ParagonIE_Sodium_Core32_Int64::fromInt(2097151 & (self::load_3(self::substr($b, 26, 3)) >> 2));
$b11 = ParagonIE_Sodium_Core32_Int64::fromInt(0x1fffffff & (self::load_4(self::substr($b, 28, 4)) >> 7));
$c0 = ParagonIE_Sodium_Core32_Int64::fromInt(2097151 & self::load_3(self::substr($c, 0, 3)));
$c1 = ParagonIE_Sodium_Core32_Int64::fromInt(2097151 & (self::load_4(self::substr($c, 2, 4)) >> 5));
$c2 = ParagonIE_Sodium_Core32_Int64::fromInt(2097151 & (self::load_3(self::substr($c, 5, 3)) >> 2));
$c3 = ParagonIE_Sodium_Core32_Int64::fromInt(2097151 & (self::load_4(self::substr($c, 7, 4)) >> 7));
$c4 = ParagonIE_Sodium_Core32_Int64::fromInt(2097151 & (self::load_4(self::substr($c, 10, 4)) >> 4));
$c5 = ParagonIE_Sodium_Core32_Int64::fromInt(2097151 & (self::load_3(self::substr($c, 13, 3)) >> 1));
$c6 = ParagonIE_Sodium_Core32_Int64::fromInt(2097151 & (self::load_4(self::substr($c, 15, 4)) >> 6));
$c7 = ParagonIE_Sodium_Core32_Int64::fromInt(2097151 & (self::load_3(self::substr($c, 18, 3)) >> 3));
$c8 = ParagonIE_Sodium_Core32_Int64::fromInt(2097151 & self::load_3(self::substr($c, 21, 3)));
$c9 = ParagonIE_Sodium_Core32_Int64::fromInt(2097151 & (self::load_4(self::substr($c, 23, 4)) >> 5));
$c10 = ParagonIE_Sodium_Core32_Int64::fromInt(2097151 & (self::load_3(self::substr($c, 26, 3)) >> 2));
$c11 = ParagonIE_Sodium_Core32_Int64::fromInt(0x1fffffff & (self::load_4(self::substr($c, 28, 4)) >> 7));
/* Can't really avoid the pyramid here: */
/**
* @var ParagonIE_Sodium_Core32_Int64 $s0
* @var ParagonIE_Sodium_Core32_Int64 $s1
* @var ParagonIE_Sodium_Core32_Int64 $s2
* @var ParagonIE_Sodium_Core32_Int64 $s3
* @var ParagonIE_Sodium_Core32_Int64 $s4
* @var ParagonIE_Sodium_Core32_Int64 $s5
* @var ParagonIE_Sodium_Core32_Int64 $s6
* @var ParagonIE_Sodium_Core32_Int64 $s7
* @var ParagonIE_Sodium_Core32_Int64 $s8
* @var ParagonIE_Sodium_Core32_Int64 $s9
* @var ParagonIE_Sodium_Core32_Int64 $s10
* @var ParagonIE_Sodium_Core32_Int64 $s11
* @var ParagonIE_Sodium_Core32_Int64 $s12
* @var ParagonIE_Sodium_Core32_Int64 $s13
* @var ParagonIE_Sodium_Core32_Int64 $s14
* @var ParagonIE_Sodium_Core32_Int64 $s15
* @var ParagonIE_Sodium_Core32_Int64 $s16
* @var ParagonIE_Sodium_Core32_Int64 $s17
* @var ParagonIE_Sodium_Core32_Int64 $s18
* @var ParagonIE_Sodium_Core32_Int64 $s19
* @var ParagonIE_Sodium_Core32_Int64 $s20
* @var ParagonIE_Sodium_Core32_Int64 $s21
* @var ParagonIE_Sodium_Core32_Int64 $s22
* @var ParagonIE_Sodium_Core32_Int64 $s23
*/
$s0 = $c0->addInt64($a0->mulInt64($b0, 24));
$s1 = $c1->addInt64($a0->mulInt64($b1, 24))->addInt64($a1->mulInt64($b0, 24));
$s2 = $c2->addInt64($a0->mulInt64($b2, 24))->addInt64($a1->mulInt64($b1, 24))->addInt64($a2->mulInt64($b0, 24));
$s3 = $c3->addInt64($a0->mulInt64($b3, 24))->addInt64($a1->mulInt64($b2, 24))->addInt64($a2->mulInt64($b1, 24))
->addInt64($a3->mulInt64($b0, 24));
$s4 = $c4->addInt64($a0->mulInt64($b4, 24))->addInt64($a1->mulInt64($b3, 24))->addInt64($a2->mulInt64($b2, 24))
->addInt64($a3->mulInt64($b1, 24))->addInt64($a4->mulInt64($b0, 24));
$s5 = $c5->addInt64($a0->mulInt64($b5, 24))->addInt64($a1->mulInt64($b4, 24))->addInt64($a2->mulInt64($b3, 24))
->addInt64($a3->mulInt64($b2, 24))->addInt64($a4->mulInt64($b1, 24))->addInt64($a5->mulInt64($b0, 24));
$s6 = $c6->addInt64($a0->mulInt64($b6, 24))->addInt64($a1->mulInt64($b5, 24))->addInt64($a2->mulInt64($b4, 24))
->addInt64($a3->mulInt64($b3, 24))->addInt64($a4->mulInt64($b2, 24))->addInt64($a5->mulInt64($b1, 24))
->addInt64($a6->mulInt64($b0, 24));
$s7 = $c7->addInt64($a0->mulInt64($b7, 24))->addInt64($a1->mulInt64($b6, 24))->addInt64($a2->mulInt64($b5, 24))
->addInt64($a3->mulInt64($b4, 24))->addInt64($a4->mulInt64($b3, 24))->addInt64($a5->mulInt64($b2, 24))
->addInt64($a6->mulInt64($b1, 24))->addInt64($a7->mulInt64($b0, 24));
$s8 = $c8->addInt64($a0->mulInt64($b8, 24))->addInt64($a1->mulInt64($b7, 24))->addInt64($a2->mulInt64($b6, 24))
->addInt64($a3->mulInt64($b5, 24))->addInt64($a4->mulInt64($b4, 24))->addInt64($a5->mulInt64($b3, 24))
->addInt64($a6->mulInt64($b2, 24))->addInt64($a7->mulInt64($b1, 24))->addInt64($a8->mulInt64($b0, 24));
$s9 = $c9->addInt64($a0->mulInt64($b9, 24))->addInt64($a1->mulInt64($b8, 24))->addInt64($a2->mulInt64($b7, 24))
->addInt64($a3->mulInt64($b6, 24))->addInt64($a4->mulInt64($b5, 24))->addInt64($a5->mulInt64($b4, 24))
->addInt64($a6->mulInt64($b3, 24))->addInt64($a7->mulInt64($b2, 24))->addInt64($a8->mulInt64($b1, 24))
->addInt64($a9->mulInt64($b0, 24));
$s10 = $c10->addInt64($a0->mulInt64($b10, 24))->addInt64($a1->mulInt64($b9, 24))->addInt64($a2->mulInt64($b8, 24))
->addInt64($a3->mulInt64($b7, 24))->addInt64($a4->mulInt64($b6, 24))->addInt64($a5->mulInt64($b5, 24))
->addInt64($a6->mulInt64($b4, 24))->addInt64($a7->mulInt64($b3, 24))->addInt64($a8->mulInt64($b2, 24))
->addInt64($a9->mulInt64($b1, 24))->addInt64($a10->mulInt64($b0, 24));
$s11 = $c11->addInt64($a0->mulInt64($b11, 24))->addInt64($a1->mulInt64($b10, 24))->addInt64($a2->mulInt64($b9, 24))
->addInt64($a3->mulInt64($b8, 24))->addInt64($a4->mulInt64($b7, 24))->addInt64($a5->mulInt64($b6, 24))
->addInt64($a6->mulInt64($b5, 24))->addInt64($a7->mulInt64($b4, 24))->addInt64($a8->mulInt64($b3, 24))
->addInt64($a9->mulInt64($b2, 24))->addInt64($a10->mulInt64($b1, 24))->addInt64($a11->mulInt64($b0, 24));
$s12 = $a1->mulInt64($b11, 24)->addInt64($a2->mulInt64($b10, 24))->addInt64($a3->mulInt64($b9, 24))
->addInt64($a4->mulInt64($b8, 24))->addInt64($a5->mulInt64($b7, 24))->addInt64($a6->mulInt64($b6, 24))
->addInt64($a7->mulInt64($b5, 24))->addInt64($a8->mulInt64($b4, 24))->addInt64($a9->mulInt64($b3, 24))
->addInt64($a10->mulInt64($b2, 24))->addInt64($a11->mulInt64($b1, 24));
$s13 = $a2->mulInt64($b11, 24)->addInt64($a3->mulInt64($b10, 24))->addInt64($a4->mulInt64($b9, 24))
->addInt64($a5->mulInt64($b8, 24))->addInt64($a6->mulInt64($b7, 24))->addInt64($a7->mulInt64($b6, 24))
->addInt64($a8->mulInt64($b5, 24))->addInt64($a9->mulInt64($b4, 24))->addInt64($a10->mulInt64($b3, 24))
->addInt64($a11->mulInt64($b2, 24));
$s14 = $a3->mulInt64($b11, 24)->addInt64($a4->mulInt64($b10, 24))->addInt64($a5->mulInt64($b9, 24))
->addInt64($a6->mulInt64($b8, 24))->addInt64($a7->mulInt64($b7, 24))->addInt64($a8->mulInt64($b6, 24))
->addInt64($a9->mulInt64($b5, 24))->addInt64($a10->mulInt64($b4, 24))->addInt64($a11->mulInt64($b3, 24));
$s15 = $a4->mulInt64($b11, 24)->addInt64($a5->mulInt64($b10, 24))->addInt64($a6->mulInt64($b9, 24))
->addInt64($a7->mulInt64($b8, 24))->addInt64($a8->mulInt64($b7, 24))->addInt64($a9->mulInt64($b6, 24))
->addInt64($a10->mulInt64($b5, 24))->addInt64($a11->mulInt64($b4, 24));
$s16 = $a5->mulInt64($b11, 24)->addInt64($a6->mulInt64($b10, 24))->addInt64($a7->mulInt64($b9, 24))
->addInt64($a8->mulInt64($b8, 24))->addInt64($a9->mulInt64($b7, 24))->addInt64($a10->mulInt64($b6, 24))
->addInt64($a11->mulInt64($b5, 24));
$s17 = $a6->mulInt64($b11, 24)->addInt64($a7->mulInt64($b10, 24))->addInt64($a8->mulInt64($b9, 24))
->addInt64($a9->mulInt64($b8, 24))->addInt64($a10->mulInt64($b7, 24))->addInt64($a11->mulInt64($b6, 24));
$s18 = $a7->mulInt64($b11, 24)->addInt64($a8->mulInt64($b10, 24))->addInt64($a9->mulInt64($b9, 24))
->addInt64($a10->mulInt64($b8, 24))->addInt64($a11->mulInt64($b7, 24));
$s19 = $a8->mulInt64($b11, 24)->addInt64($a9->mulInt64($b10, 24))->addInt64($a10->mulInt64($b9, 24))
->addInt64($a11->mulInt64($b8, 24));
$s20 = $a9->mulInt64($b11, 24)->addInt64($a10->mulInt64($b10, 24))->addInt64($a11->mulInt64($b9, 24));
$s21 = $a10->mulInt64($b11, 24)->addInt64($a11->mulInt64($b10, 24));
$s22 = $a11->mulInt64($b11, 24);
$s23 = new ParagonIE_Sodium_Core32_Int64();
$carry0 = $s0->addInt(1 << 20)->shiftRight(21);
$s1 = $s1->addInt64($carry0);
$s0 = $s0->subInt64($carry0->shiftLeft(21));
$carry2 = $s2->addInt(1 << 20)->shiftRight(21);
$s3 = $s3->addInt64($carry2);
$s2 = $s2->subInt64($carry2->shiftLeft(21));
$carry4 = $s4->addInt(1 << 20)->shiftRight(21);
$s5 = $s5->addInt64($carry4);
$s4 = $s4->subInt64($carry4->shiftLeft(21));
$carry6 = $s6->addInt(1 << 20)->shiftRight(21);
$s7 = $s7->addInt64($carry6);
$s6 = $s6->subInt64($carry6->shiftLeft(21));
$carry8 = $s8->addInt(1 << 20)->shiftRight(21);
$s9 = $s9->addInt64($carry8);
$s8 = $s8->subInt64($carry8->shiftLeft(21));
$carry10 = $s10->addInt(1 << 20)->shiftRight(21);
$s11 = $s11->addInt64($carry10);
$s10 = $s10->subInt64($carry10->shiftLeft(21));
$carry12 = $s12->addInt(1 << 20)->shiftRight(21);
$s13 = $s13->addInt64($carry12);
$s12 = $s12->subInt64($carry12->shiftLeft(21));
$carry14 = $s14->addInt(1 << 20)->shiftRight(21);
$s15 = $s15->addInt64($carry14);
$s14 = $s14->subInt64($carry14->shiftLeft(21));
$carry16 = $s16->addInt(1 << 20)->shiftRight(21);
$s17 = $s17->addInt64($carry16);
$s16 = $s16->subInt64($carry16->shiftLeft(21));
$carry18 = $s18->addInt(1 << 20)->shiftRight(21);
$s19 = $s19->addInt64($carry18);
$s18 = $s18->subInt64($carry18->shiftLeft(21));
$carry20 = $s20->addInt(1 << 20)->shiftRight(21);
$s21 = $s21->addInt64($carry20);
$s20 = $s20->subInt64($carry20->shiftLeft(21));
$carry22 = $s22->addInt(1 << 20)->shiftRight(21);
$s23 = $s23->addInt64($carry22);
$s22 = $s22->subInt64($carry22->shiftLeft(21));
$carry1 = $s1->addInt(1 << 20)->shiftRight(21);
$s2 = $s2->addInt64($carry1);
$s1 = $s1->subInt64($carry1->shiftLeft(21));
$carry3 = $s3->addInt(1 << 20)->shiftRight(21);
$s4 = $s4->addInt64($carry3);
$s3 = $s3->subInt64($carry3->shiftLeft(21));
$carry5 = $s5->addInt(1 << 20)->shiftRight(21);
$s6 = $s6->addInt64($carry5);
$s5 = $s5->subInt64($carry5->shiftLeft(21));
$carry7 = $s7->addInt(1 << 20)->shiftRight(21);
$s8 = $s8->addInt64($carry7);
$s7 = $s7->subInt64($carry7->shiftLeft(21));
$carry9 = $s9->addInt(1 << 20)->shiftRight(21);
$s10 = $s10->addInt64($carry9);
$s9 = $s9->subInt64($carry9->shiftLeft(21));
$carry11 = $s11->addInt(1 << 20)->shiftRight(21);
$s12 = $s12->addInt64($carry11);
$s11 = $s11->subInt64($carry11->shiftLeft(21));
$carry13 = $s13->addInt(1 << 20)->shiftRight(21);
$s14 = $s14->addInt64($carry13);
$s13 = $s13->subInt64($carry13->shiftLeft(21));
$carry15 = $s15->addInt(1 << 20)->shiftRight(21);
$s16 = $s16->addInt64($carry15);
$s15 = $s15->subInt64($carry15->shiftLeft(21));
$carry17 = $s17->addInt(1 << 20)->shiftRight(21);
$s18 = $s18->addInt64($carry17);
$s17 = $s17->subInt64($carry17->shiftLeft(21));
$carry19 = $s19->addInt(1 << 20)->shiftRight(21);
$s20 = $s20->addInt64($carry19);
$s19 = $s19->subInt64($carry19->shiftLeft(21));
$carry21 = $s21->addInt(1 << 20)->shiftRight(21);
$s22 = $s22->addInt64($carry21);
$s21 = $s21->subInt64($carry21->shiftLeft(21));
$s11 = $s11->addInt64($s23->mulInt(666643, 20));
$s12 = $s12->addInt64($s23->mulInt(470296, 19));
$s13 = $s13->addInt64($s23->mulInt(654183, 20));
$s14 = $s14->subInt64($s23->mulInt(997805, 20));
$s15 = $s15->addInt64($s23->mulInt(136657, 18));
$s16 = $s16->subInt64($s23->mulInt(683901, 20));
$s10 = $s10->addInt64($s22->mulInt(666643, 20));
$s11 = $s11->addInt64($s22->mulInt(470296, 19));
$s12 = $s12->addInt64($s22->mulInt(654183, 20));
$s13 = $s13->subInt64($s22->mulInt(997805, 20));
$s14 = $s14->addInt64($s22->mulInt(136657, 18));
$s15 = $s15->subInt64($s22->mulInt(683901, 20));
$s9 = $s9->addInt64($s21->mulInt(666643, 20));
$s10 = $s10->addInt64($s21->mulInt(470296, 19));
$s11 = $s11->addInt64($s21->mulInt(654183, 20));
$s12 = $s12->subInt64($s21->mulInt(997805, 20));
$s13 = $s13->addInt64($s21->mulInt(136657, 18));
$s14 = $s14->subInt64($s21->mulInt(683901, 20));
$s8 = $s8->addInt64($s20->mulInt(666643, 20));
$s9 = $s9->addInt64($s20->mulInt(470296, 19));
$s10 = $s10->addInt64($s20->mulInt(654183, 20));
$s11 = $s11->subInt64($s20->mulInt(997805, 20));
$s12 = $s12->addInt64($s20->mulInt(136657, 18));
$s13 = $s13->subInt64($s20->mulInt(683901, 20));
$s7 = $s7->addInt64($s19->mulInt(666643, 20));
$s8 = $s8->addInt64($s19->mulInt(470296, 19));
$s9 = $s9->addInt64($s19->mulInt(654183, 20));
$s10 = $s10->subInt64($s19->mulInt(997805, 20));
$s11 = $s11->addInt64($s19->mulInt(136657, 18));
$s12 = $s12->subInt64($s19->mulInt(683901, 20));
$s6 = $s6->addInt64($s18->mulInt(666643, 20));
$s7 = $s7->addInt64($s18->mulInt(470296, 19));
$s8 = $s8->addInt64($s18->mulInt(654183, 20));
$s9 = $s9->subInt64($s18->mulInt(997805, 20));
$s10 = $s10->addInt64($s18->mulInt(136657, 18));
$s11 = $s11->subInt64($s18->mulInt(683901, 20));
$carry6 = $s6->addInt(1 << 20)->shiftRight(21);
$s7 = $s7->addInt64($carry6);
$s6 = $s6->subInt64($carry6->shiftLeft(21));
$carry8 = $s8->addInt(1 << 20)->shiftRight(21);
$s9 = $s9->addInt64($carry8);
$s8 = $s8->subInt64($carry8->shiftLeft(21));
$carry10 = $s10->addInt(1 << 20)->shiftRight(21);
$s11 = $s11->addInt64($carry10);
$s10 = $s10->subInt64($carry10->shiftLeft(21));
$carry12 = $s12->addInt(1 << 20)->shiftRight(21);
$s13 = $s13->addInt64($carry12);
$s12 = $s12->subInt64($carry12->shiftLeft(21));
$carry14 = $s14->addInt(1 << 20)->shiftRight(21);
$s15 = $s15->addInt64($carry14);
$s14 = $s14->subInt64($carry14->shiftLeft(21));
$carry16 = $s16->addInt(1 << 20)->shiftRight(21);
$s17 = $s17->addInt64($carry16);
$s16 = $s16->subInt64($carry16->shiftLeft(21));
$carry7 = $s7->addInt(1 << 20)->shiftRight(21);
$s8 = $s8->addInt64($carry7);
$s7 = $s7->subInt64($carry7->shiftLeft(21));
$carry9 = $s9->addInt(1 << 20)->shiftRight(21);
$s10 = $s10->addInt64($carry9);
$s9 = $s9->subInt64($carry9->shiftLeft(21));
$carry11 = $s11->addInt(1 << 20)->shiftRight(21);
$s12 = $s12->addInt64($carry11);
$s11 = $s11->subInt64($carry11->shiftLeft(21));
$carry13 = $s13->addInt(1 << 20)->shiftRight(21);
$s14 = $s14->addInt64($carry13);
$s13 = $s13->subInt64($carry13->shiftLeft(21));
$carry15 = $s15->addInt(1 << 20)->shiftRight(21);
$s16 = $s16->addInt64($carry15);
$s15 = $s15->subInt64($carry15->shiftLeft(21));
$s5 = $s5->addInt64($s17->mulInt(666643, 20));
$s6 = $s6->addInt64($s17->mulInt(470296, 19));
$s7 = $s7->addInt64($s17->mulInt(654183, 20));
$s8 = $s8->subInt64($s17->mulInt(997805, 20));
$s9 = $s9->addInt64($s17->mulInt(136657, 18));
$s10 = $s10->subInt64($s17->mulInt(683901, 20));
$s4 = $s4->addInt64($s16->mulInt(666643, 20));
$s5 = $s5->addInt64($s16->mulInt(470296, 19));
$s6 = $s6->addInt64($s16->mulInt(654183, 20));
$s7 = $s7->subInt64($s16->mulInt(997805, 20));
$s8 = $s8->addInt64($s16->mulInt(136657, 18));
$s9 = $s9->subInt64($s16->mulInt(683901, 20));
$s3 = $s3->addInt64($s15->mulInt(666643, 20));
$s4 = $s4->addInt64($s15->mulInt(470296, 19));
$s5 = $s5->addInt64($s15->mulInt(654183, 20));
$s6 = $s6->subInt64($s15->mulInt(997805, 20));
$s7 = $s7->addInt64($s15->mulInt(136657, 18));
$s8 = $s8->subInt64($s15->mulInt(683901, 20));
$s2 = $s2->addInt64($s14->mulInt(666643, 20));
$s3 = $s3->addInt64($s14->mulInt(470296, 19));
$s4 = $s4->addInt64($s14->mulInt(654183, 20));
$s5 = $s5->subInt64($s14->mulInt(997805, 20));
$s6 = $s6->addInt64($s14->mulInt(136657, 18));
$s7 = $s7->subInt64($s14->mulInt(683901, 20));
$s1 = $s1->addInt64($s13->mulInt(666643, 20));
$s2 = $s2->addInt64($s13->mulInt(470296, 19));
$s3 = $s3->addInt64($s13->mulInt(654183, 20));
$s4 = $s4->subInt64($s13->mulInt(997805, 20));
$s5 = $s5->addInt64($s13->mulInt(136657, 18));
$s6 = $s6->subInt64($s13->mulInt(683901, 20));
$s0 = $s0->addInt64($s12->mulInt(666643, 20));
$s1 = $s1->addInt64($s12->mulInt(470296, 19));
$s2 = $s2->addInt64($s12->mulInt(654183, 20));
$s3 = $s3->subInt64($s12->mulInt(997805, 20));
$s4 = $s4->addInt64($s12->mulInt(136657, 18));
$s5 = $s5->subInt64($s12->mulInt(683901, 20));
$s12 = new ParagonIE_Sodium_Core32_Int64();
$carry0 = $s0->addInt(1 << 20)->shiftRight(21);
$s1 = $s1->addInt64($carry0);
$s0 = $s0->subInt64($carry0->shiftLeft(21));
$carry2 = $s2->addInt(1 << 20)->shiftRight(21);
$s3 = $s3->addInt64($carry2);
$s2 = $s2->subInt64($carry2->shiftLeft(21));
$carry4 = $s4->addInt(1 << 20)->shiftRight(21);
$s5 = $s5->addInt64($carry4);
$s4 = $s4->subInt64($carry4->shiftLeft(21));
$carry6 = $s6->addInt(1 << 20)->shiftRight(21);
$s7 = $s7->addInt64($carry6);
$s6 = $s6->subInt64($carry6->shiftLeft(21));
$carry8 = $s8->addInt(1 << 20)->shiftRight(21);
$s9 = $s9->addInt64($carry8);
$s8 = $s8->subInt64($carry8->shiftLeft(21));
$carry10 = $s10->addInt(1 << 20)->shiftRight(21);
$s11 = $s11->addInt64($carry10);
$s10 = $s10->subInt64($carry10->shiftLeft(21));
$carry1 = $s1->addInt(1 << 20)->shiftRight(21);
$s2 = $s2->addInt64($carry1);
$s1 = $s1->subInt64($carry1->shiftLeft(21));
$carry3 = $s3->addInt(1 << 20)->shiftRight(21);
$s4 = $s4->addInt64($carry3);
$s3 = $s3->subInt64($carry3->shiftLeft(21));
$carry5 = $s5->addInt(1 << 20)->shiftRight(21);
$s6 = $s6->addInt64($carry5);
$s5 = $s5->subInt64($carry5->shiftLeft(21));
$carry7 = $s7->addInt(1 << 20)->shiftRight(21);
$s8 = $s8->addInt64($carry7);
$s7 = $s7->subInt64($carry7->shiftLeft(21));
$carry9 = $s9->addInt(1 << 20)->shiftRight(21);
$s10 = $s10->addInt64($carry9);
$s9 = $s9->subInt64($carry9->shiftLeft(21));
$carry11 = $s11->addInt(1 << 20)->shiftRight(21);
$s12 = $s12->addInt64($carry11);
$s11 = $s11->subInt64($carry11->shiftLeft(21));
$s0 = $s0->addInt64($s12->mulInt(666643, 20));
$s1 = $s1->addInt64($s12->mulInt(470296, 19));
$s2 = $s2->addInt64($s12->mulInt(654183, 20));
$s3 = $s3->subInt64($s12->mulInt(997805, 20));
$s4 = $s4->addInt64($s12->mulInt(136657, 18));
$s5 = $s5->subInt64($s12->mulInt(683901, 20));
$s12 = new ParagonIE_Sodium_Core32_Int64();
$carry0 = $s0->shiftRight(21);
$s1 = $s1->addInt64($carry0);
$s0 = $s0->subInt64($carry0->shiftLeft(21));
$carry1 = $s1->shiftRight(21);
$s2 = $s2->addInt64($carry1);
$s1 = $s1->subInt64($carry1->shiftLeft(21));
$carry2 = $s2->shiftRight(21);
$s3 = $s3->addInt64($carry2);
$s2 = $s2->subInt64($carry2->shiftLeft(21));
$carry3 = $s3->shiftRight(21);
$s4 = $s4->addInt64($carry3);
$s3 = $s3->subInt64($carry3->shiftLeft(21));
$carry4 = $s4->shiftRight(21);
$s5 = $s5->addInt64($carry4);
$s4 = $s4->subInt64($carry4->shiftLeft(21));
$carry5 = $s5->shiftRight(21);
$s6 = $s6->addInt64($carry5);
$s5 = $s5->subInt64($carry5->shiftLeft(21));
$carry6 = $s6->shiftRight(21);
$s7 = $s7->addInt64($carry6);
$s6 = $s6->subInt64($carry6->shiftLeft(21));
$carry7 = $s7->shiftRight(21);
$s8 = $s8->addInt64($carry7);
$s7 = $s7->subInt64($carry7->shiftLeft(21));
$carry8 = $s8->shiftRight(21);
$s9 = $s9->addInt64($carry8);
$s8 = $s8->subInt64($carry8->shiftLeft(21));
$carry9 = $s9->shiftRight(21);
$s10 = $s10->addInt64($carry9);
$s9 = $s9->subInt64($carry9->shiftLeft(21));
$carry10 = $s10->shiftRight(21);
$s11 = $s11->addInt64($carry10);
$s10 = $s10->subInt64($carry10->shiftLeft(21));
$carry11 = $s11->shiftRight(21);
$s12 = $s12->addInt64($carry11);
$s11 = $s11->subInt64($carry11->shiftLeft(21));
$s0 = $s0->addInt64($s12->mulInt(666643, 20));
$s1 = $s1->addInt64($s12->mulInt(470296, 19));
$s2 = $s2->addInt64($s12->mulInt(654183, 20));
$s3 = $s3->subInt64($s12->mulInt(997805, 20));
$s4 = $s4->addInt64($s12->mulInt(136657, 18));
$s5 = $s5->subInt64($s12->mulInt(683901, 20));
$carry0 = $s0->shiftRight(21);
$s1 = $s1->addInt64($carry0);
$s0 = $s0->subInt64($carry0->shiftLeft(21));
$carry1 = $s1->shiftRight(21);
$s2 = $s2->addInt64($carry1);
$s1 = $s1->subInt64($carry1->shiftLeft(21));
$carry2 = $s2->shiftRight(21);
$s3 = $s3->addInt64($carry2);
$s2 = $s2->subInt64($carry2->shiftLeft(21));
$carry3 = $s3->shiftRight(21);
$s4 = $s4->addInt64($carry3);
$s3 = $s3->subInt64($carry3->shiftLeft(21));
$carry4 = $s4->shiftRight(21);
$s5 = $s5->addInt64($carry4);
$s4 = $s4->subInt64($carry4->shiftLeft(21));
$carry5 = $s5->shiftRight(21);
$s6 = $s6->addInt64($carry5);
$s5 = $s5->subInt64($carry5->shiftLeft(21));
$carry6 = $s6->shiftRight(21);
$s7 = $s7->addInt64($carry6);
$s6 = $s6->subInt64($carry6->shiftLeft(21));
$carry7 = $s7->shiftRight(21);
$s8 = $s8->addInt64($carry7);
$s7 = $s7->subInt64($carry7->shiftLeft(21));
$carry8 = $s10->shiftRight(21);
$s9 = $s9->addInt64($carry8);
$s8 = $s8->subInt64($carry8->shiftLeft(21));
$carry9 = $s9->shiftRight(21);
$s10 = $s10->addInt64($carry9);
$s9 = $s9->subInt64($carry9->shiftLeft(21));
$carry10 = $s10->shiftRight(21);
$s11 = $s11->addInt64($carry10);
$s10 = $s10->subInt64($carry10->shiftLeft(21));
$S0 = $s0->toInt();
$S1 = $s1->toInt();
$S2 = $s2->toInt();
$S3 = $s3->toInt();
$S4 = $s4->toInt();
$S5 = $s5->toInt();
$S6 = $s6->toInt();
$S7 = $s7->toInt();
$S8 = $s8->toInt();
$S9 = $s9->toInt();
$S10 = $s10->toInt();
$S11 = $s11->toInt();
/**
* @var array<int, int>
*/
$arr = array(
(int) (0xff & ($S0 >> 0)),
(int) (0xff & ($S0 >> 8)),
(int) (0xff & (($S0 >> 16) | ($S1 << 5))),
(int) (0xff & ($S1 >> 3)),
(int) (0xff & ($S1 >> 11)),
(int) (0xff & (($S1 >> 19) | ($S2 << 2))),
(int) (0xff & ($S2 >> 6)),
(int) (0xff & (($S2 >> 14) | ($S3 << 7))),
(int) (0xff & ($S3 >> 1)),
(int) (0xff & ($S3 >> 9)),
(int) (0xff & (($S3 >> 17) | ($S4 << 4))),
(int) (0xff & ($S4 >> 4)),
(int) (0xff & ($S4 >> 12)),
(int) (0xff & (($S4 >> 20) | ($S5 << 1))),
(int) (0xff & ($S5 >> 7)),
(int) (0xff & (($S5 >> 15) | ($S6 << 6))),
(int) (0xff & ($S6 >> 2)),
(int) (0xff & ($S6 >> 10)),
(int) (0xff & (($S6 >> 18) | ($S7 << 3))),
(int) (0xff & ($S7 >> 5)),
(int) (0xff & ($S7 >> 13)),
(int) (0xff & ($S8 >> 0)),
(int) (0xff & ($S8 >> 8)),
(int) (0xff & (($S8 >> 16) | ($S9 << 5))),
(int) (0xff & ($S9 >> 3)),
(int) (0xff & ($S9 >> 11)),
(int) (0xff & (($S9 >> 19) | ($S10 << 2))),
(int) (0xff & ($S10 >> 6)),
(int) (0xff & (($S10 >> 14) | ($S11 << 7))),
(int) (0xff & ($S11 >> 1)),
(int) (0xff & ($S11 >> 9)),
(int) (0xff & ($S11 >> 17))
);
return self::intArrayToString($arr);
}
/**
* @internal You should not use this directly from another application
*
* @param string $s
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function sc_reduce($s)
{
/**
* @var ParagonIE_Sodium_Core32_Int64 $s0
* @var ParagonIE_Sodium_Core32_Int64 $s1
* @var ParagonIE_Sodium_Core32_Int64 $s2
* @var ParagonIE_Sodium_Core32_Int64 $s3
* @var ParagonIE_Sodium_Core32_Int64 $s4
* @var ParagonIE_Sodium_Core32_Int64 $s5
* @var ParagonIE_Sodium_Core32_Int64 $s6
* @var ParagonIE_Sodium_Core32_Int64 $s7
* @var ParagonIE_Sodium_Core32_Int64 $s8
* @var ParagonIE_Sodium_Core32_Int64 $s9
* @var ParagonIE_Sodium_Core32_Int64 $s10
* @var ParagonIE_Sodium_Core32_Int64 $s11
* @var ParagonIE_Sodium_Core32_Int64 $s12
* @var ParagonIE_Sodium_Core32_Int64 $s13
* @var ParagonIE_Sodium_Core32_Int64 $s14
* @var ParagonIE_Sodium_Core32_Int64 $s15
* @var ParagonIE_Sodium_Core32_Int64 $s16
* @var ParagonIE_Sodium_Core32_Int64 $s17
* @var ParagonIE_Sodium_Core32_Int64 $s18
* @var ParagonIE_Sodium_Core32_Int64 $s19
* @var ParagonIE_Sodium_Core32_Int64 $s20
* @var ParagonIE_Sodium_Core32_Int64 $s21
* @var ParagonIE_Sodium_Core32_Int64 $s22
* @var ParagonIE_Sodium_Core32_Int64 $s23
*/
$s0 = ParagonIE_Sodium_Core32_Int64::fromInt(2097151 & self::load_3(self::substr($s, 0, 3)));
$s1 = ParagonIE_Sodium_Core32_Int64::fromInt(2097151 & (self::load_4(self::substr($s, 2, 4)) >> 5));
$s2 = ParagonIE_Sodium_Core32_Int64::fromInt(2097151 & (self::load_3(self::substr($s, 5, 3)) >> 2));
$s3 = ParagonIE_Sodium_Core32_Int64::fromInt(2097151 & (self::load_4(self::substr($s, 7, 4)) >> 7));
$s4 = ParagonIE_Sodium_Core32_Int64::fromInt(2097151 & (self::load_4(self::substr($s, 10, 4)) >> 4));
$s5 = ParagonIE_Sodium_Core32_Int64::fromInt(2097151 & (self::load_3(self::substr($s, 13, 3)) >> 1));
$s6 = ParagonIE_Sodium_Core32_Int64::fromInt(2097151 & (self::load_4(self::substr($s, 15, 4)) >> 6));
$s7 = ParagonIE_Sodium_Core32_Int64::fromInt(2097151 & (self::load_3(self::substr($s, 18, 4)) >> 3));
$s8 = ParagonIE_Sodium_Core32_Int64::fromInt(2097151 & self::load_3(self::substr($s, 21, 3)));
$s9 = ParagonIE_Sodium_Core32_Int64::fromInt(2097151 & (self::load_4(self::substr($s, 23, 4)) >> 5));
$s10 = ParagonIE_Sodium_Core32_Int64::fromInt(2097151 & (self::load_3(self::substr($s, 26, 3)) >> 2));
$s11 = ParagonIE_Sodium_Core32_Int64::fromInt(2097151 & (self::load_4(self::substr($s, 28, 4)) >> 7));
$s12 = ParagonIE_Sodium_Core32_Int64::fromInt(2097151 & (self::load_4(self::substr($s, 31, 4)) >> 4));
$s13 = ParagonIE_Sodium_Core32_Int64::fromInt(2097151 & (self::load_3(self::substr($s, 34, 3)) >> 1));
$s14 = ParagonIE_Sodium_Core32_Int64::fromInt(2097151 & (self::load_4(self::substr($s, 36, 4)) >> 6));
$s15 = ParagonIE_Sodium_Core32_Int64::fromInt(2097151 & (self::load_3(self::substr($s, 39, 4)) >> 3));
$s16 = ParagonIE_Sodium_Core32_Int64::fromInt(2097151 & self::load_3(self::substr($s, 42, 3)));
$s17 = ParagonIE_Sodium_Core32_Int64::fromInt(2097151 & (self::load_4(self::substr($s, 44, 4)) >> 5));
$s18 = ParagonIE_Sodium_Core32_Int64::fromInt(2097151 & (self::load_3(self::substr($s, 47, 3)) >> 2));
$s19 = ParagonIE_Sodium_Core32_Int64::fromInt(2097151 & (self::load_4(self::substr($s, 49, 4)) >> 7));
$s20 = ParagonIE_Sodium_Core32_Int64::fromInt(2097151 & (self::load_4(self::substr($s, 52, 4)) >> 4));
$s21 = ParagonIE_Sodium_Core32_Int64::fromInt(2097151 & (self::load_3(self::substr($s, 55, 3)) >> 1));
$s22 = ParagonIE_Sodium_Core32_Int64::fromInt(2097151 & (self::load_4(self::substr($s, 57, 4)) >> 6));
$s23 = ParagonIE_Sodium_Core32_Int64::fromInt(0x1fffffff & (self::load_4(self::substr($s, 60, 4)) >> 3));
$s11 = $s11->addInt64($s23->mulInt(666643, 20));
$s12 = $s12->addInt64($s23->mulInt(470296, 19));
$s13 = $s13->addInt64($s23->mulInt(654183, 20));
$s14 = $s14->subInt64($s23->mulInt(997805, 20));
$s15 = $s15->addInt64($s23->mulInt(136657, 18));
$s16 = $s16->subInt64($s23->mulInt(683901, 20));
$s10 = $s10->addInt64($s22->mulInt(666643, 20));
$s11 = $s11->addInt64($s22->mulInt(470296, 19));
$s12 = $s12->addInt64($s22->mulInt(654183, 20));
$s13 = $s13->subInt64($s22->mulInt(997805, 20));
$s14 = $s14->addInt64($s22->mulInt(136657, 18));
$s15 = $s15->subInt64($s22->mulInt(683901, 20));
$s9 = $s9->addInt64($s21->mulInt(666643, 20));
$s10 = $s10->addInt64($s21->mulInt(470296, 19));
$s11 = $s11->addInt64($s21->mulInt(654183, 20));
$s12 = $s12->subInt64($s21->mulInt(997805, 20));
$s13 = $s13->addInt64($s21->mulInt(136657, 18));
$s14 = $s14->subInt64($s21->mulInt(683901, 20));
$s8 = $s8->addInt64($s20->mulInt(666643, 20));
$s9 = $s9->addInt64($s20->mulInt(470296, 19));
$s10 = $s10->addInt64($s20->mulInt(654183, 20));
$s11 = $s11->subInt64($s20->mulInt(997805, 20));
$s12 = $s12->addInt64($s20->mulInt(136657, 18));
$s13 = $s13->subInt64($s20->mulInt(683901, 20));
$s7 = $s7->addInt64($s19->mulInt(666643, 20));
$s8 = $s8->addInt64($s19->mulInt(470296, 19));
$s9 = $s9->addInt64($s19->mulInt(654183, 20));
$s10 = $s10->subInt64($s19->mulInt(997805, 20));
$s11 = $s11->addInt64($s19->mulInt(136657, 18));
$s12 = $s12->subInt64($s19->mulInt(683901, 20));
$s6 = $s6->addInt64($s18->mulInt(666643, 20));
$s7 = $s7->addInt64($s18->mulInt(470296, 19));
$s8 = $s8->addInt64($s18->mulInt(654183, 20));
$s9 = $s9->subInt64($s18->mulInt(997805, 20));
$s10 = $s10->addInt64($s18->mulInt(136657, 18));
$s11 = $s11->subInt64($s18->mulInt(683901, 20));
$carry6 = $s6->addInt(1 << 20)->shiftRight(21);
$s7 = $s7->addInt64($carry6);
$s6 = $s6->subInt64($carry6->shiftLeft(21));
$carry8 = $s8->addInt(1 << 20)->shiftRight(21);
$s9 = $s9->addInt64($carry8);
$s8 = $s8->subInt64($carry8->shiftLeft(21));
$carry10 = $s10->addInt(1 << 20)->shiftRight(21);
$s11 = $s11->addInt64($carry10);
$s10 = $s10->subInt64($carry10->shiftLeft(21));
$carry12 = $s12->addInt(1 << 20)->shiftRight(21);
$s13 = $s13->addInt64($carry12);
$s12 = $s12->subInt64($carry12->shiftLeft(21));
$carry14 = $s14->addInt(1 << 20)->shiftRight(21);
$s15 = $s15->addInt64($carry14);
$s14 = $s14->subInt64($carry14->shiftLeft(21));
$carry16 = $s16->addInt(1 << 20)->shiftRight(21);
$s17 = $s17->addInt64($carry16);
$s16 = $s16->subInt64($carry16->shiftLeft(21));
$carry7 = $s7->addInt(1 << 20)->shiftRight(21);
$s8 = $s8->addInt64($carry7);
$s7 = $s7->subInt64($carry7->shiftLeft(21));
$carry9 = $s9->addInt(1 << 20)->shiftRight(21);
$s10 = $s10->addInt64($carry9);
$s9 = $s9->subInt64($carry9->shiftLeft(21));
$carry11 = $s11->addInt(1 << 20)->shiftRight(21);
$s12 = $s12->addInt64($carry11);
$s11 = $s11->subInt64($carry11->shiftLeft(21));
$carry13 = $s13->addInt(1 << 20)->shiftRight(21);
$s14 = $s14->addInt64($carry13);
$s13 = $s13->subInt64($carry13->shiftLeft(21));
$carry15 = $s15->addInt(1 << 20)->shiftRight(21);
$s16 = $s16->addInt64($carry15);
$s15 = $s15->subInt64($carry15->shiftLeft(21));
$s5 = $s5->addInt64($s17->mulInt(666643, 20));
$s6 = $s6->addInt64($s17->mulInt(470296, 19));
$s7 = $s7->addInt64($s17->mulInt(654183, 20));
$s8 = $s8->subInt64($s17->mulInt(997805, 20));
$s9 = $s9->addInt64($s17->mulInt(136657, 18));
$s10 = $s10->subInt64($s17->mulInt(683901, 20));
$s4 = $s4->addInt64($s16->mulInt(666643, 20));
$s5 = $s5->addInt64($s16->mulInt(470296, 19));
$s6 = $s6->addInt64($s16->mulInt(654183, 20));
$s7 = $s7->subInt64($s16->mulInt(997805, 20));
$s8 = $s8->addInt64($s16->mulInt(136657, 18));
$s9 = $s9->subInt64($s16->mulInt(683901, 20));
$s3 = $s3->addInt64($s15->mulInt(666643, 20));
$s4 = $s4->addInt64($s15->mulInt(470296, 19));
$s5 = $s5->addInt64($s15->mulInt(654183, 20));
$s6 = $s6->subInt64($s15->mulInt(997805, 20));
$s7 = $s7->addInt64($s15->mulInt(136657, 18));
$s8 = $s8->subInt64($s15->mulInt(683901, 20));
$s2 = $s2->addInt64($s14->mulInt(666643, 20));
$s3 = $s3->addInt64($s14->mulInt(470296, 19));
$s4 = $s4->addInt64($s14->mulInt(654183, 20));
$s5 = $s5->subInt64($s14->mulInt(997805, 20));
$s6 = $s6->addInt64($s14->mulInt(136657, 18));
$s7 = $s7->subInt64($s14->mulInt(683901, 20));
$s1 = $s1->addInt64($s13->mulInt(666643, 20));
$s2 = $s2->addInt64($s13->mulInt(470296, 19));
$s3 = $s3->addInt64($s13->mulInt(654183, 20));
$s4 = $s4->subInt64($s13->mulInt(997805, 20));
$s5 = $s5->addInt64($s13->mulInt(136657, 18));
$s6 = $s6->subInt64($s13->mulInt(683901, 20));
$s0 = $s0->addInt64($s12->mulInt(666643, 20));
$s1 = $s1->addInt64($s12->mulInt(470296, 19));
$s2 = $s2->addInt64($s12->mulInt(654183, 20));
$s3 = $s3->subInt64($s12->mulInt(997805, 20));
$s4 = $s4->addInt64($s12->mulInt(136657, 18));
$s5 = $s5->subInt64($s12->mulInt(683901, 20));
$s12 = new ParagonIE_Sodium_Core32_Int64();
$carry0 = $s0->addInt(1 << 20)->shiftRight(21);
$s1 = $s1->addInt64($carry0);
$s0 = $s0->subInt64($carry0->shiftLeft(21));
$carry2 = $s2->addInt(1 << 20)->shiftRight(21);
$s3 = $s3->addInt64($carry2);
$s2 = $s2->subInt64($carry2->shiftLeft(21));
$carry4 = $s4->addInt(1 << 20)->shiftRight(21);
$s5 = $s5->addInt64($carry4);
$s4 = $s4->subInt64($carry4->shiftLeft(21));
$carry6 = $s6->addInt(1 << 20)->shiftRight(21);
$s7 = $s7->addInt64($carry6);
$s6 = $s6->subInt64($carry6->shiftLeft(21));
$carry8 = $s8->addInt(1 << 20)->shiftRight(21);
$s9 = $s9->addInt64($carry8);
$s8 = $s8->subInt64($carry8->shiftLeft(21));
$carry10 = $s10->addInt(1 << 20)->shiftRight(21);
$s11 = $s11->addInt64($carry10);
$s10 = $s10->subInt64($carry10->shiftLeft(21));
$carry1 = $s1->addInt(1 << 20)->shiftRight(21);
$s2 = $s2->addInt64($carry1);
$s1 = $s1->subInt64($carry1->shiftLeft(21));
$carry3 = $s3->addInt(1 << 20)->shiftRight(21);
$s4 = $s4->addInt64($carry3);
$s3 = $s3->subInt64($carry3->shiftLeft(21));
$carry5 = $s5->addInt(1 << 20)->shiftRight(21);
$s6 = $s6->addInt64($carry5);
$s5 = $s5->subInt64($carry5->shiftLeft(21));
$carry7 = $s7->addInt(1 << 20)->shiftRight(21);
$s8 = $s8->addInt64($carry7);
$s7 = $s7->subInt64($carry7->shiftLeft(21));
$carry9 = $s9->addInt(1 << 20)->shiftRight(21);
$s10 = $s10->addInt64($carry9);
$s9 = $s9->subInt64($carry9->shiftLeft(21));
$carry11 = $s11->addInt(1 << 20)->shiftRight(21);
$s12 = $s12->addInt64($carry11);
$s11 = $s11->subInt64($carry11->shiftLeft(21));
$s0 = $s0->addInt64($s12->mulInt(666643, 20));
$s1 = $s1->addInt64($s12->mulInt(470296, 19));
$s2 = $s2->addInt64($s12->mulInt(654183, 20));
$s3 = $s3->subInt64($s12->mulInt(997805, 20));
$s4 = $s4->addInt64($s12->mulInt(136657, 18));
$s5 = $s5->subInt64($s12->mulInt(683901, 20));
$s12 = new ParagonIE_Sodium_Core32_Int64();
$carry0 = $s0->shiftRight(21);
$s1 = $s1->addInt64($carry0);
$s0 = $s0->subInt64($carry0->shiftLeft(21));
$carry1 = $s1->shiftRight(21);
$s2 = $s2->addInt64($carry1);
$s1 = $s1->subInt64($carry1->shiftLeft(21));
$carry2 = $s2->shiftRight(21);
$s3 = $s3->addInt64($carry2);
$s2 = $s2->subInt64($carry2->shiftLeft(21));
$carry3 = $s3->shiftRight(21);
$s4 = $s4->addInt64($carry3);
$s3 = $s3->subInt64($carry3->shiftLeft(21));
$carry4 = $s4->shiftRight(21);
$s5 = $s5->addInt64($carry4);
$s4 = $s4->subInt64($carry4->shiftLeft(21));
$carry5 = $s5->shiftRight(21);
$s6 = $s6->addInt64($carry5);
$s5 = $s5->subInt64($carry5->shiftLeft(21));
$carry6 = $s6->shiftRight(21);
$s7 = $s7->addInt64($carry6);
$s6 = $s6->subInt64($carry6->shiftLeft(21));
$carry7 = $s7->shiftRight(21);
$s8 = $s8->addInt64($carry7);
$s7 = $s7->subInt64($carry7->shiftLeft(21));
$carry8 = $s8->shiftRight(21);
$s9 = $s9->addInt64($carry8);
$s8 = $s8->subInt64($carry8->shiftLeft(21));
$carry9 = $s9->shiftRight(21);
$s10 = $s10->addInt64($carry9);
$s9 = $s9->subInt64($carry9->shiftLeft(21));
$carry10 = $s10->shiftRight(21);
$s11 = $s11->addInt64($carry10);
$s10 = $s10->subInt64($carry10->shiftLeft(21));
$carry11 = $s11->shiftRight(21);
$s12 = $s12->addInt64($carry11);
$s11 = $s11->subInt64($carry11->shiftLeft(21));
$s0 = $s0->addInt64($s12->mulInt(666643, 20));
$s1 = $s1->addInt64($s12->mulInt(470296, 19));
$s2 = $s2->addInt64($s12->mulInt(654183, 20));
$s3 = $s3->subInt64($s12->mulInt(997805, 20));
$s4 = $s4->addInt64($s12->mulInt(136657, 18));
$s5 = $s5->subInt64($s12->mulInt(683901, 20));
$carry0 = $s0->shiftRight(21);
$s1 = $s1->addInt64($carry0);
$s0 = $s0->subInt64($carry0->shiftLeft(21));
$carry1 = $s1->shiftRight(21);
$s2 = $s2->addInt64($carry1);
$s1 = $s1->subInt64($carry1->shiftLeft(21));
$carry2 = $s2->shiftRight(21);
$s3 = $s3->addInt64($carry2);
$s2 = $s2->subInt64($carry2->shiftLeft(21));
$carry3 = $s3->shiftRight(21);
$s4 = $s4->addInt64($carry3);
$s3 = $s3->subInt64($carry3->shiftLeft(21));
$carry4 = $s4->shiftRight(21);
$s5 = $s5->addInt64($carry4);
$s4 = $s4->subInt64($carry4->shiftLeft(21));
$carry5 = $s5->shiftRight(21);
$s6 = $s6->addInt64($carry5);
$s5 = $s5->subInt64($carry5->shiftLeft(21));
$carry6 = $s6->shiftRight(21);
$s7 = $s7->addInt64($carry6);
$s6 = $s6->subInt64($carry6->shiftLeft(21));
$carry7 = $s7->shiftRight(21);
$s8 = $s8->addInt64($carry7);
$s7 = $s7->subInt64($carry7->shiftLeft(21));
$carry8 = $s8->shiftRight(21);
$s9 = $s9->addInt64($carry8);
$s8 = $s8->subInt64($carry8->shiftLeft(21));
$carry9 = $s9->shiftRight(21);
$s10 = $s10->addInt64($carry9);
$s9 = $s9->subInt64($carry9->shiftLeft(21));
$carry10 = $s10->shiftRight(21);
$s11 = $s11->addInt64($carry10);
$s10 = $s10->subInt64($carry10->shiftLeft(21));
$S0 = $s0->toInt32()->toInt();
$S1 = $s1->toInt32()->toInt();
$S2 = $s2->toInt32()->toInt();
$S3 = $s3->toInt32()->toInt();
$S4 = $s4->toInt32()->toInt();
$S5 = $s5->toInt32()->toInt();
$S6 = $s6->toInt32()->toInt();
$S7 = $s7->toInt32()->toInt();
$S8 = $s8->toInt32()->toInt();
$S9 = $s9->toInt32()->toInt();
$S10 = $s10->toInt32()->toInt();
$S11 = $s11->toInt32()->toInt();
/**
* @var array<int, int>
*/
$arr = array(
(int) ($S0 >> 0),
(int) ($S0 >> 8),
(int) (($S0 >> 16) | ($S1 << 5)),
(int) ($S1 >> 3),
(int) ($S1 >> 11),
(int) (($S1 >> 19) | ($S2 << 2)),
(int) ($S2 >> 6),
(int) (($S2 >> 14) | ($S3 << 7)),
(int) ($S3 >> 1),
(int) ($S3 >> 9),
(int) (($S3 >> 17) | ($S4 << 4)),
(int) ($S4 >> 4),
(int) ($S4 >> 12),
(int) (($S4 >> 20) | ($S5 << 1)),
(int) ($S5 >> 7),
(int) (($S5 >> 15) | ($S6 << 6)),
(int) ($S6 >> 2),
(int) ($S6 >> 10),
(int) (($S6 >> 18) | ($S7 << 3)),
(int) ($S7 >> 5),
(int) ($S7 >> 13),
(int) ($S8 >> 0),
(int) ($S8 >> 8),
(int) (($S8 >> 16) | ($S9 << 5)),
(int) ($S9 >> 3),
(int) ($S9 >> 11),
(int) (($S9 >> 19) | ($S10 << 2)),
(int) ($S10 >> 6),
(int) (($S10 >> 14) | ($S11 << 7)),
(int) ($S11 >> 1),
(int) ($S11 >> 9),
(int) $S11 >> 17
);
return self::intArrayToString($arr);
}
/**
* multiply by the order of the main subgroup l = 2^252+27742317777372353535851937790883648493
*
* @param ParagonIE_Sodium_Core32_Curve25519_Ge_P3 $A
* @return ParagonIE_Sodium_Core32_Curve25519_Ge_P3
* @throws SodiumException
* @throws TypeError
*/
public static function ge_mul_l(ParagonIE_Sodium_Core32_Curve25519_Ge_P3 $A)
{
$aslide = array(
13, 0, 0, 0, 0, -1, 0, 0, 0, 0, -11, 0, 0, 0, 0, 0, 0, -5, 0, 0, 0,
0, 0, 0, -3, 0, 0, 0, 0, -13, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 3, 0,
0, 0, 0, -13, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0,
0, 0, 11, 0, 0, 0, 0, -13, 0, 0, 0, 0, 0, 0, -3, 0, 0, 0, 0, 0, -1,
0, 0, 0, 0, 3, 0, 0, 0, 0, -11, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0,
0, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 7, 0, 0, 0, 0, 5, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1
);
/** @var array<int, ParagonIE_Sodium_Core32_Curve25519_Ge_Cached> $Ai size 8 */
$Ai = array();
# ge_p3_to_cached(&Ai[0], A);
$Ai[0] = self::ge_p3_to_cached($A);
# ge_p3_dbl(&t, A);
$t = self::ge_p3_dbl($A);
# ge_p1p1_to_p3(&A2, &t);
$A2 = self::ge_p1p1_to_p3($t);
for ($i = 1; $i < 8; ++$i) {
# ge_add(&t, &A2, &Ai[0]);
$t = self::ge_add($A2, $Ai[$i - 1]);
# ge_p1p1_to_p3(&u, &t);
$u = self::ge_p1p1_to_p3($t);
# ge_p3_to_cached(&Ai[i], &u);
$Ai[$i] = self::ge_p3_to_cached($u);
}
$r = self::ge_p3_0();
for ($i = 252; $i >= 0; --$i) {
$t = self::ge_p3_dbl($r);
if ($aslide[$i] > 0) {
# ge_p1p1_to_p3(&u, &t);
$u = self::ge_p1p1_to_p3($t);
# ge_add(&t, &u, &Ai[aslide[i] / 2]);
$t = self::ge_add($u, $Ai[(int)($aslide[$i] / 2)]);
} elseif ($aslide[$i] < 0) {
# ge_p1p1_to_p3(&u, &t);
$u = self::ge_p1p1_to_p3($t);
# ge_sub(&t, &u, &Ai[(-aslide[i]) / 2]);
$t = self::ge_sub($u, $Ai[(int)(-$aslide[$i] / 2)]);
}
}
# ge_p1p1_to_p3(r, &t);
return self::ge_p1p1_to_p3($t);
}
}
Core32/Poly1305.php 0000604 00000003062 15133021213 0007532 0 ustar 00 <?php
if (class_exists('ParagonIE_Sodium_Core32_Poly1305', false)) {
return;
}
/**
* Class ParagonIE_Sodium_Core32_Poly1305
*/
abstract class ParagonIE_Sodium_Core32_Poly1305 extends ParagonIE_Sodium_Core32_Util
{
const BLOCK_SIZE = 16;
/**
* @internal You should not use this directly from another application
*
* @param string $m
* @param string $key
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function onetimeauth($m, $key)
{
if (self::strlen($key) < 32) {
throw new InvalidArgumentException(
'Key must be 32 bytes long.'
);
}
$state = new ParagonIE_Sodium_Core32_Poly1305_State(
self::substr($key, 0, 32)
);
return $state
->update($m)
->finish();
}
/**
* @internal You should not use this directly from another application
*
* @param string $mac
* @param string $m
* @param string $key
* @return bool
* @throws SodiumException
* @throws TypeError
*/
public static function onetimeauth_verify($mac, $m, $key)
{
if (self::strlen($key) < 32) {
throw new InvalidArgumentException(
'Key must be 32 bytes long.'
);
}
$state = new ParagonIE_Sodium_Core32_Poly1305_State(
self::substr($key, 0, 32)
);
$calc = $state
->update($m)
->finish();
return self::verify_16($calc, $mac);
}
}
Core32/Ed25519.php 0000644 00000036567 15133021213 0007260 0 ustar 00 <?php
if (class_exists('ParagonIE_Sodium_Core32_Ed25519', false)) {
return;
}
if (!class_exists('ParagonIE_Sodium_Core32_Curve25519')) {
require_once dirname(__FILE__) . '/Curve25519.php';
}
/**
* Class ParagonIE_Sodium_Core32_Ed25519
*/
abstract class ParagonIE_Sodium_Core32_Ed25519 extends ParagonIE_Sodium_Core32_Curve25519
{
const KEYPAIR_BYTES = 96;
const SEED_BYTES = 32;
/**
* @internal You should not use this directly from another application
*
* @return string (96 bytes)
* @throws Exception
* @throws SodiumException
* @throws TypeError
*/
public static function keypair()
{
$seed = random_bytes(self::SEED_BYTES);
$pk = '';
$sk = '';
self::seed_keypair($pk, $sk, $seed);
return $sk . $pk;
}
/**
* @internal You should not use this directly from another application
*
* @param string $pk
* @param string $sk
* @param string $seed
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function seed_keypair(&$pk, &$sk, $seed)
{
if (self::strlen($seed) !== self::SEED_BYTES) {
throw new RangeException('crypto_sign keypair seed must be 32 bytes long');
}
/** @var string $pk */
$pk = self::publickey_from_secretkey($seed);
$sk = $seed . $pk;
return $sk;
}
/**
* @internal You should not use this directly from another application
*
* @param string $keypair
* @return string
* @throws TypeError
*/
public static function secretkey($keypair)
{
if (self::strlen($keypair) !== self::KEYPAIR_BYTES) {
throw new RangeException('crypto_sign keypair must be 96 bytes long');
}
return self::substr($keypair, 0, 64);
}
/**
* @internal You should not use this directly from another application
*
* @param string $keypair
* @return string
* @throws RangeException
* @throws TypeError
*/
public static function publickey($keypair)
{
if (self::strlen($keypair) !== self::KEYPAIR_BYTES) {
throw new RangeException('crypto_sign keypair must be 96 bytes long');
}
return self::substr($keypair, 64, 32);
}
/**
* @internal You should not use this directly from another application
*
* @param string $sk
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function publickey_from_secretkey($sk)
{
/** @var string $sk */
$sk = hash('sha512', self::substr($sk, 0, 32), true);
$sk[0] = self::intToChr(
self::chrToInt($sk[0]) & 248
);
$sk[31] = self::intToChr(
(self::chrToInt($sk[31]) & 63) | 64
);
return self::sk_to_pk($sk);
}
/**
* @param string $pk
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function pk_to_curve25519($pk)
{
if (self::small_order($pk)) {
throw new SodiumException('Public key is on a small order');
}
$A = self::ge_frombytes_negate_vartime($pk);
$p1 = self::ge_mul_l($A);
if (!self::fe_isnonzero($p1->X)) {
throw new SodiumException('Unexpected zero result');
}
# fe_1(one_minus_y);
# fe_sub(one_minus_y, one_minus_y, A.Y);
# fe_invert(one_minus_y, one_minus_y);
$one_minux_y = self::fe_invert(
self::fe_sub(
self::fe_1(),
$A->Y
)
);
# fe_1(x);
# fe_add(x, x, A.Y);
# fe_mul(x, x, one_minus_y);
$x = self::fe_mul(
self::fe_add(self::fe_1(), $A->Y),
$one_minux_y
);
# fe_tobytes(curve25519_pk, x);
return self::fe_tobytes($x);
}
/**
* @internal You should not use this directly from another application
*
* @param string $sk
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function sk_to_pk($sk)
{
return self::ge_p3_tobytes(
self::ge_scalarmult_base(
self::substr($sk, 0, 32)
)
);
}
/**
* @internal You should not use this directly from another application
*
* @param string $message
* @param string $sk
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function sign($message, $sk)
{
/** @var string $signature */
$signature = self::sign_detached($message, $sk);
return $signature . $message;
}
/**
* @internal You should not use this directly from another application
*
* @param string $message A signed message
* @param string $pk Public key
* @return string Message (without signature)
* @throws SodiumException
* @throws TypeError
*/
public static function sign_open($message, $pk)
{
/** @var string $signature */
$signature = self::substr($message, 0, 64);
/** @var string $message */
$message = self::substr($message, 64);
if (self::verify_detached($signature, $message, $pk)) {
return $message;
}
throw new SodiumException('Invalid signature');
}
/**
* @internal You should not use this directly from another application
*
* @param string $message
* @param string $sk
* @return string
* @throws SodiumException
* @throws TypeError
* @psalm-suppress PossiblyInvalidArgument
*/
public static function sign_detached($message, $sk)
{
# crypto_hash_sha512(az, sk, 32);
$az = hash('sha512', self::substr($sk, 0, 32), true);
# az[0] &= 248;
# az[31] &= 63;
# az[31] |= 64;
$az[0] = self::intToChr(self::chrToInt($az[0]) & 248);
$az[31] = self::intToChr((self::chrToInt($az[31]) & 63) | 64);
# crypto_hash_sha512_init(&hs);
# crypto_hash_sha512_update(&hs, az + 32, 32);
# crypto_hash_sha512_update(&hs, m, mlen);
# crypto_hash_sha512_final(&hs, nonce);
$hs = hash_init('sha512');
self::hash_update($hs, self::substr($az, 32, 32));
self::hash_update($hs, $message);
$nonceHash = hash_final($hs, true);
# memmove(sig + 32, sk + 32, 32);
$pk = self::substr($sk, 32, 32);
# sc_reduce(nonce);
# ge_scalarmult_base(&R, nonce);
# ge_p3_tobytes(sig, &R);
$nonce = self::sc_reduce($nonceHash) . self::substr($nonceHash, 32);
$sig = self::ge_p3_tobytes(
self::ge_scalarmult_base($nonce)
);
# crypto_hash_sha512_init(&hs);
# crypto_hash_sha512_update(&hs, sig, 64);
# crypto_hash_sha512_update(&hs, m, mlen);
# crypto_hash_sha512_final(&hs, hram);
$hs = hash_init('sha512');
self::hash_update($hs, self::substr($sig, 0, 32));
self::hash_update($hs, self::substr($pk, 0, 32));
self::hash_update($hs, $message);
$hramHash = hash_final($hs, true);
# sc_reduce(hram);
# sc_muladd(sig + 32, hram, az, nonce);
$hram = self::sc_reduce($hramHash);
$sigAfter = self::sc_muladd($hram, $az, $nonce);
$sig = self::substr($sig, 0, 32) . self::substr($sigAfter, 0, 32);
try {
ParagonIE_Sodium_Compat::memzero($az);
} catch (SodiumException $ex) {
$az = null;
}
return $sig;
}
/**
* @internal You should not use this directly from another application
*
* @param string $sig
* @param string $message
* @param string $pk
* @return bool
* @throws SodiumException
* @throws TypeError
*/
public static function verify_detached($sig, $message, $pk)
{
if (self::strlen($sig) < 64) {
throw new SodiumException('Signature is too short');
}
if ((self::chrToInt($sig[63]) & 240) && self::check_S_lt_L(self::substr($sig, 32, 32))) {
throw new SodiumException('S < L - Invalid signature');
}
if (self::small_order($sig)) {
throw new SodiumException('Signature is on too small of an order');
}
if ((self::chrToInt($sig[63]) & 224) !== 0) {
throw new SodiumException('Invalid signature');
}
$d = 0;
for ($i = 0; $i < 32; ++$i) {
$d |= self::chrToInt($pk[$i]);
}
if ($d === 0) {
throw new SodiumException('All zero public key');
}
/** @var bool The original value of ParagonIE_Sodium_Compat::$fastMult */
$orig = ParagonIE_Sodium_Compat::$fastMult;
// Set ParagonIE_Sodium_Compat::$fastMult to true to speed up verification.
ParagonIE_Sodium_Compat::$fastMult = true;
/** @var ParagonIE_Sodium_Core32_Curve25519_Ge_P3 $A */
$A = self::ge_frombytes_negate_vartime($pk);
/** @var string $hDigest */
$hDigest = hash(
'sha512',
self::substr($sig, 0, 32) .
self::substr($pk, 0, 32) .
$message,
true
);
/** @var string $h */
$h = self::sc_reduce($hDigest) . self::substr($hDigest, 32);
/** @var ParagonIE_Sodium_Core32_Curve25519_Ge_P2 $R */
$R = self::ge_double_scalarmult_vartime(
$h,
$A,
self::substr($sig, 32)
);
/** @var string $rcheck */
$rcheck = self::ge_tobytes($R);
// Reset ParagonIE_Sodium_Compat::$fastMult to what it was before.
ParagonIE_Sodium_Compat::$fastMult = $orig;
return self::verify_32($rcheck, self::substr($sig, 0, 32));
}
/**
* @internal You should not use this directly from another application
*
* @param string $S
* @return bool
* @throws SodiumException
* @throws TypeError
*/
public static function check_S_lt_L($S)
{
if (self::strlen($S) < 32) {
throw new SodiumException('Signature must be 32 bytes');
}
static $L = array(
0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58,
0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10
);
/** @var array<int, int> $L */
$c = 0;
$n = 1;
$i = 32;
do {
--$i;
$x = self::chrToInt($S[$i]);
$c |= (
(($x - $L[$i]) >> 8) & $n
);
$n &= (
(($x ^ $L[$i]) - 1) >> 8
);
} while ($i !== 0);
return $c === 0;
}
/**
* @param string $R
* @return bool
* @throws SodiumException
* @throws TypeError
*/
public static function small_order($R)
{
static $blocklist = array(
/* 0 (order 4) */
array(
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
),
/* 1 (order 1) */
array(
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
),
/* 2707385501144840649318225287225658788936804267575313519463743609750303402022 (order 8) */
array(
0x26, 0xe8, 0x95, 0x8f, 0xc2, 0xb2, 0x27, 0xb0,
0x45, 0xc3, 0xf4, 0x89, 0xf2, 0xef, 0x98, 0xf0,
0xd5, 0xdf, 0xac, 0x05, 0xd3, 0xc6, 0x33, 0x39,
0xb1, 0x38, 0x02, 0x88, 0x6d, 0x53, 0xfc, 0x05
),
/* 55188659117513257062467267217118295137698188065244968500265048394206261417927 (order 8) */
array(
0xc7, 0x17, 0x6a, 0x70, 0x3d, 0x4d, 0xd8, 0x4f,
0xba, 0x3c, 0x0b, 0x76, 0x0d, 0x10, 0x67, 0x0f,
0x2a, 0x20, 0x53, 0xfa, 0x2c, 0x39, 0xcc, 0xc6,
0x4e, 0xc7, 0xfd, 0x77, 0x92, 0xac, 0x03, 0x7a
),
/* p-1 (order 2) */
array(
0x13, 0xe8, 0x95, 0x8f, 0xc2, 0xb2, 0x27, 0xb0,
0x45, 0xc3, 0xf4, 0x89, 0xf2, 0xef, 0x98, 0xf0,
0xd5, 0xdf, 0xac, 0x05, 0xd3, 0xc6, 0x33, 0x39,
0xb1, 0x38, 0x02, 0x88, 0x6d, 0x53, 0xfc, 0x85
),
/* p (order 4) */
array(
0xb4, 0x17, 0x6a, 0x70, 0x3d, 0x4d, 0xd8, 0x4f,
0xba, 0x3c, 0x0b, 0x76, 0x0d, 0x10, 0x67, 0x0f,
0x2a, 0x20, 0x53, 0xfa, 0x2c, 0x39, 0xcc, 0xc6,
0x4e, 0xc7, 0xfd, 0x77, 0x92, 0xac, 0x03, 0xfa
),
/* p+1 (order 1) */
array(
0xec, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f
),
/* p+2707385501144840649318225287225658788936804267575313519463743609750303402022 (order 8) */
array(
0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f
),
/* p+55188659117513257062467267217118295137698188065244968500265048394206261417927 (order 8) */
array(
0xee, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f
),
/* 2p-1 (order 2) */
array(
0xd9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
),
/* 2p (order 4) */
array(
0xda, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
),
/* 2p+1 (order 1) */
array(
0xdb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
)
);
/** @var array<int, array<int, int>> $blocklist */
$countBlocklist = count($blocklist);
for ($i = 0; $i < $countBlocklist; ++$i) {
$c = 0;
for ($j = 0; $j < 32; ++$j) {
$c |= self::chrToInt($R[$j]) ^ $blocklist[$i][$j];
}
if ($c === 0) {
return true;
}
}
return false;
}
}
Core32/Util.php 0000604 00000000321 15133021213 0007206 0 ustar 00 <?php
if (class_exists('ParagonIE_Sodium_Core32_Util', false)) {
return;
}
/**
* Class ParagonIE_Sodium_Core_Util
*/
abstract class ParagonIE_Sodium_Core32_Util extends ParagonIE_Sodium_Core_Util
{
}
Core32/X25519.php 0000604 00000025442 15133021213 0007121 0 ustar 00 <?php
if (class_exists('ParagonIE_Sodium_Core32_X25519', false)) {
return;
}
/**
* Class ParagonIE_Sodium_Core32_X25519
*/
abstract class ParagonIE_Sodium_Core32_X25519 extends ParagonIE_Sodium_Core32_Curve25519
{
/**
* Alters the objects passed to this method in place.
*
* @internal You should not use this directly from another application
*
* @param ParagonIE_Sodium_Core32_Curve25519_Fe $f
* @param ParagonIE_Sodium_Core32_Curve25519_Fe $g
* @param int $b
* @return void
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedMethodCall
*/
public static function fe_cswap(
ParagonIE_Sodium_Core32_Curve25519_Fe $f,
ParagonIE_Sodium_Core32_Curve25519_Fe $g,
$b = 0
) {
$f0 = (int) $f[0]->toInt();
$f1 = (int) $f[1]->toInt();
$f2 = (int) $f[2]->toInt();
$f3 = (int) $f[3]->toInt();
$f4 = (int) $f[4]->toInt();
$f5 = (int) $f[5]->toInt();
$f6 = (int) $f[6]->toInt();
$f7 = (int) $f[7]->toInt();
$f8 = (int) $f[8]->toInt();
$f9 = (int) $f[9]->toInt();
$g0 = (int) $g[0]->toInt();
$g1 = (int) $g[1]->toInt();
$g2 = (int) $g[2]->toInt();
$g3 = (int) $g[3]->toInt();
$g4 = (int) $g[4]->toInt();
$g5 = (int) $g[5]->toInt();
$g6 = (int) $g[6]->toInt();
$g7 = (int) $g[7]->toInt();
$g8 = (int) $g[8]->toInt();
$g9 = (int) $g[9]->toInt();
$b = -$b;
/** @var int $x0 */
$x0 = ($f0 ^ $g0) & $b;
/** @var int $x1 */
$x1 = ($f1 ^ $g1) & $b;
/** @var int $x2 */
$x2 = ($f2 ^ $g2) & $b;
/** @var int $x3 */
$x3 = ($f3 ^ $g3) & $b;
/** @var int $x4 */
$x4 = ($f4 ^ $g4) & $b;
/** @var int $x5 */
$x5 = ($f5 ^ $g5) & $b;
/** @var int $x6 */
$x6 = ($f6 ^ $g6) & $b;
/** @var int $x7 */
$x7 = ($f7 ^ $g7) & $b;
/** @var int $x8 */
$x8 = ($f8 ^ $g8) & $b;
/** @var int $x9 */
$x9 = ($f9 ^ $g9) & $b;
$f[0] = ParagonIE_Sodium_Core32_Int32::fromInt($f0 ^ $x0);
$f[1] = ParagonIE_Sodium_Core32_Int32::fromInt($f1 ^ $x1);
$f[2] = ParagonIE_Sodium_Core32_Int32::fromInt($f2 ^ $x2);
$f[3] = ParagonIE_Sodium_Core32_Int32::fromInt($f3 ^ $x3);
$f[4] = ParagonIE_Sodium_Core32_Int32::fromInt($f4 ^ $x4);
$f[5] = ParagonIE_Sodium_Core32_Int32::fromInt($f5 ^ $x5);
$f[6] = ParagonIE_Sodium_Core32_Int32::fromInt($f6 ^ $x6);
$f[7] = ParagonIE_Sodium_Core32_Int32::fromInt($f7 ^ $x7);
$f[8] = ParagonIE_Sodium_Core32_Int32::fromInt($f8 ^ $x8);
$f[9] = ParagonIE_Sodium_Core32_Int32::fromInt($f9 ^ $x9);
$g[0] = ParagonIE_Sodium_Core32_Int32::fromInt($g0 ^ $x0);
$g[1] = ParagonIE_Sodium_Core32_Int32::fromInt($g1 ^ $x1);
$g[2] = ParagonIE_Sodium_Core32_Int32::fromInt($g2 ^ $x2);
$g[3] = ParagonIE_Sodium_Core32_Int32::fromInt($g3 ^ $x3);
$g[4] = ParagonIE_Sodium_Core32_Int32::fromInt($g4 ^ $x4);
$g[5] = ParagonIE_Sodium_Core32_Int32::fromInt($g5 ^ $x5);
$g[6] = ParagonIE_Sodium_Core32_Int32::fromInt($g6 ^ $x6);
$g[7] = ParagonIE_Sodium_Core32_Int32::fromInt($g7 ^ $x7);
$g[8] = ParagonIE_Sodium_Core32_Int32::fromInt($g8 ^ $x8);
$g[9] = ParagonIE_Sodium_Core32_Int32::fromInt($g9 ^ $x9);
}
/**
* @internal You should not use this directly from another application
*
* @param ParagonIE_Sodium_Core32_Curve25519_Fe $f
* @return ParagonIE_Sodium_Core32_Curve25519_Fe
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedAssignment
* @psalm-suppress MixedMethodCall
*/
public static function fe_mul121666(ParagonIE_Sodium_Core32_Curve25519_Fe $f)
{
/** @var array<int, ParagonIE_Sodium_Core32_Int64> $h */
$h = array();
for ($i = 0; $i < 10; ++$i) {
$h[$i] = $f[$i]->toInt64()->mulInt(121666, 17);
}
$carry9 = $h[9]->addInt(1 << 24)->shiftRight(25);
$h[0] = $h[0]->addInt64($carry9->mulInt(19, 5));
$h[9] = $h[9]->subInt64($carry9->shiftLeft(25));
$carry1 = $h[1]->addInt(1 << 24)->shiftRight(25);
$h[2] = $h[2]->addInt64($carry1);
$h[1] = $h[1]->subInt64($carry1->shiftLeft(25));
$carry3 = $h[3]->addInt(1 << 24)->shiftRight(25);
$h[4] = $h[4]->addInt64($carry3);
$h[3] = $h[3]->subInt64($carry3->shiftLeft(25));
$carry5 = $h[5]->addInt(1 << 24)->shiftRight(25);
$h[6] = $h[6]->addInt64($carry5);
$h[5] = $h[5]->subInt64($carry5->shiftLeft(25));
$carry7 = $h[7]->addInt(1 << 24)->shiftRight(25);
$h[8] = $h[8]->addInt64($carry7);
$h[7] = $h[7]->subInt64($carry7->shiftLeft(25));
$carry0 = $h[0]->addInt(1 << 25)->shiftRight(26);
$h[1] = $h[1]->addInt64($carry0);
$h[0] = $h[0]->subInt64($carry0->shiftLeft(26));
$carry2 = $h[2]->addInt(1 << 25)->shiftRight(26);
$h[3] = $h[3]->addInt64($carry2);
$h[2] = $h[2]->subInt64($carry2->shiftLeft(26));
$carry4 = $h[4]->addInt(1 << 25)->shiftRight(26);
$h[5] = $h[5]->addInt64($carry4);
$h[4] = $h[4]->subInt64($carry4->shiftLeft(26));
$carry6 = $h[6]->addInt(1 << 25)->shiftRight(26);
$h[7] = $h[7]->addInt64($carry6);
$h[6] = $h[6]->subInt64($carry6->shiftLeft(26));
$carry8 = $h[8]->addInt(1 << 25)->shiftRight(26);
$h[9] = $h[9]->addInt64($carry8);
$h[8] = $h[8]->subInt64($carry8->shiftLeft(26));
for ($i = 0; $i < 10; ++$i) {
$h[$i] = $h[$i]->toInt32();
}
/** @var array<int, ParagonIE_Sodium_Core32_Int32> $h2 */
$h2 = $h;
return ParagonIE_Sodium_Core32_Curve25519_Fe::fromArray($h2);
}
/**
* @internal You should not use this directly from another application
*
* Inline comments preceded by # are from libsodium's ref10 code.
*
* @param string $n
* @param string $p
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function crypto_scalarmult_curve25519_ref10($n, $p)
{
# for (i = 0;i < 32;++i) e[i] = n[i];
$e = '' . $n;
# e[0] &= 248;
$e[0] = self::intToChr(
self::chrToInt($e[0]) & 248
);
# e[31] &= 127;
# e[31] |= 64;
$e[31] = self::intToChr(
(self::chrToInt($e[31]) & 127) | 64
);
# fe_frombytes(x1,p);
$x1 = self::fe_frombytes($p);
# fe_1(x2);
$x2 = self::fe_1();
# fe_0(z2);
$z2 = self::fe_0();
# fe_copy(x3,x1);
$x3 = self::fe_copy($x1);
# fe_1(z3);
$z3 = self::fe_1();
# swap = 0;
/** @var int $swap */
$swap = 0;
# for (pos = 254;pos >= 0;--pos) {
for ($pos = 254; $pos >= 0; --$pos) {
# b = e[pos / 8] >> (pos & 7);
/** @var int $b */
$b = self::chrToInt(
$e[(int) floor($pos / 8)]
) >> ($pos & 7);
# b &= 1;
$b &= 1;
# swap ^= b;
$swap ^= $b;
# fe_cswap(x2,x3,swap);
self::fe_cswap($x2, $x3, $swap);
# fe_cswap(z2,z3,swap);
self::fe_cswap($z2, $z3, $swap);
# swap = b;
/** @var int $swap */
$swap = $b;
# fe_sub(tmp0,x3,z3);
$tmp0 = self::fe_sub($x3, $z3);
# fe_sub(tmp1,x2,z2);
$tmp1 = self::fe_sub($x2, $z2);
# fe_add(x2,x2,z2);
$x2 = self::fe_add($x2, $z2);
# fe_add(z2,x3,z3);
$z2 = self::fe_add($x3, $z3);
# fe_mul(z3,tmp0,x2);
$z3 = self::fe_mul($tmp0, $x2);
# fe_mul(z2,z2,tmp1);
$z2 = self::fe_mul($z2, $tmp1);
# fe_sq(tmp0,tmp1);
$tmp0 = self::fe_sq($tmp1);
# fe_sq(tmp1,x2);
$tmp1 = self::fe_sq($x2);
# fe_add(x3,z3,z2);
$x3 = self::fe_add($z3, $z2);
# fe_sub(z2,z3,z2);
$z2 = self::fe_sub($z3, $z2);
# fe_mul(x2,tmp1,tmp0);
$x2 = self::fe_mul($tmp1, $tmp0);
# fe_sub(tmp1,tmp1,tmp0);
$tmp1 = self::fe_sub($tmp1, $tmp0);
# fe_sq(z2,z2);
$z2 = self::fe_sq($z2);
# fe_mul121666(z3,tmp1);
$z3 = self::fe_mul121666($tmp1);
# fe_sq(x3,x3);
$x3 = self::fe_sq($x3);
# fe_add(tmp0,tmp0,z3);
$tmp0 = self::fe_add($tmp0, $z3);
# fe_mul(z3,x1,z2);
$z3 = self::fe_mul($x1, $z2);
# fe_mul(z2,tmp1,tmp0);
$z2 = self::fe_mul($tmp1, $tmp0);
}
# fe_cswap(x2,x3,swap);
self::fe_cswap($x2, $x3, $swap);
# fe_cswap(z2,z3,swap);
self::fe_cswap($z2, $z3, $swap);
# fe_invert(z2,z2);
$z2 = self::fe_invert($z2);
# fe_mul(x2,x2,z2);
$x2 = self::fe_mul($x2, $z2);
# fe_tobytes(q,x2);
return (string) self::fe_tobytes($x2);
}
/**
* @internal You should not use this directly from another application
*
* @param ParagonIE_Sodium_Core32_Curve25519_Fe $edwardsY
* @param ParagonIE_Sodium_Core32_Curve25519_Fe $edwardsZ
* @return ParagonIE_Sodium_Core32_Curve25519_Fe
* @throws SodiumException
* @throws TypeError
*/
public static function edwards_to_montgomery(
ParagonIE_Sodium_Core32_Curve25519_Fe $edwardsY,
ParagonIE_Sodium_Core32_Curve25519_Fe $edwardsZ
) {
$tempX = self::fe_add($edwardsZ, $edwardsY);
$tempZ = self::fe_sub($edwardsZ, $edwardsY);
$tempZ = self::fe_invert($tempZ);
return self::fe_mul($tempX, $tempZ);
}
/**
* @internal You should not use this directly from another application
*
* @param string $n
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function crypto_scalarmult_curve25519_ref10_base($n)
{
# for (i = 0;i < 32;++i) e[i] = n[i];
$e = '' . $n;
# e[0] &= 248;
$e[0] = self::intToChr(
self::chrToInt($e[0]) & 248
);
# e[31] &= 127;
# e[31] |= 64;
$e[31] = self::intToChr(
(self::chrToInt($e[31]) & 127) | 64
);
$A = self::ge_scalarmult_base($e);
if (
!($A->Y instanceof ParagonIE_Sodium_Core32_Curve25519_Fe)
||
!($A->Z instanceof ParagonIE_Sodium_Core32_Curve25519_Fe)
) {
throw new TypeError('Null points encountered');
}
$pk = self::edwards_to_montgomery($A->Y, $A->Z);
return self::fe_tobytes($pk);
}
}
Core32/HChaCha20.php 0000604 00000012261 15133021213 0007660 0 ustar 00 <?php
if (class_exists('ParagonIE_Sodium_Core32_HChaCha20', false)) {
return;
}
/**
* Class ParagonIE_Sodium_Core_HChaCha20
*/
class ParagonIE_Sodium_Core32_HChaCha20 extends ParagonIE_Sodium_Core32_ChaCha20
{
/**
* @param string $in
* @param string $key
* @param string|null $c
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function hChaCha20($in = '', $key = '', $c = null)
{
$ctx = array();
if ($c === null) {
$ctx[0] = new ParagonIE_Sodium_Core32_Int32(array(0x6170, 0x7865));
$ctx[1] = new ParagonIE_Sodium_Core32_Int32(array(0x3320, 0x646e));
$ctx[2] = new ParagonIE_Sodium_Core32_Int32(array(0x7962, 0x2d32));
$ctx[3] = new ParagonIE_Sodium_Core32_Int32(array(0x6b20, 0x6574));
} else {
$ctx[0] = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($c, 0, 4));
$ctx[1] = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($c, 4, 4));
$ctx[2] = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($c, 8, 4));
$ctx[3] = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($c, 12, 4));
}
$ctx[4] = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($key, 0, 4));
$ctx[5] = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($key, 4, 4));
$ctx[6] = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($key, 8, 4));
$ctx[7] = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($key, 12, 4));
$ctx[8] = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($key, 16, 4));
$ctx[9] = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($key, 20, 4));
$ctx[10] = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($key, 24, 4));
$ctx[11] = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($key, 28, 4));
$ctx[12] = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($in, 0, 4));
$ctx[13] = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($in, 4, 4));
$ctx[14] = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($in, 8, 4));
$ctx[15] = ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($in, 12, 4));
return self::hChaCha20Bytes($ctx);
}
/**
* @param array $ctx
* @return string
* @throws SodiumException
* @throws TypeError
*/
protected static function hChaCha20Bytes(array $ctx)
{
/** @var ParagonIE_Sodium_Core32_Int32 $x0 */
$x0 = $ctx[0];
/** @var ParagonIE_Sodium_Core32_Int32 $x1 */
$x1 = $ctx[1];
/** @var ParagonIE_Sodium_Core32_Int32 $x2 */
$x2 = $ctx[2];
/** @var ParagonIE_Sodium_Core32_Int32 $x3 */
$x3 = $ctx[3];
/** @var ParagonIE_Sodium_Core32_Int32 $x4 */
$x4 = $ctx[4];
/** @var ParagonIE_Sodium_Core32_Int32 $x5 */
$x5 = $ctx[5];
/** @var ParagonIE_Sodium_Core32_Int32 $x6 */
$x6 = $ctx[6];
/** @var ParagonIE_Sodium_Core32_Int32 $x7 */
$x7 = $ctx[7];
/** @var ParagonIE_Sodium_Core32_Int32 $x8 */
$x8 = $ctx[8];
/** @var ParagonIE_Sodium_Core32_Int32 $x9 */
$x9 = $ctx[9];
/** @var ParagonIE_Sodium_Core32_Int32 $x10 */
$x10 = $ctx[10];
/** @var ParagonIE_Sodium_Core32_Int32 $x11 */
$x11 = $ctx[11];
/** @var ParagonIE_Sodium_Core32_Int32 $x12 */
$x12 = $ctx[12];
/** @var ParagonIE_Sodium_Core32_Int32 $x13 */
$x13 = $ctx[13];
/** @var ParagonIE_Sodium_Core32_Int32 $x14 */
$x14 = $ctx[14];
/** @var ParagonIE_Sodium_Core32_Int32 $x15 */
$x15 = $ctx[15];
for ($i = 0; $i < 10; ++$i) {
# QUARTERROUND( x0, x4, x8, x12)
list($x0, $x4, $x8, $x12) = self::quarterRound($x0, $x4, $x8, $x12);
# QUARTERROUND( x1, x5, x9, x13)
list($x1, $x5, $x9, $x13) = self::quarterRound($x1, $x5, $x9, $x13);
# QUARTERROUND( x2, x6, x10, x14)
list($x2, $x6, $x10, $x14) = self::quarterRound($x2, $x6, $x10, $x14);
# QUARTERROUND( x3, x7, x11, x15)
list($x3, $x7, $x11, $x15) = self::quarterRound($x3, $x7, $x11, $x15);
# QUARTERROUND( x0, x5, x10, x15)
list($x0, $x5, $x10, $x15) = self::quarterRound($x0, $x5, $x10, $x15);
# QUARTERROUND( x1, x6, x11, x12)
list($x1, $x6, $x11, $x12) = self::quarterRound($x1, $x6, $x11, $x12);
# QUARTERROUND( x2, x7, x8, x13)
list($x2, $x7, $x8, $x13) = self::quarterRound($x2, $x7, $x8, $x13);
# QUARTERROUND( x3, x4, x9, x14)
list($x3, $x4, $x9, $x14) = self::quarterRound($x3, $x4, $x9, $x14);
}
return $x0->toReverseString() .
$x1->toReverseString() .
$x2->toReverseString() .
$x3->toReverseString() .
$x12->toReverseString() .
$x13->toReverseString() .
$x14->toReverseString() .
$x15->toReverseString();
}
}
Core32/Poly1305/State.php 0000644 00000037135 15133021213 0010626 0 ustar 00 <?php
if (class_exists('ParagonIE_Sodium_Core32_Poly1305_State', false)) {
return;
}
/**
* Class ParagonIE_Sodium_Core32_Poly1305_State
*/
class ParagonIE_Sodium_Core32_Poly1305_State extends ParagonIE_Sodium_Core32_Util
{
/**
* @var array<int, int>
*/
protected $buffer = array();
/**
* @var bool
*/
protected $final = false;
/**
* @var array<int, ParagonIE_Sodium_Core32_Int32>
*/
public $h;
/**
* @var int
*/
protected $leftover = 0;
/**
* @var array<int, ParagonIE_Sodium_Core32_Int32>
*/
public $r;
/**
* @var array<int, ParagonIE_Sodium_Core32_Int64>
*/
public $pad;
/**
* ParagonIE_Sodium_Core32_Poly1305_State constructor.
*
* @internal You should not use this directly from another application
*
* @param string $key
* @throws InvalidArgumentException
* @throws SodiumException
* @throws TypeError
*/
public function __construct($key = '')
{
if (self::strlen($key) < 32) {
throw new InvalidArgumentException(
'Poly1305 requires a 32-byte key'
);
}
/* r &= 0xffffffc0ffffffc0ffffffc0fffffff */
$this->r = array(
// st->r[0] = ...
ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($key, 0, 4))
->setUnsignedInt(true)
->mask(0x3ffffff),
// st->r[1] = ...
ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($key, 3, 4))
->setUnsignedInt(true)
->shiftRight(2)
->mask(0x3ffff03),
// st->r[2] = ...
ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($key, 6, 4))
->setUnsignedInt(true)
->shiftRight(4)
->mask(0x3ffc0ff),
// st->r[3] = ...
ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($key, 9, 4))
->setUnsignedInt(true)
->shiftRight(6)
->mask(0x3f03fff),
// st->r[4] = ...
ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($key, 12, 4))
->setUnsignedInt(true)
->shiftRight(8)
->mask(0x00fffff)
);
/* h = 0 */
$this->h = array(
new ParagonIE_Sodium_Core32_Int32(array(0, 0), true),
new ParagonIE_Sodium_Core32_Int32(array(0, 0), true),
new ParagonIE_Sodium_Core32_Int32(array(0, 0), true),
new ParagonIE_Sodium_Core32_Int32(array(0, 0), true),
new ParagonIE_Sodium_Core32_Int32(array(0, 0), true)
);
/* save pad for later */
$this->pad = array(
ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($key, 16, 4))
->setUnsignedInt(true)->toInt64(),
ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($key, 20, 4))
->setUnsignedInt(true)->toInt64(),
ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($key, 24, 4))
->setUnsignedInt(true)->toInt64(),
ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($key, 28, 4))
->setUnsignedInt(true)->toInt64(),
);
$this->leftover = 0;
$this->final = false;
}
/**
* @internal You should not use this directly from another application
*
* @param string $message
* @return self
* @throws SodiumException
* @throws TypeError
*/
public function update($message = '')
{
$bytes = self::strlen($message);
/* handle leftover */
if ($this->leftover) {
/** @var int $want */
$want = ParagonIE_Sodium_Core32_Poly1305::BLOCK_SIZE - $this->leftover;
if ($want > $bytes) {
$want = $bytes;
}
for ($i = 0; $i < $want; ++$i) {
$mi = self::chrToInt($message[$i]);
$this->buffer[$this->leftover + $i] = $mi;
}
// We snip off the leftmost bytes.
$message = self::substr($message, $want);
$bytes = self::strlen($message);
$this->leftover += $want;
if ($this->leftover < ParagonIE_Sodium_Core32_Poly1305::BLOCK_SIZE) {
// We still don't have enough to run $this->blocks()
return $this;
}
$this->blocks(
self::intArrayToString($this->buffer),
ParagonIE_Sodium_Core32_Poly1305::BLOCK_SIZE
);
$this->leftover = 0;
}
/* process full blocks */
if ($bytes >= ParagonIE_Sodium_Core32_Poly1305::BLOCK_SIZE) {
/** @var int $want */
$want = $bytes & ~(ParagonIE_Sodium_Core32_Poly1305::BLOCK_SIZE - 1);
if ($want >= ParagonIE_Sodium_Core32_Poly1305::BLOCK_SIZE) {
/** @var string $block */
$block = self::substr($message, 0, $want);
if (self::strlen($block) >= ParagonIE_Sodium_Core32_Poly1305::BLOCK_SIZE) {
$this->blocks($block, $want);
$message = self::substr($message, $want);
$bytes = self::strlen($message);
}
}
}
/* store leftover */
if ($bytes) {
for ($i = 0; $i < $bytes; ++$i) {
$mi = self::chrToInt($message[$i]);
$this->buffer[$this->leftover + $i] = $mi;
}
$this->leftover = (int) $this->leftover + $bytes;
}
return $this;
}
/**
* @internal You should not use this directly from another application
*
* @param string $message
* @param int $bytes
* @return self
* @throws SodiumException
* @throws TypeError
*/
public function blocks($message, $bytes)
{
if (self::strlen($message) < 16) {
$message = str_pad($message, 16, "\x00", STR_PAD_RIGHT);
}
$hibit = ParagonIE_Sodium_Core32_Int32::fromInt((int) ($this->final ? 0 : 1 << 24)); /* 1 << 128 */
$hibit->setUnsignedInt(true);
$zero = new ParagonIE_Sodium_Core32_Int64(array(0, 0, 0, 0), true);
/**
* @var ParagonIE_Sodium_Core32_Int64 $d0
* @var ParagonIE_Sodium_Core32_Int64 $d1
* @var ParagonIE_Sodium_Core32_Int64 $d2
* @var ParagonIE_Sodium_Core32_Int64 $d3
* @var ParagonIE_Sodium_Core32_Int64 $d4
* @var ParagonIE_Sodium_Core32_Int64 $r0
* @var ParagonIE_Sodium_Core32_Int64 $r1
* @var ParagonIE_Sodium_Core32_Int64 $r2
* @var ParagonIE_Sodium_Core32_Int64 $r3
* @var ParagonIE_Sodium_Core32_Int64 $r4
*
* @var ParagonIE_Sodium_Core32_Int32 $h0
* @var ParagonIE_Sodium_Core32_Int32 $h1
* @var ParagonIE_Sodium_Core32_Int32 $h2
* @var ParagonIE_Sodium_Core32_Int32 $h3
* @var ParagonIE_Sodium_Core32_Int32 $h4
*/
$r0 = $this->r[0]->toInt64();
$r1 = $this->r[1]->toInt64();
$r2 = $this->r[2]->toInt64();
$r3 = $this->r[3]->toInt64();
$r4 = $this->r[4]->toInt64();
$s1 = $r1->toInt64()->mulInt(5, 3);
$s2 = $r2->toInt64()->mulInt(5, 3);
$s3 = $r3->toInt64()->mulInt(5, 3);
$s4 = $r4->toInt64()->mulInt(5, 3);
$h0 = $this->h[0];
$h1 = $this->h[1];
$h2 = $this->h[2];
$h3 = $this->h[3];
$h4 = $this->h[4];
while ($bytes >= ParagonIE_Sodium_Core32_Poly1305::BLOCK_SIZE) {
/* h += m[i] */
$h0 = $h0->addInt32(
ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($message, 0, 4))
->mask(0x3ffffff)
)->toInt64();
$h1 = $h1->addInt32(
ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($message, 3, 4))
->shiftRight(2)
->mask(0x3ffffff)
)->toInt64();
$h2 = $h2->addInt32(
ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($message, 6, 4))
->shiftRight(4)
->mask(0x3ffffff)
)->toInt64();
$h3 = $h3->addInt32(
ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($message, 9, 4))
->shiftRight(6)
->mask(0x3ffffff)
)->toInt64();
$h4 = $h4->addInt32(
ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($message, 12, 4))
->shiftRight(8)
->orInt32($hibit)
)->toInt64();
/* h *= r */
$d0 = $zero
->addInt64($h0->mulInt64($r0, 27))
->addInt64($s4->mulInt64($h1, 27))
->addInt64($s3->mulInt64($h2, 27))
->addInt64($s2->mulInt64($h3, 27))
->addInt64($s1->mulInt64($h4, 27));
$d1 = $zero
->addInt64($h0->mulInt64($r1, 27))
->addInt64($h1->mulInt64($r0, 27))
->addInt64($s4->mulInt64($h2, 27))
->addInt64($s3->mulInt64($h3, 27))
->addInt64($s2->mulInt64($h4, 27));
$d2 = $zero
->addInt64($h0->mulInt64($r2, 27))
->addInt64($h1->mulInt64($r1, 27))
->addInt64($h2->mulInt64($r0, 27))
->addInt64($s4->mulInt64($h3, 27))
->addInt64($s3->mulInt64($h4, 27));
$d3 = $zero
->addInt64($h0->mulInt64($r3, 27))
->addInt64($h1->mulInt64($r2, 27))
->addInt64($h2->mulInt64($r1, 27))
->addInt64($h3->mulInt64($r0, 27))
->addInt64($s4->mulInt64($h4, 27));
$d4 = $zero
->addInt64($h0->mulInt64($r4, 27))
->addInt64($h1->mulInt64($r3, 27))
->addInt64($h2->mulInt64($r2, 27))
->addInt64($h3->mulInt64($r1, 27))
->addInt64($h4->mulInt64($r0, 27));
/* (partial) h %= p */
$c = $d0->shiftRight(26);
$h0 = $d0->toInt32()->mask(0x3ffffff);
$d1 = $d1->addInt64($c);
$c = $d1->shiftRight(26);
$h1 = $d1->toInt32()->mask(0x3ffffff);
$d2 = $d2->addInt64($c);
$c = $d2->shiftRight(26);
$h2 = $d2->toInt32()->mask(0x3ffffff);
$d3 = $d3->addInt64($c);
$c = $d3->shiftRight(26);
$h3 = $d3->toInt32()->mask(0x3ffffff);
$d4 = $d4->addInt64($c);
$c = $d4->shiftRight(26);
$h4 = $d4->toInt32()->mask(0x3ffffff);
$h0 = $h0->addInt32($c->toInt32()->mulInt(5, 3));
$c = $h0->shiftRight(26);
$h0 = $h0->mask(0x3ffffff);
$h1 = $h1->addInt32($c);
// Chop off the left 32 bytes.
$message = self::substr(
$message,
ParagonIE_Sodium_Core32_Poly1305::BLOCK_SIZE
);
$bytes -= ParagonIE_Sodium_Core32_Poly1305::BLOCK_SIZE;
}
/** @var array<int, ParagonIE_Sodium_Core32_Int32> $h */
$this->h = array($h0, $h1, $h2, $h3, $h4);
return $this;
}
/**
* @internal You should not use this directly from another application
*
* @return string
* @throws SodiumException
* @throws TypeError
*/
public function finish()
{
/* process the remaining block */
if ($this->leftover) {
$i = $this->leftover;
$this->buffer[$i++] = 1;
for (; $i < ParagonIE_Sodium_Core32_Poly1305::BLOCK_SIZE; ++$i) {
$this->buffer[$i] = 0;
}
$this->final = true;
$this->blocks(
self::substr(
self::intArrayToString($this->buffer),
0,
ParagonIE_Sodium_Core32_Poly1305::BLOCK_SIZE
),
$b = ParagonIE_Sodium_Core32_Poly1305::BLOCK_SIZE
);
}
/**
* @var ParagonIE_Sodium_Core32_Int32 $f
* @var ParagonIE_Sodium_Core32_Int32 $g0
* @var ParagonIE_Sodium_Core32_Int32 $g1
* @var ParagonIE_Sodium_Core32_Int32 $g2
* @var ParagonIE_Sodium_Core32_Int32 $g3
* @var ParagonIE_Sodium_Core32_Int32 $g4
* @var ParagonIE_Sodium_Core32_Int32 $h0
* @var ParagonIE_Sodium_Core32_Int32 $h1
* @var ParagonIE_Sodium_Core32_Int32 $h2
* @var ParagonIE_Sodium_Core32_Int32 $h3
* @var ParagonIE_Sodium_Core32_Int32 $h4
*/
$h0 = $this->h[0];
$h1 = $this->h[1];
$h2 = $this->h[2];
$h3 = $this->h[3];
$h4 = $this->h[4];
$c = $h1->shiftRight(26); # $c = $h1 >> 26;
$h1 = $h1->mask(0x3ffffff); # $h1 &= 0x3ffffff;
$h2 = $h2->addInt32($c); # $h2 += $c;
$c = $h2->shiftRight(26); # $c = $h2 >> 26;
$h2 = $h2->mask(0x3ffffff); # $h2 &= 0x3ffffff;
$h3 = $h3->addInt32($c); # $h3 += $c;
$c = $h3->shiftRight(26); # $c = $h3 >> 26;
$h3 = $h3->mask(0x3ffffff); # $h3 &= 0x3ffffff;
$h4 = $h4->addInt32($c); # $h4 += $c;
$c = $h4->shiftRight(26); # $c = $h4 >> 26;
$h4 = $h4->mask(0x3ffffff); # $h4 &= 0x3ffffff;
$h0 = $h0->addInt32($c->mulInt(5, 3)); # $h0 += self::mul($c, 5);
$c = $h0->shiftRight(26); # $c = $h0 >> 26;
$h0 = $h0->mask(0x3ffffff); # $h0 &= 0x3ffffff;
$h1 = $h1->addInt32($c); # $h1 += $c;
/* compute h + -p */
$g0 = $h0->addInt(5);
$c = $g0->shiftRight(26);
$g0 = $g0->mask(0x3ffffff);
$g1 = $h1->addInt32($c);
$c = $g1->shiftRight(26);
$g1 = $g1->mask(0x3ffffff);
$g2 = $h2->addInt32($c);
$c = $g2->shiftRight(26);
$g2 = $g2->mask(0x3ffffff);
$g3 = $h3->addInt32($c);
$c = $g3->shiftRight(26);
$g3 = $g3->mask(0x3ffffff);
$g4 = $h4->addInt32($c)->subInt(1 << 26);
# $mask = ($g4 >> 31) - 1;
/* select h if h < p, or h + -p if h >= p */
$mask = (int) (($g4->toInt() >> 31) + 1);
$g0 = $g0->mask($mask);
$g1 = $g1->mask($mask);
$g2 = $g2->mask($mask);
$g3 = $g3->mask($mask);
$g4 = $g4->mask($mask);
/** @var int $mask */
$mask = ~$mask;
$h0 = $h0->mask($mask)->orInt32($g0);
$h1 = $h1->mask($mask)->orInt32($g1);
$h2 = $h2->mask($mask)->orInt32($g2);
$h3 = $h3->mask($mask)->orInt32($g3);
$h4 = $h4->mask($mask)->orInt32($g4);
/* h = h % (2^128) */
$h0 = $h0->orInt32($h1->shiftLeft(26));
$h1 = $h1->shiftRight(6)->orInt32($h2->shiftLeft(20));
$h2 = $h2->shiftRight(12)->orInt32($h3->shiftLeft(14));
$h3 = $h3->shiftRight(18)->orInt32($h4->shiftLeft(8));
/* mac = (h + pad) % (2^128) */
$f = $h0->toInt64()->addInt64($this->pad[0]);
$h0 = $f->toInt32();
$f = $h1->toInt64()->addInt64($this->pad[1])->addInt($h0->overflow);
$h1 = $f->toInt32();
$f = $h2->toInt64()->addInt64($this->pad[2])->addInt($h1->overflow);
$h2 = $f->toInt32();
$f = $h3->toInt64()->addInt64($this->pad[3])->addInt($h2->overflow);
$h3 = $f->toInt32();
return $h0->toReverseString() .
$h1->toReverseString() .
$h2->toReverseString() .
$h3->toReverseString();
}
}
Core32/SecretStream/State.php 0000604 00000007110 15133021213 0011755 0 ustar 00 <?php
/**
* Class ParagonIE_Sodium_Core32_SecretStream_State
*/
class ParagonIE_Sodium_Core32_SecretStream_State
{
/** @var string $key */
protected $key;
/** @var int $counter */
protected $counter;
/** @var string $nonce */
protected $nonce;
/** @var string $_pad */
protected $_pad;
/**
* ParagonIE_Sodium_Core32_SecretStream_State constructor.
* @param string $key
* @param string|null $nonce
*/
public function __construct($key, $nonce = null)
{
$this->key = $key;
$this->counter = 1;
if (is_null($nonce)) {
$nonce = str_repeat("\0", 12);
}
$this->nonce = str_pad($nonce, 12, "\0", STR_PAD_RIGHT);;
$this->_pad = str_repeat("\0", 4);
}
/**
* @return self
*/
public function counterReset()
{
$this->counter = 1;
$this->_pad = str_repeat("\0", 4);
return $this;
}
/**
* @return string
*/
public function getKey()
{
return $this->key;
}
/**
* @return string
*/
public function getCounter()
{
return ParagonIE_Sodium_Core32_Util::store32_le($this->counter);
}
/**
* @return string
*/
public function getNonce()
{
if (!is_string($this->nonce)) {
$this->nonce = str_repeat("\0", 12);
}
if (ParagonIE_Sodium_Core32_Util::strlen($this->nonce) !== 12) {
$this->nonce = str_pad($this->nonce, 12, "\0", STR_PAD_RIGHT);
}
return $this->nonce;
}
/**
* @return string
*/
public function getCombinedNonce()
{
return $this->getCounter() .
ParagonIE_Sodium_Core32_Util::substr($this->getNonce(), 0, 8);
}
/**
* @return self
*/
public function incrementCounter()
{
++$this->counter;
return $this;
}
/**
* @return bool
*/
public function needsRekey()
{
return ($this->counter & 0xffff) === 0;
}
/**
* @param string $newKeyAndNonce
* @return self
*/
public function rekey($newKeyAndNonce)
{
$this->key = ParagonIE_Sodium_Core32_Util::substr($newKeyAndNonce, 0, 32);
$this->nonce = str_pad(
ParagonIE_Sodium_Core32_Util::substr($newKeyAndNonce, 32),
12,
"\0",
STR_PAD_RIGHT
);
return $this;
}
/**
* @param string $str
* @return self
*/
public function xorNonce($str)
{
$this->nonce = ParagonIE_Sodium_Core32_Util::xorStrings(
$this->getNonce(),
str_pad(
ParagonIE_Sodium_Core32_Util::substr($str, 0, 8),
12,
"\0",
STR_PAD_RIGHT
)
);
return $this;
}
/**
* @param string $string
* @return self
*/
public static function fromString($string)
{
$state = new ParagonIE_Sodium_Core32_SecretStream_State(
ParagonIE_Sodium_Core32_Util::substr($string, 0, 32)
);
$state->counter = ParagonIE_Sodium_Core32_Util::load_4(
ParagonIE_Sodium_Core32_Util::substr($string, 32, 4)
);
$state->nonce = ParagonIE_Sodium_Core32_Util::substr($string, 36, 12);
$state->_pad = ParagonIE_Sodium_Core32_Util::substr($string, 48, 8);
return $state;
}
/**
* @return string
*/
public function toString()
{
return $this->key .
$this->getCounter() .
$this->nonce .
$this->_pad;
}
}
Core32/XSalsa20.php 0000604 00000002543 15133021213 0007636 0 ustar 00 <?php
if (class_exists('ParagonIE_Sodium_Core32_XSalsa20', false)) {
return;
}
/**
* Class ParagonIE_Sodium_Core32_XSalsa20
*/
abstract class ParagonIE_Sodium_Core32_XSalsa20 extends ParagonIE_Sodium_Core32_HSalsa20
{
/**
* Expand a key and nonce into an xsalsa20 keystream.
*
* @internal You should not use this directly from another application
*
* @param int $len
* @param string $nonce
* @param string $key
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function xsalsa20($len, $nonce, $key)
{
$ret = self::salsa20(
$len,
self::substr($nonce, 16, 8),
self::hsalsa20($nonce, $key)
);
return $ret;
}
/**
* Encrypt a string with XSalsa20. Doesn't provide integrity.
*
* @internal You should not use this directly from another application
*
* @param string $message
* @param string $nonce
* @param string $key
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function xsalsa20_xor($message, $nonce, $key)
{
return self::xorStrings(
$message,
self::xsalsa20(
self::strlen($message),
$nonce,
$key
)
);
}
}
SodiumException.php 0000604 00000000236 15133021213 0010360 0 ustar 00 <?php
if (!class_exists('SodiumException', false)) {
/**
* Class SodiumException
*/
class SodiumException extends Exception
{
}
}
Crypto32.php 0000604 00000153517 15133021213 0006701 0 ustar 00 <?php
if (class_exists('ParagonIE_Sodium_Crypto32', false)) {
return;
}
/**
* Class ParagonIE_Sodium_Crypto
*
* ATTENTION!
*
* If you are using this library, you should be using
* ParagonIE_Sodium_Compat in your code, not this class.
*/
abstract class ParagonIE_Sodium_Crypto32
{
const aead_chacha20poly1305_KEYBYTES = 32;
const aead_chacha20poly1305_NSECBYTES = 0;
const aead_chacha20poly1305_NPUBBYTES = 8;
const aead_chacha20poly1305_ABYTES = 16;
const aead_chacha20poly1305_IETF_KEYBYTES = 32;
const aead_chacha20poly1305_IETF_NSECBYTES = 0;
const aead_chacha20poly1305_IETF_NPUBBYTES = 12;
const aead_chacha20poly1305_IETF_ABYTES = 16;
const aead_xchacha20poly1305_IETF_KEYBYTES = 32;
const aead_xchacha20poly1305_IETF_NSECBYTES = 0;
const aead_xchacha20poly1305_IETF_NPUBBYTES = 24;
const aead_xchacha20poly1305_IETF_ABYTES = 16;
const box_curve25519xsalsa20poly1305_SEEDBYTES = 32;
const box_curve25519xsalsa20poly1305_PUBLICKEYBYTES = 32;
const box_curve25519xsalsa20poly1305_SECRETKEYBYTES = 32;
const box_curve25519xsalsa20poly1305_BEFORENMBYTES = 32;
const box_curve25519xsalsa20poly1305_NONCEBYTES = 24;
const box_curve25519xsalsa20poly1305_MACBYTES = 16;
const box_curve25519xsalsa20poly1305_BOXZEROBYTES = 16;
const box_curve25519xsalsa20poly1305_ZEROBYTES = 32;
const onetimeauth_poly1305_BYTES = 16;
const onetimeauth_poly1305_KEYBYTES = 32;
const secretbox_xsalsa20poly1305_KEYBYTES = 32;
const secretbox_xsalsa20poly1305_NONCEBYTES = 24;
const secretbox_xsalsa20poly1305_MACBYTES = 16;
const secretbox_xsalsa20poly1305_BOXZEROBYTES = 16;
const secretbox_xsalsa20poly1305_ZEROBYTES = 32;
const secretbox_xchacha20poly1305_KEYBYTES = 32;
const secretbox_xchacha20poly1305_NONCEBYTES = 24;
const secretbox_xchacha20poly1305_MACBYTES = 16;
const secretbox_xchacha20poly1305_BOXZEROBYTES = 16;
const secretbox_xchacha20poly1305_ZEROBYTES = 32;
const stream_salsa20_KEYBYTES = 32;
/**
* AEAD Decryption with ChaCha20-Poly1305
*
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
*
* @param string $message
* @param string $ad
* @param string $nonce
* @param string $key
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function aead_chacha20poly1305_decrypt(
$message = '',
$ad = '',
$nonce = '',
$key = ''
) {
/** @var int $len - Length of message (ciphertext + MAC) */
$len = ParagonIE_Sodium_Core32_Util::strlen($message);
/** @var int $clen - Length of ciphertext */
$clen = $len - self::aead_chacha20poly1305_ABYTES;
/** @var int $adlen - Length of associated data */
$adlen = ParagonIE_Sodium_Core32_Util::strlen($ad);
/** @var string $mac - Message authentication code */
$mac = ParagonIE_Sodium_Core32_Util::substr(
$message,
$clen,
self::aead_chacha20poly1305_ABYTES
);
/** @var string $ciphertext - The encrypted message (sans MAC) */
$ciphertext = ParagonIE_Sodium_Core32_Util::substr($message, 0, $clen);
/** @var string The first block of the chacha20 keystream, used as a poly1305 key */
$block0 = ParagonIE_Sodium_Core32_ChaCha20::stream(
32,
$nonce,
$key
);
/* Recalculate the Poly1305 authentication tag (MAC): */
$state = new ParagonIE_Sodium_Core32_Poly1305_State($block0);
try {
ParagonIE_Sodium_Compat::memzero($block0);
} catch (SodiumException $ex) {
$block0 = null;
}
$state->update($ad);
$state->update(ParagonIE_Sodium_Core32_Util::store64_le($adlen));
$state->update($ciphertext);
$state->update(ParagonIE_Sodium_Core32_Util::store64_le($clen));
$computed_mac = $state->finish();
/* Compare the given MAC with the recalculated MAC: */
if (!ParagonIE_Sodium_Core32_Util::verify_16($computed_mac, $mac)) {
throw new SodiumException('Invalid MAC');
}
// Here, we know that the MAC is valid, so we decrypt and return the plaintext
return ParagonIE_Sodium_Core32_ChaCha20::streamXorIc(
$ciphertext,
$nonce,
$key,
ParagonIE_Sodium_Core32_Util::store64_le(1)
);
}
/**
* AEAD Encryption with ChaCha20-Poly1305
*
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
*
* @param string $message
* @param string $ad
* @param string $nonce
* @param string $key
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function aead_chacha20poly1305_encrypt(
$message = '',
$ad = '',
$nonce = '',
$key = ''
) {
/** @var int $len - Length of the plaintext message */
$len = ParagonIE_Sodium_Core32_Util::strlen($message);
/** @var int $adlen - Length of the associated data */
$adlen = ParagonIE_Sodium_Core32_Util::strlen($ad);
/** @var string The first block of the chacha20 keystream, used as a poly1305 key */
$block0 = ParagonIE_Sodium_Core32_ChaCha20::stream(
32,
$nonce,
$key
);
$state = new ParagonIE_Sodium_Core32_Poly1305_State($block0);
try {
ParagonIE_Sodium_Compat::memzero($block0);
} catch (SodiumException $ex) {
$block0 = null;
}
/** @var string $ciphertext - Raw encrypted data */
$ciphertext = ParagonIE_Sodium_Core32_ChaCha20::streamXorIc(
$message,
$nonce,
$key,
ParagonIE_Sodium_Core32_Util::store64_le(1)
);
$state->update($ad);
$state->update(ParagonIE_Sodium_Core32_Util::store64_le($adlen));
$state->update($ciphertext);
$state->update(ParagonIE_Sodium_Core32_Util::store64_le($len));
return $ciphertext . $state->finish();
}
/**
* AEAD Decryption with ChaCha20-Poly1305, IETF mode (96-bit nonce)
*
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
*
* @param string $message
* @param string $ad
* @param string $nonce
* @param string $key
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function aead_chacha20poly1305_ietf_decrypt(
$message = '',
$ad = '',
$nonce = '',
$key = ''
) {
/** @var int $adlen - Length of associated data */
$adlen = ParagonIE_Sodium_Core32_Util::strlen($ad);
/** @var int $len - Length of message (ciphertext + MAC) */
$len = ParagonIE_Sodium_Core32_Util::strlen($message);
/** @var int $clen - Length of ciphertext */
$clen = $len - self::aead_chacha20poly1305_IETF_ABYTES;
/** @var string The first block of the chacha20 keystream, used as a poly1305 key */
$block0 = ParagonIE_Sodium_Core32_ChaCha20::ietfStream(
32,
$nonce,
$key
);
/** @var string $mac - Message authentication code */
$mac = ParagonIE_Sodium_Core32_Util::substr(
$message,
$len - self::aead_chacha20poly1305_IETF_ABYTES,
self::aead_chacha20poly1305_IETF_ABYTES
);
/** @var string $ciphertext - The encrypted message (sans MAC) */
$ciphertext = ParagonIE_Sodium_Core32_Util::substr(
$message,
0,
$len - self::aead_chacha20poly1305_IETF_ABYTES
);
/* Recalculate the Poly1305 authentication tag (MAC): */
$state = new ParagonIE_Sodium_Core32_Poly1305_State($block0);
try {
ParagonIE_Sodium_Compat::memzero($block0);
} catch (SodiumException $ex) {
$block0 = null;
}
$state->update($ad);
$state->update(str_repeat("\x00", ((0x10 - $adlen) & 0xf)));
$state->update($ciphertext);
$state->update(str_repeat("\x00", (0x10 - $clen) & 0xf));
$state->update(ParagonIE_Sodium_Core32_Util::store64_le($adlen));
$state->update(ParagonIE_Sodium_Core32_Util::store64_le($clen));
$computed_mac = $state->finish();
/* Compare the given MAC with the recalculated MAC: */
if (!ParagonIE_Sodium_Core32_Util::verify_16($computed_mac, $mac)) {
throw new SodiumException('Invalid MAC');
}
// Here, we know that the MAC is valid, so we decrypt and return the plaintext
return ParagonIE_Sodium_Core32_ChaCha20::ietfStreamXorIc(
$ciphertext,
$nonce,
$key,
ParagonIE_Sodium_Core32_Util::store64_le(1)
);
}
/**
* AEAD Encryption with ChaCha20-Poly1305, IETF mode (96-bit nonce)
*
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
*
* @param string $message
* @param string $ad
* @param string $nonce
* @param string $key
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function aead_chacha20poly1305_ietf_encrypt(
$message = '',
$ad = '',
$nonce = '',
$key = ''
) {
/** @var int $len - Length of the plaintext message */
$len = ParagonIE_Sodium_Core32_Util::strlen($message);
/** @var int $adlen - Length of the associated data */
$adlen = ParagonIE_Sodium_Core32_Util::strlen($ad);
/** @var string The first block of the chacha20 keystream, used as a poly1305 key */
$block0 = ParagonIE_Sodium_Core32_ChaCha20::ietfStream(
32,
$nonce,
$key
);
$state = new ParagonIE_Sodium_Core32_Poly1305_State($block0);
try {
ParagonIE_Sodium_Compat::memzero($block0);
} catch (SodiumException $ex) {
$block0 = null;
}
/** @var string $ciphertext - Raw encrypted data */
$ciphertext = ParagonIE_Sodium_Core32_ChaCha20::ietfStreamXorIc(
$message,
$nonce,
$key,
ParagonIE_Sodium_Core32_Util::store64_le(1)
);
$state->update($ad);
$state->update(str_repeat("\x00", ((0x10 - $adlen) & 0xf)));
$state->update($ciphertext);
$state->update(str_repeat("\x00", ((0x10 - $len) & 0xf)));
$state->update(ParagonIE_Sodium_Core32_Util::store64_le($adlen));
$state->update(ParagonIE_Sodium_Core32_Util::store64_le($len));
return $ciphertext . $state->finish();
}
/**
* AEAD Decryption with ChaCha20-Poly1305, IETF mode (96-bit nonce)
*
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
*
* @param string $message
* @param string $ad
* @param string $nonce
* @param string $key
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function aead_xchacha20poly1305_ietf_decrypt(
$message = '',
$ad = '',
$nonce = '',
$key = ''
) {
$subkey = ParagonIE_Sodium_Core32_HChaCha20::hChaCha20(
ParagonIE_Sodium_Core32_Util::substr($nonce, 0, 16),
$key
);
$nonceLast = "\x00\x00\x00\x00" .
ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8);
return self::aead_chacha20poly1305_ietf_decrypt($message, $ad, $nonceLast, $subkey);
}
/**
* AEAD Encryption with ChaCha20-Poly1305, IETF mode (96-bit nonce)
*
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
*
* @param string $message
* @param string $ad
* @param string $nonce
* @param string $key
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function aead_xchacha20poly1305_ietf_encrypt(
$message = '',
$ad = '',
$nonce = '',
$key = ''
) {
$subkey = ParagonIE_Sodium_Core32_HChaCha20::hChaCha20(
ParagonIE_Sodium_Core32_Util::substr($nonce, 0, 16),
$key
);
$nonceLast = "\x00\x00\x00\x00" .
ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8);
return self::aead_chacha20poly1305_ietf_encrypt($message, $ad, $nonceLast, $subkey);
}
/**
* HMAC-SHA-512-256 (a.k.a. the leftmost 256 bits of HMAC-SHA-512)
*
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
*
* @param string $message
* @param string $key
* @return string
* @throws TypeError
*/
public static function auth($message, $key)
{
return ParagonIE_Sodium_Core32_Util::substr(
hash_hmac('sha512', $message, $key, true),
0,
32
);
}
/**
* HMAC-SHA-512-256 validation. Constant-time via hash_equals().
*
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
*
* @param string $mac
* @param string $message
* @param string $key
* @return bool
* @throws SodiumException
* @throws TypeError
*/
public static function auth_verify($mac, $message, $key)
{
return ParagonIE_Sodium_Core32_Util::hashEquals(
$mac,
self::auth($message, $key)
);
}
/**
* X25519 key exchange followed by XSalsa20Poly1305 symmetric encryption
*
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
*
* @param string $plaintext
* @param string $nonce
* @param string $keypair
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function box($plaintext, $nonce, $keypair)
{
return self::secretbox(
$plaintext,
$nonce,
self::box_beforenm(
self::box_secretkey($keypair),
self::box_publickey($keypair)
)
);
}
/**
* X25519-XSalsa20-Poly1305 with one ephemeral X25519 keypair.
*
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
*
* @param string $message
* @param string $publicKey
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function box_seal($message, $publicKey)
{
/** @var string $ephemeralKeypair */
$ephemeralKeypair = self::box_keypair();
/** @var string $ephemeralSK */
$ephemeralSK = self::box_secretkey($ephemeralKeypair);
/** @var string $ephemeralPK */
$ephemeralPK = self::box_publickey($ephemeralKeypair);
/** @var string $nonce */
$nonce = self::generichash(
$ephemeralPK . $publicKey,
'',
24
);
/** @var string $keypair - The combined keypair used in crypto_box() */
$keypair = self::box_keypair_from_secretkey_and_publickey($ephemeralSK, $publicKey);
/** @var string $ciphertext Ciphertext + MAC from crypto_box */
$ciphertext = self::box($message, $nonce, $keypair);
try {
ParagonIE_Sodium_Compat::memzero($ephemeralKeypair);
ParagonIE_Sodium_Compat::memzero($ephemeralSK);
ParagonIE_Sodium_Compat::memzero($nonce);
} catch (SodiumException $ex) {
$ephemeralKeypair = null;
$ephemeralSK = null;
$nonce = null;
}
return $ephemeralPK . $ciphertext;
}
/**
* Opens a message encrypted via box_seal().
*
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
*
* @param string $message
* @param string $keypair
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function box_seal_open($message, $keypair)
{
/** @var string $ephemeralPK */
$ephemeralPK = ParagonIE_Sodium_Core32_Util::substr($message, 0, 32);
/** @var string $ciphertext (ciphertext + MAC) */
$ciphertext = ParagonIE_Sodium_Core32_Util::substr($message, 32);
/** @var string $secretKey */
$secretKey = self::box_secretkey($keypair);
/** @var string $publicKey */
$publicKey = self::box_publickey($keypair);
/** @var string $nonce */
$nonce = self::generichash(
$ephemeralPK . $publicKey,
'',
24
);
/** @var string $keypair */
$keypair = self::box_keypair_from_secretkey_and_publickey($secretKey, $ephemeralPK);
/** @var string $m */
$m = self::box_open($ciphertext, $nonce, $keypair);
try {
ParagonIE_Sodium_Compat::memzero($secretKey);
ParagonIE_Sodium_Compat::memzero($ephemeralPK);
ParagonIE_Sodium_Compat::memzero($nonce);
} catch (SodiumException $ex) {
$secretKey = null;
$ephemeralPK = null;
$nonce = null;
}
return $m;
}
/**
* Used by crypto_box() to get the crypto_secretbox() key.
*
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
*
* @param string $sk
* @param string $pk
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function box_beforenm($sk, $pk)
{
return ParagonIE_Sodium_Core32_HSalsa20::hsalsa20(
str_repeat("\x00", 16),
self::scalarmult($sk, $pk)
);
}
/**
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
*
* @return string
* @throws Exception
* @throws SodiumException
* @throws TypeError
*/
public static function box_keypair()
{
$sKey = random_bytes(32);
$pKey = self::scalarmult_base($sKey);
return $sKey . $pKey;
}
/**
* @param string $seed
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function box_seed_keypair($seed)
{
$sKey = ParagonIE_Sodium_Core32_Util::substr(
hash('sha512', $seed, true),
0,
32
);
$pKey = self::scalarmult_base($sKey);
return $sKey . $pKey;
}
/**
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
*
* @param string $sKey
* @param string $pKey
* @return string
* @throws TypeError
*/
public static function box_keypair_from_secretkey_and_publickey($sKey, $pKey)
{
return ParagonIE_Sodium_Core32_Util::substr($sKey, 0, 32) .
ParagonIE_Sodium_Core32_Util::substr($pKey, 0, 32);
}
/**
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
*
* @param string $keypair
* @return string
* @throws RangeException
* @throws TypeError
*/
public static function box_secretkey($keypair)
{
if (ParagonIE_Sodium_Core32_Util::strlen($keypair) !== 64) {
throw new RangeException(
'Must be ParagonIE_Sodium_Compat::CRYPTO_BOX_KEYPAIRBYTES bytes long.'
);
}
return ParagonIE_Sodium_Core32_Util::substr($keypair, 0, 32);
}
/**
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
*
* @param string $keypair
* @return string
* @throws RangeException
* @throws TypeError
*/
public static function box_publickey($keypair)
{
if (ParagonIE_Sodium_Core32_Util::strlen($keypair) !== ParagonIE_Sodium_Compat::CRYPTO_BOX_KEYPAIRBYTES) {
throw new RangeException(
'Must be ParagonIE_Sodium_Compat::CRYPTO_BOX_KEYPAIRBYTES bytes long.'
);
}
return ParagonIE_Sodium_Core32_Util::substr($keypair, 32, 32);
}
/**
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
*
* @param string $sKey
* @return string
* @throws RangeException
* @throws SodiumException
* @throws TypeError
*/
public static function box_publickey_from_secretkey($sKey)
{
if (ParagonIE_Sodium_Core32_Util::strlen($sKey) !== ParagonIE_Sodium_Compat::CRYPTO_BOX_SECRETKEYBYTES) {
throw new RangeException(
'Must be ParagonIE_Sodium_Compat::CRYPTO_BOX_SECRETKEYBYTES bytes long.'
);
}
return self::scalarmult_base($sKey);
}
/**
* Decrypt a message encrypted with box().
*
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
*
* @param string $ciphertext
* @param string $nonce
* @param string $keypair
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function box_open($ciphertext, $nonce, $keypair)
{
return self::secretbox_open(
$ciphertext,
$nonce,
self::box_beforenm(
self::box_secretkey($keypair),
self::box_publickey($keypair)
)
);
}
/**
* Calculate a BLAKE2b hash.
*
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
*
* @param string $message
* @param string|null $key
* @param int $outlen
* @return string
* @throws RangeException
* @throws SodiumException
* @throws TypeError
*/
public static function generichash($message, $key = '', $outlen = 32)
{
// This ensures that ParagonIE_Sodium_Core32_BLAKE2b::$iv is initialized
ParagonIE_Sodium_Core32_BLAKE2b::pseudoConstructor();
$k = null;
if (!empty($key)) {
/** @var SplFixedArray $k */
$k = ParagonIE_Sodium_Core32_BLAKE2b::stringToSplFixedArray($key);
if ($k->count() > ParagonIE_Sodium_Core32_BLAKE2b::KEYBYTES) {
throw new RangeException('Invalid key size');
}
}
/** @var SplFixedArray $in */
$in = ParagonIE_Sodium_Core32_BLAKE2b::stringToSplFixedArray($message);
/** @var SplFixedArray $ctx */
$ctx = ParagonIE_Sodium_Core32_BLAKE2b::init($k, $outlen);
ParagonIE_Sodium_Core32_BLAKE2b::update($ctx, $in, $in->count());
/** @var SplFixedArray $out */
$out = new SplFixedArray($outlen);
$out = ParagonIE_Sodium_Core32_BLAKE2b::finish($ctx, $out);
/** @var array<int, int> */
$outArray = $out->toArray();
return ParagonIE_Sodium_Core32_Util::intArrayToString($outArray);
}
/**
* Finalize a BLAKE2b hashing context, returning the hash.
*
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
*
* @param string $ctx
* @param int $outlen
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function generichash_final($ctx, $outlen = 32)
{
if (!is_string($ctx)) {
throw new TypeError('Context must be a string');
}
$out = new SplFixedArray($outlen);
/** @var SplFixedArray $context */
$context = ParagonIE_Sodium_Core32_BLAKE2b::stringToContext($ctx);
/** @var SplFixedArray $out */
$out = ParagonIE_Sodium_Core32_BLAKE2b::finish($context, $out);
/** @var array<int, int> */
$outArray = $out->toArray();
return ParagonIE_Sodium_Core32_Util::intArrayToString($outArray);
}
/**
* Initialize a hashing context for BLAKE2b.
*
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
*
* @param string $key
* @param int $outputLength
* @return string
* @throws RangeException
* @throws SodiumException
* @throws TypeError
*/
public static function generichash_init($key = '', $outputLength = 32)
{
// This ensures that ParagonIE_Sodium_Core32_BLAKE2b::$iv is initialized
ParagonIE_Sodium_Core32_BLAKE2b::pseudoConstructor();
$k = null;
if (!empty($key)) {
$k = ParagonIE_Sodium_Core32_BLAKE2b::stringToSplFixedArray($key);
if ($k->count() > ParagonIE_Sodium_Core32_BLAKE2b::KEYBYTES) {
throw new RangeException('Invalid key size');
}
}
/** @var SplFixedArray $ctx */
$ctx = ParagonIE_Sodium_Core32_BLAKE2b::init($k, $outputLength);
return ParagonIE_Sodium_Core32_BLAKE2b::contextToString($ctx);
}
/**
* Initialize a hashing context for BLAKE2b.
*
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
*
* @param string $key
* @param int $outputLength
* @param string $salt
* @param string $personal
* @return string
* @throws RangeException
* @throws SodiumException
* @throws TypeError
*/
public static function generichash_init_salt_personal(
$key = '',
$outputLength = 32,
$salt = '',
$personal = ''
) {
// This ensures that ParagonIE_Sodium_Core32_BLAKE2b::$iv is initialized
ParagonIE_Sodium_Core32_BLAKE2b::pseudoConstructor();
$k = null;
if (!empty($key)) {
$k = ParagonIE_Sodium_Core32_BLAKE2b::stringToSplFixedArray($key);
if ($k->count() > ParagonIE_Sodium_Core32_BLAKE2b::KEYBYTES) {
throw new RangeException('Invalid key size');
}
}
if (!empty($salt)) {
$s = ParagonIE_Sodium_Core32_BLAKE2b::stringToSplFixedArray($salt);
} else {
$s = null;
}
if (!empty($salt)) {
$p = ParagonIE_Sodium_Core32_BLAKE2b::stringToSplFixedArray($personal);
} else {
$p = null;
}
/** @var SplFixedArray $ctx */
$ctx = ParagonIE_Sodium_Core32_BLAKE2b::init($k, $outputLength, $s, $p);
return ParagonIE_Sodium_Core32_BLAKE2b::contextToString($ctx);
}
/**
* Update a hashing context for BLAKE2b with $message
*
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
*
* @param string $ctx
* @param string $message
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function generichash_update($ctx, $message)
{
// This ensures that ParagonIE_Sodium_Core32_BLAKE2b::$iv is initialized
ParagonIE_Sodium_Core32_BLAKE2b::pseudoConstructor();
/** @var SplFixedArray $context */
$context = ParagonIE_Sodium_Core32_BLAKE2b::stringToContext($ctx);
/** @var SplFixedArray $in */
$in = ParagonIE_Sodium_Core32_BLAKE2b::stringToSplFixedArray($message);
ParagonIE_Sodium_Core32_BLAKE2b::update($context, $in, $in->count());
return ParagonIE_Sodium_Core32_BLAKE2b::contextToString($context);
}
/**
* Libsodium's crypto_kx().
*
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
*
* @param string $my_sk
* @param string $their_pk
* @param string $client_pk
* @param string $server_pk
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function keyExchange($my_sk, $their_pk, $client_pk, $server_pk)
{
return self::generichash(
self::scalarmult($my_sk, $their_pk) .
$client_pk .
$server_pk
);
}
/**
* ECDH over Curve25519
*
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
*
* @param string $sKey
* @param string $pKey
* @return string
*
* @throws SodiumException
* @throws TypeError
*/
public static function scalarmult($sKey, $pKey)
{
$q = ParagonIE_Sodium_Core32_X25519::crypto_scalarmult_curve25519_ref10($sKey, $pKey);
self::scalarmult_throw_if_zero($q);
return $q;
}
/**
* ECDH over Curve25519, using the basepoint.
* Used to get a secret key from a public key.
*
* @param string $secret
* @return string
*
* @throws SodiumException
* @throws TypeError
*/
public static function scalarmult_base($secret)
{
$q = ParagonIE_Sodium_Core32_X25519::crypto_scalarmult_curve25519_ref10_base($secret);
self::scalarmult_throw_if_zero($q);
return $q;
}
/**
* This throws an Error if a zero public key was passed to the function.
*
* @param string $q
* @return void
* @throws SodiumException
* @throws TypeError
*/
protected static function scalarmult_throw_if_zero($q)
{
$d = 0;
for ($i = 0; $i < self::box_curve25519xsalsa20poly1305_SECRETKEYBYTES; ++$i) {
$d |= ParagonIE_Sodium_Core32_Util::chrToInt($q[$i]);
}
/* branch-free variant of === 0 */
if (-(1 & (($d - 1) >> 8))) {
throw new SodiumException('Zero public key is not allowed');
}
}
/**
* XSalsa20-Poly1305 authenticated symmetric-key encryption.
*
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
*
* @param string $plaintext
* @param string $nonce
* @param string $key
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function secretbox($plaintext, $nonce, $key)
{
/** @var string $subkey */
$subkey = ParagonIE_Sodium_Core32_HSalsa20::hsalsa20($nonce, $key);
/** @var string $block0 */
$block0 = str_repeat("\x00", 32);
/** @var int $mlen - Length of the plaintext message */
$mlen = ParagonIE_Sodium_Core32_Util::strlen($plaintext);
$mlen0 = $mlen;
if ($mlen0 > 64 - self::secretbox_xsalsa20poly1305_ZEROBYTES) {
$mlen0 = 64 - self::secretbox_xsalsa20poly1305_ZEROBYTES;
}
$block0 .= ParagonIE_Sodium_Core32_Util::substr($plaintext, 0, $mlen0);
/** @var string $block0 */
$block0 = ParagonIE_Sodium_Core32_Salsa20::salsa20_xor(
$block0,
ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8),
$subkey
);
/** @var string $c */
$c = ParagonIE_Sodium_Core32_Util::substr(
$block0,
self::secretbox_xsalsa20poly1305_ZEROBYTES
);
if ($mlen > $mlen0) {
$c .= ParagonIE_Sodium_Core32_Salsa20::salsa20_xor_ic(
ParagonIE_Sodium_Core32_Util::substr(
$plaintext,
self::secretbox_xsalsa20poly1305_ZEROBYTES
),
ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8),
1,
$subkey
);
}
$state = new ParagonIE_Sodium_Core32_Poly1305_State(
ParagonIE_Sodium_Core32_Util::substr(
$block0,
0,
self::onetimeauth_poly1305_KEYBYTES
)
);
try {
ParagonIE_Sodium_Compat::memzero($block0);
ParagonIE_Sodium_Compat::memzero($subkey);
} catch (SodiumException $ex) {
$block0 = null;
$subkey = null;
}
$state->update($c);
/** @var string $c - MAC || ciphertext */
$c = $state->finish() . $c;
unset($state);
return $c;
}
/**
* Decrypt a ciphertext generated via secretbox().
*
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
*
* @param string $ciphertext
* @param string $nonce
* @param string $key
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function secretbox_open($ciphertext, $nonce, $key)
{
/** @var string $mac */
$mac = ParagonIE_Sodium_Core32_Util::substr(
$ciphertext,
0,
self::secretbox_xsalsa20poly1305_MACBYTES
);
/** @var string $c */
$c = ParagonIE_Sodium_Core32_Util::substr(
$ciphertext,
self::secretbox_xsalsa20poly1305_MACBYTES
);
/** @var int $clen */
$clen = ParagonIE_Sodium_Core32_Util::strlen($c);
/** @var string $subkey */
$subkey = ParagonIE_Sodium_Core32_HSalsa20::hsalsa20($nonce, $key);
/** @var string $block0 */
$block0 = ParagonIE_Sodium_Core32_Salsa20::salsa20(
64,
ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8),
$subkey
);
$verified = ParagonIE_Sodium_Core32_Poly1305::onetimeauth_verify(
$mac,
$c,
ParagonIE_Sodium_Core32_Util::substr($block0, 0, 32)
);
if (!$verified) {
try {
ParagonIE_Sodium_Compat::memzero($subkey);
} catch (SodiumException $ex) {
$subkey = null;
}
throw new SodiumException('Invalid MAC');
}
/** @var string $m - Decrypted message */
$m = ParagonIE_Sodium_Core32_Util::xorStrings(
ParagonIE_Sodium_Core32_Util::substr($block0, self::secretbox_xsalsa20poly1305_ZEROBYTES),
ParagonIE_Sodium_Core32_Util::substr($c, 0, self::secretbox_xsalsa20poly1305_ZEROBYTES)
);
if ($clen > self::secretbox_xsalsa20poly1305_ZEROBYTES) {
// We had more than 1 block, so let's continue to decrypt the rest.
$m .= ParagonIE_Sodium_Core32_Salsa20::salsa20_xor_ic(
ParagonIE_Sodium_Core32_Util::substr(
$c,
self::secretbox_xsalsa20poly1305_ZEROBYTES
),
ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8),
1,
(string) $subkey
);
}
return $m;
}
/**
* XChaCha20-Poly1305 authenticated symmetric-key encryption.
*
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
*
* @param string $plaintext
* @param string $nonce
* @param string $key
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function secretbox_xchacha20poly1305($plaintext, $nonce, $key)
{
/** @var string $subkey */
$subkey = ParagonIE_Sodium_Core32_HChaCha20::hChaCha20(
ParagonIE_Sodium_Core32_Util::substr($nonce, 0, 16),
$key
);
$nonceLast = ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8);
/** @var string $block0 */
$block0 = str_repeat("\x00", 32);
/** @var int $mlen - Length of the plaintext message */
$mlen = ParagonIE_Sodium_Core32_Util::strlen($plaintext);
$mlen0 = $mlen;
if ($mlen0 > 64 - self::secretbox_xchacha20poly1305_ZEROBYTES) {
$mlen0 = 64 - self::secretbox_xchacha20poly1305_ZEROBYTES;
}
$block0 .= ParagonIE_Sodium_Core32_Util::substr($plaintext, 0, $mlen0);
/** @var string $block0 */
$block0 = ParagonIE_Sodium_Core32_ChaCha20::streamXorIc(
$block0,
$nonceLast,
$subkey
);
/** @var string $c */
$c = ParagonIE_Sodium_Core32_Util::substr(
$block0,
self::secretbox_xchacha20poly1305_ZEROBYTES
);
if ($mlen > $mlen0) {
$c .= ParagonIE_Sodium_Core32_ChaCha20::streamXorIc(
ParagonIE_Sodium_Core32_Util::substr(
$plaintext,
self::secretbox_xchacha20poly1305_ZEROBYTES
),
$nonceLast,
$subkey,
ParagonIE_Sodium_Core32_Util::store64_le(1)
);
}
$state = new ParagonIE_Sodium_Core32_Poly1305_State(
ParagonIE_Sodium_Core32_Util::substr(
$block0,
0,
self::onetimeauth_poly1305_KEYBYTES
)
);
try {
ParagonIE_Sodium_Compat::memzero($block0);
ParagonIE_Sodium_Compat::memzero($subkey);
} catch (SodiumException $ex) {
$block0 = null;
$subkey = null;
}
$state->update($c);
/** @var string $c - MAC || ciphertext */
$c = $state->finish() . $c;
unset($state);
return $c;
}
/**
* Decrypt a ciphertext generated via secretbox_xchacha20poly1305().
*
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
*
* @param string $ciphertext
* @param string $nonce
* @param string $key
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function secretbox_xchacha20poly1305_open($ciphertext, $nonce, $key)
{
/** @var string $mac */
$mac = ParagonIE_Sodium_Core32_Util::substr(
$ciphertext,
0,
self::secretbox_xchacha20poly1305_MACBYTES
);
/** @var string $c */
$c = ParagonIE_Sodium_Core32_Util::substr(
$ciphertext,
self::secretbox_xchacha20poly1305_MACBYTES
);
/** @var int $clen */
$clen = ParagonIE_Sodium_Core32_Util::strlen($c);
/** @var string $subkey */
$subkey = ParagonIE_Sodium_Core32_HChaCha20::hchacha20($nonce, $key);
/** @var string $block0 */
$block0 = ParagonIE_Sodium_Core32_ChaCha20::stream(
64,
ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8),
$subkey
);
$verified = ParagonIE_Sodium_Core32_Poly1305::onetimeauth_verify(
$mac,
$c,
ParagonIE_Sodium_Core32_Util::substr($block0, 0, 32)
);
if (!$verified) {
try {
ParagonIE_Sodium_Compat::memzero($subkey);
} catch (SodiumException $ex) {
$subkey = null;
}
throw new SodiumException('Invalid MAC');
}
/** @var string $m - Decrypted message */
$m = ParagonIE_Sodium_Core32_Util::xorStrings(
ParagonIE_Sodium_Core32_Util::substr($block0, self::secretbox_xchacha20poly1305_ZEROBYTES),
ParagonIE_Sodium_Core32_Util::substr($c, 0, self::secretbox_xchacha20poly1305_ZEROBYTES)
);
if ($clen > self::secretbox_xchacha20poly1305_ZEROBYTES) {
// We had more than 1 block, so let's continue to decrypt the rest.
$m .= ParagonIE_Sodium_Core32_ChaCha20::streamXorIc(
ParagonIE_Sodium_Core32_Util::substr(
$c,
self::secretbox_xchacha20poly1305_ZEROBYTES
),
ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8),
(string) $subkey,
ParagonIE_Sodium_Core32_Util::store64_le(1)
);
}
return $m;
}
/**
* @param string $key
* @return array<int, string> Returns a state and a header.
* @throws Exception
* @throws SodiumException
*/
public static function secretstream_xchacha20poly1305_init_push($key)
{
# randombytes_buf(out, crypto_secretstream_xchacha20poly1305_HEADERBYTES);
$out = random_bytes(24);
# crypto_core_hchacha20(state->k, out, k, NULL);
$subkey = ParagonIE_Sodium_Core32_HChaCha20::hChaCha20($out, $key);
$state = new ParagonIE_Sodium_Core32_SecretStream_State(
$subkey,
ParagonIE_Sodium_Core32_Util::substr($out, 16, 8) . str_repeat("\0", 4)
);
# _crypto_secretstream_xchacha20poly1305_counter_reset(state);
$state->counterReset();
# memcpy(STATE_INONCE(state), out + crypto_core_hchacha20_INPUTBYTES,
# crypto_secretstream_xchacha20poly1305_INONCEBYTES);
# memset(state->_pad, 0, sizeof state->_pad);
return array(
$state->toString(),
$out
);
}
/**
* @param string $key
* @param string $header
* @return string Returns a state.
* @throws Exception
*/
public static function secretstream_xchacha20poly1305_init_pull($key, $header)
{
# crypto_core_hchacha20(state->k, in, k, NULL);
$subkey = ParagonIE_Sodium_Core32_HChaCha20::hChaCha20(
ParagonIE_Sodium_Core32_Util::substr($header, 0, 16),
$key
);
$state = new ParagonIE_Sodium_Core32_SecretStream_State(
$subkey,
ParagonIE_Sodium_Core32_Util::substr($header, 16)
);
$state->counterReset();
# memcpy(STATE_INONCE(state), in + crypto_core_hchacha20_INPUTBYTES,
# crypto_secretstream_xchacha20poly1305_INONCEBYTES);
# memset(state->_pad, 0, sizeof state->_pad);
# return 0;
return $state->toString();
}
/**
* @param string $state
* @param string $msg
* @param string $aad
* @param int $tag
* @return string
* @throws SodiumException
*/
public static function secretstream_xchacha20poly1305_push(&$state, $msg, $aad = '', $tag = 0)
{
$st = ParagonIE_Sodium_Core32_SecretStream_State::fromString($state);
# crypto_onetimeauth_poly1305_state poly1305_state;
# unsigned char block[64U];
# unsigned char slen[8U];
# unsigned char *c;
# unsigned char *mac;
$msglen = ParagonIE_Sodium_Core32_Util::strlen($msg);
$aadlen = ParagonIE_Sodium_Core32_Util::strlen($aad);
if ((($msglen + 63) >> 6) > 0xfffffffe) {
throw new SodiumException(
'message cannot be larger than SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_MESSAGEBYTES_MAX bytes'
);
}
# if (outlen_p != NULL) {
# *outlen_p = 0U;
# }
# if (mlen > crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX) {
# sodium_misuse();
# }
# crypto_stream_chacha20_ietf(block, sizeof block, state->nonce, state->k);
# crypto_onetimeauth_poly1305_init(&poly1305_state, block);
# sodium_memzero(block, sizeof block);
$auth = new ParagonIE_Sodium_Core32_Poly1305_State(
ParagonIE_Sodium_Core32_ChaCha20::ietfStream(32, $st->getCombinedNonce(), $st->getKey())
);
# crypto_onetimeauth_poly1305_update(&poly1305_state, ad, adlen);
$auth->update($aad);
# crypto_onetimeauth_poly1305_update(&poly1305_state, _pad0,
# (0x10 - adlen) & 0xf);
$auth->update(str_repeat("\0", ((0x10 - $aadlen) & 0xf)));
# memset(block, 0, sizeof block);
# block[0] = tag;
# crypto_stream_chacha20_ietf_xor_ic(block, block, sizeof block,
# state->nonce, 1U, state->k);
$block = ParagonIE_Sodium_Core32_ChaCha20::ietfStreamXorIc(
ParagonIE_Sodium_Core32_Util::intToChr($tag) . str_repeat("\0", 63),
$st->getCombinedNonce(),
$st->getKey(),
ParagonIE_Sodium_Core32_Util::store64_le(1)
);
# crypto_onetimeauth_poly1305_update(&poly1305_state, block, sizeof block);
$auth->update($block);
# out[0] = block[0];
$out = $block[0];
# c = out + (sizeof tag);
# crypto_stream_chacha20_ietf_xor_ic(c, m, mlen, state->nonce, 2U, state->k);
$cipher = ParagonIE_Sodium_Core32_ChaCha20::ietfStreamXorIc(
$msg,
$st->getCombinedNonce(),
$st->getKey(),
ParagonIE_Sodium_Core32_Util::store64_le(2)
);
# crypto_onetimeauth_poly1305_update(&poly1305_state, c, mlen);
$auth->update($cipher);
$out .= $cipher;
unset($cipher);
# crypto_onetimeauth_poly1305_update
# (&poly1305_state, _pad0, (0x10 - (sizeof block) + mlen) & 0xf);
$auth->update(str_repeat("\0", ((0x10 - 64 + $msglen) & 0xf)));
# STORE64_LE(slen, (uint64_t) adlen);
$slen = ParagonIE_Sodium_Core32_Util::store64_le($aadlen);
# crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen);
$auth->update($slen);
# STORE64_LE(slen, (sizeof block) + mlen);
$slen = ParagonIE_Sodium_Core32_Util::store64_le(64 + $msglen);
# crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen);
$auth->update($slen);
# mac = c + mlen;
# crypto_onetimeauth_poly1305_final(&poly1305_state, mac);
$mac = $auth->finish();
$out .= $mac;
# sodium_memzero(&poly1305_state, sizeof poly1305_state);
unset($auth);
# XOR_BUF(STATE_INONCE(state), mac,
# crypto_secretstream_xchacha20poly1305_INONCEBYTES);
$st->xorNonce($mac);
# sodium_increment(STATE_COUNTER(state),
# crypto_secretstream_xchacha20poly1305_COUNTERBYTES);
$st->incrementCounter();
// Overwrite by reference:
$state = $st->toString();
/** @var bool $rekey */
$rekey = ($tag & ParagonIE_Sodium_Compat::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_REKEY) !== 0;
# if ((tag & crypto_secretstream_xchacha20poly1305_TAG_REKEY) != 0 ||
# sodium_is_zero(STATE_COUNTER(state),
# crypto_secretstream_xchacha20poly1305_COUNTERBYTES)) {
# crypto_secretstream_xchacha20poly1305_rekey(state);
# }
if ($rekey || $st->needsRekey()) {
// DO REKEY
self::secretstream_xchacha20poly1305_rekey($state);
}
# if (outlen_p != NULL) {
# *outlen_p = crypto_secretstream_xchacha20poly1305_ABYTES + mlen;
# }
return $out;
}
/**
* @param string $state
* @param string $cipher
* @param string $aad
* @return bool|array{0: string, 1: int}
* @throws SodiumException
*/
public static function secretstream_xchacha20poly1305_pull(&$state, $cipher, $aad = '')
{
$st = ParagonIE_Sodium_Core32_SecretStream_State::fromString($state);
$cipherlen = ParagonIE_Sodium_Core32_Util::strlen($cipher);
# mlen = inlen - crypto_secretstream_xchacha20poly1305_ABYTES;
$msglen = $cipherlen - ParagonIE_Sodium_Compat::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_ABYTES;
$aadlen = ParagonIE_Sodium_Core32_Util::strlen($aad);
# if (mlen > crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX) {
# sodium_misuse();
# }
if ((($msglen + 63) >> 6) > 0xfffffffe) {
throw new SodiumException(
'message cannot be larger than SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_MESSAGEBYTES_MAX bytes'
);
}
# crypto_stream_chacha20_ietf(block, sizeof block, state->nonce, state->k);
# crypto_onetimeauth_poly1305_init(&poly1305_state, block);
# sodium_memzero(block, sizeof block);
$auth = new ParagonIE_Sodium_Core32_Poly1305_State(
ParagonIE_Sodium_Core32_ChaCha20::ietfStream(32, $st->getCombinedNonce(), $st->getKey())
);
# crypto_onetimeauth_poly1305_update(&poly1305_state, ad, adlen);
$auth->update($aad);
# crypto_onetimeauth_poly1305_update(&poly1305_state, _pad0,
# (0x10 - adlen) & 0xf);
$auth->update(str_repeat("\0", ((0x10 - $aadlen) & 0xf)));
# memset(block, 0, sizeof block);
# block[0] = in[0];
# crypto_stream_chacha20_ietf_xor_ic(block, block, sizeof block,
# state->nonce, 1U, state->k);
$block = ParagonIE_Sodium_Core32_ChaCha20::ietfStreamXorIc(
$cipher[0] . str_repeat("\0", 63),
$st->getCombinedNonce(),
$st->getKey(),
ParagonIE_Sodium_Core32_Util::store64_le(1)
);
# tag = block[0];
# block[0] = in[0];
# crypto_onetimeauth_poly1305_update(&poly1305_state, block, sizeof block);
$tag = ParagonIE_Sodium_Core32_Util::chrToInt($block[0]);
$block[0] = $cipher[0];
$auth->update($block);
# c = in + (sizeof tag);
# crypto_onetimeauth_poly1305_update(&poly1305_state, c, mlen);
$auth->update(ParagonIE_Sodium_Core32_Util::substr($cipher, 1, $msglen));
# crypto_onetimeauth_poly1305_update
# (&poly1305_state, _pad0, (0x10 - (sizeof block) + mlen) & 0xf);
$auth->update(str_repeat("\0", ((0x10 - 64 + $msglen) & 0xf)));
# STORE64_LE(slen, (uint64_t) adlen);
# crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen);
$slen = ParagonIE_Sodium_Core32_Util::store64_le($aadlen);
$auth->update($slen);
# STORE64_LE(slen, (sizeof block) + mlen);
# crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen);
$slen = ParagonIE_Sodium_Core32_Util::store64_le(64 + $msglen);
$auth->update($slen);
# crypto_onetimeauth_poly1305_final(&poly1305_state, mac);
# sodium_memzero(&poly1305_state, sizeof poly1305_state);
$mac = $auth->finish();
# stored_mac = c + mlen;
# if (sodium_memcmp(mac, stored_mac, sizeof mac) != 0) {
# sodium_memzero(mac, sizeof mac);
# return -1;
# }
$stored = ParagonIE_Sodium_Core32_Util::substr($cipher, $msglen + 1, 16);
if (!ParagonIE_Sodium_Core32_Util::hashEquals($mac, $stored)) {
return false;
}
# crypto_stream_chacha20_ietf_xor_ic(m, c, mlen, state->nonce, 2U, state->k);
$out = ParagonIE_Sodium_Core32_ChaCha20::ietfStreamXorIc(
ParagonIE_Sodium_Core32_Util::substr($cipher, 1, $msglen),
$st->getCombinedNonce(),
$st->getKey(),
ParagonIE_Sodium_Core32_Util::store64_le(2)
);
# XOR_BUF(STATE_INONCE(state), mac,
# crypto_secretstream_xchacha20poly1305_INONCEBYTES);
$st->xorNonce($mac);
# sodium_increment(STATE_COUNTER(state),
# crypto_secretstream_xchacha20poly1305_COUNTERBYTES);
$st->incrementCounter();
# if ((tag & crypto_secretstream_xchacha20poly1305_TAG_REKEY) != 0 ||
# sodium_is_zero(STATE_COUNTER(state),
# crypto_secretstream_xchacha20poly1305_COUNTERBYTES)) {
# crypto_secretstream_xchacha20poly1305_rekey(state);
# }
// Overwrite by reference:
$state = $st->toString();
/** @var bool $rekey */
$rekey = ($tag & ParagonIE_Sodium_Compat::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_REKEY) !== 0;
if ($rekey || $st->needsRekey()) {
// DO REKEY
self::secretstream_xchacha20poly1305_rekey($state);
}
return array($out, $tag);
}
/**
* @param string $state
* @return void
* @throws SodiumException
*/
public static function secretstream_xchacha20poly1305_rekey(&$state)
{
$st = ParagonIE_Sodium_Core32_SecretStream_State::fromString($state);
# unsigned char new_key_and_inonce[crypto_stream_chacha20_ietf_KEYBYTES +
# crypto_secretstream_xchacha20poly1305_INONCEBYTES];
# size_t i;
# for (i = 0U; i < crypto_stream_chacha20_ietf_KEYBYTES; i++) {
# new_key_and_inonce[i] = state->k[i];
# }
$new_key_and_inonce = $st->getKey();
# for (i = 0U; i < crypto_secretstream_xchacha20poly1305_INONCEBYTES; i++) {
# new_key_and_inonce[crypto_stream_chacha20_ietf_KEYBYTES + i] =
# STATE_INONCE(state)[i];
# }
$new_key_and_inonce .= ParagonIE_Sodium_Core32_Util::substR($st->getNonce(), 0, 8);
# crypto_stream_chacha20_ietf_xor(new_key_and_inonce, new_key_and_inonce,
# sizeof new_key_and_inonce,
# state->nonce, state->k);
$st->rekey(ParagonIE_Sodium_Core32_ChaCha20::ietfStreamXorIc(
$new_key_and_inonce,
$st->getCombinedNonce(),
$st->getKey(),
ParagonIE_Sodium_Core32_Util::store64_le(0)
));
# for (i = 0U; i < crypto_stream_chacha20_ietf_KEYBYTES; i++) {
# state->k[i] = new_key_and_inonce[i];
# }
# for (i = 0U; i < crypto_secretstream_xchacha20poly1305_INONCEBYTES; i++) {
# STATE_INONCE(state)[i] =
# new_key_and_inonce[crypto_stream_chacha20_ietf_KEYBYTES + i];
# }
# _crypto_secretstream_xchacha20poly1305_counter_reset(state);
$st->counterReset();
$state = $st->toString();
}
/**
* Detached Ed25519 signature.
*
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
*
* @param string $message
* @param string $sk
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function sign_detached($message, $sk)
{
return ParagonIE_Sodium_Core32_Ed25519::sign_detached($message, $sk);
}
/**
* Attached Ed25519 signature. (Returns a signed message.)
*
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
*
* @param string $message
* @param string $sk
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function sign($message, $sk)
{
return ParagonIE_Sodium_Core32_Ed25519::sign($message, $sk);
}
/**
* Opens a signed message. If valid, returns the message.
*
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
*
* @param string $signedMessage
* @param string $pk
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function sign_open($signedMessage, $pk)
{
return ParagonIE_Sodium_Core32_Ed25519::sign_open($signedMessage, $pk);
}
/**
* Verify a detached signature of a given message and public key.
*
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
*
* @param string $signature
* @param string $message
* @param string $pk
* @return bool
* @throws SodiumException
* @throws TypeError
*/
public static function sign_verify_detached($signature, $message, $pk)
{
return ParagonIE_Sodium_Core32_Ed25519::verify_detached($signature, $message, $pk);
}
}
PHP52/SplFixedArray.php 0000604 00000010024 15133021213 0010550 0 ustar 00 <?php
if (class_exists('SplFixedArray')) {
return;
}
/**
* The SplFixedArray class provides the main functionalities of array. The
* main differences between a SplFixedArray and a normal PHP array is that
* the SplFixedArray is of fixed length and allows only integers within
* the range as indexes. The advantage is that it allows a faster array
* implementation.
*/
class SplFixedArray implements Iterator, ArrayAccess, Countable
{
/** @var array<int, mixed> */
private $internalArray = array();
/** @var int $size */
private $size = 0;
/**
* SplFixedArray constructor.
* @param int $size
*/
public function __construct($size = 0)
{
$this->size = $size;
$this->internalArray = array();
}
/**
* @return int
*/
public function count()
{
return count($this->internalArray);
}
/**
* @return array
*/
public function toArray()
{
ksort($this->internalArray);
return (array) $this->internalArray;
}
/**
* @param array $array
* @param bool $save_indexes
* @return SplFixedArray
* @psalm-suppress MixedAssignment
*/
public static function fromArray(array $array, $save_indexes = true)
{
$self = new SplFixedArray(count($array));
if($save_indexes) {
foreach($array as $key => $value) {
$self[(int) $key] = $value;
}
} else {
$i = 0;
foreach (array_values($array) as $value) {
$self[$i] = $value;
$i++;
}
}
return $self;
}
/**
* @return int
*/
public function getSize()
{
return $this->size;
}
/**
* @param int $size
* @return bool
*/
public function setSize($size)
{
$this->size = $size;
return true;
}
/**
* @param string|int $index
* @return bool
*/
public function offsetExists($index)
{
return array_key_exists((int) $index, $this->internalArray);
}
/**
* @param string|int $index
* @return mixed
*/
public function offsetGet($index)
{
/** @psalm-suppress MixedReturnStatement */
return $this->internalArray[(int) $index];
}
/**
* @param string|int $index
* @param mixed $newval
* @psalm-suppress MixedAssignment
*/
public function offsetSet($index, $newval)
{
$this->internalArray[(int) $index] = $newval;
}
/**
* @param string|int $index
*/
public function offsetUnset($index)
{
unset($this->internalArray[(int) $index]);
}
/**
* Rewind iterator back to the start
* @link https://php.net/manual/en/splfixedarray.rewind.php
* @return void
* @since 5.3.0
*/
public function rewind()
{
reset($this->internalArray);
}
/**
* Return current array entry
* @link https://php.net/manual/en/splfixedarray.current.php
* @return mixed The current element value.
* @since 5.3.0
*/
public function current()
{
/** @psalm-suppress MixedReturnStatement */
return current($this->internalArray);
}
/**
* Return current array index
* @return int The current array index.
*/
public function key()
{
return key($this->internalArray);
}
/**
* @return void
*/
public function next()
{
next($this->internalArray);
}
/**
* Check whether the array contains more elements
* @link https://php.net/manual/en/splfixedarray.valid.php
* @return bool true if the array contains any more elements, false otherwise.
*/
public function valid()
{
if (empty($this->internalArray)) {
return false;
}
$result = next($this->internalArray) !== false;
prev($this->internalArray);
return $result;
}
/**
* Do nothing.
*/
public function __wakeup()
{
// NOP
}
} Core/Poly1305.php 0000604 00000003046 15133021213 0007367 0 ustar 00 <?php
if (class_exists('ParagonIE_Sodium_Core_Poly1305', false)) {
return;
}
/**
* Class ParagonIE_Sodium_Core_Poly1305
*/
abstract class ParagonIE_Sodium_Core_Poly1305 extends ParagonIE_Sodium_Core_Util
{
const BLOCK_SIZE = 16;
/**
* @internal You should not use this directly from another application
*
* @param string $m
* @param string $key
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function onetimeauth($m, $key)
{
if (self::strlen($key) < 32) {
throw new InvalidArgumentException(
'Key must be 32 bytes long.'
);
}
$state = new ParagonIE_Sodium_Core_Poly1305_State(
self::substr($key, 0, 32)
);
return $state
->update($m)
->finish();
}
/**
* @internal You should not use this directly from another application
*
* @param string $mac
* @param string $m
* @param string $key
* @return bool
* @throws SodiumException
* @throws TypeError
*/
public static function onetimeauth_verify($mac, $m, $key)
{
if (self::strlen($key) < 32) {
throw new InvalidArgumentException(
'Key must be 32 bytes long.'
);
}
$state = new ParagonIE_Sodium_Core_Poly1305_State(
self::substr($key, 0, 32)
);
$calc = $state
->update($m)
->finish();
return self::verify_16($calc, $mac);
}
}
Core/Ristretto255.php 0000604 00000052574 15133021213 0010400 0 ustar 00 <?php
/**
* Class ParagonIE_Sodium_Core_Ristretto255
*/
class ParagonIE_Sodium_Core_Ristretto255 extends ParagonIE_Sodium_Core_Ed25519
{
const crypto_core_ristretto255_HASHBYTES = 64;
const HASH_SC_L = 48;
const CORE_H2C_SHA256 = 1;
const CORE_H2C_SHA512 = 2;
/**
* @param ParagonIE_Sodium_Core_Curve25519_Fe $f
* @param int $b
* @return ParagonIE_Sodium_Core_Curve25519_Fe
*/
public static function fe_cneg(ParagonIE_Sodium_Core_Curve25519_Fe $f, $b)
{
$negf = self::fe_neg($f);
return self::fe_cmov($f, $negf, $b);
}
/**
* @param ParagonIE_Sodium_Core_Curve25519_Fe $f
* @return ParagonIE_Sodium_Core_Curve25519_Fe
* @throws SodiumException
*/
public static function fe_abs(ParagonIE_Sodium_Core_Curve25519_Fe $f)
{
return self::fe_cneg($f, self::fe_isnegative($f));
}
/**
* Returns 0 if this field element results in all NUL bytes.
*
* @internal You should not use this directly from another application
*
* @param ParagonIE_Sodium_Core_Curve25519_Fe $f
* @return int
* @throws SodiumException
*/
public static function fe_iszero(ParagonIE_Sodium_Core_Curve25519_Fe $f)
{
static $zero;
if ($zero === null) {
$zero = str_repeat("\x00", 32);
}
/** @var string $zero */
$str = self::fe_tobytes($f);
$d = 0;
for ($i = 0; $i < 32; ++$i) {
$d |= self::chrToInt($str[$i]);
}
return (($d - 1) >> 31) & 1;
}
/**
* @param ParagonIE_Sodium_Core_Curve25519_Fe $u
* @param ParagonIE_Sodium_Core_Curve25519_Fe $v
* @return array{x: ParagonIE_Sodium_Core_Curve25519_Fe, nonsquare: int}
*
* @throws SodiumException
*/
public static function ristretto255_sqrt_ratio_m1(
ParagonIE_Sodium_Core_Curve25519_Fe $u,
ParagonIE_Sodium_Core_Curve25519_Fe $v
) {
$sqrtm1 = ParagonIE_Sodium_Core_Curve25519_Fe::fromArray(self::$sqrtm1);
$v3 = self::fe_mul(
self::fe_sq($v),
$v
); /* v3 = v^3 */
$x = self::fe_mul(
self::fe_mul(
self::fe_sq($v3),
$u
),
$v
); /* x = uv^7 */
$x = self::fe_mul(
self::fe_mul(
self::fe_pow22523($x), /* x = (uv^7)^((q-5)/8) */
$v3
),
$u
); /* x = uv^3(uv^7)^((q-5)/8) */
$vxx = self::fe_mul(
self::fe_sq($x),
$v
); /* vx^2 */
$m_root_check = self::fe_sub($vxx, $u); /* vx^2-u */
$p_root_check = self::fe_add($vxx, $u); /* vx^2+u */
$f_root_check = self::fe_mul($u, $sqrtm1); /* u*sqrt(-1) */
$f_root_check = self::fe_add($vxx, $f_root_check); /* vx^2+u*sqrt(-1) */
$has_m_root = self::fe_iszero($m_root_check);
$has_p_root = self::fe_iszero($p_root_check);
$has_f_root = self::fe_iszero($f_root_check);
$x_sqrtm1 = self::fe_mul($x, $sqrtm1); /* x*sqrt(-1) */
$x = self::fe_abs(
self::fe_cmov($x, $x_sqrtm1, $has_p_root | $has_f_root)
);
return array(
'x' => $x,
'nonsquare' => $has_m_root | $has_p_root
);
}
/**
* @param string $s
* @return int
* @throws SodiumException
*/
public static function ristretto255_point_is_canonical($s)
{
$c = (self::chrToInt($s[31]) & 0x7f) ^ 0x7f;
for ($i = 30; $i > 0; --$i) {
$c |= self::chrToInt($s[$i]) ^ 0xff;
}
$c = ($c - 1) >> 8;
$d = (0xed - 1 - self::chrToInt($s[0])) >> 8;
$e = self::chrToInt($s[31]) >> 7;
return 1 - ((($c & $d) | $e | self::chrToInt($s[0])) & 1);
}
/**
* @param string $s
* @param bool $skipCanonicalCheck
* @return array{h: ParagonIE_Sodium_Core_Curve25519_Ge_P3, res: int}
* @throws SodiumException
*/
public static function ristretto255_frombytes($s, $skipCanonicalCheck = false)
{
if (!$skipCanonicalCheck) {
if (!self::ristretto255_point_is_canonical($s)) {
throw new SodiumException('S is not canonical');
}
}
$s_ = self::fe_frombytes($s);
$ss = self::fe_sq($s_); /* ss = s^2 */
$u1 = self::fe_sub(self::fe_1(), $ss); /* u1 = 1-ss */
$u1u1 = self::fe_sq($u1); /* u1u1 = u1^2 */
$u2 = self::fe_add(self::fe_1(), $ss); /* u2 = 1+ss */
$u2u2 = self::fe_sq($u2); /* u2u2 = u2^2 */
$v = self::fe_mul(
ParagonIE_Sodium_Core_Curve25519_Fe::fromArray(self::$d),
$u1u1
); /* v = d*u1^2 */
$v = self::fe_neg($v); /* v = -d*u1^2 */
$v = self::fe_sub($v, $u2u2); /* v = -(d*u1^2)-u2^2 */
$v_u2u2 = self::fe_mul($v, $u2u2); /* v_u2u2 = v*u2^2 */
// fe25519_1(one);
// notsquare = ristretto255_sqrt_ratio_m1(inv_sqrt, one, v_u2u2);
$one = self::fe_1();
$result = self::ristretto255_sqrt_ratio_m1($one, $v_u2u2);
$inv_sqrt = $result['x'];
$notsquare = $result['nonsquare'];
$h = new ParagonIE_Sodium_Core_Curve25519_Ge_P3();
$h->X = self::fe_mul($inv_sqrt, $u2);
$h->Y = self::fe_mul(self::fe_mul($inv_sqrt, $h->X), $v);
$h->X = self::fe_mul($h->X, $s_);
$h->X = self::fe_abs(
self::fe_add($h->X, $h->X)
);
$h->Y = self::fe_mul($u1, $h->Y);
$h->Z = self::fe_1();
$h->T = self::fe_mul($h->X, $h->Y);
$res = - ((1 - $notsquare) | self::fe_isnegative($h->T) | self::fe_iszero($h->Y));
return array('h' => $h, 'res' => $res);
}
/**
* @param ParagonIE_Sodium_Core_Curve25519_Ge_P3 $h
* @return string
* @throws SodiumException
*/
public static function ristretto255_p3_tobytes(ParagonIE_Sodium_Core_Curve25519_Ge_P3 $h)
{
$sqrtm1 = ParagonIE_Sodium_Core_Curve25519_Fe::fromArray(self::$sqrtm1);
$invsqrtamd = ParagonIE_Sodium_Core_Curve25519_Fe::fromArray(self::$invsqrtamd);
$u1 = self::fe_add($h->Z, $h->Y); /* u1 = Z+Y */
$zmy = self::fe_sub($h->Z, $h->Y); /* zmy = Z-Y */
$u1 = self::fe_mul($u1, $zmy); /* u1 = (Z+Y)*(Z-Y) */
$u2 = self::fe_mul($h->X, $h->Y); /* u2 = X*Y */
$u1_u2u2 = self::fe_mul(self::fe_sq($u2), $u1); /* u1_u2u2 = u1*u2^2 */
$one = self::fe_1();
// fe25519_1(one);
// (void) ristretto255_sqrt_ratio_m1(inv_sqrt, one, u1_u2u2);
$result = self::ristretto255_sqrt_ratio_m1($one, $u1_u2u2);
$inv_sqrt = $result['x'];
$den1 = self::fe_mul($inv_sqrt, $u1); /* den1 = inv_sqrt*u1 */
$den2 = self::fe_mul($inv_sqrt, $u2); /* den2 = inv_sqrt*u2 */
$z_inv = self::fe_mul($h->T, self::fe_mul($den1, $den2)); /* z_inv = den1*den2*T */
$ix = self::fe_mul($h->X, $sqrtm1); /* ix = X*sqrt(-1) */
$iy = self::fe_mul($h->Y, $sqrtm1); /* iy = Y*sqrt(-1) */
$eden = self::fe_mul($den1, $invsqrtamd);
$t_z_inv = self::fe_mul($h->T, $z_inv); /* t_z_inv = T*z_inv */
$rotate = self::fe_isnegative($t_z_inv);
$x_ = self::fe_copy($h->X);
$y_ = self::fe_copy($h->Y);
$den_inv = self::fe_copy($den2);
$x_ = self::fe_cmov($x_, $iy, $rotate);
$y_ = self::fe_cmov($y_, $ix, $rotate);
$den_inv = self::fe_cmov($den_inv, $eden, $rotate);
$x_z_inv = self::fe_mul($x_, $z_inv);
$y_ = self::fe_cneg($y_, self::fe_isnegative($x_z_inv));
// fe25519_sub(s_, h->Z, y_);
// fe25519_mul(s_, den_inv, s_);
// fe25519_abs(s_, s_);
// fe25519_tobytes(s, s_);
return self::fe_tobytes(
self::fe_abs(
self::fe_mul(
$den_inv,
self::fe_sub($h->Z, $y_)
)
)
);
}
/**
* @param ParagonIE_Sodium_Core_Curve25519_Fe $t
* @return ParagonIE_Sodium_Core_Curve25519_Ge_P3
*
* @throws SodiumException
*/
public static function ristretto255_elligator(ParagonIE_Sodium_Core_Curve25519_Fe $t)
{
$sqrtm1 = ParagonIE_Sodium_Core_Curve25519_Fe::fromArray(self::$sqrtm1);
$onemsqd = ParagonIE_Sodium_Core_Curve25519_Fe::fromArray(self::$onemsqd);
$d = ParagonIE_Sodium_Core_Curve25519_Fe::fromArray(self::$d);
$sqdmone = ParagonIE_Sodium_Core_Curve25519_Fe::fromArray(self::$sqdmone);
$sqrtadm1 = ParagonIE_Sodium_Core_Curve25519_Fe::fromArray(self::$sqrtadm1);
$one = self::fe_1();
$r = self::fe_mul($sqrtm1, self::fe_sq($t)); /* r = sqrt(-1)*t^2 */
$u = self::fe_mul(self::fe_add($r, $one), $onemsqd); /* u = (r+1)*(1-d^2) */
$c = self::fe_neg(self::fe_1()); /* c = -1 */
$rpd = self::fe_add($r, $d); /* rpd = r+d */
$v = self::fe_mul(
self::fe_sub(
$c,
self::fe_mul($r, $d)
),
$rpd
); /* v = (c-r*d)*(r+d) */
$result = self::ristretto255_sqrt_ratio_m1($u, $v);
$s = $result['x'];
$wasnt_square = 1 - $result['nonsquare'];
$s_prime = self::fe_neg(
self::fe_abs(
self::fe_mul($s, $t)
)
); /* s_prime = -|s*t| */
$s = self::fe_cmov($s, $s_prime, $wasnt_square);
$c = self::fe_cmov($c, $r, $wasnt_square);
// fe25519_sub(n, r, one); /* n = r-1 */
// fe25519_mul(n, n, c); /* n = c*(r-1) */
// fe25519_mul(n, n, ed25519_sqdmone); /* n = c*(r-1)*(d-1)^2 */
// fe25519_sub(n, n, v); /* n = c*(r-1)*(d-1)^2-v */
$n = self::fe_sub(
self::fe_mul(
self::fe_mul(
self::fe_sub($r, $one),
$c
),
$sqdmone
),
$v
); /* n = c*(r-1)*(d-1)^2-v */
$w0 = self::fe_mul(
self::fe_add($s, $s),
$v
); /* w0 = 2s*v */
$w1 = self::fe_mul($n, $sqrtadm1); /* w1 = n*sqrt(ad-1) */
$ss = self::fe_sq($s); /* ss = s^2 */
$w2 = self::fe_sub($one, $ss); /* w2 = 1-s^2 */
$w3 = self::fe_add($one, $ss); /* w3 = 1+s^2 */
return new ParagonIE_Sodium_Core_Curve25519_Ge_P3(
self::fe_mul($w0, $w3),
self::fe_mul($w2, $w1),
self::fe_mul($w1, $w3),
self::fe_mul($w0, $w2)
);
}
/**
* @param string $h
* @return string
* @throws SodiumException
*/
public static function ristretto255_from_hash($h)
{
if (self::strlen($h) !== 64) {
throw new SodiumException('Hash must be 64 bytes');
}
//fe25519_frombytes(r0, h);
//fe25519_frombytes(r1, h + 32);
$r0 = self::fe_frombytes(self::substr($h, 0, 32));
$r1 = self::fe_frombytes(self::substr($h, 32, 32));
//ristretto255_elligator(&p0, r0);
//ristretto255_elligator(&p1, r1);
$p0 = self::ristretto255_elligator($r0);
$p1 = self::ristretto255_elligator($r1);
//ge25519_p3_to_cached(&p1_cached, &p1);
//ge25519_add_cached(&p_p1p1, &p0, &p1_cached);
$p_p1p1 = self::ge_add(
$p0,
self::ge_p3_to_cached($p1)
);
//ge25519_p1p1_to_p3(&p, &p_p1p1);
//ristretto255_p3_tobytes(s, &p);
return self::ristretto255_p3_tobytes(
self::ge_p1p1_to_p3($p_p1p1)
);
}
/**
* @param string $p
* @return int
* @throws SodiumException
*/
public static function is_valid_point($p)
{
$result = self::ristretto255_frombytes($p);
if ($result['res'] !== 0) {
return 0;
}
return 1;
}
/**
* @param string $p
* @param string $q
* @return string
* @throws SodiumException
*/
public static function ristretto255_add($p, $q)
{
$p_res = self::ristretto255_frombytes($p);
$q_res = self::ristretto255_frombytes($q);
if ($p_res['res'] !== 0 || $q_res['res'] !== 0) {
throw new SodiumException('Could not add points');
}
$p_p3 = $p_res['h'];
$q_p3 = $q_res['h'];
$q_cached = self::ge_p3_to_cached($q_p3);
$r_p1p1 = self::ge_add($p_p3, $q_cached);
$r_p3 = self::ge_p1p1_to_p3($r_p1p1);
return self::ristretto255_p3_tobytes($r_p3);
}
/**
* @param string $p
* @param string $q
* @return string
* @throws SodiumException
*/
public static function ristretto255_sub($p, $q)
{
$p_res = self::ristretto255_frombytes($p);
$q_res = self::ristretto255_frombytes($q);
if ($p_res['res'] !== 0 || $q_res['res'] !== 0) {
throw new SodiumException('Could not add points');
}
$p_p3 = $p_res['h'];
$q_p3 = $q_res['h'];
$q_cached = self::ge_p3_to_cached($q_p3);
$r_p1p1 = self::ge_sub($p_p3, $q_cached);
$r_p3 = self::ge_p1p1_to_p3($r_p1p1);
return self::ristretto255_p3_tobytes($r_p3);
}
/**
* @param int $hLen
* @param ?string $ctx
* @param string $msg
* @return string
* @throws SodiumException
* @psalm-suppress PossiblyInvalidArgument hash API
*/
protected static function h2c_string_to_hash_sha256($hLen, $ctx, $msg)
{
$h = array_fill(0, $hLen, 0);
$ctx_len = !is_null($ctx) ? self::strlen($ctx) : 0;
if ($hLen > 0xff) {
throw new SodiumException('Hash must be less than 256 bytes');
}
if ($ctx_len > 0xff) {
$st = hash_init('sha256');
self::hash_update($st, "H2C-OVERSIZE-DST-");
self::hash_update($st, $ctx);
$ctx = hash_final($st, true);
$ctx_len = 32;
}
$t = array(0, $hLen, 0);
$ux = str_repeat("\0", 64);
$st = hash_init('sha256');
self::hash_update($st, $ux);
self::hash_update($st, $msg);
self::hash_update($st, self::intArrayToString($t));
self::hash_update($st, $ctx);
self::hash_update($st, self::intToChr($ctx_len));
$u0 = hash_final($st, true);
for ($i = 0; $i < $hLen; $i += 64) {
$ux = self::xorStrings($ux, $u0);
++$t[2];
$st = hash_init('sha256');
self::hash_update($st, $ux);
self::hash_update($st, self::intToChr($t[2]));
self::hash_update($st, $ctx);
self::hash_update($st, self::intToChr($ctx_len));
$ux = hash_final($st, true);
$amount = min($hLen - $i, 64);
for ($j = 0; $j < $amount; ++$j) {
$h[$i + $j] = self::chrToInt($ux[$i]);
}
}
return self::intArrayToString(array_slice($h, 0, $hLen));
}
/**
* @param int $hLen
* @param ?string $ctx
* @param string $msg
* @return string
* @throws SodiumException
* @psalm-suppress PossiblyInvalidArgument hash API
*/
protected static function h2c_string_to_hash_sha512($hLen, $ctx, $msg)
{
$h = array_fill(0, $hLen, 0);
$ctx_len = !is_null($ctx) ? self::strlen($ctx) : 0;
if ($hLen > 0xff) {
throw new SodiumException('Hash must be less than 256 bytes');
}
if ($ctx_len > 0xff) {
$st = hash_init('sha256');
self::hash_update($st, "H2C-OVERSIZE-DST-");
self::hash_update($st, $ctx);
$ctx = hash_final($st, true);
$ctx_len = 32;
}
$t = array(0, $hLen, 0);
$ux = str_repeat("\0", 128);
$st = hash_init('sha512');
self::hash_update($st, $ux);
self::hash_update($st, $msg);
self::hash_update($st, self::intArrayToString($t));
self::hash_update($st, $ctx);
self::hash_update($st, self::intToChr($ctx_len));
$u0 = hash_final($st, true);
for ($i = 0; $i < $hLen; $i += 128) {
$ux = self::xorStrings($ux, $u0);
++$t[2];
$st = hash_init('sha512');
self::hash_update($st, $ux);
self::hash_update($st, self::intToChr($t[2]));
self::hash_update($st, $ctx);
self::hash_update($st, self::intToChr($ctx_len));
$ux = hash_final($st, true);
$amount = min($hLen - $i, 128);
for ($j = 0; $j < $amount; ++$j) {
$h[$i + $j] = self::chrToInt($ux[$i]);
}
}
return self::intArrayToString(array_slice($h, 0, $hLen));
}
/**
* @param int $hLen
* @param ?string $ctx
* @param string $msg
* @param int $hash_alg
* @return string
* @throws SodiumException
*/
public static function h2c_string_to_hash($hLen, $ctx, $msg, $hash_alg)
{
switch ($hash_alg) {
case self::CORE_H2C_SHA256:
return self::h2c_string_to_hash_sha256($hLen, $ctx, $msg);
case self::CORE_H2C_SHA512:
return self::h2c_string_to_hash_sha512($hLen, $ctx, $msg);
default:
throw new SodiumException('Invalid H2C hash algorithm');
}
}
/**
* @param ?string $ctx
* @param string $msg
* @param int $hash_alg
* @return string
* @throws SodiumException
*/
protected static function _string_to_element($ctx, $msg, $hash_alg)
{
return self::ristretto255_from_hash(
self::h2c_string_to_hash(self::crypto_core_ristretto255_HASHBYTES, $ctx, $msg, $hash_alg)
);
}
/**
* @return string
* @throws SodiumException
* @throws Exception
*/
public static function ristretto255_random()
{
return self::ristretto255_from_hash(
ParagonIE_Sodium_Compat::randombytes_buf(self::crypto_core_ristretto255_HASHBYTES)
);
}
/**
* @return string
* @throws SodiumException
*/
public static function ristretto255_scalar_random()
{
return self::scalar_random();
}
/**
* @param string $s
* @return string
* @throws SodiumException
*/
public static function ristretto255_scalar_complement($s)
{
return self::scalar_complement($s);
}
/**
* @param string $s
* @return string
*/
public static function ristretto255_scalar_invert($s)
{
return self::sc25519_invert($s);
}
/**
* @param string $s
* @return string
* @throws SodiumException
*/
public static function ristretto255_scalar_negate($s)
{
return self::scalar_negate($s);
}
/**
* @param string $x
* @param string $y
* @return string
*/
public static function ristretto255_scalar_add($x, $y)
{
return self::scalar_add($x, $y);
}
/**
* @param string $x
* @param string $y
* @return string
*/
public static function ristretto255_scalar_sub($x, $y)
{
return self::scalar_sub($x, $y);
}
/**
* @param string $x
* @param string $y
* @return string
*/
public static function ristretto255_scalar_mul($x, $y)
{
return self::sc25519_mul($x, $y);
}
/**
* @param string $ctx
* @param string $msg
* @param int $hash_alg
* @return string
* @throws SodiumException
*/
public static function ristretto255_scalar_from_string($ctx, $msg, $hash_alg)
{
$h = array_fill(0, 64, 0);
$h_be = self::stringToIntArray(
self::h2c_string_to_hash(
self::HASH_SC_L, $ctx, $msg, $hash_alg
)
);
for ($i = 0; $i < self::HASH_SC_L; ++$i) {
$h[$i] = $h_be[self::HASH_SC_L - 1 - $i];
}
return self::ristretto255_scalar_reduce(self::intArrayToString($h));
}
/**
* @param string $s
* @return string
*/
public static function ristretto255_scalar_reduce($s)
{
return self::sc_reduce($s);
}
/**
* @param string $n
* @param string $p
* @return string
* @throws SodiumException
*/
public static function scalarmult_ristretto255($n, $p)
{
if (self::strlen($n) !== 32) {
throw new SodiumException('Scalar must be 32 bytes, ' . self::strlen($p) . ' given.');
}
if (self::strlen($p) !== 32) {
throw new SodiumException('Point must be 32 bytes, ' . self::strlen($p) . ' given.');
}
$result = self::ristretto255_frombytes($p);
if ($result['res'] !== 0) {
throw new SodiumException('Could not multiply points');
}
$P = $result['h'];
$t = self::stringToIntArray($n);
$t[31] &= 0x7f;
$Q = self::ge_scalarmult(self::intArrayToString($t), $P);
$q = self::ristretto255_p3_tobytes($Q);
if (ParagonIE_Sodium_Compat::is_zero($q)) {
throw new SodiumException('An unknown error has occurred');
}
return $q;
}
/**
* @param string $n
* @return string
* @throws SodiumException
*/
public static function scalarmult_ristretto255_base($n)
{
$t = self::stringToIntArray($n);
$t[31] &= 0x7f;
$Q = self::ge_scalarmult_base(self::intArrayToString($t));
$q = self::ristretto255_p3_tobytes($Q);
if (ParagonIE_Sodium_Compat::is_zero($q)) {
throw new SodiumException('An unknown error has occurred');
}
return $q;
}
}
Core/AEGIS128L.php 0000644 00000007124 15133021213 0007337 0 ustar 00 <?php
if (!defined('SODIUM_COMPAT_AEGIS_C0')) {
define('SODIUM_COMPAT_AEGIS_C0', "\x00\x01\x01\x02\x03\x05\x08\x0d\x15\x22\x37\x59\x90\xe9\x79\x62");
}
if (!defined('SODIUM_COMPAT_AEGIS_C1')) {
define('SODIUM_COMPAT_AEGIS_C1', "\xdb\x3d\x18\x55\x6d\xc2\x2f\xf1\x20\x11\x31\x42\x73\xb5\x28\xdd");
}
class ParagonIE_Sodium_Core_AEGIS128L extends ParagonIE_Sodium_Core_AES
{
/**
* @param string $ct
* @param string $tag
* @param string $ad
* @param string $key
* @param string $nonce
* @return string
* @throws SodiumException
*/
public static function decrypt($ct, $tag, $ad, $key, $nonce)
{
$state = self::init($key, $nonce);
$ad_blocks = (self::strlen($ad) + 31) >> 5;
for ($i = 0; $i < $ad_blocks; ++$i) {
$ai = self::substr($ad, $i << 5, 32);
if (self::strlen($ai) < 32) {
$ai = str_pad($ai, 32, "\0", STR_PAD_RIGHT);
}
$state->absorb($ai);
}
$msg = '';
$cn = self::strlen($ct) & 31;
$ct_blocks = self::strlen($ct) >> 5;
for ($i = 0; $i < $ct_blocks; ++$i) {
$msg .= $state->dec(self::substr($ct, $i << 5, 32));
}
if ($cn) {
$start = $ct_blocks << 5;
$msg .= $state->decPartial(self::substr($ct, $start, $cn));
}
$expected_tag = $state->finalize(
self::strlen($ad) << 3,
self::strlen($msg) << 3
);
if (!self::hashEquals($expected_tag, $tag)) {
try {
// The RFC says to erase msg, so we shall try:
ParagonIE_Sodium_Compat::memzero($msg);
} catch (SodiumException $ex) {
// Do nothing if we cannot memzero
}
throw new SodiumException('verification failed');
}
return $msg;
}
/**
* @param string $msg
* @param string $ad
* @param string $key
* @param string $nonce
* @return array
*
* @throws SodiumException
*/
public static function encrypt($msg, $ad, $key, $nonce)
{
$state = self::init($key, $nonce);
// ad_blocks = Split(ZeroPad(ad, 256), 256)
// for ai in ad_blocks:
// Absorb(ai)
$ad_len = self::strlen($ad);
$msg_len = self::strlen($msg);
$ad_blocks = ($ad_len + 31) >> 5;
for ($i = 0; $i < $ad_blocks; ++$i) {
$ai = self::substr($ad, $i << 5, 32);
if (self::strlen($ai) < 32) {
$ai = str_pad($ai, 32, "\0", STR_PAD_RIGHT);
}
$state->absorb($ai);
}
// msg_blocks = Split(ZeroPad(msg, 256), 256)
// for xi in msg_blocks:
// ct = ct || Enc(xi)
$ct = '';
$msg_blocks = ($msg_len + 31) >> 5;
for ($i = 0; $i < $msg_blocks; ++$i) {
$xi = self::substr($msg, $i << 5, 32);
if (self::strlen($xi) < 32) {
$xi = str_pad($xi, 32, "\0", STR_PAD_RIGHT);
}
$ct .= $state->enc($xi);
}
// tag = Finalize(|ad|, |msg|)
// ct = Truncate(ct, |msg|)
$tag = $state->finalize(
$ad_len << 3,
$msg_len << 3
);
// return ct and tag
return array(
self::substr($ct, 0, $msg_len),
$tag
);
}
/**
* @param string $key
* @param string $nonce
* @return ParagonIE_Sodium_Core_AEGIS_State128L
*/
public static function init($key, $nonce)
{
return ParagonIE_Sodium_Core_AEGIS_State128L::init($key, $nonce);
}
}
Core/HChaCha20.php 0000604 00000007437 15133021213 0007524 0 ustar 00 <?php
if (class_exists('ParagonIE_Sodium_Core_HChaCha20', false)) {
return;
}
/**
* Class ParagonIE_Sodium_Core_HChaCha20
*/
class ParagonIE_Sodium_Core_HChaCha20 extends ParagonIE_Sodium_Core_ChaCha20
{
/**
* @param string $in
* @param string $key
* @param string|null $c
* @return string
* @throws TypeError
*/
public static function hChaCha20($in = '', $key = '', $c = null)
{
$ctx = array();
if ($c === null) {
$ctx[0] = 0x61707865;
$ctx[1] = 0x3320646e;
$ctx[2] = 0x79622d32;
$ctx[3] = 0x6b206574;
} else {
$ctx[0] = self::load_4(self::substr($c, 0, 4));
$ctx[1] = self::load_4(self::substr($c, 4, 4));
$ctx[2] = self::load_4(self::substr($c, 8, 4));
$ctx[3] = self::load_4(self::substr($c, 12, 4));
}
$ctx[4] = self::load_4(self::substr($key, 0, 4));
$ctx[5] = self::load_4(self::substr($key, 4, 4));
$ctx[6] = self::load_4(self::substr($key, 8, 4));
$ctx[7] = self::load_4(self::substr($key, 12, 4));
$ctx[8] = self::load_4(self::substr($key, 16, 4));
$ctx[9] = self::load_4(self::substr($key, 20, 4));
$ctx[10] = self::load_4(self::substr($key, 24, 4));
$ctx[11] = self::load_4(self::substr($key, 28, 4));
$ctx[12] = self::load_4(self::substr($in, 0, 4));
$ctx[13] = self::load_4(self::substr($in, 4, 4));
$ctx[14] = self::load_4(self::substr($in, 8, 4));
$ctx[15] = self::load_4(self::substr($in, 12, 4));
return self::hChaCha20Bytes($ctx);
}
/**
* @param array $ctx
* @return string
* @throws TypeError
*/
protected static function hChaCha20Bytes(array $ctx)
{
$x0 = (int) $ctx[0];
$x1 = (int) $ctx[1];
$x2 = (int) $ctx[2];
$x3 = (int) $ctx[3];
$x4 = (int) $ctx[4];
$x5 = (int) $ctx[5];
$x6 = (int) $ctx[6];
$x7 = (int) $ctx[7];
$x8 = (int) $ctx[8];
$x9 = (int) $ctx[9];
$x10 = (int) $ctx[10];
$x11 = (int) $ctx[11];
$x12 = (int) $ctx[12];
$x13 = (int) $ctx[13];
$x14 = (int) $ctx[14];
$x15 = (int) $ctx[15];
for ($i = 0; $i < 10; ++$i) {
# QUARTERROUND( x0, x4, x8, x12)
list($x0, $x4, $x8, $x12) = self::quarterRound($x0, $x4, $x8, $x12);
# QUARTERROUND( x1, x5, x9, x13)
list($x1, $x5, $x9, $x13) = self::quarterRound($x1, $x5, $x9, $x13);
# QUARTERROUND( x2, x6, x10, x14)
list($x2, $x6, $x10, $x14) = self::quarterRound($x2, $x6, $x10, $x14);
# QUARTERROUND( x3, x7, x11, x15)
list($x3, $x7, $x11, $x15) = self::quarterRound($x3, $x7, $x11, $x15);
# QUARTERROUND( x0, x5, x10, x15)
list($x0, $x5, $x10, $x15) = self::quarterRound($x0, $x5, $x10, $x15);
# QUARTERROUND( x1, x6, x11, x12)
list($x1, $x6, $x11, $x12) = self::quarterRound($x1, $x6, $x11, $x12);
# QUARTERROUND( x2, x7, x8, x13)
list($x2, $x7, $x8, $x13) = self::quarterRound($x2, $x7, $x8, $x13);
# QUARTERROUND( x3, x4, x9, x14)
list($x3, $x4, $x9, $x14) = self::quarterRound($x3, $x4, $x9, $x14);
}
return self::store32_le((int) ($x0 & 0xffffffff)) .
self::store32_le((int) ($x1 & 0xffffffff)) .
self::store32_le((int) ($x2 & 0xffffffff)) .
self::store32_le((int) ($x3 & 0xffffffff)) .
self::store32_le((int) ($x12 & 0xffffffff)) .
self::store32_le((int) ($x13 & 0xffffffff)) .
self::store32_le((int) ($x14 & 0xffffffff)) .
self::store32_le((int) ($x15 & 0xffffffff));
}
}
Core/Ed25519.php 0000644 00000042114 15133021213 0007074 0 ustar 00 <?php
if (class_exists('ParagonIE_Sodium_Core_Ed25519', false)) {
return;
}
if (!class_exists('ParagonIE_Sodium_Core_Curve25519', false)) {
require_once dirname(__FILE__) . '/Curve25519.php';
}
/**
* Class ParagonIE_Sodium_Core_Ed25519
*/
abstract class ParagonIE_Sodium_Core_Ed25519 extends ParagonIE_Sodium_Core_Curve25519
{
const KEYPAIR_BYTES = 96;
const SEED_BYTES = 32;
const SCALAR_BYTES = 32;
/**
* @internal You should not use this directly from another application
*
* @return string (96 bytes)
* @throws Exception
* @throws SodiumException
* @throws TypeError
*/
public static function keypair()
{
$seed = random_bytes(self::SEED_BYTES);
$pk = '';
$sk = '';
self::seed_keypair($pk, $sk, $seed);
return $sk . $pk;
}
/**
* @internal You should not use this directly from another application
*
* @param string $pk
* @param string $sk
* @param string $seed
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function seed_keypair(&$pk, &$sk, $seed)
{
if (self::strlen($seed) !== self::SEED_BYTES) {
throw new RangeException('crypto_sign keypair seed must be 32 bytes long');
}
/** @var string $pk */
$pk = self::publickey_from_secretkey($seed);
$sk = $seed . $pk;
return $sk;
}
/**
* @internal You should not use this directly from another application
*
* @param string $keypair
* @return string
* @throws TypeError
*/
public static function secretkey($keypair)
{
if (self::strlen($keypair) !== self::KEYPAIR_BYTES) {
throw new RangeException('crypto_sign keypair must be 96 bytes long');
}
return self::substr($keypair, 0, 64);
}
/**
* @internal You should not use this directly from another application
*
* @param string $keypair
* @return string
* @throws TypeError
*/
public static function publickey($keypair)
{
if (self::strlen($keypair) !== self::KEYPAIR_BYTES) {
throw new RangeException('crypto_sign keypair must be 96 bytes long');
}
return self::substr($keypair, 64, 32);
}
/**
* @internal You should not use this directly from another application
*
* @param string $sk
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function publickey_from_secretkey($sk)
{
/** @var string $sk */
$sk = hash('sha512', self::substr($sk, 0, 32), true);
$sk[0] = self::intToChr(
self::chrToInt($sk[0]) & 248
);
$sk[31] = self::intToChr(
(self::chrToInt($sk[31]) & 63) | 64
);
return self::sk_to_pk($sk);
}
/**
* @param string $pk
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function pk_to_curve25519($pk)
{
if (self::small_order($pk)) {
throw new SodiumException('Public key is on a small order');
}
$A = self::ge_frombytes_negate_vartime(self::substr($pk, 0, 32));
$p1 = self::ge_mul_l($A);
if (!self::fe_isnonzero($p1->X)) {
throw new SodiumException('Unexpected zero result');
}
# fe_1(one_minus_y);
# fe_sub(one_minus_y, one_minus_y, A.Y);
# fe_invert(one_minus_y, one_minus_y);
$one_minux_y = self::fe_invert(
self::fe_sub(
self::fe_1(),
$A->Y
)
);
# fe_1(x);
# fe_add(x, x, A.Y);
# fe_mul(x, x, one_minus_y);
$x = self::fe_mul(
self::fe_add(self::fe_1(), $A->Y),
$one_minux_y
);
# fe_tobytes(curve25519_pk, x);
return self::fe_tobytes($x);
}
/**
* @internal You should not use this directly from another application
*
* @param string $sk
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function sk_to_pk($sk)
{
return self::ge_p3_tobytes(
self::ge_scalarmult_base(
self::substr($sk, 0, 32)
)
);
}
/**
* @internal You should not use this directly from another application
*
* @param string $message
* @param string $sk
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function sign($message, $sk)
{
/** @var string $signature */
$signature = self::sign_detached($message, $sk);
return $signature . $message;
}
/**
* @internal You should not use this directly from another application
*
* @param string $message A signed message
* @param string $pk Public key
* @return string Message (without signature)
* @throws SodiumException
* @throws TypeError
*/
public static function sign_open($message, $pk)
{
/** @var string $signature */
$signature = self::substr($message, 0, 64);
/** @var string $message */
$message = self::substr($message, 64);
if (self::verify_detached($signature, $message, $pk)) {
return $message;
}
throw new SodiumException('Invalid signature');
}
/**
* @internal You should not use this directly from another application
*
* @param string $message
* @param string $sk
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function sign_detached($message, $sk)
{
# crypto_hash_sha512(az, sk, 32);
$az = hash('sha512', self::substr($sk, 0, 32), true);
# az[0] &= 248;
# az[31] &= 63;
# az[31] |= 64;
$az[0] = self::intToChr(self::chrToInt($az[0]) & 248);
$az[31] = self::intToChr((self::chrToInt($az[31]) & 63) | 64);
# crypto_hash_sha512_init(&hs);
# crypto_hash_sha512_update(&hs, az + 32, 32);
# crypto_hash_sha512_update(&hs, m, mlen);
# crypto_hash_sha512_final(&hs, nonce);
$hs = hash_init('sha512');
hash_update($hs, self::substr($az, 32, 32));
hash_update($hs, $message);
$nonceHash = hash_final($hs, true);
# memmove(sig + 32, sk + 32, 32);
$pk = self::substr($sk, 32, 32);
# sc_reduce(nonce);
# ge_scalarmult_base(&R, nonce);
# ge_p3_tobytes(sig, &R);
$nonce = self::sc_reduce($nonceHash) . self::substr($nonceHash, 32);
$sig = self::ge_p3_tobytes(
self::ge_scalarmult_base($nonce)
);
# crypto_hash_sha512_init(&hs);
# crypto_hash_sha512_update(&hs, sig, 64);
# crypto_hash_sha512_update(&hs, m, mlen);
# crypto_hash_sha512_final(&hs, hram);
$hs = hash_init('sha512');
hash_update($hs, self::substr($sig, 0, 32));
hash_update($hs, self::substr($pk, 0, 32));
hash_update($hs, $message);
$hramHash = hash_final($hs, true);
# sc_reduce(hram);
# sc_muladd(sig + 32, hram, az, nonce);
$hram = self::sc_reduce($hramHash);
$sigAfter = self::sc_muladd($hram, $az, $nonce);
$sig = self::substr($sig, 0, 32) . self::substr($sigAfter, 0, 32);
try {
ParagonIE_Sodium_Compat::memzero($az);
} catch (SodiumException $ex) {
$az = null;
}
return $sig;
}
/**
* @internal You should not use this directly from another application
*
* @param string $sig
* @param string $message
* @param string $pk
* @return bool
* @throws SodiumException
* @throws TypeError
*/
public static function verify_detached($sig, $message, $pk)
{
if (self::strlen($sig) < 64) {
throw new SodiumException('Signature is too short');
}
if ((self::chrToInt($sig[63]) & 240) && self::check_S_lt_L(self::substr($sig, 32, 32))) {
throw new SodiumException('S < L - Invalid signature');
}
if (self::small_order($sig)) {
throw new SodiumException('Signature is on too small of an order');
}
if ((self::chrToInt($sig[63]) & 224) !== 0) {
throw new SodiumException('Invalid signature');
}
$d = 0;
for ($i = 0; $i < 32; ++$i) {
$d |= self::chrToInt($pk[$i]);
}
if ($d === 0) {
throw new SodiumException('All zero public key');
}
/** @var bool The original value of ParagonIE_Sodium_Compat::$fastMult */
$orig = ParagonIE_Sodium_Compat::$fastMult;
// Set ParagonIE_Sodium_Compat::$fastMult to true to speed up verification.
ParagonIE_Sodium_Compat::$fastMult = true;
/** @var ParagonIE_Sodium_Core_Curve25519_Ge_P3 $A */
$A = self::ge_frombytes_negate_vartime($pk);
/** @var string $hDigest */
$hDigest = hash(
'sha512',
self::substr($sig, 0, 32) .
self::substr($pk, 0, 32) .
$message,
true
);
/** @var string $h */
$h = self::sc_reduce($hDigest) . self::substr($hDigest, 32);
/** @var ParagonIE_Sodium_Core_Curve25519_Ge_P2 $R */
$R = self::ge_double_scalarmult_vartime(
$h,
$A,
self::substr($sig, 32)
);
/** @var string $rcheck */
$rcheck = self::ge_tobytes($R);
// Reset ParagonIE_Sodium_Compat::$fastMult to what it was before.
ParagonIE_Sodium_Compat::$fastMult = $orig;
return self::verify_32($rcheck, self::substr($sig, 0, 32));
}
/**
* @internal You should not use this directly from another application
*
* @param string $S
* @return bool
* @throws SodiumException
* @throws TypeError
*/
public static function check_S_lt_L($S)
{
if (self::strlen($S) < 32) {
throw new SodiumException('Signature must be 32 bytes');
}
$L = array(
0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58,
0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10
);
$c = 0;
$n = 1;
$i = 32;
/** @var array<int, int> $L */
do {
--$i;
$x = self::chrToInt($S[$i]);
$c |= (
(($x - $L[$i]) >> 8) & $n
);
$n &= (
(($x ^ $L[$i]) - 1) >> 8
);
} while ($i !== 0);
return $c === 0;
}
/**
* @param string $R
* @return bool
* @throws SodiumException
* @throws TypeError
*/
public static function small_order($R)
{
/** @var array<int, array<int, int>> $blocklist */
$blocklist = array(
/* 0 (order 4) */
array(
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
),
/* 1 (order 1) */
array(
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
),
/* 2707385501144840649318225287225658788936804267575313519463743609750303402022 (order 8) */
array(
0x26, 0xe8, 0x95, 0x8f, 0xc2, 0xb2, 0x27, 0xb0,
0x45, 0xc3, 0xf4, 0x89, 0xf2, 0xef, 0x98, 0xf0,
0xd5, 0xdf, 0xac, 0x05, 0xd3, 0xc6, 0x33, 0x39,
0xb1, 0x38, 0x02, 0x88, 0x6d, 0x53, 0xfc, 0x05
),
/* 55188659117513257062467267217118295137698188065244968500265048394206261417927 (order 8) */
array(
0xc7, 0x17, 0x6a, 0x70, 0x3d, 0x4d, 0xd8, 0x4f,
0xba, 0x3c, 0x0b, 0x76, 0x0d, 0x10, 0x67, 0x0f,
0x2a, 0x20, 0x53, 0xfa, 0x2c, 0x39, 0xcc, 0xc6,
0x4e, 0xc7, 0xfd, 0x77, 0x92, 0xac, 0x03, 0x7a
),
/* p-1 (order 2) */
array(
0x13, 0xe8, 0x95, 0x8f, 0xc2, 0xb2, 0x27, 0xb0,
0x45, 0xc3, 0xf4, 0x89, 0xf2, 0xef, 0x98, 0xf0,
0xd5, 0xdf, 0xac, 0x05, 0xd3, 0xc6, 0x33, 0x39,
0xb1, 0x38, 0x02, 0x88, 0x6d, 0x53, 0xfc, 0x85
),
/* p (order 4) */
array(
0xb4, 0x17, 0x6a, 0x70, 0x3d, 0x4d, 0xd8, 0x4f,
0xba, 0x3c, 0x0b, 0x76, 0x0d, 0x10, 0x67, 0x0f,
0x2a, 0x20, 0x53, 0xfa, 0x2c, 0x39, 0xcc, 0xc6,
0x4e, 0xc7, 0xfd, 0x77, 0x92, 0xac, 0x03, 0xfa
),
/* p+1 (order 1) */
array(
0xec, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f
),
/* p+2707385501144840649318225287225658788936804267575313519463743609750303402022 (order 8) */
array(
0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f
),
/* p+55188659117513257062467267217118295137698188065244968500265048394206261417927 (order 8) */
array(
0xee, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f
),
/* 2p-1 (order 2) */
array(
0xd9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
),
/* 2p (order 4) */
array(
0xda, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
),
/* 2p+1 (order 1) */
array(
0xdb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
)
);
/** @var int $countBlocklist */
$countBlocklist = count($blocklist);
for ($i = 0; $i < $countBlocklist; ++$i) {
$c = 0;
for ($j = 0; $j < 32; ++$j) {
$c |= self::chrToInt($R[$j]) ^ (int) $blocklist[$i][$j];
}
if ($c === 0) {
return true;
}
}
return false;
}
/**
* @param string $s
* @return string
* @throws SodiumException
*/
public static function scalar_complement($s)
{
$t_ = self::L . str_repeat("\x00", 32);
sodium_increment($t_);
$s_ = $s . str_repeat("\x00", 32);
ParagonIE_Sodium_Compat::sub($t_, $s_);
return self::sc_reduce($t_);
}
/**
* @return string
* @throws SodiumException
*/
public static function scalar_random()
{
do {
$r = ParagonIE_Sodium_Compat::randombytes_buf(self::SCALAR_BYTES);
$r[self::SCALAR_BYTES - 1] = self::intToChr(
self::chrToInt($r[self::SCALAR_BYTES - 1]) & 0x1f
);
} while (
!self::check_S_lt_L($r) || ParagonIE_Sodium_Compat::is_zero($r)
);
return $r;
}
/**
* @param string $s
* @return string
* @throws SodiumException
*/
public static function scalar_negate($s)
{
$t_ = self::L . str_repeat("\x00", 32) ;
$s_ = $s . str_repeat("\x00", 32) ;
ParagonIE_Sodium_Compat::sub($t_, $s_);
return self::sc_reduce($t_);
}
/**
* @param string $a
* @param string $b
* @return string
* @throws SodiumException
*/
public static function scalar_add($a, $b)
{
$a_ = $a . str_repeat("\x00", 32);
$b_ = $b . str_repeat("\x00", 32);
ParagonIE_Sodium_Compat::add($a_, $b_);
return self::sc_reduce($a_);
}
/**
* @param string $x
* @param string $y
* @return string
* @throws SodiumException
*/
public static function scalar_sub($x, $y)
{
$yn = self::scalar_negate($y);
return self::scalar_add($x, $yn);
}
}
Core/Poly1305/State.php 0000644 00000031160 15133021213 0010451 0 ustar 00 <?php
if (class_exists('ParagonIE_Sodium_Core_Poly1305_State', false)) {
return;
}
/**
* Class ParagonIE_Sodium_Core_Poly1305_State
*/
class ParagonIE_Sodium_Core_Poly1305_State extends ParagonIE_Sodium_Core_Util
{
/**
* @var array<int, int>
*/
protected $buffer = array();
/**
* @var bool
*/
protected $final = false;
/**
* @var array<int, int>
*/
public $h;
/**
* @var int
*/
protected $leftover = 0;
/**
* @var int[]
*/
public $r;
/**
* @var int[]
*/
public $pad;
/**
* ParagonIE_Sodium_Core_Poly1305_State constructor.
*
* @internal You should not use this directly from another application
*
* @param string $key
* @throws InvalidArgumentException
* @throws TypeError
*/
public function __construct($key = '')
{
if (self::strlen($key) < 32) {
throw new InvalidArgumentException(
'Poly1305 requires a 32-byte key'
);
}
/* r &= 0xffffffc0ffffffc0ffffffc0fffffff */
$this->r = array(
(int) ((self::load_4(self::substr($key, 0, 4))) & 0x3ffffff),
(int) ((self::load_4(self::substr($key, 3, 4)) >> 2) & 0x3ffff03),
(int) ((self::load_4(self::substr($key, 6, 4)) >> 4) & 0x3ffc0ff),
(int) ((self::load_4(self::substr($key, 9, 4)) >> 6) & 0x3f03fff),
(int) ((self::load_4(self::substr($key, 12, 4)) >> 8) & 0x00fffff)
);
/* h = 0 */
$this->h = array(0, 0, 0, 0, 0);
/* save pad for later */
$this->pad = array(
self::load_4(self::substr($key, 16, 4)),
self::load_4(self::substr($key, 20, 4)),
self::load_4(self::substr($key, 24, 4)),
self::load_4(self::substr($key, 28, 4)),
);
$this->leftover = 0;
$this->final = false;
}
/**
* Zero internal buffer upon destruction
*/
public function __destruct()
{
$this->r[0] ^= $this->r[0];
$this->r[1] ^= $this->r[1];
$this->r[2] ^= $this->r[2];
$this->r[3] ^= $this->r[3];
$this->r[4] ^= $this->r[4];
$this->h[0] ^= $this->h[0];
$this->h[1] ^= $this->h[1];
$this->h[2] ^= $this->h[2];
$this->h[3] ^= $this->h[3];
$this->h[4] ^= $this->h[4];
$this->pad[0] ^= $this->pad[0];
$this->pad[1] ^= $this->pad[1];
$this->pad[2] ^= $this->pad[2];
$this->pad[3] ^= $this->pad[3];
$this->leftover = 0;
$this->final = true;
}
/**
* @internal You should not use this directly from another application
*
* @param string $message
* @return self
* @throws SodiumException
* @throws TypeError
*/
public function update($message = '')
{
$bytes = self::strlen($message);
if ($bytes < 1) {
return $this;
}
/* handle leftover */
if ($this->leftover) {
$want = ParagonIE_Sodium_Core_Poly1305::BLOCK_SIZE - $this->leftover;
if ($want > $bytes) {
$want = $bytes;
}
for ($i = 0; $i < $want; ++$i) {
$mi = self::chrToInt($message[$i]);
$this->buffer[$this->leftover + $i] = $mi;
}
// We snip off the leftmost bytes.
$message = self::substr($message, $want);
$bytes = self::strlen($message);
$this->leftover += $want;
if ($this->leftover < ParagonIE_Sodium_Core_Poly1305::BLOCK_SIZE) {
// We still don't have enough to run $this->blocks()
return $this;
}
$this->blocks(
self::intArrayToString($this->buffer),
ParagonIE_Sodium_Core_Poly1305::BLOCK_SIZE
);
$this->leftover = 0;
}
/* process full blocks */
if ($bytes >= ParagonIE_Sodium_Core_Poly1305::BLOCK_SIZE) {
/** @var int $want */
$want = $bytes & ~(ParagonIE_Sodium_Core_Poly1305::BLOCK_SIZE - 1);
if ($want >= ParagonIE_Sodium_Core_Poly1305::BLOCK_SIZE) {
$block = self::substr($message, 0, $want);
if (self::strlen($block) >= ParagonIE_Sodium_Core_Poly1305::BLOCK_SIZE) {
$this->blocks($block, $want);
$message = self::substr($message, $want);
$bytes = self::strlen($message);
}
}
}
/* store leftover */
if ($bytes) {
for ($i = 0; $i < $bytes; ++$i) {
$mi = self::chrToInt($message[$i]);
$this->buffer[$this->leftover + $i] = $mi;
}
$this->leftover = (int) $this->leftover + $bytes;
}
return $this;
}
/**
* @internal You should not use this directly from another application
*
* @param string $message
* @param int $bytes
* @return self
* @throws TypeError
*/
public function blocks($message, $bytes)
{
if (self::strlen($message) < 16) {
$message = str_pad($message, 16, "\x00", STR_PAD_RIGHT);
}
/** @var int $hibit */
$hibit = $this->final ? 0 : 1 << 24; /* 1 << 128 */
$r0 = (int) $this->r[0];
$r1 = (int) $this->r[1];
$r2 = (int) $this->r[2];
$r3 = (int) $this->r[3];
$r4 = (int) $this->r[4];
$s1 = self::mul($r1, 5, 3);
$s2 = self::mul($r2, 5, 3);
$s3 = self::mul($r3, 5, 3);
$s4 = self::mul($r4, 5, 3);
$h0 = $this->h[0];
$h1 = $this->h[1];
$h2 = $this->h[2];
$h3 = $this->h[3];
$h4 = $this->h[4];
while ($bytes >= ParagonIE_Sodium_Core_Poly1305::BLOCK_SIZE) {
/* h += m[i] */
$h0 += self::load_4(self::substr($message, 0, 4)) & 0x3ffffff;
$h1 += (self::load_4(self::substr($message, 3, 4)) >> 2) & 0x3ffffff;
$h2 += (self::load_4(self::substr($message, 6, 4)) >> 4) & 0x3ffffff;
$h3 += (self::load_4(self::substr($message, 9, 4)) >> 6) & 0x3ffffff;
$h4 += (self::load_4(self::substr($message, 12, 4)) >> 8) | $hibit;
/* h *= r */
$d0 = (
self::mul($h0, $r0, 27) +
self::mul($s4, $h1, 27) +
self::mul($s3, $h2, 27) +
self::mul($s2, $h3, 27) +
self::mul($s1, $h4, 27)
);
$d1 = (
self::mul($h0, $r1, 27) +
self::mul($h1, $r0, 27) +
self::mul($s4, $h2, 27) +
self::mul($s3, $h3, 27) +
self::mul($s2, $h4, 27)
);
$d2 = (
self::mul($h0, $r2, 27) +
self::mul($h1, $r1, 27) +
self::mul($h2, $r0, 27) +
self::mul($s4, $h3, 27) +
self::mul($s3, $h4, 27)
);
$d3 = (
self::mul($h0, $r3, 27) +
self::mul($h1, $r2, 27) +
self::mul($h2, $r1, 27) +
self::mul($h3, $r0, 27) +
self::mul($s4, $h4, 27)
);
$d4 = (
self::mul($h0, $r4, 27) +
self::mul($h1, $r3, 27) +
self::mul($h2, $r2, 27) +
self::mul($h3, $r1, 27) +
self::mul($h4, $r0, 27)
);
/* (partial) h %= p */
/** @var int $c */
$c = $d0 >> 26;
/** @var int $h0 */
$h0 = $d0 & 0x3ffffff;
$d1 += $c;
/** @var int $c */
$c = $d1 >> 26;
/** @var int $h1 */
$h1 = $d1 & 0x3ffffff;
$d2 += $c;
/** @var int $c */
$c = $d2 >> 26;
/** @var int $h2 */
$h2 = $d2 & 0x3ffffff;
$d3 += $c;
/** @var int $c */
$c = $d3 >> 26;
/** @var int $h3 */
$h3 = $d3 & 0x3ffffff;
$d4 += $c;
/** @var int $c */
$c = $d4 >> 26;
/** @var int $h4 */
$h4 = $d4 & 0x3ffffff;
$h0 += (int) self::mul($c, 5, 3);
/** @var int $c */
$c = $h0 >> 26;
/** @var int $h0 */
$h0 &= 0x3ffffff;
$h1 += $c;
// Chop off the left 32 bytes.
$message = self::substr(
$message,
ParagonIE_Sodium_Core_Poly1305::BLOCK_SIZE
);
$bytes -= ParagonIE_Sodium_Core_Poly1305::BLOCK_SIZE;
}
$this->h = array(
(int) ($h0 & 0xffffffff),
(int) ($h1 & 0xffffffff),
(int) ($h2 & 0xffffffff),
(int) ($h3 & 0xffffffff),
(int) ($h4 & 0xffffffff)
);
return $this;
}
/**
* @internal You should not use this directly from another application
*
* @return string
* @throws TypeError
*/
public function finish()
{
/* process the remaining block */
if ($this->leftover) {
$i = $this->leftover;
$this->buffer[$i++] = 1;
for (; $i < ParagonIE_Sodium_Core_Poly1305::BLOCK_SIZE; ++$i) {
$this->buffer[$i] = 0;
}
$this->final = true;
$this->blocks(
self::substr(
self::intArrayToString($this->buffer),
0,
ParagonIE_Sodium_Core_Poly1305::BLOCK_SIZE
),
ParagonIE_Sodium_Core_Poly1305::BLOCK_SIZE
);
}
$h0 = (int) $this->h[0];
$h1 = (int) $this->h[1];
$h2 = (int) $this->h[2];
$h3 = (int) $this->h[3];
$h4 = (int) $this->h[4];
/** @var int $c */
$c = $h1 >> 26;
/** @var int $h1 */
$h1 &= 0x3ffffff;
/** @var int $h2 */
$h2 += $c;
/** @var int $c */
$c = $h2 >> 26;
/** @var int $h2 */
$h2 &= 0x3ffffff;
$h3 += $c;
/** @var int $c */
$c = $h3 >> 26;
$h3 &= 0x3ffffff;
$h4 += $c;
/** @var int $c */
$c = $h4 >> 26;
$h4 &= 0x3ffffff;
/** @var int $h0 */
$h0 += self::mul($c, 5, 3);
/** @var int $c */
$c = $h0 >> 26;
/** @var int $h0 */
$h0 &= 0x3ffffff;
/** @var int $h1 */
$h1 += $c;
/* compute h + -p */
/** @var int $g0 */
$g0 = $h0 + 5;
/** @var int $c */
$c = $g0 >> 26;
/** @var int $g0 */
$g0 &= 0x3ffffff;
/** @var int $g1 */
$g1 = $h1 + $c;
/** @var int $c */
$c = $g1 >> 26;
$g1 &= 0x3ffffff;
/** @var int $g2 */
$g2 = $h2 + $c;
/** @var int $c */
$c = $g2 >> 26;
/** @var int $g2 */
$g2 &= 0x3ffffff;
/** @var int $g3 */
$g3 = $h3 + $c;
/** @var int $c */
$c = $g3 >> 26;
/** @var int $g3 */
$g3 &= 0x3ffffff;
/** @var int $g4 */
$g4 = ($h4 + $c - (1 << 26)) & 0xffffffff;
/* select h if h < p, or h + -p if h >= p */
/** @var int $mask */
$mask = ($g4 >> 31) - 1;
$g0 &= $mask;
$g1 &= $mask;
$g2 &= $mask;
$g3 &= $mask;
$g4 &= $mask;
/** @var int $mask */
$mask = ~$mask & 0xffffffff;
/** @var int $h0 */
$h0 = ($h0 & $mask) | $g0;
/** @var int $h1 */
$h1 = ($h1 & $mask) | $g1;
/** @var int $h2 */
$h2 = ($h2 & $mask) | $g2;
/** @var int $h3 */
$h3 = ($h3 & $mask) | $g3;
/** @var int $h4 */
$h4 = ($h4 & $mask) | $g4;
/* h = h % (2^128) */
/** @var int $h0 */
$h0 = (($h0) | ($h1 << 26)) & 0xffffffff;
/** @var int $h1 */
$h1 = (($h1 >> 6) | ($h2 << 20)) & 0xffffffff;
/** @var int $h2 */
$h2 = (($h2 >> 12) | ($h3 << 14)) & 0xffffffff;
/** @var int $h3 */
$h3 = (($h3 >> 18) | ($h4 << 8)) & 0xffffffff;
/* mac = (h + pad) % (2^128) */
$f = (int) ($h0 + $this->pad[0]);
$h0 = (int) $f;
$f = (int) ($h1 + $this->pad[1] + ($f >> 32));
$h1 = (int) $f;
$f = (int) ($h2 + $this->pad[2] + ($f >> 32));
$h2 = (int) $f;
$f = (int) ($h3 + $this->pad[3] + ($f >> 32));
$h3 = (int) $f;
return self::store32_le($h0 & 0xffffffff) .
self::store32_le($h1 & 0xffffffff) .
self::store32_le($h2 & 0xffffffff) .
self::store32_le($h3 & 0xffffffff);
}
}
Core/Base64/UrlSafe.php 0000604 00000017063 15133021213 0010524 0 ustar 00 <?php
/**
* Class ParagonIE_Sodium_Core_Base64UrlSafe
*
* Copyright (c) 2016 - 2018 Paragon Initiative Enterprises.
* Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot com)
*/
class ParagonIE_Sodium_Core_Base64_UrlSafe
{
// COPY ParagonIE_Sodium_Core_Base64_Common STARTING HERE
/**
* Encode into Base64
*
* Base64 character set "[A-Z][a-z][0-9]+/"
*
* @param string $src
* @return string
* @throws TypeError
*/
public static function encode($src)
{
return self::doEncode($src, true);
}
/**
* Encode into Base64, no = padding
*
* Base64 character set "[A-Z][a-z][0-9]+/"
*
* @param string $src
* @return string
* @throws TypeError
*/
public static function encodeUnpadded($src)
{
return self::doEncode($src, false);
}
/**
* @param string $src
* @param bool $pad Include = padding?
* @return string
* @throws TypeError
*/
protected static function doEncode($src, $pad = true)
{
$dest = '';
$srcLen = ParagonIE_Sodium_Core_Util::strlen($src);
// Main loop (no padding):
for ($i = 0; $i + 3 <= $srcLen; $i += 3) {
/** @var array<int, int> $chunk */
$chunk = unpack('C*', ParagonIE_Sodium_Core_Util::substr($src, $i, 3));
$b0 = $chunk[1];
$b1 = $chunk[2];
$b2 = $chunk[3];
$dest .=
self::encode6Bits( $b0 >> 2 ) .
self::encode6Bits((($b0 << 4) | ($b1 >> 4)) & 63) .
self::encode6Bits((($b1 << 2) | ($b2 >> 6)) & 63) .
self::encode6Bits( $b2 & 63);
}
// The last chunk, which may have padding:
if ($i < $srcLen) {
/** @var array<int, int> $chunk */
$chunk = unpack('C*', ParagonIE_Sodium_Core_Util::substr($src, $i, $srcLen - $i));
$b0 = $chunk[1];
if ($i + 1 < $srcLen) {
$b1 = $chunk[2];
$dest .=
self::encode6Bits($b0 >> 2) .
self::encode6Bits((($b0 << 4) | ($b1 >> 4)) & 63) .
self::encode6Bits(($b1 << 2) & 63);
if ($pad) {
$dest .= '=';
}
} else {
$dest .=
self::encode6Bits( $b0 >> 2) .
self::encode6Bits(($b0 << 4) & 63);
if ($pad) {
$dest .= '==';
}
}
}
return $dest;
}
/**
* decode from base64 into binary
*
* Base64 character set "./[A-Z][a-z][0-9]"
*
* @param string $src
* @param bool $strictPadding
* @return string
* @throws RangeException
* @throws TypeError
* @psalm-suppress RedundantCondition
*/
public static function decode($src, $strictPadding = false)
{
// Remove padding
$srcLen = ParagonIE_Sodium_Core_Util::strlen($src);
if ($srcLen === 0) {
return '';
}
if ($strictPadding) {
if (($srcLen & 3) === 0) {
if ($src[$srcLen - 1] === '=') {
$srcLen--;
if ($src[$srcLen - 1] === '=') {
$srcLen--;
}
}
}
if (($srcLen & 3) === 1) {
throw new RangeException(
'Incorrect padding'
);
}
if ($src[$srcLen - 1] === '=') {
throw new RangeException(
'Incorrect padding'
);
}
} else {
$src = rtrim($src, '=');
$srcLen = ParagonIE_Sodium_Core_Util::strlen($src);
}
$err = 0;
$dest = '';
// Main loop (no padding):
for ($i = 0; $i + 4 <= $srcLen; $i += 4) {
/** @var array<int, int> $chunk */
$chunk = unpack('C*', ParagonIE_Sodium_Core_Util::substr($src, $i, 4));
$c0 = self::decode6Bits($chunk[1]);
$c1 = self::decode6Bits($chunk[2]);
$c2 = self::decode6Bits($chunk[3]);
$c3 = self::decode6Bits($chunk[4]);
$dest .= pack(
'CCC',
((($c0 << 2) | ($c1 >> 4)) & 0xff),
((($c1 << 4) | ($c2 >> 2)) & 0xff),
((($c2 << 6) | $c3) & 0xff)
);
$err |= ($c0 | $c1 | $c2 | $c3) >> 8;
}
// The last chunk, which may have padding:
if ($i < $srcLen) {
/** @var array<int, int> $chunk */
$chunk = unpack('C*', ParagonIE_Sodium_Core_Util::substr($src, $i, $srcLen - $i));
$c0 = self::decode6Bits($chunk[1]);
if ($i + 2 < $srcLen) {
$c1 = self::decode6Bits($chunk[2]);
$c2 = self::decode6Bits($chunk[3]);
$dest .= pack(
'CC',
((($c0 << 2) | ($c1 >> 4)) & 0xff),
((($c1 << 4) | ($c2 >> 2)) & 0xff)
);
$err |= ($c0 | $c1 | $c2) >> 8;
} elseif ($i + 1 < $srcLen) {
$c1 = self::decode6Bits($chunk[2]);
$dest .= pack(
'C',
((($c0 << 2) | ($c1 >> 4)) & 0xff)
);
$err |= ($c0 | $c1) >> 8;
} elseif ($i < $srcLen && $strictPadding) {
$err |= 1;
}
}
/** @var bool $check */
$check = ($err === 0);
if (!$check) {
throw new RangeException(
'Base64::decode() only expects characters in the correct base64 alphabet'
);
}
return $dest;
}
// COPY ParagonIE_Sodium_Core_Base64_Common ENDING HERE
/**
* Uses bitwise operators instead of table-lookups to turn 6-bit integers
* into 8-bit integers.
*
* Base64 character set:
* [A-Z] [a-z] [0-9] + /
* 0x41-0x5a, 0x61-0x7a, 0x30-0x39, 0x2b, 0x2f
*
* @param int $src
* @return int
*/
protected static function decode6Bits($src)
{
$ret = -1;
// if ($src > 0x40 && $src < 0x5b) $ret += $src - 0x41 + 1; // -64
$ret += (((0x40 - $src) & ($src - 0x5b)) >> 8) & ($src - 64);
// if ($src > 0x60 && $src < 0x7b) $ret += $src - 0x61 + 26 + 1; // -70
$ret += (((0x60 - $src) & ($src - 0x7b)) >> 8) & ($src - 70);
// if ($src > 0x2f && $src < 0x3a) $ret += $src - 0x30 + 52 + 1; // 5
$ret += (((0x2f - $src) & ($src - 0x3a)) >> 8) & ($src + 5);
// if ($src == 0x2c) $ret += 62 + 1;
$ret += (((0x2c - $src) & ($src - 0x2e)) >> 8) & 63;
// if ($src == 0x5f) ret += 63 + 1;
$ret += (((0x5e - $src) & ($src - 0x60)) >> 8) & 64;
return $ret;
}
/**
* Uses bitwise operators instead of table-lookups to turn 8-bit integers
* into 6-bit integers.
*
* @param int $src
* @return string
*/
protected static function encode6Bits($src)
{
$diff = 0x41;
// if ($src > 25) $diff += 0x61 - 0x41 - 26; // 6
$diff += ((25 - $src) >> 8) & 6;
// if ($src > 51) $diff += 0x30 - 0x61 - 26; // -75
$diff -= ((51 - $src) >> 8) & 75;
// if ($src > 61) $diff += 0x2d - 0x30 - 10; // -13
$diff -= ((61 - $src) >> 8) & 13;
// if ($src > 62) $diff += 0x5f - 0x2b - 1; // 3
$diff += ((62 - $src) >> 8) & 49;
return pack('C', $src + $diff);
}
}
Core/Base64/Original.php 0000604 00000017055 15133021213 0010730 0 ustar 00 <?php
/**
* Class ParagonIE_Sodium_Core_Base64
*
* Copyright (c) 2016 - 2018 Paragon Initiative Enterprises.
* Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot com)
*/
class ParagonIE_Sodium_Core_Base64_Original
{
// COPY ParagonIE_Sodium_Core_Base64_Common STARTING HERE
/**
* Encode into Base64
*
* Base64 character set "[A-Z][a-z][0-9]+/"
*
* @param string $src
* @return string
* @throws TypeError
*/
public static function encode($src)
{
return self::doEncode($src, true);
}
/**
* Encode into Base64, no = padding
*
* Base64 character set "[A-Z][a-z][0-9]+/"
*
* @param string $src
* @return string
* @throws TypeError
*/
public static function encodeUnpadded($src)
{
return self::doEncode($src, false);
}
/**
* @param string $src
* @param bool $pad Include = padding?
* @return string
* @throws TypeError
*/
protected static function doEncode($src, $pad = true)
{
$dest = '';
$srcLen = ParagonIE_Sodium_Core_Util::strlen($src);
// Main loop (no padding):
for ($i = 0; $i + 3 <= $srcLen; $i += 3) {
/** @var array<int, int> $chunk */
$chunk = unpack('C*', ParagonIE_Sodium_Core_Util::substr($src, $i, 3));
$b0 = $chunk[1];
$b1 = $chunk[2];
$b2 = $chunk[3];
$dest .=
self::encode6Bits( $b0 >> 2 ) .
self::encode6Bits((($b0 << 4) | ($b1 >> 4)) & 63) .
self::encode6Bits((($b1 << 2) | ($b2 >> 6)) & 63) .
self::encode6Bits( $b2 & 63);
}
// The last chunk, which may have padding:
if ($i < $srcLen) {
/** @var array<int, int> $chunk */
$chunk = unpack('C*', ParagonIE_Sodium_Core_Util::substr($src, $i, $srcLen - $i));
$b0 = $chunk[1];
if ($i + 1 < $srcLen) {
$b1 = $chunk[2];
$dest .=
self::encode6Bits($b0 >> 2) .
self::encode6Bits((($b0 << 4) | ($b1 >> 4)) & 63) .
self::encode6Bits(($b1 << 2) & 63);
if ($pad) {
$dest .= '=';
}
} else {
$dest .=
self::encode6Bits( $b0 >> 2) .
self::encode6Bits(($b0 << 4) & 63);
if ($pad) {
$dest .= '==';
}
}
}
return $dest;
}
/**
* decode from base64 into binary
*
* Base64 character set "./[A-Z][a-z][0-9]"
*
* @param string $src
* @param bool $strictPadding
* @return string
* @throws RangeException
* @throws TypeError
* @psalm-suppress RedundantCondition
*/
public static function decode($src, $strictPadding = false)
{
// Remove padding
$srcLen = ParagonIE_Sodium_Core_Util::strlen($src);
if ($srcLen === 0) {
return '';
}
if ($strictPadding) {
if (($srcLen & 3) === 0) {
if ($src[$srcLen - 1] === '=') {
$srcLen--;
if ($src[$srcLen - 1] === '=') {
$srcLen--;
}
}
}
if (($srcLen & 3) === 1) {
throw new RangeException(
'Incorrect padding'
);
}
if ($src[$srcLen - 1] === '=') {
throw new RangeException(
'Incorrect padding'
);
}
} else {
$src = rtrim($src, '=');
$srcLen = ParagonIE_Sodium_Core_Util::strlen($src);
}
$err = 0;
$dest = '';
// Main loop (no padding):
for ($i = 0; $i + 4 <= $srcLen; $i += 4) {
/** @var array<int, int> $chunk */
$chunk = unpack('C*', ParagonIE_Sodium_Core_Util::substr($src, $i, 4));
$c0 = self::decode6Bits($chunk[1]);
$c1 = self::decode6Bits($chunk[2]);
$c2 = self::decode6Bits($chunk[3]);
$c3 = self::decode6Bits($chunk[4]);
$dest .= pack(
'CCC',
((($c0 << 2) | ($c1 >> 4)) & 0xff),
((($c1 << 4) | ($c2 >> 2)) & 0xff),
((($c2 << 6) | $c3) & 0xff)
);
$err |= ($c0 | $c1 | $c2 | $c3) >> 8;
}
// The last chunk, which may have padding:
if ($i < $srcLen) {
/** @var array<int, int> $chunk */
$chunk = unpack('C*', ParagonIE_Sodium_Core_Util::substr($src, $i, $srcLen - $i));
$c0 = self::decode6Bits($chunk[1]);
if ($i + 2 < $srcLen) {
$c1 = self::decode6Bits($chunk[2]);
$c2 = self::decode6Bits($chunk[3]);
$dest .= pack(
'CC',
((($c0 << 2) | ($c1 >> 4)) & 0xff),
((($c1 << 4) | ($c2 >> 2)) & 0xff)
);
$err |= ($c0 | $c1 | $c2) >> 8;
} elseif ($i + 1 < $srcLen) {
$c1 = self::decode6Bits($chunk[2]);
$dest .= pack(
'C',
((($c0 << 2) | ($c1 >> 4)) & 0xff)
);
$err |= ($c0 | $c1) >> 8;
} elseif ($i < $srcLen && $strictPadding) {
$err |= 1;
}
}
/** @var bool $check */
$check = ($err === 0);
if (!$check) {
throw new RangeException(
'Base64::decode() only expects characters in the correct base64 alphabet'
);
}
return $dest;
}
// COPY ParagonIE_Sodium_Core_Base64_Common ENDING HERE
/**
* Uses bitwise operators instead of table-lookups to turn 6-bit integers
* into 8-bit integers.
*
* Base64 character set:
* [A-Z] [a-z] [0-9] + /
* 0x41-0x5a, 0x61-0x7a, 0x30-0x39, 0x2b, 0x2f
*
* @param int $src
* @return int
*/
protected static function decode6Bits($src)
{
$ret = -1;
// if ($src > 0x40 && $src < 0x5b) $ret += $src - 0x41 + 1; // -64
$ret += (((0x40 - $src) & ($src - 0x5b)) >> 8) & ($src - 64);
// if ($src > 0x60 && $src < 0x7b) $ret += $src - 0x61 + 26 + 1; // -70
$ret += (((0x60 - $src) & ($src - 0x7b)) >> 8) & ($src - 70);
// if ($src > 0x2f && $src < 0x3a) $ret += $src - 0x30 + 52 + 1; // 5
$ret += (((0x2f - $src) & ($src - 0x3a)) >> 8) & ($src + 5);
// if ($src == 0x2b) $ret += 62 + 1;
$ret += (((0x2a - $src) & ($src - 0x2c)) >> 8) & 63;
// if ($src == 0x2f) ret += 63 + 1;
$ret += (((0x2e - $src) & ($src - 0x30)) >> 8) & 64;
return $ret;
}
/**
* Uses bitwise operators instead of table-lookups to turn 8-bit integers
* into 6-bit integers.
*
* @param int $src
* @return string
*/
protected static function encode6Bits($src)
{
$diff = 0x41;
// if ($src > 25) $diff += 0x61 - 0x41 - 26; // 6
$diff += ((25 - $src) >> 8) & 6;
// if ($src > 51) $diff += 0x30 - 0x61 - 26; // -75
$diff -= ((51 - $src) >> 8) & 75;
// if ($src > 61) $diff += 0x2b - 0x30 - 10; // -15
$diff -= ((61 - $src) >> 8) & 15;
// if ($src > 62) $diff += 0x2f - 0x2b - 1; // 3
$diff += ((62 - $src) >> 8) & 3;
return pack('C', $src + $diff);
}
}
Core/Curve25519/Fe.php 0000644 00000006021 15133021213 0010157 0 ustar 00 <?php
if (class_exists('ParagonIE_Sodium_Core_Curve25519_Fe', false)) {
return;
}
/**
* Class ParagonIE_Sodium_Core_Curve25519_Fe
*
* This represents a Field Element
*/
class ParagonIE_Sodium_Core_Curve25519_Fe implements ArrayAccess
{
/**
* @var array<int, int>
*/
protected $container = array();
/**
* @var int
*/
protected $size = 10;
/**
* @internal You should not use this directly from another application
*
* @param array<int, int> $array
* @param bool $save_indexes
* @return self
*/
public static function fromArray($array, $save_indexes = null)
{
$count = count($array);
if ($save_indexes) {
$keys = array_keys($array);
} else {
$keys = range(0, $count - 1);
}
$array = array_values($array);
/** @var array<int, int> $keys */
$obj = new ParagonIE_Sodium_Core_Curve25519_Fe();
if ($save_indexes) {
for ($i = 0; $i < $count; ++$i) {
$obj->offsetSet($keys[$i], $array[$i]);
}
} else {
for ($i = 0; $i < $count; ++$i) {
$obj->offsetSet($i, $array[$i]);
}
}
return $obj;
}
/**
* @internal You should not use this directly from another application
*
* @param int|null $offset
* @param int $value
* @return void
* @psalm-suppress MixedArrayOffset
*/
#[ReturnTypeWillChange]
public function offsetSet($offset, $value)
{
if (!is_int($value)) {
throw new InvalidArgumentException('Expected an integer');
}
if (is_null($offset)) {
$this->container[] = $value;
} else {
$this->container[$offset] = $value;
}
}
/**
* @internal You should not use this directly from another application
*
* @param int $offset
* @return bool
* @psalm-suppress MixedArrayOffset
*/
#[ReturnTypeWillChange]
public function offsetExists($offset)
{
return isset($this->container[$offset]);
}
/**
* @internal You should not use this directly from another application
*
* @param int $offset
* @return void
* @psalm-suppress MixedArrayOffset
*/
#[ReturnTypeWillChange]
public function offsetUnset($offset)
{
unset($this->container[$offset]);
}
/**
* @internal You should not use this directly from another application
*
* @param int $offset
* @return int
* @psalm-suppress MixedArrayOffset
*/
#[ReturnTypeWillChange]
public function offsetGet($offset)
{
if (!isset($this->container[$offset])) {
$this->container[$offset] = 0;
}
return (int) ($this->container[$offset]);
}
/**
* @internal You should not use this directly from another application
*
* @return array
*/
public function __debugInfo()
{
return array(implode(', ', $this->container));
}
}
Core/Curve25519/README.md 0000604 00000000332 15133021213 0010366 0 ustar 00 # Curve25519 Data Structures
These are PHP implementation of the [structs used in the ref10 curve25519 code](https://github.com/jedisct1/libsodium/blob/master/src/libsodium/include/sodium/private/curve25519_ref10.h).
Core/Curve25519/H.php 0000604 00000327571 15133021213 0010030 0 ustar 00 <?php
if (class_exists('ParagonIE_Sodium_Core_Curve25519_H', false)) {
return;
}
/**
* Class ParagonIE_Sodium_Core_Curve25519_H
*
* This just contains the constants in the ref10/base.h file
*/
class ParagonIE_Sodium_Core_Curve25519_H extends ParagonIE_Sodium_Core_Util
{
/**
* See: libsodium's crypto_core/curve25519/ref10/base.h
*
* @var array<int, array<int, array<int, array<int, int>>>> Basically, int[32][8][3][10]
*/
protected static $base = array(
array(
array(
array(25967493, -14356035, 29566456, 3660896, -12694345, 4014787, 27544626, -11754271, -6079156, 2047605),
array(-12545711, 934262, -2722910, 3049990, -727428, 9406986, 12720692, 5043384, 19500929, -15469378),
array(-8738181, 4489570, 9688441, -14785194, 10184609, -12363380, 29287919, 11864899, -24514362, -4438546),
),
array(
array(-12815894, -12976347, -21581243, 11784320, -25355658, -2750717, -11717903, -3814571, -358445, -10211303),
array(-21703237, 6903825, 27185491, 6451973, -29577724, -9554005, -15616551, 11189268, -26829678, -5319081),
array(26966642, 11152617, 32442495, 15396054, 14353839, -12752335, -3128826, -9541118, -15472047, -4166697),
),
array(
array(15636291, -9688557, 24204773, -7912398, 616977, -16685262, 27787600, -14772189, 28944400, -1550024),
array(16568933, 4717097, -11556148, -1102322, 15682896, -11807043, 16354577, -11775962, 7689662, 11199574),
array(30464156, -5976125, -11779434, -15670865, 23220365, 15915852, 7512774, 10017326, -17749093, -9920357),
),
array(
array(-17036878, 13921892, 10945806, -6033431, 27105052, -16084379, -28926210, 15006023, 3284568, -6276540),
array(23599295, -8306047, -11193664, -7687416, 13236774, 10506355, 7464579, 9656445, 13059162, 10374397),
array(7798556, 16710257, 3033922, 2874086, 28997861, 2835604, 32406664, -3839045, -641708, -101325),
),
array(
array(10861363, 11473154, 27284546, 1981175, -30064349, 12577861, 32867885, 14515107, -15438304, 10819380),
array(4708026, 6336745, 20377586, 9066809, -11272109, 6594696, -25653668, 12483688, -12668491, 5581306),
array(19563160, 16186464, -29386857, 4097519, 10237984, -4348115, 28542350, 13850243, -23678021, -15815942),
),
array(
array(-15371964, -12862754, 32573250, 4720197, -26436522, 5875511, -19188627, -15224819, -9818940, -12085777),
array(-8549212, 109983, 15149363, 2178705, 22900618, 4543417, 3044240, -15689887, 1762328, 14866737),
array(-18199695, -15951423, -10473290, 1707278, -17185920, 3916101, -28236412, 3959421, 27914454, 4383652),
),
array(
array(5153746, 9909285, 1723747, -2777874, 30523605, 5516873, 19480852, 5230134, -23952439, -15175766),
array(-30269007, -3463509, 7665486, 10083793, 28475525, 1649722, 20654025, 16520125, 30598449, 7715701),
array(28881845, 14381568, 9657904, 3680757, -20181635, 7843316, -31400660, 1370708, 29794553, -1409300),
),
array(
array(14499471, -2729599, -33191113, -4254652, 28494862, 14271267, 30290735, 10876454, -33154098, 2381726),
array(-7195431, -2655363, -14730155, 462251, -27724326, 3941372, -6236617, 3696005, -32300832, 15351955),
array(27431194, 8222322, 16448760, -3907995, -18707002, 11938355, -32961401, -2970515, 29551813, 10109425),
),
),
array(
array(
array(-13657040, -13155431, -31283750, 11777098, 21447386, 6519384, -2378284, -1627556, 10092783, -4764171),
array(27939166, 14210322, 4677035, 16277044, -22964462, -12398139, -32508754, 12005538, -17810127, 12803510),
array(17228999, -15661624, -1233527, 300140, -1224870, -11714777, 30364213, -9038194, 18016357, 4397660),
),
array(
array(-10958843, -7690207, 4776341, -14954238, 27850028, -15602212, -26619106, 14544525, -17477504, 982639),
array(29253598, 15796703, -2863982, -9908884, 10057023, 3163536, 7332899, -4120128, -21047696, 9934963),
array(5793303, 16271923, -24131614, -10116404, 29188560, 1206517, -14747930, 4559895, -30123922, -10897950),
),
array(
array(-27643952, -11493006, 16282657, -11036493, 28414021, -15012264, 24191034, 4541697, -13338309, 5500568),
array(12650548, -1497113, 9052871, 11355358, -17680037, -8400164, -17430592, 12264343, 10874051, 13524335),
array(25556948, -3045990, 714651, 2510400, 23394682, -10415330, 33119038, 5080568, -22528059, 5376628),
),
array(
array(-26088264, -4011052, -17013699, -3537628, -6726793, 1920897, -22321305, -9447443, 4535768, 1569007),
array(-2255422, 14606630, -21692440, -8039818, 28430649, 8775819, -30494562, 3044290, 31848280, 12543772),
array(-22028579, 2943893, -31857513, 6777306, 13784462, -4292203, -27377195, -2062731, 7718482, 14474653),
),
array(
array(2385315, 2454213, -22631320, 46603, -4437935, -15680415, 656965, -7236665, 24316168, -5253567),
array(13741529, 10911568, -33233417, -8603737, -20177830, -1033297, 33040651, -13424532, -20729456, 8321686),
array(21060490, -2212744, 15712757, -4336099, 1639040, 10656336, 23845965, -11874838, -9984458, 608372),
),
array(
array(-13672732, -15087586, -10889693, -7557059, -6036909, 11305547, 1123968, -6780577, 27229399, 23887),
array(-23244140, -294205, -11744728, 14712571, -29465699, -2029617, 12797024, -6440308, -1633405, 16678954),
array(-29500620, 4770662, -16054387, 14001338, 7830047, 9564805, -1508144, -4795045, -17169265, 4904953),
),
array(
array(24059557, 14617003, 19037157, -15039908, 19766093, -14906429, 5169211, 16191880, 2128236, -4326833),
array(-16981152, 4124966, -8540610, -10653797, 30336522, -14105247, -29806336, 916033, -6882542, -2986532),
array(-22630907, 12419372, -7134229, -7473371, -16478904, 16739175, 285431, 2763829, 15736322, 4143876),
),
array(
array(2379352, 11839345, -4110402, -5988665, 11274298, 794957, 212801, -14594663, 23527084, -16458268),
array(33431127, -11130478, -17838966, -15626900, 8909499, 8376530, -32625340, 4087881, -15188911, -14416214),
array(1767683, 7197987, -13205226, -2022635, -13091350, 448826, 5799055, 4357868, -4774191, -16323038),
),
),
array(
array(
array(6721966, 13833823, -23523388, -1551314, 26354293, -11863321, 23365147, -3949732, 7390890, 2759800),
array(4409041, 2052381, 23373853, 10530217, 7676779, -12885954, 21302353, -4264057, 1244380, -12919645),
array(-4421239, 7169619, 4982368, -2957590, 30256825, -2777540, 14086413, 9208236, 15886429, 16489664),
),
array(
array(1996075, 10375649, 14346367, 13311202, -6874135, -16438411, -13693198, 398369, -30606455, -712933),
array(-25307465, 9795880, -2777414, 14878809, -33531835, 14780363, 13348553, 12076947, -30836462, 5113182),
array(-17770784, 11797796, 31950843, 13929123, -25888302, 12288344, -30341101, -7336386, 13847711, 5387222),
),
array(
array(-18582163, -3416217, 17824843, -2340966, 22744343, -10442611, 8763061, 3617786, -19600662, 10370991),
array(20246567, -14369378, 22358229, -543712, 18507283, -10413996, 14554437, -8746092, 32232924, 16763880),
array(9648505, 10094563, 26416693, 14745928, -30374318, -6472621, 11094161, 15689506, 3140038, -16510092),
),
array(
array(-16160072, 5472695, 31895588, 4744994, 8823515, 10365685, -27224800, 9448613, -28774454, 366295),
array(19153450, 11523972, -11096490, -6503142, -24647631, 5420647, 28344573, 8041113, 719605, 11671788),
array(8678025, 2694440, -6808014, 2517372, 4964326, 11152271, -15432916, -15266516, 27000813, -10195553),
),
array(
array(-15157904, 7134312, 8639287, -2814877, -7235688, 10421742, 564065, 5336097, 6750977, -14521026),
array(11836410, -3979488, 26297894, 16080799, 23455045, 15735944, 1695823, -8819122, 8169720, 16220347),
array(-18115838, 8653647, 17578566, -6092619, -8025777, -16012763, -11144307, -2627664, -5990708, -14166033),
),
array(
array(-23308498, -10968312, 15213228, -10081214, -30853605, -11050004, 27884329, 2847284, 2655861, 1738395),
array(-27537433, -14253021, -25336301, -8002780, -9370762, 8129821, 21651608, -3239336, -19087449, -11005278),
array(1533110, 3437855, 23735889, 459276, 29970501, 11335377, 26030092, 5821408, 10478196, 8544890),
),
array(
array(32173121, -16129311, 24896207, 3921497, 22579056, -3410854, 19270449, 12217473, 17789017, -3395995),
array(-30552961, -2228401, -15578829, -10147201, 13243889, 517024, 15479401, -3853233, 30460520, 1052596),
array(-11614875, 13323618, 32618793, 8175907, -15230173, 12596687, 27491595, -4612359, 3179268, -9478891),
),
array(
array(31947069, -14366651, -4640583, -15339921, -15125977, -6039709, -14756777, -16411740, 19072640, -9511060),
array(11685058, 11822410, 3158003, -13952594, 33402194, -4165066, 5977896, -5215017, 473099, 5040608),
array(-20290863, 8198642, -27410132, 11602123, 1290375, -2799760, 28326862, 1721092, -19558642, -3131606),
),
),
array(
array(
array(7881532, 10687937, 7578723, 7738378, -18951012, -2553952, 21820786, 8076149, -27868496, 11538389),
array(-19935666, 3899861, 18283497, -6801568, -15728660, -11249211, 8754525, 7446702, -5676054, 5797016),
array(-11295600, -3793569, -15782110, -7964573, 12708869, -8456199, 2014099, -9050574, -2369172, -5877341),
),
array(
array(-22472376, -11568741, -27682020, 1146375, 18956691, 16640559, 1192730, -3714199, 15123619, 10811505),
array(14352098, -3419715, -18942044, 10822655, 32750596, 4699007, -70363, 15776356, -28886779, -11974553),
array(-28241164, -8072475, -4978962, -5315317, 29416931, 1847569, -20654173, -16484855, 4714547, -9600655),
),
array(
array(15200332, 8368572, 19679101, 15970074, -31872674, 1959451, 24611599, -4543832, -11745876, 12340220),
array(12876937, -10480056, 33134381, 6590940, -6307776, 14872440, 9613953, 8241152, 15370987, 9608631),
array(-4143277, -12014408, 8446281, -391603, 4407738, 13629032, -7724868, 15866074, -28210621, -8814099),
),
array(
array(26660628, -15677655, 8393734, 358047, -7401291, 992988, -23904233, 858697, 20571223, 8420556),
array(14620715, 13067227, -15447274, 8264467, 14106269, 15080814, 33531827, 12516406, -21574435, -12476749),
array(236881, 10476226, 57258, -14677024, 6472998, 2466984, 17258519, 7256740, 8791136, 15069930),
),
array(
array(1276410, -9371918, 22949635, -16322807, -23493039, -5702186, 14711875, 4874229, -30663140, -2331391),
array(5855666, 4990204, -13711848, 7294284, -7804282, 1924647, -1423175, -7912378, -33069337, 9234253),
array(20590503, -9018988, 31529744, -7352666, -2706834, 10650548, 31559055, -11609587, 18979186, 13396066),
),
array(
array(24474287, 4968103, 22267082, 4407354, 24063882, -8325180, -18816887, 13594782, 33514650, 7021958),
array(-11566906, -6565505, -21365085, 15928892, -26158305, 4315421, -25948728, -3916677, -21480480, 12868082),
array(-28635013, 13504661, 19988037, -2132761, 21078225, 6443208, -21446107, 2244500, -12455797, -8089383),
),
array(
array(-30595528, 13793479, -5852820, 319136, -25723172, -6263899, 33086546, 8957937, -15233648, 5540521),
array(-11630176, -11503902, -8119500, -7643073, 2620056, 1022908, -23710744, -1568984, -16128528, -14962807),
array(23152971, 775386, 27395463, 14006635, -9701118, 4649512, 1689819, 892185, -11513277, -15205948),
),
array(
array(9770129, 9586738, 26496094, 4324120, 1556511, -3550024, 27453819, 4763127, -19179614, 5867134),
array(-32765025, 1927590, 31726409, -4753295, 23962434, -16019500, 27846559, 5931263, -29749703, -16108455),
array(27461885, -2977536, 22380810, 1815854, -23033753, -3031938, 7283490, -15148073, -19526700, 7734629),
),
),
array(
array(
array(-8010264, -9590817, -11120403, 6196038, 29344158, -13430885, 7585295, -3176626, 18549497, 15302069),
array(-32658337, -6171222, -7672793, -11051681, 6258878, 13504381, 10458790, -6418461, -8872242, 8424746),
array(24687205, 8613276, -30667046, -3233545, 1863892, -1830544, 19206234, 7134917, -11284482, -828919),
),
array(
array(11334899, -9218022, 8025293, 12707519, 17523892, -10476071, 10243738, -14685461, -5066034, 16498837),
array(8911542, 6887158, -9584260, -6958590, 11145641, -9543680, 17303925, -14124238, 6536641, 10543906),
array(-28946384, 15479763, -17466835, 568876, -1497683, 11223454, -2669190, -16625574, -27235709, 8876771),
),
array(
array(-25742899, -12566864, -15649966, -846607, -33026686, -796288, -33481822, 15824474, -604426, -9039817),
array(10330056, 70051, 7957388, -9002667, 9764902, 15609756, 27698697, -4890037, 1657394, 3084098),
array(10477963, -7470260, 12119566, -13250805, 29016247, -5365589, 31280319, 14396151, -30233575, 15272409),
),
array(
array(-12288309, 3169463, 28813183, 16658753, 25116432, -5630466, -25173957, -12636138, -25014757, 1950504),
array(-26180358, 9489187, 11053416, -14746161, -31053720, 5825630, -8384306, -8767532, 15341279, 8373727),
array(28685821, 7759505, -14378516, -12002860, -31971820, 4079242, 298136, -10232602, -2878207, 15190420),
),
array(
array(-32932876, 13806336, -14337485, -15794431, -24004620, 10940928, 8669718, 2742393, -26033313, -6875003),
array(-1580388, -11729417, -25979658, -11445023, -17411874, -10912854, 9291594, -16247779, -12154742, 6048605),
array(-30305315, 14843444, 1539301, 11864366, 20201677, 1900163, 13934231, 5128323, 11213262, 9168384),
),
array(
array(-26280513, 11007847, 19408960, -940758, -18592965, -4328580, -5088060, -11105150, 20470157, -16398701),
array(-23136053, 9282192, 14855179, -15390078, -7362815, -14408560, -22783952, 14461608, 14042978, 5230683),
array(29969567, -2741594, -16711867, -8552442, 9175486, -2468974, 21556951, 3506042, -5933891, -12449708),
),
array(
array(-3144746, 8744661, 19704003, 4581278, -20430686, 6830683, -21284170, 8971513, -28539189, 15326563),
array(-19464629, 10110288, -17262528, -3503892, -23500387, 1355669, -15523050, 15300988, -20514118, 9168260),
array(-5353335, 4488613, -23803248, 16314347, 7780487, -15638939, -28948358, 9601605, 33087103, -9011387),
),
array(
array(-19443170, -15512900, -20797467, -12445323, -29824447, 10229461, -27444329, -15000531, -5996870, 15664672),
array(23294591, -16632613, -22650781, -8470978, 27844204, 11461195, 13099750, -2460356, 18151676, 13417686),
array(-24722913, -4176517, -31150679, 5988919, -26858785, 6685065, 1661597, -12551441, 15271676, -15452665),
),
),
array(
array(
array(11433042, -13228665, 8239631, -5279517, -1985436, -725718, -18698764, 2167544, -6921301, -13440182),
array(-31436171, 15575146, 30436815, 12192228, -22463353, 9395379, -9917708, -8638997, 12215110, 12028277),
array(14098400, 6555944, 23007258, 5757252, -15427832, -12950502, 30123440, 4617780, -16900089, -655628),
),
array(
array(-4026201, -15240835, 11893168, 13718664, -14809462, 1847385, -15819999, 10154009, 23973261, -12684474),
array(-26531820, -3695990, -1908898, 2534301, -31870557, -16550355, 18341390, -11419951, 32013174, -10103539),
array(-25479301, 10876443, -11771086, -14625140, -12369567, 1838104, 21911214, 6354752, 4425632, -837822),
),
array(
array(-10433389, -14612966, 22229858, -3091047, -13191166, 776729, -17415375, -12020462, 4725005, 14044970),
array(19268650, -7304421, 1555349, 8692754, -21474059, -9910664, 6347390, -1411784, -19522291, -16109756),
array(-24864089, 12986008, -10898878, -5558584, -11312371, -148526, 19541418, 8180106, 9282262, 10282508),
),
array(
array(-26205082, 4428547, -8661196, -13194263, 4098402, -14165257, 15522535, 8372215, 5542595, -10702683),
array(-10562541, 14895633, 26814552, -16673850, -17480754, -2489360, -2781891, 6993761, -18093885, 10114655),
array(-20107055, -929418, 31422704, 10427861, -7110749, 6150669, -29091755, -11529146, 25953725, -106158),
),
array(
array(-4234397, -8039292, -9119125, 3046000, 2101609, -12607294, 19390020, 6094296, -3315279, 12831125),
array(-15998678, 7578152, 5310217, 14408357, -33548620, -224739, 31575954, 6326196, 7381791, -2421839),
array(-20902779, 3296811, 24736065, -16328389, 18374254, 7318640, 6295303, 8082724, -15362489, 12339664),
),
array(
array(27724736, 2291157, 6088201, -14184798, 1792727, 5857634, 13848414, 15768922, 25091167, 14856294),
array(-18866652, 8331043, 24373479, 8541013, -701998, -9269457, 12927300, -12695493, -22182473, -9012899),
array(-11423429, -5421590, 11632845, 3405020, 30536730, -11674039, -27260765, 13866390, 30146206, 9142070),
),
array(
array(3924129, -15307516, -13817122, -10054960, 12291820, -668366, -27702774, 9326384, -8237858, 4171294),
array(-15921940, 16037937, 6713787, 16606682, -21612135, 2790944, 26396185, 3731949, 345228, -5462949),
array(-21327538, 13448259, 25284571, 1143661, 20614966, -8849387, 2031539, -12391231, -16253183, -13582083),
),
array(
array(31016211, -16722429, 26371392, -14451233, -5027349, 14854137, 17477601, 3842657, 28012650, -16405420),
array(-5075835, 9368966, -8562079, -4600902, -15249953, 6970560, -9189873, 16292057, -8867157, 3507940),
array(29439664, 3537914, 23333589, 6997794, -17555561, -11018068, -15209202, -15051267, -9164929, 6580396),
),
),
array(
array(
array(-12185861, -7679788, 16438269, 10826160, -8696817, -6235611, 17860444, -9273846, -2095802, 9304567),
array(20714564, -4336911, 29088195, 7406487, 11426967, -5095705, 14792667, -14608617, 5289421, -477127),
array(-16665533, -10650790, -6160345, -13305760, 9192020, -1802462, 17271490, 12349094, 26939669, -3752294),
),
array(
array(-12889898, 9373458, 31595848, 16374215, 21471720, 13221525, -27283495, -12348559, -3698806, 117887),
array(22263325, -6560050, 3984570, -11174646, -15114008, -566785, 28311253, 5358056, -23319780, 541964),
array(16259219, 3261970, 2309254, -15534474, -16885711, -4581916, 24134070, -16705829, -13337066, -13552195),
),
array(
array(9378160, -13140186, -22845982, -12745264, 28198281, -7244098, -2399684, -717351, 690426, 14876244),
array(24977353, -314384, -8223969, -13465086, 28432343, -1176353, -13068804, -12297348, -22380984, 6618999),
array(-1538174, 11685646, 12944378, 13682314, -24389511, -14413193, 8044829, -13817328, 32239829, -5652762),
),
array(
array(-18603066, 4762990, -926250, 8885304, -28412480, -3187315, 9781647, -10350059, 32779359, 5095274),
array(-33008130, -5214506, -32264887, -3685216, 9460461, -9327423, -24601656, 14506724, 21639561, -2630236),
array(-16400943, -13112215, 25239338, 15531969, 3987758, -4499318, -1289502, -6863535, 17874574, 558605),
),
array(
array(-13600129, 10240081, 9171883, 16131053, -20869254, 9599700, 33499487, 5080151, 2085892, 5119761),
array(-22205145, -2519528, -16381601, 414691, -25019550, 2170430, 30634760, -8363614, -31999993, -5759884),
array(-6845704, 15791202, 8550074, -1312654, 29928809, -12092256, 27534430, -7192145, -22351378, 12961482),
),
array(
array(-24492060, -9570771, 10368194, 11582341, -23397293, -2245287, 16533930, 8206996, -30194652, -5159638),
array(-11121496, -3382234, 2307366, 6362031, -135455, 8868177, -16835630, 7031275, 7589640, 8945490),
array(-32152748, 8917967, 6661220, -11677616, -1192060, -15793393, 7251489, -11182180, 24099109, -14456170),
),
array(
array(5019558, -7907470, 4244127, -14714356, -26933272, 6453165, -19118182, -13289025, -6231896, -10280736),
array(10853594, 10721687, 26480089, 5861829, -22995819, 1972175, -1866647, -10557898, -3363451, -6441124),
array(-17002408, 5906790, 221599, -6563147, 7828208, -13248918, 24362661, -2008168, -13866408, 7421392),
),
array(
array(8139927, -6546497, 32257646, -5890546, 30375719, 1886181, -21175108, 15441252, 28826358, -4123029),
array(6267086, 9695052, 7709135, -16603597, -32869068, -1886135, 14795160, -7840124, 13746021, -1742048),
array(28584902, 7787108, -6732942, -15050729, 22846041, -7571236, -3181936, -363524, 4771362, -8419958),
),
),
array(
array(
array(24949256, 6376279, -27466481, -8174608, -18646154, -9930606, 33543569, -12141695, 3569627, 11342593),
array(26514989, 4740088, 27912651, 3697550, 19331575, -11472339, 6809886, 4608608, 7325975, -14801071),
array(-11618399, -14554430, -24321212, 7655128, -1369274, 5214312, -27400540, 10258390, -17646694, -8186692),
),
array(
array(11431204, 15823007, 26570245, 14329124, 18029990, 4796082, -31446179, 15580664, 9280358, -3973687),
array(-160783, -10326257, -22855316, -4304997, -20861367, -13621002, -32810901, -11181622, -15545091, 4387441),
array(-20799378, 12194512, 3937617, -5805892, -27154820, 9340370, -24513992, 8548137, 20617071, -7482001),
),
array(
array(-938825, -3930586, -8714311, 16124718, 24603125, -6225393, -13775352, -11875822, 24345683, 10325460),
array(-19855277, -1568885, -22202708, 8714034, 14007766, 6928528, 16318175, -1010689, 4766743, 3552007),
array(-21751364, -16730916, 1351763, -803421, -4009670, 3950935, 3217514, 14481909, 10988822, -3994762),
),
array(
array(15564307, -14311570, 3101243, 5684148, 30446780, -8051356, 12677127, -6505343, -8295852, 13296005),
array(-9442290, 6624296, -30298964, -11913677, -4670981, -2057379, 31521204, 9614054, -30000824, 12074674),
array(4771191, -135239, 14290749, -13089852, 27992298, 14998318, -1413936, -1556716, 29832613, -16391035),
),
array(
array(7064884, -7541174, -19161962, -5067537, -18891269, -2912736, 25825242, 5293297, -27122660, 13101590),
array(-2298563, 2439670, -7466610, 1719965, -27267541, -16328445, 32512469, -5317593, -30356070, -4190957),
array(-30006540, 10162316, -33180176, 3981723, -16482138, -13070044, 14413974, 9515896, 19568978, 9628812),
),
array(
array(33053803, 199357, 15894591, 1583059, 27380243, -4580435, -17838894, -6106839, -6291786, 3437740),
array(-18978877, 3884493, 19469877, 12726490, 15913552, 13614290, -22961733, 70104, 7463304, 4176122),
array(-27124001, 10659917, 11482427, -16070381, 12771467, -6635117, -32719404, -5322751, 24216882, 5944158),
),
array(
array(8894125, 7450974, -2664149, -9765752, -28080517, -12389115, 19345746, 14680796, 11632993, 5847885),
array(26942781, -2315317, 9129564, -4906607, 26024105, 11769399, -11518837, 6367194, -9727230, 4782140),
array(19916461, -4828410, -22910704, -11414391, 25606324, -5972441, 33253853, 8220911, 6358847, -1873857),
),
array(
array(801428, -2081702, 16569428, 11065167, 29875704, 96627, 7908388, -4480480, -13538503, 1387155),
array(19646058, 5720633, -11416706, 12814209, 11607948, 12749789, 14147075, 15156355, -21866831, 11835260),
array(19299512, 1155910, 28703737, 14890794, 2925026, 7269399, 26121523, 15467869, -26560550, 5052483),
),
),
array(
array(
array(-3017432, 10058206, 1980837, 3964243, 22160966, 12322533, -6431123, -12618185, 12228557, -7003677),
array(32944382, 14922211, -22844894, 5188528, 21913450, -8719943, 4001465, 13238564, -6114803, 8653815),
array(22865569, -4652735, 27603668, -12545395, 14348958, 8234005, 24808405, 5719875, 28483275, 2841751),
),
array(
array(-16420968, -1113305, -327719, -12107856, 21886282, -15552774, -1887966, -315658, 19932058, -12739203),
array(-11656086, 10087521, -8864888, -5536143, -19278573, -3055912, 3999228, 13239134, -4777469, -13910208),
array(1382174, -11694719, 17266790, 9194690, -13324356, 9720081, 20403944, 11284705, -14013818, 3093230),
),
array(
array(16650921, -11037932, -1064178, 1570629, -8329746, 7352753, -302424, 16271225, -24049421, -6691850),
array(-21911077, -5927941, -4611316, -5560156, -31744103, -10785293, 24123614, 15193618, -21652117, -16739389),
array(-9935934, -4289447, -25279823, 4372842, 2087473, 10399484, 31870908, 14690798, 17361620, 11864968),
),
array(
array(-11307610, 6210372, 13206574, 5806320, -29017692, -13967200, -12331205, -7486601, -25578460, -16240689),
array(14668462, -12270235, 26039039, 15305210, 25515617, 4542480, 10453892, 6577524, 9145645, -6443880),
array(5974874, 3053895, -9433049, -10385191, -31865124, 3225009, -7972642, 3936128, -5652273, -3050304),
),
array(
array(30625386, -4729400, -25555961, -12792866, -20484575, 7695099, 17097188, -16303496, -27999779, 1803632),
array(-3553091, 9865099, -5228566, 4272701, -5673832, -16689700, 14911344, 12196514, -21405489, 7047412),
array(20093277, 9920966, -11138194, -5343857, 13161587, 12044805, -32856851, 4124601, -32343828, -10257566),
),
array(
array(-20788824, 14084654, -13531713, 7842147, 19119038, -13822605, 4752377, -8714640, -21679658, 2288038),
array(-26819236, -3283715, 29965059, 3039786, -14473765, 2540457, 29457502, 14625692, -24819617, 12570232),
array(-1063558, -11551823, 16920318, 12494842, 1278292, -5869109, -21159943, -3498680, -11974704, 4724943),
),
array(
array(17960970, -11775534, -4140968, -9702530, -8876562, -1410617, -12907383, -8659932, -29576300, 1903856),
array(23134274, -14279132, -10681997, -1611936, 20684485, 15770816, -12989750, 3190296, 26955097, 14109738),
array(15308788, 5320727, -30113809, -14318877, 22902008, 7767164, 29425325, -11277562, 31960942, 11934971),
),
array(
array(-27395711, 8435796, 4109644, 12222639, -24627868, 14818669, 20638173, 4875028, 10491392, 1379718),
array(-13159415, 9197841, 3875503, -8936108, -1383712, -5879801, 33518459, 16176658, 21432314, 12180697),
array(-11787308, 11500838, 13787581, -13832590, -22430679, 10140205, 1465425, 12689540, -10301319, -13872883),
),
),
array(
array(
array(5414091, -15386041, -21007664, 9643570, 12834970, 1186149, -2622916, -1342231, 26128231, 6032912),
array(-26337395, -13766162, 32496025, -13653919, 17847801, -12669156, 3604025, 8316894, -25875034, -10437358),
array(3296484, 6223048, 24680646, -12246460, -23052020, 5903205, -8862297, -4639164, 12376617, 3188849),
),
array(
array(29190488, -14659046, 27549113, -1183516, 3520066, -10697301, 32049515, -7309113, -16109234, -9852307),
array(-14744486, -9309156, 735818, -598978, -20407687, -5057904, 25246078, -15795669, 18640741, -960977),
array(-6928835, -16430795, 10361374, 5642961, 4910474, 12345252, -31638386, -494430, 10530747, 1053335),
),
array(
array(-29265967, -14186805, -13538216, -12117373, -19457059, -10655384, -31462369, -2948985, 24018831, 15026644),
array(-22592535, -3145277, -2289276, 5953843, -13440189, 9425631, 25310643, 13003497, -2314791, -15145616),
array(-27419985, -603321, -8043984, -1669117, -26092265, 13987819, -27297622, 187899, -23166419, -2531735),
),
array(
array(-21744398, -13810475, 1844840, 5021428, -10434399, -15911473, 9716667, 16266922, -5070217, 726099),
array(29370922, -6053998, 7334071, -15342259, 9385287, 2247707, -13661962, -4839461, 30007388, -15823341),
array(-936379, 16086691, 23751945, -543318, -1167538, -5189036, 9137109, 730663, 9835848, 4555336),
),
array(
array(-23376435, 1410446, -22253753, -12899614, 30867635, 15826977, 17693930, 544696, -11985298, 12422646),
array(31117226, -12215734, -13502838, 6561947, -9876867, -12757670, -5118685, -4096706, 29120153, 13924425),
array(-17400879, -14233209, 19675799, -2734756, -11006962, -5858820, -9383939, -11317700, 7240931, -237388),
),
array(
array(-31361739, -11346780, -15007447, -5856218, -22453340, -12152771, 1222336, 4389483, 3293637, -15551743),
array(-16684801, -14444245, 11038544, 11054958, -13801175, -3338533, -24319580, 7733547, 12796905, -6335822),
array(-8759414, -10817836, -25418864, 10783769, -30615557, -9746811, -28253339, 3647836, 3222231, -11160462),
),
array(
array(18606113, 1693100, -25448386, -15170272, 4112353, 10045021, 23603893, -2048234, -7550776, 2484985),
array(9255317, -3131197, -12156162, -1004256, 13098013, -9214866, 16377220, -2102812, -19802075, -3034702),
array(-22729289, 7496160, -5742199, 11329249, 19991973, -3347502, -31718148, 9936966, -30097688, -10618797),
),
array(
array(21878590, -5001297, 4338336, 13643897, -3036865, 13160960, 19708896, 5415497, -7360503, -4109293),
array(27736861, 10103576, 12500508, 8502413, -3413016, -9633558, 10436918, -1550276, -23659143, -8132100),
array(19492550, -12104365, -29681976, -852630, -3208171, 12403437, 30066266, 8367329, 13243957, 8709688),
),
),
array(
array(
array(12015105, 2801261, 28198131, 10151021, 24818120, -4743133, -11194191, -5645734, 5150968, 7274186),
array(2831366, -12492146, 1478975, 6122054, 23825128, -12733586, 31097299, 6083058, 31021603, -9793610),
array(-2529932, -2229646, 445613, 10720828, -13849527, -11505937, -23507731, 16354465, 15067285, -14147707),
),
array(
array(7840942, 14037873, -33364863, 15934016, -728213, -3642706, 21403988, 1057586, -19379462, -12403220),
array(915865, -16469274, 15608285, -8789130, -24357026, 6060030, -17371319, 8410997, -7220461, 16527025),
array(32922597, -556987, 20336074, -16184568, 10903705, -5384487, 16957574, 52992, 23834301, 6588044),
),
array(
array(32752030, 11232950, 3381995, -8714866, 22652988, -10744103, 17159699, 16689107, -20314580, -1305992),
array(-4689649, 9166776, -25710296, -10847306, 11576752, 12733943, 7924251, -2752281, 1976123, -7249027),
array(21251222, 16309901, -2983015, -6783122, 30810597, 12967303, 156041, -3371252, 12331345, -8237197),
),
array(
array(8651614, -4477032, -16085636, -4996994, 13002507, 2950805, 29054427, -5106970, 10008136, -4667901),
array(31486080, 15114593, -14261250, 12951354, 14369431, -7387845, 16347321, -13662089, 8684155, -10532952),
array(19443825, 11385320, 24468943, -9659068, -23919258, 2187569, -26263207, -6086921, 31316348, 14219878),
),
array(
array(-28594490, 1193785, 32245219, 11392485, 31092169, 15722801, 27146014, 6992409, 29126555, 9207390),
array(32382935, 1110093, 18477781, 11028262, -27411763, -7548111, -4980517, 10843782, -7957600, -14435730),
array(2814918, 7836403, 27519878, -7868156, -20894015, -11553689, -21494559, 8550130, 28346258, 1994730),
),
array(
array(-19578299, 8085545, -14000519, -3948622, 2785838, -16231307, -19516951, 7174894, 22628102, 8115180),
array(-30405132, 955511, -11133838, -15078069, -32447087, -13278079, -25651578, 3317160, -9943017, 930272),
array(-15303681, -6833769, 28856490, 1357446, 23421993, 1057177, 24091212, -1388970, -22765376, -10650715),
),
array(
array(-22751231, -5303997, -12907607, -12768866, -15811511, -7797053, -14839018, -16554220, -1867018, 8398970),
array(-31969310, 2106403, -4736360, 1362501, 12813763, 16200670, 22981545, -6291273, 18009408, -15772772),
array(-17220923, -9545221, -27784654, 14166835, 29815394, 7444469, 29551787, -3727419, 19288549, 1325865),
),
array(
array(15100157, -15835752, -23923978, -1005098, -26450192, 15509408, 12376730, -3479146, 33166107, -8042750),
array(20909231, 13023121, -9209752, 16251778, -5778415, -8094914, 12412151, 10018715, 2213263, -13878373),
array(32529814, -11074689, 30361439, -16689753, -9135940, 1513226, 22922121, 6382134, -5766928, 8371348),
),
),
array(
array(
array(9923462, 11271500, 12616794, 3544722, -29998368, -1721626, 12891687, -8193132, -26442943, 10486144),
array(-22597207, -7012665, 8587003, -8257861, 4084309, -12970062, 361726, 2610596, -23921530, -11455195),
array(5408411, -1136691, -4969122, 10561668, 24145918, 14240566, 31319731, -4235541, 19985175, -3436086),
),
array(
array(-13994457, 16616821, 14549246, 3341099, 32155958, 13648976, -17577068, 8849297, 65030, 8370684),
array(-8320926, -12049626, 31204563, 5839400, -20627288, -1057277, -19442942, 6922164, 12743482, -9800518),
array(-2361371, 12678785, 28815050, 4759974, -23893047, 4884717, 23783145, 11038569, 18800704, 255233),
),
array(
array(-5269658, -1773886, 13957886, 7990715, 23132995, 728773, 13393847, 9066957, 19258688, -14753793),
array(-2936654, -10827535, -10432089, 14516793, -3640786, 4372541, -31934921, 2209390, -1524053, 2055794),
array(580882, 16705327, 5468415, -2683018, -30926419, -14696000, -7203346, -8994389, -30021019, 7394435),
),
array(
array(23838809, 1822728, -15738443, 15242727, 8318092, -3733104, -21672180, -3492205, -4821741, 14799921),
array(13345610, 9759151, 3371034, -16137791, 16353039, 8577942, 31129804, 13496856, -9056018, 7402518),
array(2286874, -4435931, -20042458, -2008336, -13696227, 5038122, 11006906, -15760352, 8205061, 1607563),
),
array(
array(14414086, -8002132, 3331830, -3208217, 22249151, -5594188, 18364661, -2906958, 30019587, -9029278),
array(-27688051, 1585953, -10775053, 931069, -29120221, -11002319, -14410829, 12029093, 9944378, 8024),
array(4368715, -3709630, 29874200, -15022983, -20230386, -11410704, -16114594, -999085, -8142388, 5640030),
),
array(
array(10299610, 13746483, 11661824, 16234854, 7630238, 5998374, 9809887, -16694564, 15219798, -14327783),
array(27425505, -5719081, 3055006, 10660664, 23458024, 595578, -15398605, -1173195, -18342183, 9742717),
array(6744077, 2427284, 26042789, 2720740, -847906, 1118974, 32324614, 7406442, 12420155, 1994844),
),
array(
array(14012521, -5024720, -18384453, -9578469, -26485342, -3936439, -13033478, -10909803, 24319929, -6446333),
array(16412690, -4507367, 10772641, 15929391, -17068788, -4658621, 10555945, -10484049, -30102368, -4739048),
array(22397382, -7767684, -9293161, -12792868, 17166287, -9755136, -27333065, 6199366, 21880021, -12250760),
),
array(
array(-4283307, 5368523, -31117018, 8163389, -30323063, 3209128, 16557151, 8890729, 8840445, 4957760),
array(-15447727, 709327, -6919446, -10870178, -29777922, 6522332, -21720181, 12130072, -14796503, 5005757),
array(-2114751, -14308128, 23019042, 15765735, -25269683, 6002752, 10183197, -13239326, -16395286, -2176112),
),
),
array(
array(
array(-19025756, 1632005, 13466291, -7995100, -23640451, 16573537, -32013908, -3057104, 22208662, 2000468),
array(3065073, -1412761, -25598674, -361432, -17683065, -5703415, -8164212, 11248527, -3691214, -7414184),
array(10379208, -6045554, 8877319, 1473647, -29291284, -12507580, 16690915, 2553332, -3132688, 16400289),
),
array(
array(15716668, 1254266, -18472690, 7446274, -8448918, 6344164, -22097271, -7285580, 26894937, 9132066),
array(24158887, 12938817, 11085297, -8177598, -28063478, -4457083, -30576463, 64452, -6817084, -2692882),
array(13488534, 7794716, 22236231, 5989356, 25426474, -12578208, 2350710, -3418511, -4688006, 2364226),
),
array(
array(16335052, 9132434, 25640582, 6678888, 1725628, 8517937, -11807024, -11697457, 15445875, -7798101),
array(29004207, -7867081, 28661402, -640412, -12794003, -7943086, 31863255, -4135540, -278050, -15759279),
array(-6122061, -14866665, -28614905, 14569919, -10857999, -3591829, 10343412, -6976290, -29828287, -10815811),
),
array(
array(27081650, 3463984, 14099042, -4517604, 1616303, -6205604, 29542636, 15372179, 17293797, 960709),
array(20263915, 11434237, -5765435, 11236810, 13505955, -10857102, -16111345, 6493122, -19384511, 7639714),
array(-2830798, -14839232, 25403038, -8215196, -8317012, -16173699, 18006287, -16043750, 29994677, -15808121),
),
array(
array(9769828, 5202651, -24157398, -13631392, -28051003, -11561624, -24613141, -13860782, -31184575, 709464),
array(12286395, 13076066, -21775189, -1176622, -25003198, 4057652, -32018128, -8890874, 16102007, 13205847),
array(13733362, 5599946, 10557076, 3195751, -5557991, 8536970, -25540170, 8525972, 10151379, 10394400),
),
array(
array(4024660, -16137551, 22436262, 12276534, -9099015, -2686099, 19698229, 11743039, -33302334, 8934414),
array(-15879800, -4525240, -8580747, -2934061, 14634845, -698278, -9449077, 3137094, -11536886, 11721158),
array(17555939, -5013938, 8268606, 2331751, -22738815, 9761013, 9319229, 8835153, -9205489, -1280045),
),
array(
array(-461409, -7830014, 20614118, 16688288, -7514766, -4807119, 22300304, 505429, 6108462, -6183415),
array(-5070281, 12367917, -30663534, 3234473, 32617080, -8422642, 29880583, -13483331, -26898490, -7867459),
array(-31975283, 5726539, 26934134, 10237677, -3173717, -605053, 24199304, 3795095, 7592688, -14992079),
),
array(
array(21594432, -14964228, 17466408, -4077222, 32537084, 2739898, 6407723, 12018833, -28256052, 4298412),
array(-20650503, -11961496, -27236275, 570498, 3767144, -1717540, 13891942, -1569194, 13717174, 10805743),
array(-14676630, -15644296, 15287174, 11927123, 24177847, -8175568, -796431, 14860609, -26938930, -5863836),
),
),
array(
array(
array(12962541, 5311799, -10060768, 11658280, 18855286, -7954201, 13286263, -12808704, -4381056, 9882022),
array(18512079, 11319350, -20123124, 15090309, 18818594, 5271736, -22727904, 3666879, -23967430, -3299429),
array(-6789020, -3146043, 16192429, 13241070, 15898607, -14206114, -10084880, -6661110, -2403099, 5276065),
),
array(
array(30169808, -5317648, 26306206, -11750859, 27814964, 7069267, 7152851, 3684982, 1449224, 13082861),
array(10342826, 3098505, 2119311, 193222, 25702612, 12233820, 23697382, 15056736, -21016438, -8202000),
array(-33150110, 3261608, 22745853, 7948688, 19370557, -15177665, -26171976, 6482814, -10300080, -11060101),
),
array(
array(32869458, -5408545, 25609743, 15678670, -10687769, -15471071, 26112421, 2521008, -22664288, 6904815),
array(29506923, 4457497, 3377935, -9796444, -30510046, 12935080, 1561737, 3841096, -29003639, -6657642),
array(10340844, -6630377, -18656632, -2278430, 12621151, -13339055, 30878497, -11824370, -25584551, 5181966),
),
array(
array(25940115, -12658025, 17324188, -10307374, -8671468, 15029094, 24396252, -16450922, -2322852, -12388574),
array(-21765684, 9916823, -1300409, 4079498, -1028346, 11909559, 1782390, 12641087, 20603771, -6561742),
array(-18882287, -11673380, 24849422, 11501709, 13161720, -4768874, 1925523, 11914390, 4662781, 7820689),
),
array(
array(12241050, -425982, 8132691, 9393934, 32846760, -1599620, 29749456, 12172924, 16136752, 15264020),
array(-10349955, -14680563, -8211979, 2330220, -17662549, -14545780, 10658213, 6671822, 19012087, 3772772),
array(3753511, -3421066, 10617074, 2028709, 14841030, -6721664, 28718732, -15762884, 20527771, 12988982),
),
array(
array(-14822485, -5797269, -3707987, 12689773, -898983, -10914866, -24183046, -10564943, 3299665, -12424953),
array(-16777703, -15253301, -9642417, 4978983, 3308785, 8755439, 6943197, 6461331, -25583147, 8991218),
array(-17226263, 1816362, -1673288, -6086439, 31783888, -8175991, -32948145, 7417950, -30242287, 1507265),
),
array(
array(29692663, 6829891, -10498800, 4334896, 20945975, -11906496, -28887608, 8209391, 14606362, -10647073),
array(-3481570, 8707081, 32188102, 5672294, 22096700, 1711240, -33020695, 9761487, 4170404, -2085325),
array(-11587470, 14855945, -4127778, -1531857, -26649089, 15084046, 22186522, 16002000, -14276837, -8400798),
),
array(
array(-4811456, 13761029, -31703877, -2483919, -3312471, 7869047, -7113572, -9620092, 13240845, 10965870),
array(-7742563, -8256762, -14768334, -13656260, -23232383, 12387166, 4498947, 14147411, 29514390, 4302863),
array(-13413405, -12407859, 20757302, -13801832, 14785143, 8976368, -5061276, -2144373, 17846988, -13971927),
),
),
array(
array(
array(-2244452, -754728, -4597030, -1066309, -6247172, 1455299, -21647728, -9214789, -5222701, 12650267),
array(-9906797, -16070310, 21134160, 12198166, -27064575, 708126, 387813, 13770293, -19134326, 10958663),
array(22470984, 12369526, 23446014, -5441109, -21520802, -9698723, -11772496, -11574455, -25083830, 4271862),
),
array(
array(-25169565, -10053642, -19909332, 15361595, -5984358, 2159192, 75375, -4278529, -32526221, 8469673),
array(15854970, 4148314, -8893890, 7259002, 11666551, 13824734, -30531198, 2697372, 24154791, -9460943),
array(15446137, -15806644, 29759747, 14019369, 30811221, -9610191, -31582008, 12840104, 24913809, 9815020),
),
array(
array(-4709286, -5614269, -31841498, -12288893, -14443537, 10799414, -9103676, 13438769, 18735128, 9466238),
array(11933045, 9281483, 5081055, -5183824, -2628162, -4905629, -7727821, -10896103, -22728655, 16199064),
array(14576810, 379472, -26786533, -8317236, -29426508, -10812974, -102766, 1876699, 30801119, 2164795),
),
array(
array(15995086, 3199873, 13672555, 13712240, -19378835, -4647646, -13081610, -15496269, -13492807, 1268052),
array(-10290614, -3659039, -3286592, 10948818, 23037027, 3794475, -3470338, -12600221, -17055369, 3565904),
array(29210088, -9419337, -5919792, -4952785, 10834811, -13327726, -16512102, -10820713, -27162222, -14030531),
),
array(
array(-13161890, 15508588, 16663704, -8156150, -28349942, 9019123, -29183421, -3769423, 2244111, -14001979),
array(-5152875, -3800936, -9306475, -6071583, 16243069, 14684434, -25673088, -16180800, 13491506, 4641841),
array(10813417, 643330, -19188515, -728916, 30292062, -16600078, 27548447, -7721242, 14476989, -12767431),
),
array(
array(10292079, 9984945, 6481436, 8279905, -7251514, 7032743, 27282937, -1644259, -27912810, 12651324),
array(-31185513, -813383, 22271204, 11835308, 10201545, 15351028, 17099662, 3988035, 21721536, -3148940),
array(10202177, -6545839, -31373232, -9574638, -32150642, -8119683, -12906320, 3852694, 13216206, 14842320),
),
array(
array(-15815640, -10601066, -6538952, -7258995, -6984659, -6581778, -31500847, 13765824, -27434397, 9900184),
array(14465505, -13833331, -32133984, -14738873, -27443187, 12990492, 33046193, 15796406, -7051866, -8040114),
array(30924417, -8279620, 6359016, -12816335, 16508377, 9071735, -25488601, 15413635, 9524356, -7018878),
),
array(
array(12274201, -13175547, 32627641, -1785326, 6736625, 13267305, 5237659, -5109483, 15663516, 4035784),
array(-2951309, 8903985, 17349946, 601635, -16432815, -4612556, -13732739, -15889334, -22258478, 4659091),
array(-16916263, -4952973, -30393711, -15158821, 20774812, 15897498, 5736189, 15026997, -2178256, -13455585),
),
),
array(
array(
array(-8858980, -2219056, 28571666, -10155518, -474467, -10105698, -3801496, 278095, 23440562, -290208),
array(10226241, -5928702, 15139956, 120818, -14867693, 5218603, 32937275, 11551483, -16571960, -7442864),
array(17932739, -12437276, -24039557, 10749060, 11316803, 7535897, 22503767, 5561594, -3646624, 3898661),
),
array(
array(7749907, -969567, -16339731, -16464, -25018111, 15122143, -1573531, 7152530, 21831162, 1245233),
array(26958459, -14658026, 4314586, 8346991, -5677764, 11960072, -32589295, -620035, -30402091, -16716212),
array(-12165896, 9166947, 33491384, 13673479, 29787085, 13096535, 6280834, 14587357, -22338025, 13987525),
),
array(
array(-24349909, 7778775, 21116000, 15572597, -4833266, -5357778, -4300898, -5124639, -7469781, -2858068),
array(9681908, -6737123, -31951644, 13591838, -6883821, 386950, 31622781, 6439245, -14581012, 4091397),
array(-8426427, 1470727, -28109679, -1596990, 3978627, -5123623, -19622683, 12092163, 29077877, -14741988),
),
array(
array(5269168, -6859726, -13230211, -8020715, 25932563, 1763552, -5606110, -5505881, -20017847, 2357889),
array(32264008, -15407652, -5387735, -1160093, -2091322, -3946900, 23104804, -12869908, 5727338, 189038),
array(14609123, -8954470, -6000566, -16622781, -14577387, -7743898, -26745169, 10942115, -25888931, -14884697),
),
array(
array(20513500, 5557931, -15604613, 7829531, 26413943, -2019404, -21378968, 7471781, 13913677, -5137875),
array(-25574376, 11967826, 29233242, 12948236, -6754465, 4713227, -8940970, 14059180, 12878652, 8511905),
array(-25656801, 3393631, -2955415, -7075526, -2250709, 9366908, -30223418, 6812974, 5568676, -3127656),
),
array(
array(11630004, 12144454, 2116339, 13606037, 27378885, 15676917, -17408753, -13504373, -14395196, 8070818),
array(27117696, -10007378, -31282771, -5570088, 1127282, 12772488, -29845906, 10483306, -11552749, -1028714),
array(10637467, -5688064, 5674781, 1072708, -26343588, -6982302, -1683975, 9177853, -27493162, 15431203),
),
array(
array(20525145, 10892566, -12742472, 12779443, -29493034, 16150075, -28240519, 14943142, -15056790, -7935931),
array(-30024462, 5626926, -551567, -9981087, 753598, 11981191, 25244767, -3239766, -3356550, 9594024),
array(-23752644, 2636870, -5163910, -10103818, 585134, 7877383, 11345683, -6492290, 13352335, -10977084),
),
array(
array(-1931799, -5407458, 3304649, -12884869, 17015806, -4877091, -29783850, -7752482, -13215537, -319204),
array(20239939, 6607058, 6203985, 3483793, -18386976, -779229, -20723742, 15077870, -22750759, 14523817),
array(27406042, -6041657, 27423596, -4497394, 4996214, 10002360, -28842031, -4545494, -30172742, -4805667),
),
),
array(
array(
array(11374242, 12660715, 17861383, -12540833, 10935568, 1099227, -13886076, -9091740, -27727044, 11358504),
array(-12730809, 10311867, 1510375, 10778093, -2119455, -9145702, 32676003, 11149336, -26123651, 4985768),
array(-19096303, 341147, -6197485, -239033, 15756973, -8796662, -983043, 13794114, -19414307, -15621255),
),
array(
array(6490081, 11940286, 25495923, -7726360, 8668373, -8751316, 3367603, 6970005, -1691065, -9004790),
array(1656497, 13457317, 15370807, 6364910, 13605745, 8362338, -19174622, -5475723, -16796596, -5031438),
array(-22273315, -13524424, -64685, -4334223, -18605636, -10921968, -20571065, -7007978, -99853, -10237333),
),
array(
array(17747465, 10039260, 19368299, -4050591, -20630635, -16041286, 31992683, -15857976, -29260363, -5511971),
array(31932027, -4986141, -19612382, 16366580, 22023614, 88450, 11371999, -3744247, 4882242, -10626905),
array(29796507, 37186, 19818052, 10115756, -11829032, 3352736, 18551198, 3272828, -5190932, -4162409),
),
array(
array(12501286, 4044383, -8612957, -13392385, -32430052, 5136599, -19230378, -3529697, 330070, -3659409),
array(6384877, 2899513, 17807477, 7663917, -2358888, 12363165, 25366522, -8573892, -271295, 12071499),
array(-8365515, -4042521, 25133448, -4517355, -6211027, 2265927, -32769618, 1936675, -5159697, 3829363),
),
array(
array(28425966, -5835433, -577090, -4697198, -14217555, 6870930, 7921550, -6567787, 26333140, 14267664),
array(-11067219, 11871231, 27385719, -10559544, -4585914, -11189312, 10004786, -8709488, -21761224, 8930324),
array(-21197785, -16396035, 25654216, -1725397, 12282012, 11008919, 1541940, 4757911, -26491501, -16408940),
),
array(
array(13537262, -7759490, -20604840, 10961927, -5922820, -13218065, -13156584, 6217254, -15943699, 13814990),
array(-17422573, 15157790, 18705543, 29619, 24409717, -260476, 27361681, 9257833, -1956526, -1776914),
array(-25045300, -10191966, 15366585, 15166509, -13105086, 8423556, -29171540, 12361135, -18685978, 4578290),
),
array(
array(24579768, 3711570, 1342322, -11180126, -27005135, 14124956, -22544529, 14074919, 21964432, 8235257),
array(-6528613, -2411497, 9442966, -5925588, 12025640, -1487420, -2981514, -1669206, 13006806, 2355433),
array(-16304899, -13605259, -6632427, -5142349, 16974359, -10911083, 27202044, 1719366, 1141648, -12796236),
),
array(
array(-12863944, -13219986, -8318266, -11018091, -6810145, -4843894, 13475066, -3133972, 32674895, 13715045),
array(11423335, -5468059, 32344216, 8962751, 24989809, 9241752, -13265253, 16086212, -28740881, -15642093),
array(-1409668, 12530728, -6368726, 10847387, 19531186, -14132160, -11709148, 7791794, -27245943, 4383347),
),
),
array(
array(
array(-28970898, 5271447, -1266009, -9736989, -12455236, 16732599, -4862407, -4906449, 27193557, 6245191),
array(-15193956, 5362278, -1783893, 2695834, 4960227, 12840725, 23061898, 3260492, 22510453, 8577507),
array(-12632451, 11257346, -32692994, 13548177, -721004, 10879011, 31168030, 13952092, -29571492, -3635906),
),
array(
array(3877321, -9572739, 32416692, 5405324, -11004407, -13656635, 3759769, 11935320, 5611860, 8164018),
array(-16275802, 14667797, 15906460, 12155291, -22111149, -9039718, 32003002, -8832289, 5773085, -8422109),
array(-23788118, -8254300, 1950875, 8937633, 18686727, 16459170, -905725, 12376320, 31632953, 190926),
),
array(
array(-24593607, -16138885, -8423991, 13378746, 14162407, 6901328, -8288749, 4508564, -25341555, -3627528),
array(8884438, -5884009, 6023974, 10104341, -6881569, -4941533, 18722941, -14786005, -1672488, 827625),
array(-32720583, -16289296, -32503547, 7101210, 13354605, 2659080, -1800575, -14108036, -24878478, 1541286),
),
array(
array(2901347, -1117687, 3880376, -10059388, -17620940, -3612781, -21802117, -3567481, 20456845, -1885033),
array(27019610, 12299467, -13658288, -1603234, -12861660, -4861471, -19540150, -5016058, 29439641, 15138866),
array(21536104, -6626420, -32447818, -10690208, -22408077, 5175814, -5420040, -16361163, 7779328, 109896),
),
array(
array(30279744, 14648750, -8044871, 6425558, 13639621, -743509, 28698390, 12180118, 23177719, -554075),
array(26572847, 3405927, -31701700, 12890905, -19265668, 5335866, -6493768, 2378492, 4439158, -13279347),
array(-22716706, 3489070, -9225266, -332753, 18875722, -1140095, 14819434, -12731527, -17717757, -5461437),
),
array(
array(-5056483, 16566551, 15953661, 3767752, -10436499, 15627060, -820954, 2177225, 8550082, -15114165),
array(-18473302, 16596775, -381660, 15663611, 22860960, 15585581, -27844109, -3582739, -23260460, -8428588),
array(-32480551, 15707275, -8205912, -5652081, 29464558, 2713815, -22725137, 15860482, -21902570, 1494193),
),
array(
array(-19562091, -14087393, -25583872, -9299552, 13127842, 759709, 21923482, 16529112, 8742704, 12967017),
array(-28464899, 1553205, 32536856, -10473729, -24691605, -406174, -8914625, -2933896, -29903758, 15553883),
array(21877909, 3230008, 9881174, 10539357, -4797115, 2841332, 11543572, 14513274, 19375923, -12647961),
),
array(
array(8832269, -14495485, 13253511, 5137575, 5037871, 4078777, 24880818, -6222716, 2862653, 9455043),
array(29306751, 5123106, 20245049, -14149889, 9592566, 8447059, -2077124, -2990080, 15511449, 4789663),
array(-20679756, 7004547, 8824831, -9434977, -4045704, -3750736, -5754762, 108893, 23513200, 16652362),
),
),
array(
array(
array(-33256173, 4144782, -4476029, -6579123, 10770039, -7155542, -6650416, -12936300, -18319198, 10212860),
array(2756081, 8598110, 7383731, -6859892, 22312759, -1105012, 21179801, 2600940, -9988298, -12506466),
array(-24645692, 13317462, -30449259, -15653928, 21365574, -10869657, 11344424, 864440, -2499677, -16710063),
),
array(
array(-26432803, 6148329, -17184412, -14474154, 18782929, -275997, -22561534, 211300, 2719757, 4940997),
array(-1323882, 3911313, -6948744, 14759765, -30027150, 7851207, 21690126, 8518463, 26699843, 5276295),
array(-13149873, -6429067, 9396249, 365013, 24703301, -10488939, 1321586, 149635, -15452774, 7159369),
),
array(
array(9987780, -3404759, 17507962, 9505530, 9731535, -2165514, 22356009, 8312176, 22477218, -8403385),
array(18155857, -16504990, 19744716, 9006923, 15154154, -10538976, 24256460, -4864995, -22548173, 9334109),
array(2986088, -4911893, 10776628, -3473844, 10620590, -7083203, -21413845, 14253545, -22587149, 536906),
),
array(
array(4377756, 8115836, 24567078, 15495314, 11625074, 13064599, 7390551, 10589625, 10838060, -15420424),
array(-19342404, 867880, 9277171, -3218459, -14431572, -1986443, 19295826, -15796950, 6378260, 699185),
array(7895026, 4057113, -7081772, -13077756, -17886831, -323126, -716039, 15693155, -5045064, -13373962),
),
array(
array(-7737563, -5869402, -14566319, -7406919, 11385654, 13201616, 31730678, -10962840, -3918636, -9669325),
array(10188286, -15770834, -7336361, 13427543, 22223443, 14896287, 30743455, 7116568, -21786507, 5427593),
array(696102, 13206899, 27047647, -10632082, 15285305, -9853179, 10798490, -4578720, 19236243, 12477404),
),
array(
array(-11229439, 11243796, -17054270, -8040865, -788228, -8167967, -3897669, 11180504, -23169516, 7733644),
array(17800790, -14036179, -27000429, -11766671, 23887827, 3149671, 23466177, -10538171, 10322027, 15313801),
array(26246234, 11968874, 32263343, -5468728, 6830755, -13323031, -15794704, -101982, -24449242, 10890804),
),
array(
array(-31365647, 10271363, -12660625, -6267268, 16690207, -13062544, -14982212, 16484931, 25180797, -5334884),
array(-586574, 10376444, -32586414, -11286356, 19801893, 10997610, 2276632, 9482883, 316878, 13820577),
array(-9882808, -4510367, -2115506, 16457136, -11100081, 11674996, 30756178, -7515054, 30696930, -3712849),
),
array(
array(32988917, -9603412, 12499366, 7910787, -10617257, -11931514, -7342816, -9985397, -32349517, 7392473),
array(-8855661, 15927861, 9866406, -3649411, -2396914, -16655781, -30409476, -9134995, 25112947, -2926644),
array(-2504044, -436966, 25621774, -5678772, 15085042, -5479877, -24884878, -13526194, 5537438, -13914319),
),
),
array(
array(
array(-11225584, 2320285, -9584280, 10149187, -33444663, 5808648, -14876251, -1729667, 31234590, 6090599),
array(-9633316, 116426, 26083934, 2897444, -6364437, -2688086, 609721, 15878753, -6970405, -9034768),
array(-27757857, 247744, -15194774, -9002551, 23288161, -10011936, -23869595, 6503646, 20650474, 1804084),
),
array(
array(-27589786, 15456424, 8972517, 8469608, 15640622, 4439847, 3121995, -10329713, 27842616, -202328),
array(-15306973, 2839644, 22530074, 10026331, 4602058, 5048462, 28248656, 5031932, -11375082, 12714369),
array(20807691, -7270825, 29286141, 11421711, -27876523, -13868230, -21227475, 1035546, -19733229, 12796920),
),
array(
array(12076899, -14301286, -8785001, -11848922, -25012791, 16400684, -17591495, -12899438, 3480665, -15182815),
array(-32361549, 5457597, 28548107, 7833186, 7303070, -11953545, -24363064, -15921875, -33374054, 2771025),
array(-21389266, 421932, 26597266, 6860826, 22486084, -6737172, -17137485, -4210226, -24552282, 15673397),
),
array(
array(-20184622, 2338216, 19788685, -9620956, -4001265, -8740893, -20271184, 4733254, 3727144, -12934448),
array(6120119, 814863, -11794402, -622716, 6812205, -15747771, 2019594, 7975683, 31123697, -10958981),
array(30069250, -11435332, 30434654, 2958439, 18399564, -976289, 12296869, 9204260, -16432438, 9648165),
),
array(
array(32705432, -1550977, 30705658, 7451065, -11805606, 9631813, 3305266, 5248604, -26008332, -11377501),
array(17219865, 2375039, -31570947, -5575615, -19459679, 9219903, 294711, 15298639, 2662509, -16297073),
array(-1172927, -7558695, -4366770, -4287744, -21346413, -8434326, 32087529, -1222777, 32247248, -14389861),
),
array(
array(14312628, 1221556, 17395390, -8700143, -4945741, -8684635, -28197744, -9637817, -16027623, -13378845),
array(-1428825, -9678990, -9235681, 6549687, -7383069, -468664, 23046502, 9803137, 17597934, 2346211),
array(18510800, 15337574, 26171504, 981392, -22241552, 7827556, -23491134, -11323352, 3059833, -11782870),
),
array(
array(10141598, 6082907, 17829293, -1947643, 9830092, 13613136, -25556636, -5544586, -33502212, 3592096),
array(33114168, -15889352, -26525686, -13343397, 33076705, 8716171, 1151462, 1521897, -982665, -6837803),
array(-32939165, -4255815, 23947181, -324178, -33072974, -12305637, -16637686, 3891704, 26353178, 693168),
),
array(
array(30374239, 1595580, -16884039, 13186931, 4600344, 406904, 9585294, -400668, 31375464, 14369965),
array(-14370654, -7772529, 1510301, 6434173, -18784789, -6262728, 32732230, -13108839, 17901441, 16011505),
array(18171223, -11934626, -12500402, 15197122, -11038147, -15230035, -19172240, -16046376, 8764035, 12309598),
),
),
array(
array(
array(5975908, -5243188, -19459362, -9681747, -11541277, 14015782, -23665757, 1228319, 17544096, -10593782),
array(5811932, -1715293, 3442887, -2269310, -18367348, -8359541, -18044043, -15410127, -5565381, 12348900),
array(-31399660, 11407555, 25755363, 6891399, -3256938, 14872274, -24849353, 8141295, -10632534, -585479),
),
array(
array(-12675304, 694026, -5076145, 13300344, 14015258, -14451394, -9698672, -11329050, 30944593, 1130208),
array(8247766, -6710942, -26562381, -7709309, -14401939, -14648910, 4652152, 2488540, 23550156, -271232),
array(17294316, -3788438, 7026748, 15626851, 22990044, 113481, 2267737, -5908146, -408818, -137719),
),
array(
array(16091085, -16253926, 18599252, 7340678, 2137637, -1221657, -3364161, 14550936, 3260525, -7166271),
array(-4910104, -13332887, 18550887, 10864893, -16459325, -7291596, -23028869, -13204905, -12748722, 2701326),
array(-8574695, 16099415, 4629974, -16340524, -20786213, -6005432, -10018363, 9276971, 11329923, 1862132),
),
array(
array(14763076, -15903608, -30918270, 3689867, 3511892, 10313526, -21951088, 12219231, -9037963, -940300),
array(8894987, -3446094, 6150753, 3013931, 301220, 15693451, -31981216, -2909717, -15438168, 11595570),
array(15214962, 3537601, -26238722, -14058872, 4418657, -15230761, 13947276, 10730794, -13489462, -4363670),
),
array(
array(-2538306, 7682793, 32759013, 263109, -29984731, -7955452, -22332124, -10188635, 977108, 699994),
array(-12466472, 4195084, -9211532, 550904, -15565337, 12917920, 19118110, -439841, -30534533, -14337913),
array(31788461, -14507657, 4799989, 7372237, 8808585, -14747943, 9408237, -10051775, 12493932, -5409317),
),
array(
array(-25680606, 5260744, -19235809, -6284470, -3695942, 16566087, 27218280, 2607121, 29375955, 6024730),
array(842132, -2794693, -4763381, -8722815, 26332018, -12405641, 11831880, 6985184, -9940361, 2854096),
array(-4847262, -7969331, 2516242, -5847713, 9695691, -7221186, 16512645, 960770, 12121869, 16648078),
),
array(
array(-15218652, 14667096, -13336229, 2013717, 30598287, -464137, -31504922, -7882064, 20237806, 2838411),
array(-19288047, 4453152, 15298546, -16178388, 22115043, -15972604, 12544294, -13470457, 1068881, -12499905),
array(-9558883, -16518835, 33238498, 13506958, 30505848, -1114596, -8486907, -2630053, 12521378, 4845654),
),
array(
array(-28198521, 10744108, -2958380, 10199664, 7759311, -13088600, 3409348, -873400, -6482306, -12885870),
array(-23561822, 6230156, -20382013, 10655314, -24040585, -11621172, 10477734, -1240216, -3113227, 13974498),
array(12966261, 15550616, -32038948, -1615346, 21025980, -629444, 5642325, 7188737, 18895762, 12629579),
),
),
array(
array(
array(14741879, -14946887, 22177208, -11721237, 1279741, 8058600, 11758140, 789443, 32195181, 3895677),
array(10758205, 15755439, -4509950, 9243698, -4879422, 6879879, -2204575, -3566119, -8982069, 4429647),
array(-2453894, 15725973, -20436342, -10410672, -5803908, -11040220, -7135870, -11642895, 18047436, -15281743),
),
array(
array(-25173001, -11307165, 29759956, 11776784, -22262383, -15820455, 10993114, -12850837, -17620701, -9408468),
array(21987233, 700364, -24505048, 14972008, -7774265, -5718395, 32155026, 2581431, -29958985, 8773375),
array(-25568350, 454463, -13211935, 16126715, 25240068, 8594567, 20656846, 12017935, -7874389, -13920155),
),
array(
array(6028182, 6263078, -31011806, -11301710, -818919, 2461772, -31841174, -5468042, -1721788, -2776725),
array(-12278994, 16624277, 987579, -5922598, 32908203, 1248608, 7719845, -4166698, 28408820, 6816612),
array(-10358094, -8237829, 19549651, -12169222, 22082623, 16147817, 20613181, 13982702, -10339570, 5067943),
),
array(
array(-30505967, -3821767, 12074681, 13582412, -19877972, 2443951, -19719286, 12746132, 5331210, -10105944),
array(30528811, 3601899, -1957090, 4619785, -27361822, -15436388, 24180793, -12570394, 27679908, -1648928),
array(9402404, -13957065, 32834043, 10838634, -26580150, -13237195, 26653274, -8685565, 22611444, -12715406),
),
array(
array(22190590, 1118029, 22736441, 15130463, -30460692, -5991321, 19189625, -4648942, 4854859, 6622139),
array(-8310738, -2953450, -8262579, -3388049, -10401731, -271929, 13424426, -3567227, 26404409, 13001963),
array(-31241838, -15415700, -2994250, 8939346, 11562230, -12840670, -26064365, -11621720, -15405155, 11020693),
),
array(
array(1866042, -7949489, -7898649, -10301010, 12483315, 13477547, 3175636, -12424163, 28761762, 1406734),
array(-448555, -1777666, 13018551, 3194501, -9580420, -11161737, 24760585, -4347088, 25577411, -13378680),
array(-24290378, 4759345, -690653, -1852816, 2066747, 10693769, -29595790, 9884936, -9368926, 4745410),
),
array(
array(-9141284, 6049714, -19531061, -4341411, -31260798, 9944276, -15462008, -11311852, 10931924, -11931931),
array(-16561513, 14112680, -8012645, 4817318, -8040464, -11414606, -22853429, 10856641, -20470770, 13434654),
array(22759489, -10073434, -16766264, -1871422, 13637442, -10168091, 1765144, -12654326, 28445307, -5364710),
),
array(
array(29875063, 12493613, 2795536, -3786330, 1710620, 15181182, -10195717, -8788675, 9074234, 1167180),
array(-26205683, 11014233, -9842651, -2635485, -26908120, 7532294, -18716888, -9535498, 3843903, 9367684),
array(-10969595, -6403711, 9591134, 9582310, 11349256, 108879, 16235123, 8601684, -139197, 4242895),
),
),
array(
array(
array(22092954, -13191123, -2042793, -11968512, 32186753, -11517388, -6574341, 2470660, -27417366, 16625501),
array(-11057722, 3042016, 13770083, -9257922, 584236, -544855, -7770857, 2602725, -27351616, 14247413),
array(6314175, -10264892, -32772502, 15957557, -10157730, 168750, -8618807, 14290061, 27108877, -1180880),
),
array(
array(-8586597, -7170966, 13241782, 10960156, -32991015, -13794596, 33547976, -11058889, -27148451, 981874),
array(22833440, 9293594, -32649448, -13618667, -9136966, 14756819, -22928859, -13970780, -10479804, -16197962),
array(-7768587, 3326786, -28111797, 10783824, 19178761, 14905060, 22680049, 13906969, -15933690, 3797899),
),
array(
array(21721356, -4212746, -12206123, 9310182, -3882239, -13653110, 23740224, -2709232, 20491983, -8042152),
array(9209270, -15135055, -13256557, -6167798, -731016, 15289673, 25947805, 15286587, 30997318, -6703063),
array(7392032, 16618386, 23946583, -8039892, -13265164, -1533858, -14197445, -2321576, 17649998, -250080),
),
array(
array(-9301088, -14193827, 30609526, -3049543, -25175069, -1283752, -15241566, -9525724, -2233253, 7662146),
array(-17558673, 1763594, -33114336, 15908610, -30040870, -12174295, 7335080, -8472199, -3174674, 3440183),
array(-19889700, -5977008, -24111293, -9688870, 10799743, -16571957, 40450, -4431835, 4862400, 1133),
),
array(
array(-32856209, -7873957, -5422389, 14860950, -16319031, 7956142, 7258061, 311861, -30594991, -7379421),
array(-3773428, -1565936, 28985340, 7499440, 24445838, 9325937, 29727763, 16527196, 18278453, 15405622),
array(-4381906, 8508652, -19898366, -3674424, -5984453, 15149970, -13313598, 843523, -21875062, 13626197),
),
array(
array(2281448, -13487055, -10915418, -2609910, 1879358, 16164207, -10783882, 3953792, 13340839, 15928663),
array(31727126, -7179855, -18437503, -8283652, 2875793, -16390330, -25269894, -7014826, -23452306, 5964753),
array(4100420, -5959452, -17179337, 6017714, -18705837, 12227141, -26684835, 11344144, 2538215, -7570755),
),
array(
array(-9433605, 6123113, 11159803, -2156608, 30016280, 14966241, -20474983, 1485421, -629256, -15958862),
array(-26804558, 4260919, 11851389, 9658551, -32017107, 16367492, -20205425, -13191288, 11659922, -11115118),
array(26180396, 10015009, -30844224, -8581293, 5418197, 9480663, 2231568, -10170080, 33100372, -1306171),
),
array(
array(15121113, -5201871, -10389905, 15427821, -27509937, -15992507, 21670947, 4486675, -5931810, -14466380),
array(16166486, -9483733, -11104130, 6023908, -31926798, -1364923, 2340060, -16254968, -10735770, -10039824),
array(28042865, -3557089, -12126526, 12259706, -3717498, -6945899, 6766453, -8689599, 18036436, 5803270),
),
),
array(
array(
array(-817581, 6763912, 11803561, 1585585, 10958447, -2671165, 23855391, 4598332, -6159431, -14117438),
array(-31031306, -14256194, 17332029, -2383520, 31312682, -5967183, 696309, 50292, -20095739, 11763584),
array(-594563, -2514283, -32234153, 12643980, 12650761, 14811489, 665117, -12613632, -19773211, -10713562),
),
array(
array(30464590, -11262872, -4127476, -12734478, 19835327, -7105613, -24396175, 2075773, -17020157, 992471),
array(18357185, -6994433, 7766382, 16342475, -29324918, 411174, 14578841, 8080033, -11574335, -10601610),
array(19598397, 10334610, 12555054, 2555664, 18821899, -10339780, 21873263, 16014234, 26224780, 16452269),
),
array(
array(-30223925, 5145196, 5944548, 16385966, 3976735, 2009897, -11377804, -7618186, -20533829, 3698650),
array(14187449, 3448569, -10636236, -10810935, -22663880, -3433596, 7268410, -10890444, 27394301, 12015369),
array(19695761, 16087646, 28032085, 12999827, 6817792, 11427614, 20244189, -1312777, -13259127, -3402461),
),
array(
array(30860103, 12735208, -1888245, -4699734, -16974906, 2256940, -8166013, 12298312, -8550524, -10393462),
array(-5719826, -11245325, -1910649, 15569035, 26642876, -7587760, -5789354, -15118654, -4976164, 12651793),
array(-2848395, 9953421, 11531313, -5282879, 26895123, -12697089, -13118820, -16517902, 9768698, -2533218),
),
array(
array(-24719459, 1894651, -287698, -4704085, 15348719, -8156530, 32767513, 12765450, 4940095, 10678226),
array(18860224, 15980149, -18987240, -1562570, -26233012, -11071856, -7843882, 13944024, -24372348, 16582019),
array(-15504260, 4970268, -29893044, 4175593, -20993212, -2199756, -11704054, 15444560, -11003761, 7989037),
),
array(
array(31490452, 5568061, -2412803, 2182383, -32336847, 4531686, -32078269, 6200206, -19686113, -14800171),
array(-17308668, -15879940, -31522777, -2831, -32887382, 16375549, 8680158, -16371713, 28550068, -6857132),
array(-28126887, -5688091, 16837845, -1820458, -6850681, 12700016, -30039981, 4364038, 1155602, 5988841),
),
array(
array(21890435, -13272907, -12624011, 12154349, -7831873, 15300496, 23148983, -4470481, 24618407, 8283181),
array(-33136107, -10512751, 9975416, 6841041, -31559793, 16356536, 3070187, -7025928, 1466169, 10740210),
array(-1509399, -15488185, -13503385, -10655916, 32799044, 909394, -13938903, -5779719, -32164649, -15327040),
),
array(
array(3960823, -14267803, -28026090, -15918051, -19404858, 13146868, 15567327, 951507, -3260321, -573935),
array(24740841, 5052253, -30094131, 8961361, 25877428, 6165135, -24368180, 14397372, -7380369, -6144105),
array(-28888365, 3510803, -28103278, -1158478, -11238128, -10631454, -15441463, -14453128, -1625486, -6494814),
),
),
array(
array(
array(793299, -9230478, 8836302, -6235707, -27360908, -2369593, 33152843, -4885251, -9906200, -621852),
array(5666233, 525582, 20782575, -8038419, -24538499, 14657740, 16099374, 1468826, -6171428, -15186581),
array(-4859255, -3779343, -2917758, -6748019, 7778750, 11688288, -30404353, -9871238, -1558923, -9863646),
),
array(
array(10896332, -7719704, 824275, 472601, -19460308, 3009587, 25248958, 14783338, -30581476, -15757844),
array(10566929, 12612572, -31944212, 11118703, -12633376, 12362879, 21752402, 8822496, 24003793, 14264025),
array(27713862, -7355973, -11008240, 9227530, 27050101, 2504721, 23886875, -13117525, 13958495, -5732453),
),
array(
array(-23481610, 4867226, -27247128, 3900521, 29838369, -8212291, -31889399, -10041781, 7340521, -15410068),
array(4646514, -8011124, -22766023, -11532654, 23184553, 8566613, 31366726, -1381061, -15066784, -10375192),
array(-17270517, 12723032, -16993061, 14878794, 21619651, -6197576, 27584817, 3093888, -8843694, 3849921),
),
array(
array(-9064912, 2103172, 25561640, -15125738, -5239824, 9582958, 32477045, -9017955, 5002294, -15550259),
array(-12057553, -11177906, 21115585, -13365155, 8808712, -12030708, 16489530, 13378448, -25845716, 12741426),
array(-5946367, 10645103, -30911586, 15390284, -3286982, -7118677, 24306472, 15852464, 28834118, -7646072),
),
array(
array(-17335748, -9107057, -24531279, 9434953, -8472084, -583362, -13090771, 455841, 20461858, 5491305),
array(13669248, -16095482, -12481974, -10203039, -14569770, -11893198, -24995986, 11293807, -28588204, -9421832),
array(28497928, 6272777, -33022994, 14470570, 8906179, -1225630, 18504674, -14165166, 29867745, -8795943),
),
array(
array(-16207023, 13517196, -27799630, -13697798, 24009064, -6373891, -6367600, -13175392, 22853429, -4012011),
array(24191378, 16712145, -13931797, 15217831, 14542237, 1646131, 18603514, -11037887, 12876623, -2112447),
array(17902668, 4518229, -411702, -2829247, 26878217, 5258055, -12860753, 608397, 16031844, 3723494),
),
array(
array(-28632773, 12763728, -20446446, 7577504, 33001348, -13017745, 17558842, -7872890, 23896954, -4314245),
array(-20005381, -12011952, 31520464, 605201, 2543521, 5991821, -2945064, 7229064, -9919646, -8826859),
array(28816045, 298879, -28165016, -15920938, 19000928, -1665890, -12680833, -2949325, -18051778, -2082915),
),
array(
array(16000882, -344896, 3493092, -11447198, -29504595, -13159789, 12577740, 16041268, -19715240, 7847707),
array(10151868, 10572098, 27312476, 7922682, 14825339, 4723128, -32855931, -6519018, -10020567, 3852848),
array(-11430470, 15697596, -21121557, -4420647, 5386314, 15063598, 16514493, -15932110, 29330899, -15076224),
),
),
array(
array(
array(-25499735, -4378794, -15222908, -6901211, 16615731, 2051784, 3303702, 15490, -27548796, 12314391),
array(15683520, -6003043, 18109120, -9980648, 15337968, -5997823, -16717435, 15921866, 16103996, -3731215),
array(-23169824, -10781249, 13588192, -1628807, -3798557, -1074929, -19273607, 5402699, -29815713, -9841101),
),
array(
array(23190676, 2384583, -32714340, 3462154, -29903655, -1529132, -11266856, 8911517, -25205859, 2739713),
array(21374101, -3554250, -33524649, 9874411, 15377179, 11831242, -33529904, 6134907, 4931255, 11987849),
array(-7732, -2978858, -16223486, 7277597, 105524, -322051, -31480539, 13861388, -30076310, 10117930),
),
array(
array(-29501170, -10744872, -26163768, 13051539, -25625564, 5089643, -6325503, 6704079, 12890019, 15728940),
array(-21972360, -11771379, -951059, -4418840, 14704840, 2695116, 903376, -10428139, 12885167, 8311031),
array(-17516482, 5352194, 10384213, -13811658, 7506451, 13453191, 26423267, 4384730, 1888765, -5435404),
),
array(
array(-25817338, -3107312, -13494599, -3182506, 30896459, -13921729, -32251644, -12707869, -19464434, -3340243),
array(-23607977, -2665774, -526091, 4651136, 5765089, 4618330, 6092245, 14845197, 17151279, -9854116),
array(-24830458, -12733720, -15165978, 10367250, -29530908, -265356, 22825805, -7087279, -16866484, 16176525),
),
array(
array(-23583256, 6564961, 20063689, 3798228, -4740178, 7359225, 2006182, -10363426, -28746253, -10197509),
array(-10626600, -4486402, -13320562, -5125317, 3432136, -6393229, 23632037, -1940610, 32808310, 1099883),
array(15030977, 5768825, -27451236, -2887299, -6427378, -15361371, -15277896, -6809350, 2051441, -15225865),
),
array(
array(-3362323, -7239372, 7517890, 9824992, 23555850, 295369, 5148398, -14154188, -22686354, 16633660),
array(4577086, -16752288, 13249841, -15304328, 19958763, -14537274, 18559670, -10759549, 8402478, -9864273),
array(-28406330, -1051581, -26790155, -907698, -17212414, -11030789, 9453451, -14980072, 17983010, 9967138),
),
array(
array(-25762494, 6524722, 26585488, 9969270, 24709298, 1220360, -1677990, 7806337, 17507396, 3651560),
array(-10420457, -4118111, 14584639, 15971087, -15768321, 8861010, 26556809, -5574557, -18553322, -11357135),
array(2839101, 14284142, 4029895, 3472686, 14402957, 12689363, -26642121, 8459447, -5605463, -7621941),
),
array(
array(-4839289, -3535444, 9744961, 2871048, 25113978, 3187018, -25110813, -849066, 17258084, -7977739),
array(18164541, -10595176, -17154882, -1542417, 19237078, -9745295, 23357533, -15217008, 26908270, 12150756),
array(-30264870, -7647865, 5112249, -7036672, -1499807, -6974257, 43168, -5537701, -32302074, 16215819),
),
),
array(
array(
array(-6898905, 9824394, -12304779, -4401089, -31397141, -6276835, 32574489, 12532905, -7503072, -8675347),
array(-27343522, -16515468, -27151524, -10722951, 946346, 16291093, 254968, 7168080, 21676107, -1943028),
array(21260961, -8424752, -16831886, -11920822, -23677961, 3968121, -3651949, -6215466, -3556191, -7913075),
),
array(
array(16544754, 13250366, -16804428, 15546242, -4583003, 12757258, -2462308, -8680336, -18907032, -9662799),
array(-2415239, -15577728, 18312303, 4964443, -15272530, -12653564, 26820651, 16690659, 25459437, -4564609),
array(-25144690, 11425020, 28423002, -11020557, -6144921, -15826224, 9142795, -2391602, -6432418, -1644817),
),
array(
array(-23104652, 6253476, 16964147, -3768872, -25113972, -12296437, -27457225, -16344658, 6335692, 7249989),
array(-30333227, 13979675, 7503222, -12368314, -11956721, -4621693, -30272269, 2682242, 25993170, -12478523),
array(4364628, 5930691, 32304656, -10044554, -8054781, 15091131, 22857016, -10598955, 31820368, 15075278),
),
array(
array(31879134, -8918693, 17258761, 90626, -8041836, -4917709, 24162788, -9650886, -17970238, 12833045),
array(19073683, 14851414, -24403169, -11860168, 7625278, 11091125, -19619190, 2074449, -9413939, 14905377),
array(24483667, -11935567, -2518866, -11547418, -1553130, 15355506, -25282080, 9253129, 27628530, -7555480),
),
array(
array(17597607, 8340603, 19355617, 552187, 26198470, -3176583, 4593324, -9157582, -14110875, 15297016),
array(510886, 14337390, -31785257, 16638632, 6328095, 2713355, -20217417, -11864220, 8683221, 2921426),
array(18606791, 11874196, 27155355, -5281482, -24031742, 6265446, -25178240, -1278924, 4674690, 13890525),
),
array(
array(13609624, 13069022, -27372361, -13055908, 24360586, 9592974, 14977157, 9835105, 4389687, 288396),
array(9922506, -519394, 13613107, 5883594, -18758345, -434263, -12304062, 8317628, 23388070, 16052080),
array(12720016, 11937594, -31970060, -5028689, 26900120, 8561328, -20155687, -11632979, -14754271, -10812892),
),
array(
array(15961858, 14150409, 26716931, -665832, -22794328, 13603569, 11829573, 7467844, -28822128, 929275),
array(11038231, -11582396, -27310482, -7316562, -10498527, -16307831, -23479533, -9371869, -21393143, 2465074),
array(20017163, -4323226, 27915242, 1529148, 12396362, 15675764, 13817261, -9658066, 2463391, -4622140),
),
array(
array(-16358878, -12663911, -12065183, 4996454, -1256422, 1073572, 9583558, 12851107, 4003896, 12673717),
array(-1731589, -15155870, -3262930, 16143082, 19294135, 13385325, 14741514, -9103726, 7903886, 2348101),
array(24536016, -16515207, 12715592, -3862155, 1511293, 10047386, -3842346, -7129159, -28377538, 10048127),
),
),
array(
array(
array(-12622226, -6204820, 30718825, 2591312, -10617028, 12192840, 18873298, -7297090, -32297756, 15221632),
array(-26478122, -11103864, 11546244, -1852483, 9180880, 7656409, -21343950, 2095755, 29769758, 6593415),
array(-31994208, -2907461, 4176912, 3264766, 12538965, -868111, 26312345, -6118678, 30958054, 8292160),
),
array(
array(31429822, -13959116, 29173532, 15632448, 12174511, -2760094, 32808831, 3977186, 26143136, -3148876),
array(22648901, 1402143, -22799984, 13746059, 7936347, 365344, -8668633, -1674433, -3758243, -2304625),
array(-15491917, 8012313, -2514730, -12702462, -23965846, -10254029, -1612713, -1535569, -16664475, 8194478),
),
array(
array(27338066, -7507420, -7414224, 10140405, -19026427, -6589889, 27277191, 8855376, 28572286, 3005164),
array(26287124, 4821776, 25476601, -4145903, -3764513, -15788984, -18008582, 1182479, -26094821, -13079595),
array(-7171154, 3178080, 23970071, 6201893, -17195577, -4489192, -21876275, -13982627, 32208683, -1198248),
),
array(
array(-16657702, 2817643, -10286362, 14811298, 6024667, 13349505, -27315504, -10497842, -27672585, -11539858),
array(15941029, -9405932, -21367050, 8062055, 31876073, -238629, -15278393, -1444429, 15397331, -4130193),
array(8934485, -13485467, -23286397, -13423241, -32446090, 14047986, 31170398, -1441021, -27505566, 15087184),
),
array(
array(-18357243, -2156491, 24524913, -16677868, 15520427, -6360776, -15502406, 11461896, 16788528, -5868942),
array(-1947386, 16013773, 21750665, 3714552, -17401782, -16055433, -3770287, -10323320, 31322514, -11615635),
array(21426655, -5650218, -13648287, -5347537, -28812189, -4920970, -18275391, -14621414, 13040862, -12112948),
),
array(
array(11293895, 12478086, -27136401, 15083750, -29307421, 14748872, 14555558, -13417103, 1613711, 4896935),
array(-25894883, 15323294, -8489791, -8057900, 25967126, -13425460, 2825960, -4897045, -23971776, -11267415),
array(-15924766, -5229880, -17443532, 6410664, 3622847, 10243618, 20615400, 12405433, -23753030, -8436416),
),
array(
array(-7091295, 12556208, -20191352, 9025187, -17072479, 4333801, 4378436, 2432030, 23097949, -566018),
array(4565804, -16025654, 20084412, -7842817, 1724999, 189254, 24767264, 10103221, -18512313, 2424778),
array(366633, -11976806, 8173090, -6890119, 30788634, 5745705, -7168678, 1344109, -3642553, 12412659),
),
array(
array(-24001791, 7690286, 14929416, -168257, -32210835, -13412986, 24162697, -15326504, -3141501, 11179385),
array(18289522, -14724954, 8056945, 16430056, -21729724, 7842514, -6001441, -1486897, -18684645, -11443503),
array(476239, 6601091, -6152790, -9723375, 17503545, -4863900, 27672959, 13403813, 11052904, 5219329),
),
),
array(
array(
array(20678546, -8375738, -32671898, 8849123, -5009758, 14574752, 31186971, -3973730, 9014762, -8579056),
array(-13644050, -10350239, -15962508, 5075808, -1514661, -11534600, -33102500, 9160280, 8473550, -3256838),
array(24900749, 14435722, 17209120, -15292541, -22592275, 9878983, -7689309, -16335821, -24568481, 11788948),
),
array(
array(-3118155, -11395194, -13802089, 14797441, 9652448, -6845904, -20037437, 10410733, -24568470, -1458691),
array(-15659161, 16736706, -22467150, 10215878, -9097177, 7563911, 11871841, -12505194, -18513325, 8464118),
array(-23400612, 8348507, -14585951, -861714, -3950205, -6373419, 14325289, 8628612, 33313881, -8370517),
),
array(
array(-20186973, -4967935, 22367356, 5271547, -1097117, -4788838, -24805667, -10236854, -8940735, -5818269),
array(-6948785, -1795212, -32625683, -16021179, 32635414, -7374245, 15989197, -12838188, 28358192, -4253904),
array(-23561781, -2799059, -32351682, -1661963, -9147719, 10429267, -16637684, 4072016, -5351664, 5596589),
),
array(
array(-28236598, -3390048, 12312896, 6213178, 3117142, 16078565, 29266239, 2557221, 1768301, 15373193),
array(-7243358, -3246960, -4593467, -7553353, -127927, -912245, -1090902, -4504991, -24660491, 3442910),
array(-30210571, 5124043, 14181784, 8197961, 18964734, -11939093, 22597931, 7176455, -18585478, 13365930),
),
array(
array(-7877390, -1499958, 8324673, 4690079, 6261860, 890446, 24538107, -8570186, -9689599, -3031667),
array(25008904, -10771599, -4305031, -9638010, 16265036, 15721635, 683793, -11823784, 15723479, -15163481),
array(-9660625, 12374379, -27006999, -7026148, -7724114, -12314514, 11879682, 5400171, 519526, -1235876),
),
array(
array(22258397, -16332233, -7869817, 14613016, -22520255, -2950923, -20353881, 7315967, 16648397, 7605640),
array(-8081308, -8464597, -8223311, 9719710, 19259459, -15348212, 23994942, -5281555, -9468848, 4763278),
array(-21699244, 9220969, -15730624, 1084137, -25476107, -2852390, 31088447, -7764523, -11356529, 728112),
),
array(
array(26047220, -11751471, -6900323, -16521798, 24092068, 9158119, -4273545, -12555558, -29365436, -5498272),
array(17510331, -322857, 5854289, 8403524, 17133918, -3112612, -28111007, 12327945, 10750447, 10014012),
array(-10312768, 3936952, 9156313, -8897683, 16498692, -994647, -27481051, -666732, 3424691, 7540221),
),
array(
array(30322361, -6964110, 11361005, -4143317, 7433304, 4989748, -7071422, -16317219, -9244265, 15258046),
array(13054562, -2779497, 19155474, 469045, -12482797, 4566042, 5631406, 2711395, 1062915, -5136345),
array(-19240248, -11254599, -29509029, -7499965, -5835763, 13005411, -6066489, 12194497, 32960380, 1459310),
),
),
array(
array(
array(19852034, 7027924, 23669353, 10020366, 8586503, -6657907, 394197, -6101885, 18638003, -11174937),
array(31395534, 15098109, 26581030, 8030562, -16527914, -5007134, 9012486, -7584354, -6643087, -5442636),
array(-9192165, -2347377, -1997099, 4529534, 25766844, 607986, -13222, 9677543, -32294889, -6456008),
),
array(
array(-2444496, -149937, 29348902, 8186665, 1873760, 12489863, -30934579, -7839692, -7852844, -8138429),
array(-15236356, -15433509, 7766470, 746860, 26346930, -10221762, -27333451, 10754588, -9431476, 5203576),
array(31834314, 14135496, -770007, 5159118, 20917671, -16768096, -7467973, -7337524, 31809243, 7347066),
),
array(
array(-9606723, -11874240, 20414459, 13033986, 13716524, -11691881, 19797970, -12211255, 15192876, -2087490),
array(-12663563, -2181719, 1168162, -3804809, 26747877, -14138091, 10609330, 12694420, 33473243, -13382104),
array(33184999, 11180355, 15832085, -11385430, -1633671, 225884, 15089336, -11023903, -6135662, 14480053),
),
array(
array(31308717, -5619998, 31030840, -1897099, 15674547, -6582883, 5496208, 13685227, 27595050, 8737275),
array(-20318852, -15150239, 10933843, -16178022, 8335352, -7546022, -31008351, -12610604, 26498114, 66511),
array(22644454, -8761729, -16671776, 4884562, -3105614, -13559366, 30540766, -4286747, -13327787, -7515095),
),
array(
array(-28017847, 9834845, 18617207, -2681312, -3401956, -13307506, 8205540, 13585437, -17127465, 15115439),
array(23711543, -672915, 31206561, -8362711, 6164647, -9709987, -33535882, -1426096, 8236921, 16492939),
array(-23910559, -13515526, -26299483, -4503841, 25005590, -7687270, 19574902, 10071562, 6708380, -6222424),
),
array(
array(2101391, -4930054, 19702731, 2367575, -15427167, 1047675, 5301017, 9328700, 29955601, -11678310),
array(3096359, 9271816, -21620864, -15521844, -14847996, -7592937, -25892142, -12635595, -9917575, 6216608),
array(-32615849, 338663, -25195611, 2510422, -29213566, -13820213, 24822830, -6146567, -26767480, 7525079),
),
array(
array(-23066649, -13985623, 16133487, -7896178, -3389565, 778788, -910336, -2782495, -19386633, 11994101),
array(21691500, -13624626, -641331, -14367021, 3285881, -3483596, -25064666, 9718258, -7477437, 13381418),
array(18445390, -4202236, 14979846, 11622458, -1727110, -3582980, 23111648, -6375247, 28535282, 15779576),
),
array(
array(30098053, 3089662, -9234387, 16662135, -21306940, 11308411, -14068454, 12021730, 9955285, -16303356),
array(9734894, -14576830, -7473633, -9138735, 2060392, 11313496, -18426029, 9924399, 20194861, 13380996),
array(-26378102, -7965207, -22167821, 15789297, -18055342, -6168792, -1984914, 15707771, 26342023, 10146099),
),
),
array(
array(
array(-26016874, -219943, 21339191, -41388, 19745256, -2878700, -29637280, 2227040, 21612326, -545728),
array(-13077387, 1184228, 23562814, -5970442, -20351244, -6348714, 25764461, 12243797, -20856566, 11649658),
array(-10031494, 11262626, 27384172, 2271902, 26947504, -15997771, 39944, 6114064, 33514190, 2333242),
),
array(
array(-21433588, -12421821, 8119782, 7219913, -21830522, -9016134, -6679750, -12670638, 24350578, -13450001),
array(-4116307, -11271533, -23886186, 4843615, -30088339, 690623, -31536088, -10406836, 8317860, 12352766),
array(18200138, -14475911, -33087759, -2696619, -23702521, -9102511, -23552096, -2287550, 20712163, 6719373),
),
array(
array(26656208, 6075253, -7858556, 1886072, -28344043, 4262326, 11117530, -3763210, 26224235, -3297458),
array(-17168938, -14854097, -3395676, -16369877, -19954045, 14050420, 21728352, 9493610, 18620611, -16428628),
array(-13323321, 13325349, 11432106, 5964811, 18609221, 6062965, -5269471, -9725556, -30701573, -16479657),
),
array(
array(-23860538, -11233159, 26961357, 1640861, -32413112, -16737940, 12248509, -5240639, 13735342, 1934062),
array(25089769, 6742589, 17081145, -13406266, 21909293, -16067981, -15136294, -3765346, -21277997, 5473616),
array(31883677, -7961101, 1083432, -11572403, 22828471, 13290673, -7125085, 12469656, 29111212, -5451014),
),
array(
array(24244947, -15050407, -26262976, 2791540, -14997599, 16666678, 24367466, 6388839, -10295587, 452383),
array(-25640782, -3417841, 5217916, 16224624, 19987036, -4082269, -24236251, -5915248, 15766062, 8407814),
array(-20406999, 13990231, 15495425, 16395525, 5377168, 15166495, -8917023, -4388953, -8067909, 2276718),
),
array(
array(30157918, 12924066, -17712050, 9245753, 19895028, 3368142, -23827587, 5096219, 22740376, -7303417),
array(2041139, -14256350, 7783687, 13876377, -25946985, -13352459, 24051124, 13742383, -15637599, 13295222),
array(33338237, -8505733, 12532113, 7977527, 9106186, -1715251, -17720195, -4612972, -4451357, -14669444),
),
array(
array(-20045281, 5454097, -14346548, 6447146, 28862071, 1883651, -2469266, -4141880, 7770569, 9620597),
array(23208068, 7979712, 33071466, 8149229, 1758231, -10834995, 30945528, -1694323, -33502340, -14767970),
array(1439958, -16270480, -1079989, -793782, 4625402, 10647766, -5043801, 1220118, 30494170, -11440799),
),
array(
array(-5037580, -13028295, -2970559, -3061767, 15640974, -6701666, -26739026, 926050, -1684339, -13333647),
array(13908495, -3549272, 30919928, -6273825, -21521863, 7989039, 9021034, 9078865, 3353509, 4033511),
array(-29663431, -15113610, 32259991, -344482, 24295849, -12912123, 23161163, 8839127, 27485041, 7356032),
),
),
array(
array(
array(9661027, 705443, 11980065, -5370154, -1628543, 14661173, -6346142, 2625015, 28431036, -16771834),
array(-23839233, -8311415, -25945511, 7480958, -17681669, -8354183, -22545972, 14150565, 15970762, 4099461),
array(29262576, 16756590, 26350592, -8793563, 8529671, -11208050, 13617293, -9937143, 11465739, 8317062),
),
array(
array(-25493081, -6962928, 32500200, -9419051, -23038724, -2302222, 14898637, 3848455, 20969334, -5157516),
array(-20384450, -14347713, -18336405, 13884722, -33039454, 2842114, -21610826, -3649888, 11177095, 14989547),
array(-24496721, -11716016, 16959896, 2278463, 12066309, 10137771, 13515641, 2581286, -28487508, 9930240),
),
array(
array(-17751622, -2097826, 16544300, -13009300, -15914807, -14949081, 18345767, -13403753, 16291481, -5314038),
array(-33229194, 2553288, 32678213, 9875984, 8534129, 6889387, -9676774, 6957617, 4368891, 9788741),
array(16660756, 7281060, -10830758, 12911820, 20108584, -8101676, -21722536, -8613148, 16250552, -11111103),
),
array(
array(-19765507, 2390526, -16551031, 14161980, 1905286, 6414907, 4689584, 10604807, -30190403, 4782747),
array(-1354539, 14736941, -7367442, -13292886, 7710542, -14155590, -9981571, 4383045, 22546403, 437323),
array(31665577, -12180464, -16186830, 1491339, -18368625, 3294682, 27343084, 2786261, -30633590, -14097016),
),
array(
array(-14467279, -683715, -33374107, 7448552, 19294360, 14334329, -19690631, 2355319, -19284671, -6114373),
array(15121312, -15796162, 6377020, -6031361, -10798111, -12957845, 18952177, 15496498, -29380133, 11754228),
array(-2637277, -13483075, 8488727, -14303896, 12728761, -1622493, 7141596, 11724556, 22761615, -10134141),
),
array(
array(16918416, 11729663, -18083579, 3022987, -31015732, -13339659, -28741185, -12227393, 32851222, 11717399),
array(11166634, 7338049, -6722523, 4531520, -29468672, -7302055, 31474879, 3483633, -1193175, -4030831),
array(-185635, 9921305, 31456609, -13536438, -12013818, 13348923, 33142652, 6546660, -19985279, -3948376),
),
array(
array(-32460596, 11266712, -11197107, -7899103, 31703694, 3855903, -8537131, -12833048, -30772034, -15486313),
array(-18006477, 12709068, 3991746, -6479188, -21491523, -10550425, -31135347, -16049879, 10928917, 3011958),
array(-6957757, -15594337, 31696059, 334240, 29576716, 14796075, -30831056, -12805180, 18008031, 10258577),
),
array(
array(-22448644, 15655569, 7018479, -4410003, -30314266, -1201591, -1853465, 1367120, 25127874, 6671743),
array(29701166, -14373934, -10878120, 9279288, -17568, 13127210, 21382910, 11042292, 25838796, 4642684),
array(-20430234, 14955537, -24126347, 8124619, -5369288, -5990470, 30468147, -13900640, 18423289, 4177476),
),
)
);
/**
* See: libsodium's crypto_core/curve25519/ref10/base2.h
*
* @var array basically int[8][3]
*/
protected static $base2 = array(
array(
array(25967493, -14356035, 29566456, 3660896, -12694345, 4014787, 27544626, -11754271, -6079156, 2047605),
array(-12545711, 934262, -2722910, 3049990, -727428, 9406986, 12720692, 5043384, 19500929, -15469378),
array(-8738181, 4489570, 9688441, -14785194, 10184609, -12363380, 29287919, 11864899, -24514362, -4438546),
),
array(
array(15636291, -9688557, 24204773, -7912398, 616977, -16685262, 27787600, -14772189, 28944400, -1550024),
array(16568933, 4717097, -11556148, -1102322, 15682896, -11807043, 16354577, -11775962, 7689662, 11199574),
array(30464156, -5976125, -11779434, -15670865, 23220365, 15915852, 7512774, 10017326, -17749093, -9920357),
),
array(
array(10861363, 11473154, 27284546, 1981175, -30064349, 12577861, 32867885, 14515107, -15438304, 10819380),
array(4708026, 6336745, 20377586, 9066809, -11272109, 6594696, -25653668, 12483688, -12668491, 5581306),
array(19563160, 16186464, -29386857, 4097519, 10237984, -4348115, 28542350, 13850243, -23678021, -15815942),
),
array(
array(5153746, 9909285, 1723747, -2777874, 30523605, 5516873, 19480852, 5230134, -23952439, -15175766),
array(-30269007, -3463509, 7665486, 10083793, 28475525, 1649722, 20654025, 16520125, 30598449, 7715701),
array(28881845, 14381568, 9657904, 3680757, -20181635, 7843316, -31400660, 1370708, 29794553, -1409300),
),
array(
array(-22518993, -6692182, 14201702, -8745502, -23510406, 8844726, 18474211, -1361450, -13062696, 13821877),
array(-6455177, -7839871, 3374702, -4740862, -27098617, -10571707, 31655028, -7212327, 18853322, -14220951),
array(4566830, -12963868, -28974889, -12240689, -7602672, -2830569, -8514358, -10431137, 2207753, -3209784),
),
array(
array(-25154831, -4185821, 29681144, 7868801, -6854661, -9423865, -12437364, -663000, -31111463, -16132436),
array(25576264, -2703214, 7349804, -11814844, 16472782, 9300885, 3844789, 15725684, 171356, 6466918),
array(23103977, 13316479, 9739013, -16149481, 817875, -15038942, 8965339, -14088058, -30714912, 16193877),
),
array(
array(-33521811, 3180713, -2394130, 14003687, -16903474, -16270840, 17238398, 4729455, -18074513, 9256800),
array(-25182317, -4174131, 32336398, 5036987, -21236817, 11360617, 22616405, 9761698, -19827198, 630305),
array(-13720693, 2639453, -24237460, -7406481, 9494427, -5774029, -6554551, -15960994, -2449256, -14291300),
),
array(
array(-3151181, -5046075, 9282714, 6866145, -31907062, -863023, -18940575, 15033784, 25105118, -7894876),
array(-24326370, 15950226, -31801215, -14592823, -11662737, -5090925, 1573892, -2625887, 2198790, -15804619),
array(-3099351, 10324967, -2241613, 7453183, -5446979, -2735503, -13812022, -16236442, -32461234, -12290683),
)
);
/**
* 37095705934669439343138083508754565189542113879843219016388785533085940283555
*
* @var array<int, int>
*/
protected static $d = array(
-10913610,
13857413,
-15372611,
6949391,
114729,
-8787816,
-6275908,
-3247719,
-18696448,
-12055116
);
/**
* 2 * d = 16295367250680780974490674513165176452449235426866156013048779062215315747161
*
* @var array<int, int>
*/
protected static $d2 = array(
-21827239,
-5839606,
-30745221,
13898782,
229458,
15978800,
-12551817,
-6495438,
29715968,
9444199
);
/**
* sqrt(-1)
*
* @var array<int, int>
*/
protected static $sqrtm1 = array(
-32595792,
-7943725,
9377950,
3500415,
12389472,
-272473,
-25146209,
-2005654,
326686,
11406482
);
/**
* 1 / sqrt(a - d)
*
* @var array<int, int>
*/
protected static $invsqrtamd = array(
6111485,
4156064,
-27798727,
12243468,
-25904040,
120897,
20826367,
-7060776,
6093568,
-1986012
);
/**
* sqrt(ad - 1) with a = -1 (mod p)
*
* @var array<int, int>
*/
protected static $sqrtadm1 = array(
24849947,
-153582,
-23613485,
6347715,
-21072328,
-667138,
-25271143,
-15367704,
-870347,
14525639
);
/**
* 1 - d ^ 2
*
* @var array<int, int>
*/
protected static $onemsqd = array(
6275446,
-16617371,
-22938544,
-3773710,
11667077,
7397348,
-27922721,
1766195,
-24433858,
672203
);
/**
* (d - 1) ^ 2
* @var array<int, int>
*/
protected static $sqdmone = array(
15551795,
-11097455,
-13425098,
-10125071,
-11896535,
10178284,
-26634327,
4729244,
-5282110,
-10116402
);
/*
* 2^252+27742317777372353535851937790883648493
static const unsigned char L[] = {
0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7,
0xa2, 0xde, 0xf9, 0xde, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10
};
*/
const L = "\xed\xd3\xf5\x5c\x1a\x63\x12\x58\xd6\x9c\xf7\xa2\xde\xf9\xde\x14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10";
}
Core/Curve25519/Ge/Cached.php 0000604 00000003345 15133021213 0011331 0 ustar 00 <?php
if (class_exists('ParagonIE_Sodium_Core_Curve25519_Ge_Cached', false)) {
return;
}
/**
* Class ParagonIE_Sodium_Core_Curve25519_Ge_Cached
*/
class ParagonIE_Sodium_Core_Curve25519_Ge_Cached
{
/**
* @var ParagonIE_Sodium_Core_Curve25519_Fe
*/
public $YplusX;
/**
* @var ParagonIE_Sodium_Core_Curve25519_Fe
*/
public $YminusX;
/**
* @var ParagonIE_Sodium_Core_Curve25519_Fe
*/
public $Z;
/**
* @var ParagonIE_Sodium_Core_Curve25519_Fe
*/
public $T2d;
/**
* ParagonIE_Sodium_Core_Curve25519_Ge_Cached constructor.
*
* @internal You should not use this directly from another application
*
* @param ParagonIE_Sodium_Core_Curve25519_Fe|null $YplusX
* @param ParagonIE_Sodium_Core_Curve25519_Fe|null $YminusX
* @param ParagonIE_Sodium_Core_Curve25519_Fe|null $Z
* @param ParagonIE_Sodium_Core_Curve25519_Fe|null $T2d
*/
public function __construct(
ParagonIE_Sodium_Core_Curve25519_Fe $YplusX = null,
ParagonIE_Sodium_Core_Curve25519_Fe $YminusX = null,
ParagonIE_Sodium_Core_Curve25519_Fe $Z = null,
ParagonIE_Sodium_Core_Curve25519_Fe $T2d = null
) {
if ($YplusX === null) {
$YplusX = new ParagonIE_Sodium_Core_Curve25519_Fe();
}
$this->YplusX = $YplusX;
if ($YminusX === null) {
$YminusX = new ParagonIE_Sodium_Core_Curve25519_Fe();
}
$this->YminusX = $YminusX;
if ($Z === null) {
$Z = new ParagonIE_Sodium_Core_Curve25519_Fe();
}
$this->Z = $Z;
if ($T2d === null) {
$T2d = new ParagonIE_Sodium_Core_Curve25519_Fe();
}
$this->T2d = $T2d;
}
}
Core/Curve25519/Ge/Precomp.php 0000604 00000002650 15133021213 0011565 0 ustar 00 <?php
if (class_exists('ParagonIE_Sodium_Core_Curve25519_Ge_Precomp', false)) {
return;
}
/**
* Class ParagonIE_Sodium_Core_Curve25519_Ge_Precomp
*/
class ParagonIE_Sodium_Core_Curve25519_Ge_Precomp
{
/**
* @var ParagonIE_Sodium_Core_Curve25519_Fe
*/
public $yplusx;
/**
* @var ParagonIE_Sodium_Core_Curve25519_Fe
*/
public $yminusx;
/**
* @var ParagonIE_Sodium_Core_Curve25519_Fe
*/
public $xy2d;
/**
* ParagonIE_Sodium_Core_Curve25519_Ge_Precomp constructor.
*
* @internal You should not use this directly from another application
*
* @param ParagonIE_Sodium_Core_Curve25519_Fe $yplusx
* @param ParagonIE_Sodium_Core_Curve25519_Fe $yminusx
* @param ParagonIE_Sodium_Core_Curve25519_Fe $xy2d
*/
public function __construct(
ParagonIE_Sodium_Core_Curve25519_Fe $yplusx = null,
ParagonIE_Sodium_Core_Curve25519_Fe $yminusx = null,
ParagonIE_Sodium_Core_Curve25519_Fe $xy2d = null
) {
if ($yplusx === null) {
$yplusx = new ParagonIE_Sodium_Core_Curve25519_Fe();
}
$this->yplusx = $yplusx;
if ($yminusx === null) {
$yminusx = new ParagonIE_Sodium_Core_Curve25519_Fe();
}
$this->yminusx = $yminusx;
if ($xy2d === null) {
$xy2d = new ParagonIE_Sodium_Core_Curve25519_Fe();
}
$this->xy2d = $xy2d;
}
}
Core/Curve25519/Ge/P1p1.php 0000604 00000003201 15133021213 0010672 0 ustar 00 <?php
if (class_exists('ParagonIE_Sodium_Core_Curve25519_Ge_P1p1', false)) {
return;
}
/**
* Class ParagonIE_Sodium_Core_Curve25519_Ge_P1p1
*/
class ParagonIE_Sodium_Core_Curve25519_Ge_P1p1
{
/**
* @var ParagonIE_Sodium_Core_Curve25519_Fe
*/
public $X;
/**
* @var ParagonIE_Sodium_Core_Curve25519_Fe
*/
public $Y;
/**
* @var ParagonIE_Sodium_Core_Curve25519_Fe
*/
public $Z;
/**
* @var ParagonIE_Sodium_Core_Curve25519_Fe
*/
public $T;
/**
* ParagonIE_Sodium_Core_Curve25519_Ge_P1p1 constructor.
*
* @internal You should not use this directly from another application
*
* @param ParagonIE_Sodium_Core_Curve25519_Fe|null $x
* @param ParagonIE_Sodium_Core_Curve25519_Fe|null $y
* @param ParagonIE_Sodium_Core_Curve25519_Fe|null $z
* @param ParagonIE_Sodium_Core_Curve25519_Fe|null $t
*/
public function __construct(
ParagonIE_Sodium_Core_Curve25519_Fe $x = null,
ParagonIE_Sodium_Core_Curve25519_Fe $y = null,
ParagonIE_Sodium_Core_Curve25519_Fe $z = null,
ParagonIE_Sodium_Core_Curve25519_Fe $t = null
) {
if ($x === null) {
$x = new ParagonIE_Sodium_Core_Curve25519_Fe();
}
$this->X = $x;
if ($y === null) {
$y = new ParagonIE_Sodium_Core_Curve25519_Fe();
}
$this->Y = $y;
if ($z === null) {
$z = new ParagonIE_Sodium_Core_Curve25519_Fe();
}
$this->Z = $z;
if ($t === null) {
$t = new ParagonIE_Sodium_Core_Curve25519_Fe();
}
$this->T = $t;
}
}
Core/Curve25519/Ge/P3.php 0000604 00000003172 15133021213 0010442 0 ustar 00 <?php
if (class_exists('ParagonIE_Sodium_Core_Curve25519_Ge_P3', false)) {
return;
}
/**
* Class ParagonIE_Sodium_Core_Curve25519_Ge_P3
*/
class ParagonIE_Sodium_Core_Curve25519_Ge_P3
{
/**
* @var ParagonIE_Sodium_Core_Curve25519_Fe
*/
public $X;
/**
* @var ParagonIE_Sodium_Core_Curve25519_Fe
*/
public $Y;
/**
* @var ParagonIE_Sodium_Core_Curve25519_Fe
*/
public $Z;
/**
* @var ParagonIE_Sodium_Core_Curve25519_Fe
*/
public $T;
/**
* ParagonIE_Sodium_Core_Curve25519_Ge_P3 constructor.
*
* @internal You should not use this directly from another application
*
* @param ParagonIE_Sodium_Core_Curve25519_Fe|null $x
* @param ParagonIE_Sodium_Core_Curve25519_Fe|null $y
* @param ParagonIE_Sodium_Core_Curve25519_Fe|null $z
* @param ParagonIE_Sodium_Core_Curve25519_Fe|null $t
*/
public function __construct(
ParagonIE_Sodium_Core_Curve25519_Fe $x = null,
ParagonIE_Sodium_Core_Curve25519_Fe $y = null,
ParagonIE_Sodium_Core_Curve25519_Fe $z = null,
ParagonIE_Sodium_Core_Curve25519_Fe $t = null
) {
if ($x === null) {
$x = new ParagonIE_Sodium_Core_Curve25519_Fe();
}
$this->X = $x;
if ($y === null) {
$y = new ParagonIE_Sodium_Core_Curve25519_Fe();
}
$this->Y = $y;
if ($z === null) {
$z = new ParagonIE_Sodium_Core_Curve25519_Fe();
}
$this->Z = $z;
if ($t === null) {
$t = new ParagonIE_Sodium_Core_Curve25519_Fe();
}
$this->T = $t;
}
}
Core/Curve25519/Ge/P2.php 0000604 00000002501 15133021213 0010434 0 ustar 00 <?php
if (class_exists('ParagonIE_Sodium_Core_Curve25519_Ge_P2', false)) {
return;
}
/**
* Class ParagonIE_Sodium_Core_Curve25519_Ge_P2
*/
class ParagonIE_Sodium_Core_Curve25519_Ge_P2
{
/**
* @var ParagonIE_Sodium_Core_Curve25519_Fe
*/
public $X;
/**
* @var ParagonIE_Sodium_Core_Curve25519_Fe
*/
public $Y;
/**
* @var ParagonIE_Sodium_Core_Curve25519_Fe
*/
public $Z;
/**
* ParagonIE_Sodium_Core_Curve25519_Ge_P2 constructor.
*
* @internal You should not use this directly from another application
*
* @param ParagonIE_Sodium_Core_Curve25519_Fe|null $x
* @param ParagonIE_Sodium_Core_Curve25519_Fe|null $y
* @param ParagonIE_Sodium_Core_Curve25519_Fe|null $z
*/
public function __construct(
ParagonIE_Sodium_Core_Curve25519_Fe $x = null,
ParagonIE_Sodium_Core_Curve25519_Fe $y = null,
ParagonIE_Sodium_Core_Curve25519_Fe $z = null
) {
if ($x === null) {
$x = new ParagonIE_Sodium_Core_Curve25519_Fe();
}
$this->X = $x;
if ($y === null) {
$y = new ParagonIE_Sodium_Core_Curve25519_Fe();
}
$this->Y = $y;
if ($z === null) {
$z = new ParagonIE_Sodium_Core_Curve25519_Fe();
}
$this->Z = $z;
}
}
Core/AEGIS/State128L.php 0000644 00000020052 15133021213 0010412 0 ustar 00 <?php
if (class_exists('ParagonIE_Sodium_Core_AEGIS_State128L', false)) {
return;
}
if (!defined('SODIUM_COMPAT_AEGIS_C0')) {
define('SODIUM_COMPAT_AEGIS_C0', "\x00\x01\x01\x02\x03\x05\x08\x0d\x15\x22\x37\x59\x90\xe9\x79\x62");
}
if (!defined('SODIUM_COMPAT_AEGIS_C1')) {
define('SODIUM_COMPAT_AEGIS_C1', "\xdb\x3d\x18\x55\x6d\xc2\x2f\xf1\x20\x11\x31\x42\x73\xb5\x28\xdd");
}
class ParagonIE_Sodium_Core_AEGIS_State128L
{
/** @var array<int, string> $state */
protected $state;
public function __construct()
{
$this->state = array_fill(0, 8, '');
}
/**
* @internal Only use this for unit tests!
* @return string[]
*/
public function getState()
{
return array_values($this->state);
}
/**
* @param array $input
* @return self
* @throws SodiumException
*
* @internal Only for unit tests
*/
public static function initForUnitTests(array $input)
{
if (count($input) < 8) {
throw new SodiumException('invalid input');
}
$state = new self();
for ($i = 0; $i < 8; ++$i) {
$state->state[$i] = $input[$i];
}
return $state;
}
/**
* @param string $key
* @param string $nonce
* @return self
*/
public static function init($key, $nonce)
{
$state = new self();
// S0 = key ^ nonce
$state->state[0] = $key ^ $nonce;
// S1 = C1
$state->state[1] = SODIUM_COMPAT_AEGIS_C1;
// S2 = C0
$state->state[2] = SODIUM_COMPAT_AEGIS_C0;
// S3 = C1
$state->state[3] = SODIUM_COMPAT_AEGIS_C1;
// S4 = key ^ nonce
$state->state[4] = $key ^ $nonce;
// S5 = key ^ C0
$state->state[5] = $key ^ SODIUM_COMPAT_AEGIS_C0;
// S6 = key ^ C1
$state->state[6] = $key ^ SODIUM_COMPAT_AEGIS_C1;
// S7 = key ^ C0
$state->state[7] = $key ^ SODIUM_COMPAT_AEGIS_C0;
// Repeat(10, Update(nonce, key))
for ($i = 0; $i < 10; ++$i) {
$state->update($nonce, $key);
}
return $state;
}
/**
* @param string $ai
* @return self
*/
public function absorb($ai)
{
if (ParagonIE_Sodium_Core_Util::strlen($ai) !== 32) {
throw new SodiumException('Input must be two AES blocks in size');
}
$t0 = ParagonIE_Sodium_Core_Util::substr($ai, 0, 16);
$t1 = ParagonIE_Sodium_Core_Util::substr($ai, 16, 16);
return $this->update($t0, $t1);
}
/**
* @param string $ci
* @return string
* @throws SodiumException
*/
public function dec($ci)
{
if (ParagonIE_Sodium_Core_Util::strlen($ci) !== 32) {
throw new SodiumException('Input must be two AES blocks in size');
}
// z0 = S6 ^ S1 ^ (S2 & S3)
$z0 = $this->state[6]
^ $this->state[1]
^ ParagonIE_Sodium_Core_Util::andStrings($this->state[2], $this->state[3]);
// z1 = S2 ^ S5 ^ (S6 & S7)
$z1 = $this->state[2]
^ $this->state[5]
^ ParagonIE_Sodium_Core_Util::andStrings($this->state[6], $this->state[7]);
// t0, t1 = Split(xi, 128)
$t0 = ParagonIE_Sodium_Core_Util::substr($ci, 0, 16);
$t1 = ParagonIE_Sodium_Core_Util::substr($ci, 16, 16);
// out0 = t0 ^ z0
// out1 = t1 ^ z1
$out0 = $t0 ^ $z0;
$out1 = $t1 ^ $z1;
// Update(out0, out1)
// xi = out0 || out1
$this->update($out0, $out1);
return $out0 . $out1;
}
/**
* @param string $cn
* @return string
*/
public function decPartial($cn)
{
$len = ParagonIE_Sodium_Core_Util::strlen($cn);
// z0 = S6 ^ S1 ^ (S2 & S3)
$z0 = $this->state[6]
^ $this->state[1]
^ ParagonIE_Sodium_Core_Util::andStrings($this->state[2], $this->state[3]);
// z1 = S2 ^ S5 ^ (S6 & S7)
$z1 = $this->state[2]
^ $this->state[5]
^ ParagonIE_Sodium_Core_Util::andStrings($this->state[6], $this->state[7]);
// t0, t1 = Split(ZeroPad(cn, 256), 128)
$cn = str_pad($cn, 32, "\0", STR_PAD_RIGHT);
$t0 = ParagonIE_Sodium_Core_Util::substr($cn, 0, 16);
$t1 = ParagonIE_Sodium_Core_Util::substr($cn, 16, 16);
// out0 = t0 ^ z0
// out1 = t1 ^ z1
$out0 = $t0 ^ $z0;
$out1 = $t1 ^ $z1;
// xn = Truncate(out0 || out1, |cn|)
$xn = ParagonIE_Sodium_Core_Util::substr($out0 . $out1, 0, $len);
// v0, v1 = Split(ZeroPad(xn, 256), 128)
$padded = str_pad($xn, 32, "\0", STR_PAD_RIGHT);
$v0 = ParagonIE_Sodium_Core_Util::substr($padded, 0, 16);
$v1 = ParagonIE_Sodium_Core_Util::substr($padded, 16, 16);
// Update(v0, v1)
$this->update($v0, $v1);
// return xn
return $xn;
}
/**
* @param string $xi
* @return string
* @throws SodiumException
*/
public function enc($xi)
{
if (ParagonIE_Sodium_Core_Util::strlen($xi) !== 32) {
throw new SodiumException('Input must be two AES blocks in size');
}
// z0 = S6 ^ S1 ^ (S2 & S3)
$z0 = $this->state[6]
^ $this->state[1]
^ ParagonIE_Sodium_Core_Util::andStrings($this->state[2], $this->state[3]);
// z1 = S2 ^ S5 ^ (S6 & S7)
$z1 = $this->state[2]
^ $this->state[5]
^ ParagonIE_Sodium_Core_Util::andStrings($this->state[6], $this->state[7]);
// t0, t1 = Split(xi, 128)
$t0 = ParagonIE_Sodium_Core_Util::substr($xi, 0, 16);
$t1 = ParagonIE_Sodium_Core_Util::substr($xi, 16, 16);
// out0 = t0 ^ z0
// out1 = t1 ^ z1
$out0 = $t0 ^ $z0;
$out1 = $t1 ^ $z1;
// Update(t0, t1)
// ci = out0 || out1
$this->update($t0, $t1);
// return ci
return $out0 . $out1;
}
/**
* @param int $ad_len_bits
* @param int $msg_len_bits
* @return string
*/
public function finalize($ad_len_bits, $msg_len_bits)
{
$encoded = ParagonIE_Sodium_Core_Util::store64_le($ad_len_bits) .
ParagonIE_Sodium_Core_Util::store64_le($msg_len_bits);
$t = $this->state[2] ^ $encoded;
for ($i = 0; $i < 7; ++$i) {
$this->update($t, $t);
}
return ($this->state[0] ^ $this->state[1] ^ $this->state[2] ^ $this->state[3]) .
($this->state[4] ^ $this->state[5] ^ $this->state[6] ^ $this->state[7]);
}
/**
* @param string $m0
* @param string $m1
* @return self
*/
public function update($m0, $m1)
{
/*
S'0 = AESRound(S7, S0 ^ M0)
S'1 = AESRound(S0, S1)
S'2 = AESRound(S1, S2)
S'3 = AESRound(S2, S3)
S'4 = AESRound(S3, S4 ^ M1)
S'5 = AESRound(S4, S5)
S'6 = AESRound(S5, S6)
S'7 = AESRound(S6, S7)
*/
list($s_0, $s_1) = ParagonIE_Sodium_Core_AES::doubleRound(
$this->state[7], $this->state[0] ^ $m0,
$this->state[0], $this->state[1]
);
list($s_2, $s_3) = ParagonIE_Sodium_Core_AES::doubleRound(
$this->state[1], $this->state[2],
$this->state[2], $this->state[3]
);
list($s_4, $s_5) = ParagonIE_Sodium_Core_AES::doubleRound(
$this->state[3], $this->state[4] ^ $m1,
$this->state[4], $this->state[5]
);
list($s_6, $s_7) = ParagonIE_Sodium_Core_AES::doubleRound(
$this->state[5], $this->state[6],
$this->state[6], $this->state[7]
);
/*
S0 = S'0
S1 = S'1
S2 = S'2
S3 = S'3
S4 = S'4
S5 = S'5
S6 = S'6
S7 = S'7
*/
$this->state[0] = $s_0;
$this->state[1] = $s_1;
$this->state[2] = $s_2;
$this->state[3] = $s_3;
$this->state[4] = $s_4;
$this->state[5] = $s_5;
$this->state[6] = $s_6;
$this->state[7] = $s_7;
return $this;
}
} Core/AEGIS/State256.php 0000644 00000014575 15133021213 0010315 0 ustar 00 <?php
if (class_exists('ParagonIE_Sodium_Core_AEGIS_State256', false)) {
return;
}
if (!defined('SODIUM_COMPAT_AEGIS_C0')) {
define('SODIUM_COMPAT_AEGIS_C0', "\x00\x01\x01\x02\x03\x05\x08\x0d\x15\x22\x37\x59\x90\xe9\x79\x62");
}
if (!defined('SODIUM_COMPAT_AEGIS_C1')) {
define('SODIUM_COMPAT_AEGIS_C1', "\xdb\x3d\x18\x55\x6d\xc2\x2f\xf1\x20\x11\x31\x42\x73\xb5\x28\xdd");
}
class ParagonIE_Sodium_Core_AEGIS_State256
{
/** @var array<int, string> $state */
protected $state;
public function __construct()
{
$this->state = array_fill(0, 6, '');
}
/**
* @internal Only use this for unit tests!
* @return string[]
*/
public function getState()
{
return array_values($this->state);
}
/**
* @param array $input
* @return self
* @throws SodiumException
*
* @internal Only for unit tests
*/
public static function initForUnitTests(array $input)
{
if (count($input) < 6) {
throw new SodiumException('invalid input');
}
$state = new self();
for ($i = 0; $i < 6; ++$i) {
$state->state[$i] = $input[$i];
}
return $state;
}
/**
* @param string $key
* @param string $nonce
* @return self
*/
public static function init($key, $nonce)
{
$state = new self();
$k0 = ParagonIE_Sodium_Core_Util::substr($key, 0, 16);
$k1 = ParagonIE_Sodium_Core_Util::substr($key, 16, 16);
$n0 = ParagonIE_Sodium_Core_Util::substr($nonce, 0, 16);
$n1 = ParagonIE_Sodium_Core_Util::substr($nonce, 16, 16);
// S0 = k0 ^ n0
// S1 = k1 ^ n1
// S2 = C1
// S3 = C0
// S4 = k0 ^ C0
// S5 = k1 ^ C1
$k0_n0 = $k0 ^ $n0;
$k1_n1 = $k1 ^ $n1;
$state->state[0] = $k0_n0;
$state->state[1] = $k1_n1;
$state->state[2] = SODIUM_COMPAT_AEGIS_C1;
$state->state[3] = SODIUM_COMPAT_AEGIS_C0;
$state->state[4] = $k0 ^ SODIUM_COMPAT_AEGIS_C0;
$state->state[5] = $k1 ^ SODIUM_COMPAT_AEGIS_C1;
// Repeat(4,
// Update(k0)
// Update(k1)
// Update(k0 ^ n0)
// Update(k1 ^ n1)
// )
for ($i = 0; $i < 4; ++$i) {
$state->update($k0);
$state->update($k1);
$state->update($k0 ^ $n0);
$state->update($k1 ^ $n1);
}
return $state;
}
/**
* @param string $ai
* @return self
* @throws SodiumException
*/
public function absorb($ai)
{
if (ParagonIE_Sodium_Core_Util::strlen($ai) !== 16) {
throw new SodiumException('Input must be an AES block in size');
}
return $this->update($ai);
}
/**
* @param string $ci
* @return string
* @throws SodiumException
*/
public function dec($ci)
{
if (ParagonIE_Sodium_Core_Util::strlen($ci) !== 16) {
throw new SodiumException('Input must be an AES block in size');
}
// z = S1 ^ S4 ^ S5 ^ (S2 & S3)
$z = $this->state[1]
^ $this->state[4]
^ $this->state[5]
^ ParagonIE_Sodium_Core_Util::andStrings($this->state[2], $this->state[3]);
$xi = $ci ^ $z;
$this->update($xi);
return $xi;
}
/**
* @param string $cn
* @return string
*/
public function decPartial($cn)
{
$len = ParagonIE_Sodium_Core_Util::strlen($cn);
// z = S1 ^ S4 ^ S5 ^ (S2 & S3)
$z = $this->state[1]
^ $this->state[4]
^ $this->state[5]
^ ParagonIE_Sodium_Core_Util::andStrings($this->state[2], $this->state[3]);
// t = ZeroPad(cn, 128)
$t = str_pad($cn, 16, "\0", STR_PAD_RIGHT);
// out = t ^ z
$out = $t ^ $z;
// xn = Truncate(out, |cn|)
$xn = ParagonIE_Sodium_Core_Util::substr($out, 0, $len);
// v = ZeroPad(xn, 128)
$v = str_pad($xn, 16, "\0", STR_PAD_RIGHT);
// Update(v)
$this->update($v);
// return xn
return $xn;
}
/**
* @param string $xi
* @return string
* @throws SodiumException
*/
public function enc($xi)
{
if (ParagonIE_Sodium_Core_Util::strlen($xi) !== 16) {
throw new SodiumException('Input must be an AES block in size');
}
// z = S1 ^ S4 ^ S5 ^ (S2 & S3)
$z = $this->state[1]
^ $this->state[4]
^ $this->state[5]
^ ParagonIE_Sodium_Core_Util::andStrings($this->state[2], $this->state[3]);
$this->update($xi);
return $xi ^ $z;
}
/**
* @param int $ad_len_bits
* @param int $msg_len_bits
* @return string
*/
public function finalize($ad_len_bits, $msg_len_bits)
{
$encoded = ParagonIE_Sodium_Core_Util::store64_le($ad_len_bits) .
ParagonIE_Sodium_Core_Util::store64_le($msg_len_bits);
$t = $this->state[3] ^ $encoded;
for ($i = 0; $i < 7; ++$i) {
$this->update($t);
}
return ($this->state[0] ^ $this->state[1] ^ $this->state[2]) .
($this->state[3] ^ $this->state[4] ^ $this->state[5]);
}
/**
* @param string $m
* @return self
*/
public function update($m)
{
/*
S'0 = AESRound(S5, S0 ^ M)
S'1 = AESRound(S0, S1)
S'2 = AESRound(S1, S2)
S'3 = AESRound(S2, S3)
S'4 = AESRound(S3, S4)
S'5 = AESRound(S4, S5)
*/
list($s_0, $s_1) = ParagonIE_Sodium_Core_AES::doubleRound(
$this->state[5],$this->state[0] ^ $m,
$this->state[0], $this->state[1]
);
list($s_2, $s_3) = ParagonIE_Sodium_Core_AES::doubleRound(
$this->state[1], $this->state[2],
$this->state[2], $this->state[3]
);
list($s_4, $s_5) = ParagonIE_Sodium_Core_AES::doubleRound(
$this->state[3], $this->state[4],
$this->state[4], $this->state[5]
);
/*
S0 = S'0
S1 = S'1
S2 = S'2
S3 = S'3
S4 = S'4
S5 = S'5
*/
$this->state[0] = $s_0;
$this->state[1] = $s_1;
$this->state[2] = $s_2;
$this->state[3] = $s_3;
$this->state[4] = $s_4;
$this->state[5] = $s_5;
return $this;
}
}
Core/Util.php 0000644 00000067150 15133021213 0007062 0 ustar 00 <?php
if (class_exists('ParagonIE_Sodium_Core_Util', false)) {
return;
}
/**
* Class ParagonIE_Sodium_Core_Util
*/
abstract class ParagonIE_Sodium_Core_Util
{
/**
* @param int $integer
* @param int $size (16, 32, 64)
* @return int
*/
public static function abs($integer, $size = 0)
{
/** @var int $realSize */
$realSize = (PHP_INT_SIZE << 3) - 1;
if ($size) {
--$size;
} else {
/** @var int $size */
$size = $realSize;
}
$negative = -(($integer >> $size) & 1);
return (int) (
($integer ^ $negative)
+
(($negative >> $realSize) & 1)
);
}
/**
* Convert a binary string into a hexadecimal string without cache-timing
* leaks
*
* @internal You should not use this directly from another application
*
* @param string $binaryString (raw binary)
* @return string
* @throws TypeError
*/
public static function bin2hex($binaryString)
{
/* Type checks: */
if (!is_string($binaryString)) {
throw new TypeError('Argument 1 must be a string, ' . gettype($binaryString) . ' given.');
}
$hex = '';
$len = self::strlen($binaryString);
for ($i = 0; $i < $len; ++$i) {
/** @var array<int, int> $chunk */
$chunk = unpack('C', $binaryString[$i]);
/** @var int $c */
$c = $chunk[1] & 0xf;
/** @var int $b */
$b = $chunk[1] >> 4;
$hex .= pack(
'CC',
(87 + $b + ((($b - 10) >> 8) & ~38)),
(87 + $c + ((($c - 10) >> 8) & ~38))
);
}
return $hex;
}
/**
* Convert a binary string into a hexadecimal string without cache-timing
* leaks, returning uppercase letters (as per RFC 4648)
*
* @internal You should not use this directly from another application
*
* @param string $bin_string (raw binary)
* @return string
* @throws TypeError
*/
public static function bin2hexUpper($bin_string)
{
$hex = '';
$len = self::strlen($bin_string);
for ($i = 0; $i < $len; ++$i) {
/** @var array<int, int> $chunk */
$chunk = unpack('C', $bin_string[$i]);
/**
* Lower 16 bits
*
* @var int $c
*/
$c = $chunk[1] & 0xf;
/**
* Upper 16 bits
* @var int $b
*/
$b = $chunk[1] >> 4;
/**
* Use pack() and binary operators to turn the two integers
* into hexadecimal characters. We don't use chr() here, because
* it uses a lookup table internally and we want to avoid
* cache-timing side-channels.
*/
$hex .= pack(
'CC',
(55 + $b + ((($b - 10) >> 8) & ~6)),
(55 + $c + ((($c - 10) >> 8) & ~6))
);
}
return $hex;
}
/**
* Cache-timing-safe variant of ord()
*
* @internal You should not use this directly from another application
*
* @param string $chr
* @return int
* @throws SodiumException
* @throws TypeError
*/
public static function chrToInt($chr)
{
/* Type checks: */
if (!is_string($chr)) {
throw new TypeError('Argument 1 must be a string, ' . gettype($chr) . ' given.');
}
if (self::strlen($chr) !== 1) {
throw new SodiumException('chrToInt() expects a string that is exactly 1 character long');
}
/** @var array<int, int> $chunk */
$chunk = unpack('C', $chr);
return (int) ($chunk[1]);
}
/**
* Compares two strings.
*
* @internal You should not use this directly from another application
*
* @param string $left
* @param string $right
* @param int $len
* @return int
* @throws SodiumException
* @throws TypeError
*/
public static function compare($left, $right, $len = null)
{
$leftLen = self::strlen($left);
$rightLen = self::strlen($right);
if ($len === null) {
$len = max($leftLen, $rightLen);
$left = str_pad($left, $len, "\x00", STR_PAD_RIGHT);
$right = str_pad($right, $len, "\x00", STR_PAD_RIGHT);
}
$gt = 0;
$eq = 1;
$i = $len;
while ($i !== 0) {
--$i;
$gt |= ((self::chrToInt($right[$i]) - self::chrToInt($left[$i])) >> 8) & $eq;
$eq &= ((self::chrToInt($right[$i]) ^ self::chrToInt($left[$i])) - 1) >> 8;
}
return ($gt + $gt + $eq) - 1;
}
/**
* If a variable does not match a given type, throw a TypeError.
*
* @param mixed $mixedVar
* @param string $type
* @param int $argumentIndex
* @throws TypeError
* @throws SodiumException
* @return void
*/
public static function declareScalarType(&$mixedVar = null, $type = 'void', $argumentIndex = 0)
{
if (func_num_args() === 0) {
/* Tautology, by default */
return;
}
if (func_num_args() === 1) {
throw new TypeError('Declared void, but passed a variable');
}
$realType = strtolower(gettype($mixedVar));
$type = strtolower($type);
switch ($type) {
case 'null':
if ($mixedVar !== null) {
throw new TypeError('Argument ' . $argumentIndex . ' must be null, ' . $realType . ' given.');
}
break;
case 'integer':
case 'int':
$allow = array('int', 'integer');
if (!in_array($type, $allow)) {
throw new TypeError('Argument ' . $argumentIndex . ' must be an integer, ' . $realType . ' given.');
}
$mixedVar = (int) $mixedVar;
break;
case 'boolean':
case 'bool':
$allow = array('bool', 'boolean');
if (!in_array($type, $allow)) {
throw new TypeError('Argument ' . $argumentIndex . ' must be a boolean, ' . $realType . ' given.');
}
$mixedVar = (bool) $mixedVar;
break;
case 'string':
if (!is_string($mixedVar)) {
throw new TypeError('Argument ' . $argumentIndex . ' must be a string, ' . $realType . ' given.');
}
$mixedVar = (string) $mixedVar;
break;
case 'decimal':
case 'double':
case 'float':
$allow = array('decimal', 'double', 'float');
if (!in_array($type, $allow)) {
throw new TypeError('Argument ' . $argumentIndex . ' must be a float, ' . $realType . ' given.');
}
$mixedVar = (float) $mixedVar;
break;
case 'object':
if (!is_object($mixedVar)) {
throw new TypeError('Argument ' . $argumentIndex . ' must be an object, ' . $realType . ' given.');
}
break;
case 'array':
if (!is_array($mixedVar)) {
if (is_object($mixedVar)) {
if ($mixedVar instanceof ArrayAccess) {
return;
}
}
throw new TypeError('Argument ' . $argumentIndex . ' must be an array, ' . $realType . ' given.');
}
break;
default:
throw new SodiumException('Unknown type (' . $realType .') does not match expect type (' . $type . ')');
}
}
/**
* Evaluate whether or not two strings are equal (in constant-time)
*
* @param string $left
* @param string $right
* @return bool
* @throws SodiumException
* @throws TypeError
*/
public static function hashEquals($left, $right)
{
/* Type checks: */
if (!is_string($left)) {
throw new TypeError('Argument 1 must be a string, ' . gettype($left) . ' given.');
}
if (!is_string($right)) {
throw new TypeError('Argument 2 must be a string, ' . gettype($right) . ' given.');
}
if (is_callable('hash_equals')) {
return hash_equals($left, $right);
}
$d = 0;
/** @var int $len */
$len = self::strlen($left);
if ($len !== self::strlen($right)) {
return false;
}
for ($i = 0; $i < $len; ++$i) {
$d |= self::chrToInt($left[$i]) ^ self::chrToInt($right[$i]);
}
if ($d !== 0) {
return false;
}
return $left === $right;
}
/**
* Catch hash_update() failures and throw instead of silently proceeding
*
* @param HashContext|resource &$hs
* @param string $data
* @return void
* @throws SodiumException
* @psalm-suppress PossiblyInvalidArgument
*/
protected static function hash_update(&$hs, $data)
{
if (!hash_update($hs, $data)) {
throw new SodiumException('hash_update() failed');
}
}
/**
* Convert a hexadecimal string into a binary string without cache-timing
* leaks
*
* @internal You should not use this directly from another application
*
* @param string $hexString
* @param string $ignore
* @param bool $strictPadding
* @return string (raw binary)
* @throws RangeException
* @throws TypeError
*/
public static function hex2bin($hexString, $ignore = '', $strictPadding = false)
{
/* Type checks: */
if (!is_string($hexString)) {
throw new TypeError('Argument 1 must be a string, ' . gettype($hexString) . ' given.');
}
if (!is_string($ignore)) {
throw new TypeError('Argument 2 must be a string, ' . gettype($hexString) . ' given.');
}
$hex_pos = 0;
$bin = '';
$c_acc = 0;
$hex_len = self::strlen($hexString);
$state = 0;
if (($hex_len & 1) !== 0) {
if ($strictPadding) {
throw new RangeException(
'Expected an even number of hexadecimal characters'
);
} else {
$hexString = '0' . $hexString;
++$hex_len;
}
}
$chunk = unpack('C*', $hexString);
while ($hex_pos < $hex_len) {
++$hex_pos;
/** @var int $c */
$c = $chunk[$hex_pos];
$c_num = $c ^ 48;
$c_num0 = ($c_num - 10) >> 8;
$c_alpha = ($c & ~32) - 55;
$c_alpha0 = (($c_alpha - 10) ^ ($c_alpha - 16)) >> 8;
if (($c_num0 | $c_alpha0) === 0) {
if ($ignore && $state === 0 && strpos($ignore, self::intToChr($c)) !== false) {
continue;
}
throw new RangeException(
'hex2bin() only expects hexadecimal characters'
);
}
$c_val = ($c_num0 & $c_num) | ($c_alpha & $c_alpha0);
if ($state === 0) {
$c_acc = $c_val * 16;
} else {
$bin .= pack('C', $c_acc | $c_val);
}
$state ^= 1;
}
return $bin;
}
/**
* Turn an array of integers into a string
*
* @internal You should not use this directly from another application
*
* @param array<int, int> $ints
* @return string
*/
public static function intArrayToString(array $ints)
{
$args = $ints;
foreach ($args as $i => $v) {
$args[$i] = (int) ($v & 0xff);
}
array_unshift($args, str_repeat('C', count($ints)));
return (string) (call_user_func_array('pack', $args));
}
/**
* Cache-timing-safe variant of ord()
*
* @internal You should not use this directly from another application
*
* @param int $int
* @return string
* @throws TypeError
*/
public static function intToChr($int)
{
return pack('C', $int);
}
/**
* Load a 3 character substring into an integer
*
* @internal You should not use this directly from another application
*
* @param string $string
* @return int
* @throws RangeException
* @throws TypeError
*/
public static function load_3($string)
{
/* Type checks: */
if (!is_string($string)) {
throw new TypeError('Argument 1 must be a string, ' . gettype($string) . ' given.');
}
/* Input validation: */
if (self::strlen($string) < 3) {
throw new RangeException(
'String must be 3 bytes or more; ' . self::strlen($string) . ' given.'
);
}
/** @var array<int, int> $unpacked */
$unpacked = unpack('V', $string . "\0");
return (int) ($unpacked[1] & 0xffffff);
}
/**
* Load a 4 character substring into an integer
*
* @internal You should not use this directly from another application
*
* @param string $string
* @return int
* @throws RangeException
* @throws TypeError
*/
public static function load_4($string)
{
/* Type checks: */
if (!is_string($string)) {
throw new TypeError('Argument 1 must be a string, ' . gettype($string) . ' given.');
}
/* Input validation: */
if (self::strlen($string) < 4) {
throw new RangeException(
'String must be 4 bytes or more; ' . self::strlen($string) . ' given.'
);
}
/** @var array<int, int> $unpacked */
$unpacked = unpack('V', $string);
return (int) $unpacked[1];
}
/**
* Load a 8 character substring into an integer
*
* @internal You should not use this directly from another application
*
* @param string $string
* @return int
* @throws RangeException
* @throws SodiumException
* @throws TypeError
*/
public static function load64_le($string)
{
/* Type checks: */
if (!is_string($string)) {
throw new TypeError('Argument 1 must be a string, ' . gettype($string) . ' given.');
}
/* Input validation: */
if (self::strlen($string) < 4) {
throw new RangeException(
'String must be 4 bytes or more; ' . self::strlen($string) . ' given.'
);
}
if (PHP_VERSION_ID >= 50603 && PHP_INT_SIZE === 8) {
/** @var array<int, int> $unpacked */
$unpacked = unpack('P', $string);
return (int) $unpacked[1];
}
/** @var int $result */
$result = (self::chrToInt($string[0]) & 0xff);
$result |= (self::chrToInt($string[1]) & 0xff) << 8;
$result |= (self::chrToInt($string[2]) & 0xff) << 16;
$result |= (self::chrToInt($string[3]) & 0xff) << 24;
$result |= (self::chrToInt($string[4]) & 0xff) << 32;
$result |= (self::chrToInt($string[5]) & 0xff) << 40;
$result |= (self::chrToInt($string[6]) & 0xff) << 48;
$result |= (self::chrToInt($string[7]) & 0xff) << 56;
return (int) $result;
}
/**
* @internal You should not use this directly from another application
*
* @param string $left
* @param string $right
* @return int
* @throws SodiumException
* @throws TypeError
*/
public static function memcmp($left, $right)
{
if (self::hashEquals($left, $right)) {
return 0;
}
return -1;
}
/**
* Multiply two integers in constant-time
*
* Micro-architecture timing side-channels caused by how your CPU
* implements multiplication are best prevented by never using the
* multiplication operators and ensuring that our code always takes
* the same number of operations to complete, regardless of the values
* of $a and $b.
*
* @internal You should not use this directly from another application
*
* @param int $a
* @param int $b
* @param int $size Limits the number of operations (useful for small,
* constant operands)
* @return int
*/
public static function mul($a, $b, $size = 0)
{
if (ParagonIE_Sodium_Compat::$fastMult) {
return (int) ($a * $b);
}
static $defaultSize = null;
/** @var int $defaultSize */
if (!$defaultSize) {
/** @var int $defaultSize */
$defaultSize = (PHP_INT_SIZE << 3) - 1;
}
if ($size < 1) {
/** @var int $size */
$size = $defaultSize;
}
/** @var int $size */
$c = 0;
/**
* Mask is either -1 or 0.
*
* -1 in binary looks like 0x1111 ... 1111
* 0 in binary looks like 0x0000 ... 0000
*
* @var int
*/
$mask = -(($b >> ((int) $defaultSize)) & 1);
/**
* Ensure $b is a positive integer, without creating
* a branching side-channel
*
* @var int $b
*/
$b = ($b & ~$mask) | ($mask & -$b);
/**
* Unless $size is provided:
*
* This loop always runs 32 times when PHP_INT_SIZE is 4.
* This loop always runs 64 times when PHP_INT_SIZE is 8.
*/
for ($i = $size; $i >= 0; --$i) {
$c += (int) ($a & -($b & 1));
$a <<= 1;
$b >>= 1;
}
$c = (int) @($c & -1);
/**
* If $b was negative, we then apply the same value to $c here.
* It doesn't matter much if $a was negative; the $c += above would
* have produced a negative integer to begin with. But a negative $b
* makes $b >>= 1 never return 0, so we would end up with incorrect
* results.
*
* The end result is what we'd expect from integer multiplication.
*/
return (int) (($c & ~$mask) | ($mask & -$c));
}
/**
* Convert any arbitrary numbers into two 32-bit integers that represent
* a 64-bit integer.
*
* @internal You should not use this directly from another application
*
* @param int|float $num
* @return array<int, int>
*/
public static function numericTo64BitInteger($num)
{
$high = 0;
/** @var int $low */
if (PHP_INT_SIZE === 4) {
$low = (int) $num;
} else {
$low = $num & 0xffffffff;
}
if ((+(abs($num))) >= 1) {
if ($num > 0) {
/** @var int $high */
$high = min((+(floor($num/4294967296))), 4294967295);
} else {
/** @var int $high */
$high = ~~((+(ceil(($num - (+((~~($num)))))/4294967296))));
}
}
return array((int) $high, (int) $low);
}
/**
* Store a 24-bit integer into a string, treating it as big-endian.
*
* @internal You should not use this directly from another application
*
* @param int $int
* @return string
* @throws TypeError
*/
public static function store_3($int)
{
/* Type checks: */
if (!is_int($int)) {
if (is_numeric($int)) {
$int = (int) $int;
} else {
throw new TypeError('Argument 1 must be an integer, ' . gettype($int) . ' given.');
}
}
/** @var string $packed */
$packed = pack('N', $int);
return self::substr($packed, 1, 3);
}
/**
* Store a 32-bit integer into a string, treating it as little-endian.
*
* @internal You should not use this directly from another application
*
* @param int $int
* @return string
* @throws TypeError
*/
public static function store32_le($int)
{
/* Type checks: */
if (!is_int($int)) {
if (is_numeric($int)) {
$int = (int) $int;
} else {
throw new TypeError('Argument 1 must be an integer, ' . gettype($int) . ' given.');
}
}
/** @var string $packed */
$packed = pack('V', $int);
return $packed;
}
/**
* Store a 32-bit integer into a string, treating it as big-endian.
*
* @internal You should not use this directly from another application
*
* @param int $int
* @return string
* @throws TypeError
*/
public static function store_4($int)
{
/* Type checks: */
if (!is_int($int)) {
if (is_numeric($int)) {
$int = (int) $int;
} else {
throw new TypeError('Argument 1 must be an integer, ' . gettype($int) . ' given.');
}
}
/** @var string $packed */
$packed = pack('N', $int);
return $packed;
}
/**
* Stores a 64-bit integer as an string, treating it as little-endian.
*
* @internal You should not use this directly from another application
*
* @param int $int
* @return string
* @throws TypeError
*/
public static function store64_le($int)
{
/* Type checks: */
if (!is_int($int)) {
if (is_numeric($int)) {
$int = (int) $int;
} else {
throw new TypeError('Argument 1 must be an integer, ' . gettype($int) . ' given.');
}
}
if (PHP_INT_SIZE === 8) {
if (PHP_VERSION_ID >= 50603) {
/** @var string $packed */
$packed = pack('P', $int);
return $packed;
}
return self::intToChr($int & 0xff) .
self::intToChr(($int >> 8) & 0xff) .
self::intToChr(($int >> 16) & 0xff) .
self::intToChr(($int >> 24) & 0xff) .
self::intToChr(($int >> 32) & 0xff) .
self::intToChr(($int >> 40) & 0xff) .
self::intToChr(($int >> 48) & 0xff) .
self::intToChr(($int >> 56) & 0xff);
}
if ($int > PHP_INT_MAX) {
list($hiB, $int) = self::numericTo64BitInteger($int);
} else {
$hiB = 0;
}
return
self::intToChr(($int ) & 0xff) .
self::intToChr(($int >> 8) & 0xff) .
self::intToChr(($int >> 16) & 0xff) .
self::intToChr(($int >> 24) & 0xff) .
self::intToChr($hiB & 0xff) .
self::intToChr(($hiB >> 8) & 0xff) .
self::intToChr(($hiB >> 16) & 0xff) .
self::intToChr(($hiB >> 24) & 0xff);
}
/**
* Safe string length
*
* @internal You should not use this directly from another application
*
* @ref mbstring.func_overload
*
* @param string $str
* @return int
* @throws TypeError
*/
public static function strlen($str)
{
/* Type checks: */
if (!is_string($str)) {
throw new TypeError('String expected');
}
return (int) (
self::isMbStringOverride()
? mb_strlen($str, '8bit')
: strlen($str)
);
}
/**
* Turn a string into an array of integers
*
* @internal You should not use this directly from another application
*
* @param string $string
* @return array<int, int>
* @throws TypeError
*/
public static function stringToIntArray($string)
{
if (!is_string($string)) {
throw new TypeError('String expected');
}
/**
* @var array<int, int>
*/
$values = array_values(
unpack('C*', $string)
);
return $values;
}
/**
* Safe substring
*
* @internal You should not use this directly from another application
*
* @ref mbstring.func_overload
*
* @param string $str
* @param int $start
* @param int $length
* @return string
* @throws TypeError
*/
public static function substr($str, $start = 0, $length = null)
{
/* Type checks: */
if (!is_string($str)) {
throw new TypeError('String expected');
}
if ($length === 0) {
return '';
}
if (self::isMbStringOverride()) {
if (PHP_VERSION_ID < 50400 && $length === null) {
$length = self::strlen($str);
}
$sub = (string) mb_substr($str, $start, $length, '8bit');
} elseif ($length === null) {
$sub = (string) substr($str, $start);
} else {
$sub = (string) substr($str, $start, $length);
}
if ($sub !== '') {
return $sub;
}
return '';
}
/**
* Compare a 16-character byte string in constant time.
*
* @internal You should not use this directly from another application
*
* @param string $a
* @param string $b
* @return bool
* @throws SodiumException
* @throws TypeError
*/
public static function verify_16($a, $b)
{
/* Type checks: */
if (!is_string($a)) {
throw new TypeError('String expected');
}
if (!is_string($b)) {
throw new TypeError('String expected');
}
return self::hashEquals(
self::substr($a, 0, 16),
self::substr($b, 0, 16)
);
}
/**
* Compare a 32-character byte string in constant time.
*
* @internal You should not use this directly from another application
*
* @param string $a
* @param string $b
* @return bool
* @throws SodiumException
* @throws TypeError
*/
public static function verify_32($a, $b)
{
/* Type checks: */
if (!is_string($a)) {
throw new TypeError('String expected');
}
if (!is_string($b)) {
throw new TypeError('String expected');
}
return self::hashEquals(
self::substr($a, 0, 32),
self::substr($b, 0, 32)
);
}
/**
* Calculate $a ^ $b for two strings.
*
* @internal You should not use this directly from another application
*
* @param string $a
* @param string $b
* @return string
* @throws TypeError
*/
public static function xorStrings($a, $b)
{
/* Type checks: */
if (!is_string($a)) {
throw new TypeError('Argument 1 must be a string');
}
if (!is_string($b)) {
throw new TypeError('Argument 2 must be a string');
}
return (string) ($a ^ $b);
}
/**
* Returns whether or not mbstring.func_overload is in effect.
*
* @internal You should not use this directly from another application
*
* Note: MB_OVERLOAD_STRING === 2, but we don't reference the constant
* (for nuisance-free PHP 8 support)
*
* @return bool
*/
protected static function isMbStringOverride()
{
static $mbstring = null;
if ($mbstring === null) {
if (!defined('MB_OVERLOAD_STRING')) {
$mbstring = false;
return $mbstring;
}
$mbstring = extension_loaded('mbstring')
&& defined('MB_OVERLOAD_STRING')
&&
((int) (ini_get('mbstring.func_overload')) & 2);
// MB_OVERLOAD_STRING === 2
}
/** @var bool $mbstring */
return $mbstring;
}
}
Core/XSalsa20.php 0000604 00000002533 15133021213 0007470 0 ustar 00 <?php
if (class_exists('ParagonIE_Sodium_Core_XSalsa20', false)) {
return;
}
/**
* Class ParagonIE_Sodium_Core_XSalsa20
*/
abstract class ParagonIE_Sodium_Core_XSalsa20 extends ParagonIE_Sodium_Core_HSalsa20
{
/**
* Expand a key and nonce into an xsalsa20 keystream.
*
* @internal You should not use this directly from another application
*
* @param int $len
* @param string $nonce
* @param string $key
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function xsalsa20($len, $nonce, $key)
{
$ret = self::salsa20(
$len,
self::substr($nonce, 16, 8),
self::hsalsa20($nonce, $key)
);
return $ret;
}
/**
* Encrypt a string with XSalsa20. Doesn't provide integrity.
*
* @internal You should not use this directly from another application
*
* @param string $message
* @param string $nonce
* @param string $key
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function xsalsa20_xor($message, $nonce, $key)
{
return self::xorStrings(
$message,
self::xsalsa20(
self::strlen($message),
$nonce,
$key
)
);
}
}
Core/SecretStream/State.php 0000604 00000007050 15133021213 0011613 0 ustar 00 <?php
/**
* Class ParagonIE_Sodium_Core_SecretStream_State
*/
class ParagonIE_Sodium_Core_SecretStream_State
{
/** @var string $key */
protected $key;
/** @var int $counter */
protected $counter;
/** @var string $nonce */
protected $nonce;
/** @var string $_pad */
protected $_pad;
/**
* ParagonIE_Sodium_Core_SecretStream_State constructor.
* @param string $key
* @param string|null $nonce
*/
public function __construct($key, $nonce = null)
{
$this->key = $key;
$this->counter = 1;
if (is_null($nonce)) {
$nonce = str_repeat("\0", 12);
}
$this->nonce = str_pad($nonce, 12, "\0", STR_PAD_RIGHT);;
$this->_pad = str_repeat("\0", 4);
}
/**
* @return self
*/
public function counterReset()
{
$this->counter = 1;
$this->_pad = str_repeat("\0", 4);
return $this;
}
/**
* @return string
*/
public function getKey()
{
return $this->key;
}
/**
* @return string
*/
public function getCounter()
{
return ParagonIE_Sodium_Core_Util::store32_le($this->counter);
}
/**
* @return string
*/
public function getNonce()
{
if (!is_string($this->nonce)) {
$this->nonce = str_repeat("\0", 12);
}
if (ParagonIE_Sodium_Core_Util::strlen($this->nonce) !== 12) {
$this->nonce = str_pad($this->nonce, 12, "\0", STR_PAD_RIGHT);
}
return $this->nonce;
}
/**
* @return string
*/
public function getCombinedNonce()
{
return $this->getCounter() .
ParagonIE_Sodium_Core_Util::substr($this->getNonce(), 0, 8);
}
/**
* @return self
*/
public function incrementCounter()
{
++$this->counter;
return $this;
}
/**
* @return bool
*/
public function needsRekey()
{
return ($this->counter & 0xffff) === 0;
}
/**
* @param string $newKeyAndNonce
* @return self
*/
public function rekey($newKeyAndNonce)
{
$this->key = ParagonIE_Sodium_Core_Util::substr($newKeyAndNonce, 0, 32);
$this->nonce = str_pad(
ParagonIE_Sodium_Core_Util::substr($newKeyAndNonce, 32),
12,
"\0",
STR_PAD_RIGHT
);
return $this;
}
/**
* @param string $str
* @return self
*/
public function xorNonce($str)
{
$this->nonce = ParagonIE_Sodium_Core_Util::xorStrings(
$this->getNonce(),
str_pad(
ParagonIE_Sodium_Core_Util::substr($str, 0, 8),
12,
"\0",
STR_PAD_RIGHT
)
);
return $this;
}
/**
* @param string $string
* @return self
*/
public static function fromString($string)
{
$state = new ParagonIE_Sodium_Core_SecretStream_State(
ParagonIE_Sodium_Core_Util::substr($string, 0, 32)
);
$state->counter = ParagonIE_Sodium_Core_Util::load_4(
ParagonIE_Sodium_Core_Util::substr($string, 32, 4)
);
$state->nonce = ParagonIE_Sodium_Core_Util::substr($string, 36, 12);
$state->_pad = ParagonIE_Sodium_Core_Util::substr($string, 48, 8);
return $state;
}
/**
* @return string
*/
public function toString()
{
return $this->key .
$this->getCounter() .
$this->nonce .
$this->_pad;
}
}
Core/ChaCha20.php 0000604 00000031206 15133021213 0007403 0 ustar 00 <?php
if (class_exists('ParagonIE_Sodium_Core_ChaCha20', false)) {
return;
}
/**
* Class ParagonIE_Sodium_Core_ChaCha20
*/
class ParagonIE_Sodium_Core_ChaCha20 extends ParagonIE_Sodium_Core_Util
{
/**
* Bitwise left rotation
*
* @internal You should not use this directly from another application
*
* @param int $v
* @param int $n
* @return int
*/
public static function rotate($v, $n)
{
$v &= 0xffffffff;
$n &= 31;
return (int) (
0xffffffff & (
($v << $n)
|
($v >> (32 - $n))
)
);
}
/**
* The ChaCha20 quarter round function. Works on four 32-bit integers.
*
* @internal You should not use this directly from another application
*
* @param int $a
* @param int $b
* @param int $c
* @param int $d
* @return array<int, int>
*/
protected static function quarterRound($a, $b, $c, $d)
{
# a = PLUS(a,b); d = ROTATE(XOR(d,a),16);
/** @var int $a */
$a = ($a + $b) & 0xffffffff;
$d = self::rotate($d ^ $a, 16);
# c = PLUS(c,d); b = ROTATE(XOR(b,c),12);
/** @var int $c */
$c = ($c + $d) & 0xffffffff;
$b = self::rotate($b ^ $c, 12);
# a = PLUS(a,b); d = ROTATE(XOR(d,a), 8);
/** @var int $a */
$a = ($a + $b) & 0xffffffff;
$d = self::rotate($d ^ $a, 8);
# c = PLUS(c,d); b = ROTATE(XOR(b,c), 7);
/** @var int $c */
$c = ($c + $d) & 0xffffffff;
$b = self::rotate($b ^ $c, 7);
return array((int) $a, (int) $b, (int) $c, (int) $d);
}
/**
* @internal You should not use this directly from another application
*
* @param ParagonIE_Sodium_Core_ChaCha20_Ctx $ctx
* @param string $message
*
* @return string
* @throws TypeError
* @throws SodiumException
*/
public static function encryptBytes(
ParagonIE_Sodium_Core_ChaCha20_Ctx $ctx,
$message = ''
) {
$bytes = self::strlen($message);
/*
j0 = ctx->input[0];
j1 = ctx->input[1];
j2 = ctx->input[2];
j3 = ctx->input[3];
j4 = ctx->input[4];
j5 = ctx->input[5];
j6 = ctx->input[6];
j7 = ctx->input[7];
j8 = ctx->input[8];
j9 = ctx->input[9];
j10 = ctx->input[10];
j11 = ctx->input[11];
j12 = ctx->input[12];
j13 = ctx->input[13];
j14 = ctx->input[14];
j15 = ctx->input[15];
*/
$j0 = (int) $ctx[0];
$j1 = (int) $ctx[1];
$j2 = (int) $ctx[2];
$j3 = (int) $ctx[3];
$j4 = (int) $ctx[4];
$j5 = (int) $ctx[5];
$j6 = (int) $ctx[6];
$j7 = (int) $ctx[7];
$j8 = (int) $ctx[8];
$j9 = (int) $ctx[9];
$j10 = (int) $ctx[10];
$j11 = (int) $ctx[11];
$j12 = (int) $ctx[12];
$j13 = (int) $ctx[13];
$j14 = (int) $ctx[14];
$j15 = (int) $ctx[15];
$c = '';
for (;;) {
if ($bytes < 64) {
$message .= str_repeat("\x00", 64 - $bytes);
}
$x0 = (int) $j0;
$x1 = (int) $j1;
$x2 = (int) $j2;
$x3 = (int) $j3;
$x4 = (int) $j4;
$x5 = (int) $j5;
$x6 = (int) $j6;
$x7 = (int) $j7;
$x8 = (int) $j8;
$x9 = (int) $j9;
$x10 = (int) $j10;
$x11 = (int) $j11;
$x12 = (int) $j12;
$x13 = (int) $j13;
$x14 = (int) $j14;
$x15 = (int) $j15;
# for (i = 20; i > 0; i -= 2) {
for ($i = 20; $i > 0; $i -= 2) {
# QUARTERROUND( x0, x4, x8, x12)
list($x0, $x4, $x8, $x12) = self::quarterRound($x0, $x4, $x8, $x12);
# QUARTERROUND( x1, x5, x9, x13)
list($x1, $x5, $x9, $x13) = self::quarterRound($x1, $x5, $x9, $x13);
# QUARTERROUND( x2, x6, x10, x14)
list($x2, $x6, $x10, $x14) = self::quarterRound($x2, $x6, $x10, $x14);
# QUARTERROUND( x3, x7, x11, x15)
list($x3, $x7, $x11, $x15) = self::quarterRound($x3, $x7, $x11, $x15);
# QUARTERROUND( x0, x5, x10, x15)
list($x0, $x5, $x10, $x15) = self::quarterRound($x0, $x5, $x10, $x15);
# QUARTERROUND( x1, x6, x11, x12)
list($x1, $x6, $x11, $x12) = self::quarterRound($x1, $x6, $x11, $x12);
# QUARTERROUND( x2, x7, x8, x13)
list($x2, $x7, $x8, $x13) = self::quarterRound($x2, $x7, $x8, $x13);
# QUARTERROUND( x3, x4, x9, x14)
list($x3, $x4, $x9, $x14) = self::quarterRound($x3, $x4, $x9, $x14);
}
/*
x0 = PLUS(x0, j0);
x1 = PLUS(x1, j1);
x2 = PLUS(x2, j2);
x3 = PLUS(x3, j3);
x4 = PLUS(x4, j4);
x5 = PLUS(x5, j5);
x6 = PLUS(x6, j6);
x7 = PLUS(x7, j7);
x8 = PLUS(x8, j8);
x9 = PLUS(x9, j9);
x10 = PLUS(x10, j10);
x11 = PLUS(x11, j11);
x12 = PLUS(x12, j12);
x13 = PLUS(x13, j13);
x14 = PLUS(x14, j14);
x15 = PLUS(x15, j15);
*/
/** @var int $x0 */
$x0 = ($x0 & 0xffffffff) + $j0;
/** @var int $x1 */
$x1 = ($x1 & 0xffffffff) + $j1;
/** @var int $x2 */
$x2 = ($x2 & 0xffffffff) + $j2;
/** @var int $x3 */
$x3 = ($x3 & 0xffffffff) + $j3;
/** @var int $x4 */
$x4 = ($x4 & 0xffffffff) + $j4;
/** @var int $x5 */
$x5 = ($x5 & 0xffffffff) + $j5;
/** @var int $x6 */
$x6 = ($x6 & 0xffffffff) + $j6;
/** @var int $x7 */
$x7 = ($x7 & 0xffffffff) + $j7;
/** @var int $x8 */
$x8 = ($x8 & 0xffffffff) + $j8;
/** @var int $x9 */
$x9 = ($x9 & 0xffffffff) + $j9;
/** @var int $x10 */
$x10 = ($x10 & 0xffffffff) + $j10;
/** @var int $x11 */
$x11 = ($x11 & 0xffffffff) + $j11;
/** @var int $x12 */
$x12 = ($x12 & 0xffffffff) + $j12;
/** @var int $x13 */
$x13 = ($x13 & 0xffffffff) + $j13;
/** @var int $x14 */
$x14 = ($x14 & 0xffffffff) + $j14;
/** @var int $x15 */
$x15 = ($x15 & 0xffffffff) + $j15;
/*
x0 = XOR(x0, LOAD32_LE(m + 0));
x1 = XOR(x1, LOAD32_LE(m + 4));
x2 = XOR(x2, LOAD32_LE(m + 8));
x3 = XOR(x3, LOAD32_LE(m + 12));
x4 = XOR(x4, LOAD32_LE(m + 16));
x5 = XOR(x5, LOAD32_LE(m + 20));
x6 = XOR(x6, LOAD32_LE(m + 24));
x7 = XOR(x7, LOAD32_LE(m + 28));
x8 = XOR(x8, LOAD32_LE(m + 32));
x9 = XOR(x9, LOAD32_LE(m + 36));
x10 = XOR(x10, LOAD32_LE(m + 40));
x11 = XOR(x11, LOAD32_LE(m + 44));
x12 = XOR(x12, LOAD32_LE(m + 48));
x13 = XOR(x13, LOAD32_LE(m + 52));
x14 = XOR(x14, LOAD32_LE(m + 56));
x15 = XOR(x15, LOAD32_LE(m + 60));
*/
$x0 ^= self::load_4(self::substr($message, 0, 4));
$x1 ^= self::load_4(self::substr($message, 4, 4));
$x2 ^= self::load_4(self::substr($message, 8, 4));
$x3 ^= self::load_4(self::substr($message, 12, 4));
$x4 ^= self::load_4(self::substr($message, 16, 4));
$x5 ^= self::load_4(self::substr($message, 20, 4));
$x6 ^= self::load_4(self::substr($message, 24, 4));
$x7 ^= self::load_4(self::substr($message, 28, 4));
$x8 ^= self::load_4(self::substr($message, 32, 4));
$x9 ^= self::load_4(self::substr($message, 36, 4));
$x10 ^= self::load_4(self::substr($message, 40, 4));
$x11 ^= self::load_4(self::substr($message, 44, 4));
$x12 ^= self::load_4(self::substr($message, 48, 4));
$x13 ^= self::load_4(self::substr($message, 52, 4));
$x14 ^= self::load_4(self::substr($message, 56, 4));
$x15 ^= self::load_4(self::substr($message, 60, 4));
/*
j12 = PLUSONE(j12);
if (!j12) {
j13 = PLUSONE(j13);
}
*/
++$j12;
if ($j12 & 0xf0000000) {
throw new SodiumException('Overflow');
}
/*
STORE32_LE(c + 0, x0);
STORE32_LE(c + 4, x1);
STORE32_LE(c + 8, x2);
STORE32_LE(c + 12, x3);
STORE32_LE(c + 16, x4);
STORE32_LE(c + 20, x5);
STORE32_LE(c + 24, x6);
STORE32_LE(c + 28, x7);
STORE32_LE(c + 32, x8);
STORE32_LE(c + 36, x9);
STORE32_LE(c + 40, x10);
STORE32_LE(c + 44, x11);
STORE32_LE(c + 48, x12);
STORE32_LE(c + 52, x13);
STORE32_LE(c + 56, x14);
STORE32_LE(c + 60, x15);
*/
$block = self::store32_le((int) ($x0 & 0xffffffff)) .
self::store32_le((int) ($x1 & 0xffffffff)) .
self::store32_le((int) ($x2 & 0xffffffff)) .
self::store32_le((int) ($x3 & 0xffffffff)) .
self::store32_le((int) ($x4 & 0xffffffff)) .
self::store32_le((int) ($x5 & 0xffffffff)) .
self::store32_le((int) ($x6 & 0xffffffff)) .
self::store32_le((int) ($x7 & 0xffffffff)) .
self::store32_le((int) ($x8 & 0xffffffff)) .
self::store32_le((int) ($x9 & 0xffffffff)) .
self::store32_le((int) ($x10 & 0xffffffff)) .
self::store32_le((int) ($x11 & 0xffffffff)) .
self::store32_le((int) ($x12 & 0xffffffff)) .
self::store32_le((int) ($x13 & 0xffffffff)) .
self::store32_le((int) ($x14 & 0xffffffff)) .
self::store32_le((int) ($x15 & 0xffffffff));
/* Partial block */
if ($bytes < 64) {
$c .= self::substr($block, 0, $bytes);
break;
}
/* Full block */
$c .= $block;
$bytes -= 64;
if ($bytes <= 0) {
break;
}
$message = self::substr($message, 64);
}
/* end for(;;) loop */
$ctx[12] = $j12;
$ctx[13] = $j13;
return $c;
}
/**
* @internal You should not use this directly from another application
*
* @param int $len
* @param string $nonce
* @param string $key
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function stream($len = 64, $nonce = '', $key = '')
{
return self::encryptBytes(
new ParagonIE_Sodium_Core_ChaCha20_Ctx($key, $nonce),
str_repeat("\x00", $len)
);
}
/**
* @internal You should not use this directly from another application
*
* @param int $len
* @param string $nonce
* @param string $key
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function ietfStream($len, $nonce = '', $key = '')
{
return self::encryptBytes(
new ParagonIE_Sodium_Core_ChaCha20_IetfCtx($key, $nonce),
str_repeat("\x00", $len)
);
}
/**
* @internal You should not use this directly from another application
*
* @param string $message
* @param string $nonce
* @param string $key
* @param string $ic
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function ietfStreamXorIc($message, $nonce = '', $key = '', $ic = '')
{
return self::encryptBytes(
new ParagonIE_Sodium_Core_ChaCha20_IetfCtx($key, $nonce, $ic),
$message
);
}
/**
* @internal You should not use this directly from another application
*
* @param string $message
* @param string $nonce
* @param string $key
* @param string $ic
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function streamXorIc($message, $nonce = '', $key = '', $ic = '')
{
return self::encryptBytes(
new ParagonIE_Sodium_Core_ChaCha20_Ctx($key, $nonce, $ic),
$message
);
}
}
Core/SipHash.php 0000604 00000020051 15133021213 0007465 0 ustar 00 <?php
if (class_exists('ParagonIE_Sodium_Core_SipHash', false)) {
return;
}
/**
* Class ParagonIE_SodiumCompat_Core_SipHash
*
* Only uses 32-bit arithmetic, while the original SipHash used 64-bit integers
*/
class ParagonIE_Sodium_Core_SipHash extends ParagonIE_Sodium_Core_Util
{
/**
* @internal You should not use this directly from another application
*
* @param int[] $v
* @return int[]
*
*/
public static function sipRound(array $v)
{
# v0 += v1;
list($v[0], $v[1]) = self::add(
array($v[0], $v[1]),
array($v[2], $v[3])
);
# v1=ROTL(v1,13);
list($v[2], $v[3]) = self::rotl_64((int) $v[2], (int) $v[3], 13);
# v1 ^= v0;
$v[2] = (int) $v[2] ^ (int) $v[0];
$v[3] = (int) $v[3] ^ (int) $v[1];
# v0=ROTL(v0,32);
list($v[0], $v[1]) = self::rotl_64((int) $v[0], (int) $v[1], 32);
# v2 += v3;
list($v[4], $v[5]) = self::add(
array((int) $v[4], (int) $v[5]),
array((int) $v[6], (int) $v[7])
);
# v3=ROTL(v3,16);
list($v[6], $v[7]) = self::rotl_64((int) $v[6], (int) $v[7], 16);
# v3 ^= v2;
$v[6] = (int) $v[6] ^ (int) $v[4];
$v[7] = (int) $v[7] ^ (int) $v[5];
# v0 += v3;
list($v[0], $v[1]) = self::add(
array((int) $v[0], (int) $v[1]),
array((int) $v[6], (int) $v[7])
);
# v3=ROTL(v3,21);
list($v[6], $v[7]) = self::rotl_64((int) $v[6], (int) $v[7], 21);
# v3 ^= v0;
$v[6] = (int) $v[6] ^ (int) $v[0];
$v[7] = (int) $v[7] ^ (int) $v[1];
# v2 += v1;
list($v[4], $v[5]) = self::add(
array((int) $v[4], (int) $v[5]),
array((int) $v[2], (int) $v[3])
);
# v1=ROTL(v1,17);
list($v[2], $v[3]) = self::rotl_64((int) $v[2], (int) $v[3], 17);
# v1 ^= v2;;
$v[2] = (int) $v[2] ^ (int) $v[4];
$v[3] = (int) $v[3] ^ (int) $v[5];
# v2=ROTL(v2,32)
list($v[4], $v[5]) = self::rotl_64((int) $v[4], (int) $v[5], 32);
return $v;
}
/**
* Add two 32 bit integers representing a 64-bit integer.
*
* @internal You should not use this directly from another application
*
* @param int[] $a
* @param int[] $b
* @return array<int, mixed>
*/
public static function add(array $a, array $b)
{
/** @var int $x1 */
$x1 = $a[1] + $b[1];
/** @var int $c */
$c = $x1 >> 32; // Carry if ($a + $b) > 0xffffffff
/** @var int $x0 */
$x0 = $a[0] + $b[0] + $c;
return array(
$x0 & 0xffffffff,
$x1 & 0xffffffff
);
}
/**
* @internal You should not use this directly from another application
*
* @param int $int0
* @param int $int1
* @param int $c
* @return array<int, mixed>
*/
public static function rotl_64($int0, $int1, $c)
{
$int0 &= 0xffffffff;
$int1 &= 0xffffffff;
$c &= 63;
if ($c === 32) {
return array($int1, $int0);
}
if ($c > 31) {
$tmp = $int1;
$int1 = $int0;
$int0 = $tmp;
$c &= 31;
}
if ($c === 0) {
return array($int0, $int1);
}
return array(
0xffffffff & (
($int0 << $c)
|
($int1 >> (32 - $c))
),
0xffffffff & (
($int1 << $c)
|
($int0 >> (32 - $c))
),
);
}
/**
* Implements Siphash-2-4 using only 32-bit numbers.
*
* When we split an int into two, the higher bits go to the lower index.
* e.g. 0xDEADBEEFAB10C92D becomes [
* 0 => 0xDEADBEEF,
* 1 => 0xAB10C92D
* ].
*
* @internal You should not use this directly from another application
*
* @param string $in
* @param string $key
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function sipHash24($in, $key)
{
$inlen = self::strlen($in);
# /* "somepseudorandomlygeneratedbytes" */
# u64 v0 = 0x736f6d6570736575ULL;
# u64 v1 = 0x646f72616e646f6dULL;
# u64 v2 = 0x6c7967656e657261ULL;
# u64 v3 = 0x7465646279746573ULL;
$v = array(
0x736f6d65, // 0
0x70736575, // 1
0x646f7261, // 2
0x6e646f6d, // 3
0x6c796765, // 4
0x6e657261, // 5
0x74656462, // 6
0x79746573 // 7
);
// v0 => $v[0], $v[1]
// v1 => $v[2], $v[3]
// v2 => $v[4], $v[5]
// v3 => $v[6], $v[7]
# u64 k0 = LOAD64_LE( k );
# u64 k1 = LOAD64_LE( k + 8 );
$k = array(
self::load_4(self::substr($key, 4, 4)),
self::load_4(self::substr($key, 0, 4)),
self::load_4(self::substr($key, 12, 4)),
self::load_4(self::substr($key, 8, 4))
);
// k0 => $k[0], $k[1]
// k1 => $k[2], $k[3]
# b = ( ( u64 )inlen ) << 56;
$b = array(
$inlen << 24,
0
);
// See docblock for why the 0th index gets the higher bits.
# v3 ^= k1;
$v[6] ^= $k[2];
$v[7] ^= $k[3];
# v2 ^= k0;
$v[4] ^= $k[0];
$v[5] ^= $k[1];
# v1 ^= k1;
$v[2] ^= $k[2];
$v[3] ^= $k[3];
# v0 ^= k0;
$v[0] ^= $k[0];
$v[1] ^= $k[1];
$left = $inlen;
# for ( ; in != end; in += 8 )
while ($left >= 8) {
# m = LOAD64_LE( in );
$m = array(
self::load_4(self::substr($in, 4, 4)),
self::load_4(self::substr($in, 0, 4))
);
# v3 ^= m;
$v[6] ^= $m[0];
$v[7] ^= $m[1];
# SIPROUND;
# SIPROUND;
$v = self::sipRound($v);
$v = self::sipRound($v);
# v0 ^= m;
$v[0] ^= $m[0];
$v[1] ^= $m[1];
$in = self::substr($in, 8);
$left -= 8;
}
# switch( left )
# {
# case 7: b |= ( ( u64 )in[ 6] ) << 48;
# case 6: b |= ( ( u64 )in[ 5] ) << 40;
# case 5: b |= ( ( u64 )in[ 4] ) << 32;
# case 4: b |= ( ( u64 )in[ 3] ) << 24;
# case 3: b |= ( ( u64 )in[ 2] ) << 16;
# case 2: b |= ( ( u64 )in[ 1] ) << 8;
# case 1: b |= ( ( u64 )in[ 0] ); break;
# case 0: break;
# }
switch ($left) {
case 7:
$b[0] |= self::chrToInt($in[6]) << 16;
case 6:
$b[0] |= self::chrToInt($in[5]) << 8;
case 5:
$b[0] |= self::chrToInt($in[4]);
case 4:
$b[1] |= self::chrToInt($in[3]) << 24;
case 3:
$b[1] |= self::chrToInt($in[2]) << 16;
case 2:
$b[1] |= self::chrToInt($in[1]) << 8;
case 1:
$b[1] |= self::chrToInt($in[0]);
case 0:
break;
}
// See docblock for why the 0th index gets the higher bits.
# v3 ^= b;
$v[6] ^= $b[0];
$v[7] ^= $b[1];
# SIPROUND;
# SIPROUND;
$v = self::sipRound($v);
$v = self::sipRound($v);
# v0 ^= b;
$v[0] ^= $b[0];
$v[1] ^= $b[1];
// Flip the lower 8 bits of v2 which is ($v[4], $v[5]) in our implementation
# v2 ^= 0xff;
$v[5] ^= 0xff;
# SIPROUND;
# SIPROUND;
# SIPROUND;
# SIPROUND;
$v = self::sipRound($v);
$v = self::sipRound($v);
$v = self::sipRound($v);
$v = self::sipRound($v);
# b = v0 ^ v1 ^ v2 ^ v3;
# STORE64_LE( out, b );
return self::store32_le($v[1] ^ $v[3] ^ $v[5] ^ $v[7]) .
self::store32_le($v[0] ^ $v[2] ^ $v[4] ^ $v[6]);
}
}
Core/HSalsa20.php 0000604 00000007131 15133021213 0007447 0 ustar 00 <?php
if (class_exists('ParagonIE_Sodium_Core_HSalsa20', false)) {
return;
}
/**
* Class ParagonIE_Sodium_Core_HSalsa20
*/
abstract class ParagonIE_Sodium_Core_HSalsa20 extends ParagonIE_Sodium_Core_Salsa20
{
/**
* Calculate an hsalsa20 hash of a single block
*
* HSalsa20 doesn't have a counter and will never be used for more than
* one block (used to derive a subkey for xsalsa20).
*
* @internal You should not use this directly from another application
*
* @param string $in
* @param string $k
* @param string|null $c
* @return string
* @throws TypeError
*/
public static function hsalsa20($in, $k, $c = null)
{
if ($c === null) {
$x0 = 0x61707865;
$x5 = 0x3320646e;
$x10 = 0x79622d32;
$x15 = 0x6b206574;
} else {
$x0 = self::load_4(self::substr($c, 0, 4));
$x5 = self::load_4(self::substr($c, 4, 4));
$x10 = self::load_4(self::substr($c, 8, 4));
$x15 = self::load_4(self::substr($c, 12, 4));
}
$x1 = self::load_4(self::substr($k, 0, 4));
$x2 = self::load_4(self::substr($k, 4, 4));
$x3 = self::load_4(self::substr($k, 8, 4));
$x4 = self::load_4(self::substr($k, 12, 4));
$x11 = self::load_4(self::substr($k, 16, 4));
$x12 = self::load_4(self::substr($k, 20, 4));
$x13 = self::load_4(self::substr($k, 24, 4));
$x14 = self::load_4(self::substr($k, 28, 4));
$x6 = self::load_4(self::substr($in, 0, 4));
$x7 = self::load_4(self::substr($in, 4, 4));
$x8 = self::load_4(self::substr($in, 8, 4));
$x9 = self::load_4(self::substr($in, 12, 4));
for ($i = self::ROUNDS; $i > 0; $i -= 2) {
$x4 ^= self::rotate($x0 + $x12, 7);
$x8 ^= self::rotate($x4 + $x0, 9);
$x12 ^= self::rotate($x8 + $x4, 13);
$x0 ^= self::rotate($x12 + $x8, 18);
$x9 ^= self::rotate($x5 + $x1, 7);
$x13 ^= self::rotate($x9 + $x5, 9);
$x1 ^= self::rotate($x13 + $x9, 13);
$x5 ^= self::rotate($x1 + $x13, 18);
$x14 ^= self::rotate($x10 + $x6, 7);
$x2 ^= self::rotate($x14 + $x10, 9);
$x6 ^= self::rotate($x2 + $x14, 13);
$x10 ^= self::rotate($x6 + $x2, 18);
$x3 ^= self::rotate($x15 + $x11, 7);
$x7 ^= self::rotate($x3 + $x15, 9);
$x11 ^= self::rotate($x7 + $x3, 13);
$x15 ^= self::rotate($x11 + $x7, 18);
$x1 ^= self::rotate($x0 + $x3, 7);
$x2 ^= self::rotate($x1 + $x0, 9);
$x3 ^= self::rotate($x2 + $x1, 13);
$x0 ^= self::rotate($x3 + $x2, 18);
$x6 ^= self::rotate($x5 + $x4, 7);
$x7 ^= self::rotate($x6 + $x5, 9);
$x4 ^= self::rotate($x7 + $x6, 13);
$x5 ^= self::rotate($x4 + $x7, 18);
$x11 ^= self::rotate($x10 + $x9, 7);
$x8 ^= self::rotate($x11 + $x10, 9);
$x9 ^= self::rotate($x8 + $x11, 13);
$x10 ^= self::rotate($x9 + $x8, 18);
$x12 ^= self::rotate($x15 + $x14, 7);
$x13 ^= self::rotate($x12 + $x15, 9);
$x14 ^= self::rotate($x13 + $x12, 13);
$x15 ^= self::rotate($x14 + $x13, 18);
}
return self::store32_le($x0) .
self::store32_le($x5) .
self::store32_le($x10) .
self::store32_le($x15) .
self::store32_le($x6) .
self::store32_le($x7) .
self::store32_le($x8) .
self::store32_le($x9);
}
}
Core/X25519.php 0000604 00000022352 15133021213 0006751 0 ustar 00 <?php
if (class_exists('ParagonIE_Sodium_Core_X25519', false)) {
return;
}
/**
* Class ParagonIE_Sodium_Core_X25519
*/
abstract class ParagonIE_Sodium_Core_X25519 extends ParagonIE_Sodium_Core_Curve25519
{
/**
* Alters the objects passed to this method in place.
*
* @internal You should not use this directly from another application
*
* @param ParagonIE_Sodium_Core_Curve25519_Fe $f
* @param ParagonIE_Sodium_Core_Curve25519_Fe $g
* @param int $b
* @return void
* @psalm-suppress MixedAssignment
*/
public static function fe_cswap(
ParagonIE_Sodium_Core_Curve25519_Fe $f,
ParagonIE_Sodium_Core_Curve25519_Fe $g,
$b = 0
) {
$f0 = (int) $f[0];
$f1 = (int) $f[1];
$f2 = (int) $f[2];
$f3 = (int) $f[3];
$f4 = (int) $f[4];
$f5 = (int) $f[5];
$f6 = (int) $f[6];
$f7 = (int) $f[7];
$f8 = (int) $f[8];
$f9 = (int) $f[9];
$g0 = (int) $g[0];
$g1 = (int) $g[1];
$g2 = (int) $g[2];
$g3 = (int) $g[3];
$g4 = (int) $g[4];
$g5 = (int) $g[5];
$g6 = (int) $g[6];
$g7 = (int) $g[7];
$g8 = (int) $g[8];
$g9 = (int) $g[9];
$b = -$b;
$x0 = ($f0 ^ $g0) & $b;
$x1 = ($f1 ^ $g1) & $b;
$x2 = ($f2 ^ $g2) & $b;
$x3 = ($f3 ^ $g3) & $b;
$x4 = ($f4 ^ $g4) & $b;
$x5 = ($f5 ^ $g5) & $b;
$x6 = ($f6 ^ $g6) & $b;
$x7 = ($f7 ^ $g7) & $b;
$x8 = ($f8 ^ $g8) & $b;
$x9 = ($f9 ^ $g9) & $b;
$f[0] = $f0 ^ $x0;
$f[1] = $f1 ^ $x1;
$f[2] = $f2 ^ $x2;
$f[3] = $f3 ^ $x3;
$f[4] = $f4 ^ $x4;
$f[5] = $f5 ^ $x5;
$f[6] = $f6 ^ $x6;
$f[7] = $f7 ^ $x7;
$f[8] = $f8 ^ $x8;
$f[9] = $f9 ^ $x9;
$g[0] = $g0 ^ $x0;
$g[1] = $g1 ^ $x1;
$g[2] = $g2 ^ $x2;
$g[3] = $g3 ^ $x3;
$g[4] = $g4 ^ $x4;
$g[5] = $g5 ^ $x5;
$g[6] = $g6 ^ $x6;
$g[7] = $g7 ^ $x7;
$g[8] = $g8 ^ $x8;
$g[9] = $g9 ^ $x9;
}
/**
* @internal You should not use this directly from another application
*
* @param ParagonIE_Sodium_Core_Curve25519_Fe $f
* @return ParagonIE_Sodium_Core_Curve25519_Fe
*/
public static function fe_mul121666(ParagonIE_Sodium_Core_Curve25519_Fe $f)
{
$h = array(
self::mul((int) $f[0], 121666, 17),
self::mul((int) $f[1], 121666, 17),
self::mul((int) $f[2], 121666, 17),
self::mul((int) $f[3], 121666, 17),
self::mul((int) $f[4], 121666, 17),
self::mul((int) $f[5], 121666, 17),
self::mul((int) $f[6], 121666, 17),
self::mul((int) $f[7], 121666, 17),
self::mul((int) $f[8], 121666, 17),
self::mul((int) $f[9], 121666, 17)
);
/** @var int $carry9 */
$carry9 = ($h[9] + (1 << 24)) >> 25;
$h[0] += self::mul($carry9, 19, 5);
$h[9] -= $carry9 << 25;
/** @var int $carry1 */
$carry1 = ($h[1] + (1 << 24)) >> 25;
$h[2] += $carry1;
$h[1] -= $carry1 << 25;
/** @var int $carry3 */
$carry3 = ($h[3] + (1 << 24)) >> 25;
$h[4] += $carry3;
$h[3] -= $carry3 << 25;
/** @var int $carry5 */
$carry5 = ($h[5] + (1 << 24)) >> 25;
$h[6] += $carry5;
$h[5] -= $carry5 << 25;
/** @var int $carry7 */
$carry7 = ($h[7] + (1 << 24)) >> 25;
$h[8] += $carry7;
$h[7] -= $carry7 << 25;
/** @var int $carry0 */
$carry0 = ($h[0] + (1 << 25)) >> 26;
$h[1] += $carry0;
$h[0] -= $carry0 << 26;
/** @var int $carry2 */
$carry2 = ($h[2] + (1 << 25)) >> 26;
$h[3] += $carry2;
$h[2] -= $carry2 << 26;
/** @var int $carry4 */
$carry4 = ($h[4] + (1 << 25)) >> 26;
$h[5] += $carry4;
$h[4] -= $carry4 << 26;
/** @var int $carry6 */
$carry6 = ($h[6] + (1 << 25)) >> 26;
$h[7] += $carry6;
$h[6] -= $carry6 << 26;
/** @var int $carry8 */
$carry8 = ($h[8] + (1 << 25)) >> 26;
$h[9] += $carry8;
$h[8] -= $carry8 << 26;
foreach ($h as $i => $value) {
$h[$i] = (int) $value;
}
return ParagonIE_Sodium_Core_Curve25519_Fe::fromArray($h);
}
/**
* @internal You should not use this directly from another application
*
* Inline comments preceded by # are from libsodium's ref10 code.
*
* @param string $n
* @param string $p
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function crypto_scalarmult_curve25519_ref10($n, $p)
{
# for (i = 0;i < 32;++i) e[i] = n[i];
$e = '' . $n;
# e[0] &= 248;
$e[0] = self::intToChr(
self::chrToInt($e[0]) & 248
);
# e[31] &= 127;
# e[31] |= 64;
$e[31] = self::intToChr(
(self::chrToInt($e[31]) & 127) | 64
);
# fe_frombytes(x1,p);
$x1 = self::fe_frombytes($p);
# fe_1(x2);
$x2 = self::fe_1();
# fe_0(z2);
$z2 = self::fe_0();
# fe_copy(x3,x1);
$x3 = self::fe_copy($x1);
# fe_1(z3);
$z3 = self::fe_1();
# swap = 0;
/** @var int $swap */
$swap = 0;
# for (pos = 254;pos >= 0;--pos) {
for ($pos = 254; $pos >= 0; --$pos) {
# b = e[pos / 8] >> (pos & 7);
/** @var int $b */
$b = self::chrToInt(
$e[(int) floor($pos / 8)]
) >> ($pos & 7);
# b &= 1;
$b &= 1;
# swap ^= b;
$swap ^= $b;
# fe_cswap(x2,x3,swap);
self::fe_cswap($x2, $x3, $swap);
# fe_cswap(z2,z3,swap);
self::fe_cswap($z2, $z3, $swap);
# swap = b;
$swap = $b;
# fe_sub(tmp0,x3,z3);
$tmp0 = self::fe_sub($x3, $z3);
# fe_sub(tmp1,x2,z2);
$tmp1 = self::fe_sub($x2, $z2);
# fe_add(x2,x2,z2);
$x2 = self::fe_add($x2, $z2);
# fe_add(z2,x3,z3);
$z2 = self::fe_add($x3, $z3);
# fe_mul(z3,tmp0,x2);
$z3 = self::fe_mul($tmp0, $x2);
# fe_mul(z2,z2,tmp1);
$z2 = self::fe_mul($z2, $tmp1);
# fe_sq(tmp0,tmp1);
$tmp0 = self::fe_sq($tmp1);
# fe_sq(tmp1,x2);
$tmp1 = self::fe_sq($x2);
# fe_add(x3,z3,z2);
$x3 = self::fe_add($z3, $z2);
# fe_sub(z2,z3,z2);
$z2 = self::fe_sub($z3, $z2);
# fe_mul(x2,tmp1,tmp0);
$x2 = self::fe_mul($tmp1, $tmp0);
# fe_sub(tmp1,tmp1,tmp0);
$tmp1 = self::fe_sub($tmp1, $tmp0);
# fe_sq(z2,z2);
$z2 = self::fe_sq($z2);
# fe_mul121666(z3,tmp1);
$z3 = self::fe_mul121666($tmp1);
# fe_sq(x3,x3);
$x3 = self::fe_sq($x3);
# fe_add(tmp0,tmp0,z3);
$tmp0 = self::fe_add($tmp0, $z3);
# fe_mul(z3,x1,z2);
$z3 = self::fe_mul($x1, $z2);
# fe_mul(z2,tmp1,tmp0);
$z2 = self::fe_mul($tmp1, $tmp0);
}
# fe_cswap(x2,x3,swap);
self::fe_cswap($x2, $x3, $swap);
# fe_cswap(z2,z3,swap);
self::fe_cswap($z2, $z3, $swap);
# fe_invert(z2,z2);
$z2 = self::fe_invert($z2);
# fe_mul(x2,x2,z2);
$x2 = self::fe_mul($x2, $z2);
# fe_tobytes(q,x2);
return self::fe_tobytes($x2);
}
/**
* @internal You should not use this directly from another application
*
* @param ParagonIE_Sodium_Core_Curve25519_Fe $edwardsY
* @param ParagonIE_Sodium_Core_Curve25519_Fe $edwardsZ
* @return ParagonIE_Sodium_Core_Curve25519_Fe
*/
public static function edwards_to_montgomery(
ParagonIE_Sodium_Core_Curve25519_Fe $edwardsY,
ParagonIE_Sodium_Core_Curve25519_Fe $edwardsZ
) {
$tempX = self::fe_add($edwardsZ, $edwardsY);
$tempZ = self::fe_sub($edwardsZ, $edwardsY);
$tempZ = self::fe_invert($tempZ);
return self::fe_mul($tempX, $tempZ);
}
/**
* @internal You should not use this directly from another application
*
* @param string $n
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function crypto_scalarmult_curve25519_ref10_base($n)
{
# for (i = 0;i < 32;++i) e[i] = n[i];
$e = '' . $n;
# e[0] &= 248;
$e[0] = self::intToChr(
self::chrToInt($e[0]) & 248
);
# e[31] &= 127;
# e[31] |= 64;
$e[31] = self::intToChr(
(self::chrToInt($e[31]) & 127) | 64
);
$A = self::ge_scalarmult_base($e);
if (
!($A->Y instanceof ParagonIE_Sodium_Core_Curve25519_Fe)
||
!($A->Z instanceof ParagonIE_Sodium_Core_Curve25519_Fe)
) {
throw new TypeError('Null points encountered');
}
$pk = self::edwards_to_montgomery($A->Y, $A->Z);
return self::fe_tobytes($pk);
}
}
Core/Curve25519.php 0000644 00000426446 15133021213 0007646 0 ustar 00 <?php
if (class_exists('ParagonIE_Sodium_Core_Curve25519', false)) {
return;
}
/**
* Class ParagonIE_Sodium_Core_Curve25519
*
* Implements Curve25519 core functions
*
* Based on the ref10 curve25519 code provided by libsodium
*
* @ref https://github.com/jedisct1/libsodium/blob/master/src/libsodium/crypto_core/curve25519/ref10/curve25519_ref10.c
*/
abstract class ParagonIE_Sodium_Core_Curve25519 extends ParagonIE_Sodium_Core_Curve25519_H
{
/**
* Get a field element of size 10 with a value of 0
*
* @internal You should not use this directly from another application
*
* @return ParagonIE_Sodium_Core_Curve25519_Fe
*/
public static function fe_0()
{
return ParagonIE_Sodium_Core_Curve25519_Fe::fromArray(
array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
);
}
/**
* Get a field element of size 10 with a value of 1
*
* @internal You should not use this directly from another application
*
* @return ParagonIE_Sodium_Core_Curve25519_Fe
*/
public static function fe_1()
{
return ParagonIE_Sodium_Core_Curve25519_Fe::fromArray(
array(1, 0, 0, 0, 0, 0, 0, 0, 0, 0)
);
}
/**
* Add two field elements.
*
* @internal You should not use this directly from another application
*
* @param ParagonIE_Sodium_Core_Curve25519_Fe $f
* @param ParagonIE_Sodium_Core_Curve25519_Fe $g
* @return ParagonIE_Sodium_Core_Curve25519_Fe
* @psalm-suppress MixedAssignment
* @psalm-suppress MixedOperand
*/
public static function fe_add(
ParagonIE_Sodium_Core_Curve25519_Fe $f,
ParagonIE_Sodium_Core_Curve25519_Fe $g
) {
/** @var array<int, int> $arr */
$arr = array();
for ($i = 0; $i < 10; ++$i) {
$arr[$i] = (int) ($f[$i] + $g[$i]);
}
return ParagonIE_Sodium_Core_Curve25519_Fe::fromArray($arr);
}
/**
* Constant-time conditional move.
*
* @internal You should not use this directly from another application
*
* @param ParagonIE_Sodium_Core_Curve25519_Fe $f
* @param ParagonIE_Sodium_Core_Curve25519_Fe $g
* @param int $b
* @return ParagonIE_Sodium_Core_Curve25519_Fe
* @psalm-suppress MixedAssignment
*/
public static function fe_cmov(
ParagonIE_Sodium_Core_Curve25519_Fe $f,
ParagonIE_Sodium_Core_Curve25519_Fe $g,
$b = 0
) {
/** @var array<int, int> $h */
$h = array();
$b *= -1;
for ($i = 0; $i < 10; ++$i) {
$x = (($f[$i] ^ $g[$i]) & $b);
$h[$i] = ($f[$i]) ^ $x;
}
return ParagonIE_Sodium_Core_Curve25519_Fe::fromArray($h);
}
/**
* Create a copy of a field element.
*
* @internal You should not use this directly from another application
*
* @param ParagonIE_Sodium_Core_Curve25519_Fe $f
* @return ParagonIE_Sodium_Core_Curve25519_Fe
*/
public static function fe_copy(ParagonIE_Sodium_Core_Curve25519_Fe $f)
{
$h = clone $f;
return $h;
}
/**
* Give: 32-byte string.
* Receive: A field element object to use for internal calculations.
*
* @internal You should not use this directly from another application
*
* @param string $s
* @return ParagonIE_Sodium_Core_Curve25519_Fe
* @throws RangeException
* @throws TypeError
*/
public static function fe_frombytes($s)
{
if (self::strlen($s) !== 32) {
throw new RangeException('Expected a 32-byte string.');
}
$h0 = self::load_4($s);
$h1 = self::load_3(self::substr($s, 4, 3)) << 6;
$h2 = self::load_3(self::substr($s, 7, 3)) << 5;
$h3 = self::load_3(self::substr($s, 10, 3)) << 3;
$h4 = self::load_3(self::substr($s, 13, 3)) << 2;
$h5 = self::load_4(self::substr($s, 16, 4));
$h6 = self::load_3(self::substr($s, 20, 3)) << 7;
$h7 = self::load_3(self::substr($s, 23, 3)) << 5;
$h8 = self::load_3(self::substr($s, 26, 3)) << 4;
$h9 = (self::load_3(self::substr($s, 29, 3)) & 8388607) << 2;
$carry9 = ($h9 + (1 << 24)) >> 25;
$h0 += self::mul($carry9, 19, 5);
$h9 -= $carry9 << 25;
$carry1 = ($h1 + (1 << 24)) >> 25;
$h2 += $carry1;
$h1 -= $carry1 << 25;
$carry3 = ($h3 + (1 << 24)) >> 25;
$h4 += $carry3;
$h3 -= $carry3 << 25;
$carry5 = ($h5 + (1 << 24)) >> 25;
$h6 += $carry5;
$h5 -= $carry5 << 25;
$carry7 = ($h7 + (1 << 24)) >> 25;
$h8 += $carry7;
$h7 -= $carry7 << 25;
$carry0 = ($h0 + (1 << 25)) >> 26;
$h1 += $carry0;
$h0 -= $carry0 << 26;
$carry2 = ($h2 + (1 << 25)) >> 26;
$h3 += $carry2;
$h2 -= $carry2 << 26;
$carry4 = ($h4 + (1 << 25)) >> 26;
$h5 += $carry4;
$h4 -= $carry4 << 26;
$carry6 = ($h6 + (1 << 25)) >> 26;
$h7 += $carry6;
$h6 -= $carry6 << 26;
$carry8 = ($h8 + (1 << 25)) >> 26;
$h9 += $carry8;
$h8 -= $carry8 << 26;
return ParagonIE_Sodium_Core_Curve25519_Fe::fromArray(
array(
(int) $h0,
(int) $h1,
(int) $h2,
(int) $h3,
(int) $h4,
(int) $h5,
(int) $h6,
(int) $h7,
(int) $h8,
(int) $h9
)
);
}
/**
* Convert a field element to a byte string.
*
* @internal You should not use this directly from another application
*
* @param ParagonIE_Sodium_Core_Curve25519_Fe $h
* @return string
*/
public static function fe_tobytes(ParagonIE_Sodium_Core_Curve25519_Fe $h)
{
$h0 = (int) $h[0];
$h1 = (int) $h[1];
$h2 = (int) $h[2];
$h3 = (int) $h[3];
$h4 = (int) $h[4];
$h5 = (int) $h[5];
$h6 = (int) $h[6];
$h7 = (int) $h[7];
$h8 = (int) $h[8];
$h9 = (int) $h[9];
$q = (self::mul($h9, 19, 5) + (1 << 24)) >> 25;
$q = ($h0 + $q) >> 26;
$q = ($h1 + $q) >> 25;
$q = ($h2 + $q) >> 26;
$q = ($h3 + $q) >> 25;
$q = ($h4 + $q) >> 26;
$q = ($h5 + $q) >> 25;
$q = ($h6 + $q) >> 26;
$q = ($h7 + $q) >> 25;
$q = ($h8 + $q) >> 26;
$q = ($h9 + $q) >> 25;
$h0 += self::mul($q, 19, 5);
$carry0 = $h0 >> 26;
$h1 += $carry0;
$h0 -= $carry0 << 26;
$carry1 = $h1 >> 25;
$h2 += $carry1;
$h1 -= $carry1 << 25;
$carry2 = $h2 >> 26;
$h3 += $carry2;
$h2 -= $carry2 << 26;
$carry3 = $h3 >> 25;
$h4 += $carry3;
$h3 -= $carry3 << 25;
$carry4 = $h4 >> 26;
$h5 += $carry4;
$h4 -= $carry4 << 26;
$carry5 = $h5 >> 25;
$h6 += $carry5;
$h5 -= $carry5 << 25;
$carry6 = $h6 >> 26;
$h7 += $carry6;
$h6 -= $carry6 << 26;
$carry7 = $h7 >> 25;
$h8 += $carry7;
$h7 -= $carry7 << 25;
$carry8 = $h8 >> 26;
$h9 += $carry8;
$h8 -= $carry8 << 26;
$carry9 = $h9 >> 25;
$h9 -= $carry9 << 25;
/**
* @var array<int, int>
*/
$s = array(
(int) (($h0 >> 0) & 0xff),
(int) (($h0 >> 8) & 0xff),
(int) (($h0 >> 16) & 0xff),
(int) ((($h0 >> 24) | ($h1 << 2)) & 0xff),
(int) (($h1 >> 6) & 0xff),
(int) (($h1 >> 14) & 0xff),
(int) ((($h1 >> 22) | ($h2 << 3)) & 0xff),
(int) (($h2 >> 5) & 0xff),
(int) (($h2 >> 13) & 0xff),
(int) ((($h2 >> 21) | ($h3 << 5)) & 0xff),
(int) (($h3 >> 3) & 0xff),
(int) (($h3 >> 11) & 0xff),
(int) ((($h3 >> 19) | ($h4 << 6)) & 0xff),
(int) (($h4 >> 2) & 0xff),
(int) (($h4 >> 10) & 0xff),
(int) (($h4 >> 18) & 0xff),
(int) (($h5 >> 0) & 0xff),
(int) (($h5 >> 8) & 0xff),
(int) (($h5 >> 16) & 0xff),
(int) ((($h5 >> 24) | ($h6 << 1)) & 0xff),
(int) (($h6 >> 7) & 0xff),
(int) (($h6 >> 15) & 0xff),
(int) ((($h6 >> 23) | ($h7 << 3)) & 0xff),
(int) (($h7 >> 5) & 0xff),
(int) (($h7 >> 13) & 0xff),
(int) ((($h7 >> 21) | ($h8 << 4)) & 0xff),
(int) (($h8 >> 4) & 0xff),
(int) (($h8 >> 12) & 0xff),
(int) ((($h8 >> 20) | ($h9 << 6)) & 0xff),
(int) (($h9 >> 2) & 0xff),
(int) (($h9 >> 10) & 0xff),
(int) (($h9 >> 18) & 0xff)
);
return self::intArrayToString($s);
}
/**
* Is a field element negative? (1 = yes, 0 = no. Used in calculations.)
*
* @internal You should not use this directly from another application
*
* @param ParagonIE_Sodium_Core_Curve25519_Fe $f
* @return int
* @throws SodiumException
* @throws TypeError
*/
public static function fe_isnegative(ParagonIE_Sodium_Core_Curve25519_Fe $f)
{
$str = self::fe_tobytes($f);
return (int) (self::chrToInt($str[0]) & 1);
}
/**
* Returns 0 if this field element results in all NUL bytes.
*
* @internal You should not use this directly from another application
*
* @param ParagonIE_Sodium_Core_Curve25519_Fe $f
* @return bool
* @throws SodiumException
* @throws TypeError
*/
public static function fe_isnonzero(ParagonIE_Sodium_Core_Curve25519_Fe $f)
{
static $zero;
if ($zero === null) {
$zero = str_repeat("\x00", 32);
}
/** @var string $zero */
/** @var string $str */
$str = self::fe_tobytes($f);
return !self::verify_32($str, (string) $zero);
}
/**
* Multiply two field elements
*
* h = f * g
*
* @internal You should not use this directly from another application
*
* @security Is multiplication a source of timing leaks? If so, can we do
* anything to prevent that from happening?
*
* @param ParagonIE_Sodium_Core_Curve25519_Fe $f
* @param ParagonIE_Sodium_Core_Curve25519_Fe $g
* @return ParagonIE_Sodium_Core_Curve25519_Fe
*/
public static function fe_mul(
ParagonIE_Sodium_Core_Curve25519_Fe $f,
ParagonIE_Sodium_Core_Curve25519_Fe $g
) {
// Ensure limbs aren't oversized.
$f = self::fe_normalize($f);
$g = self::fe_normalize($g);
$f0 = $f[0];
$f1 = $f[1];
$f2 = $f[2];
$f3 = $f[3];
$f4 = $f[4];
$f5 = $f[5];
$f6 = $f[6];
$f7 = $f[7];
$f8 = $f[8];
$f9 = $f[9];
$g0 = $g[0];
$g1 = $g[1];
$g2 = $g[2];
$g3 = $g[3];
$g4 = $g[4];
$g5 = $g[5];
$g6 = $g[6];
$g7 = $g[7];
$g8 = $g[8];
$g9 = $g[9];
$g1_19 = self::mul($g1, 19, 5);
$g2_19 = self::mul($g2, 19, 5);
$g3_19 = self::mul($g3, 19, 5);
$g4_19 = self::mul($g4, 19, 5);
$g5_19 = self::mul($g5, 19, 5);
$g6_19 = self::mul($g6, 19, 5);
$g7_19 = self::mul($g7, 19, 5);
$g8_19 = self::mul($g8, 19, 5);
$g9_19 = self::mul($g9, 19, 5);
$f1_2 = $f1 << 1;
$f3_2 = $f3 << 1;
$f5_2 = $f5 << 1;
$f7_2 = $f7 << 1;
$f9_2 = $f9 << 1;
$f0g0 = self::mul($f0, $g0, 26);
$f0g1 = self::mul($f0, $g1, 25);
$f0g2 = self::mul($f0, $g2, 26);
$f0g3 = self::mul($f0, $g3, 25);
$f0g4 = self::mul($f0, $g4, 26);
$f0g5 = self::mul($f0, $g5, 25);
$f0g6 = self::mul($f0, $g6, 26);
$f0g7 = self::mul($f0, $g7, 25);
$f0g8 = self::mul($f0, $g8, 26);
$f0g9 = self::mul($f0, $g9, 26);
$f1g0 = self::mul($f1, $g0, 26);
$f1g1_2 = self::mul($f1_2, $g1, 25);
$f1g2 = self::mul($f1, $g2, 26);
$f1g3_2 = self::mul($f1_2, $g3, 25);
$f1g4 = self::mul($f1, $g4, 26);
$f1g5_2 = self::mul($f1_2, $g5, 25);
$f1g6 = self::mul($f1, $g6, 26);
$f1g7_2 = self::mul($f1_2, $g7, 25);
$f1g8 = self::mul($f1, $g8, 26);
$f1g9_38 = self::mul($g9_19, $f1_2, 26);
$f2g0 = self::mul($f2, $g0, 26);
$f2g1 = self::mul($f2, $g1, 25);
$f2g2 = self::mul($f2, $g2, 26);
$f2g3 = self::mul($f2, $g3, 25);
$f2g4 = self::mul($f2, $g4, 26);
$f2g5 = self::mul($f2, $g5, 25);
$f2g6 = self::mul($f2, $g6, 26);
$f2g7 = self::mul($f2, $g7, 25);
$f2g8_19 = self::mul($g8_19, $f2, 26);
$f2g9_19 = self::mul($g9_19, $f2, 26);
$f3g0 = self::mul($f3, $g0, 26);
$f3g1_2 = self::mul($f3_2, $g1, 25);
$f3g2 = self::mul($f3, $g2, 26);
$f3g3_2 = self::mul($f3_2, $g3, 25);
$f3g4 = self::mul($f3, $g4, 26);
$f3g5_2 = self::mul($f3_2, $g5, 25);
$f3g6 = self::mul($f3, $g6, 26);
$f3g7_38 = self::mul($g7_19, $f3_2, 26);
$f3g8_19 = self::mul($g8_19, $f3, 25);
$f3g9_38 = self::mul($g9_19, $f3_2, 26);
$f4g0 = self::mul($f4, $g0, 26);
$f4g1 = self::mul($f4, $g1, 25);
$f4g2 = self::mul($f4, $g2, 26);
$f4g3 = self::mul($f4, $g3, 25);
$f4g4 = self::mul($f4, $g4, 26);
$f4g5 = self::mul($f4, $g5, 25);
$f4g6_19 = self::mul($g6_19, $f4, 26);
$f4g7_19 = self::mul($g7_19, $f4, 26);
$f4g8_19 = self::mul($g8_19, $f4, 26);
$f4g9_19 = self::mul($g9_19, $f4, 26);
$f5g0 = self::mul($f5, $g0, 26);
$f5g1_2 = self::mul($f5_2, $g1, 25);
$f5g2 = self::mul($f5, $g2, 26);
$f5g3_2 = self::mul($f5_2, $g3, 25);
$f5g4 = self::mul($f5, $g4, 26);
$f5g5_38 = self::mul($g5_19, $f5_2, 26);
$f5g6_19 = self::mul($g6_19, $f5, 25);
$f5g7_38 = self::mul($g7_19, $f5_2, 26);
$f5g8_19 = self::mul($g8_19, $f5, 25);
$f5g9_38 = self::mul($g9_19, $f5_2, 26);
$f6g0 = self::mul($f6, $g0, 26);
$f6g1 = self::mul($f6, $g1, 25);
$f6g2 = self::mul($f6, $g2, 26);
$f6g3 = self::mul($f6, $g3, 25);
$f6g4_19 = self::mul($g4_19, $f6, 26);
$f6g5_19 = self::mul($g5_19, $f6, 26);
$f6g6_19 = self::mul($g6_19, $f6, 26);
$f6g7_19 = self::mul($g7_19, $f6, 26);
$f6g8_19 = self::mul($g8_19, $f6, 26);
$f6g9_19 = self::mul($g9_19, $f6, 26);
$f7g0 = self::mul($f7, $g0, 26);
$f7g1_2 = self::mul($f7_2, $g1, 25);
$f7g2 = self::mul($f7, $g2, 26);
$f7g3_38 = self::mul($g3_19, $f7_2, 26);
$f7g4_19 = self::mul($g4_19, $f7, 26);
$f7g5_38 = self::mul($g5_19, $f7_2, 26);
$f7g6_19 = self::mul($g6_19, $f7, 25);
$f7g7_38 = self::mul($g7_19, $f7_2, 26);
$f7g8_19 = self::mul($g8_19, $f7, 25);
$f7g9_38 = self::mul($g9_19,$f7_2, 26);
$f8g0 = self::mul($f8, $g0, 26);
$f8g1 = self::mul($f8, $g1, 25);
$f8g2_19 = self::mul($g2_19, $f8, 26);
$f8g3_19 = self::mul($g3_19, $f8, 26);
$f8g4_19 = self::mul($g4_19, $f8, 26);
$f8g5_19 = self::mul($g5_19, $f8, 26);
$f8g6_19 = self::mul($g6_19, $f8, 26);
$f8g7_19 = self::mul($g7_19, $f8, 26);
$f8g8_19 = self::mul($g8_19, $f8, 26);
$f8g9_19 = self::mul($g9_19, $f8, 26);
$f9g0 = self::mul($f9, $g0, 26);
$f9g1_38 = self::mul($g1_19, $f9_2, 26);
$f9g2_19 = self::mul($g2_19, $f9, 25);
$f9g3_38 = self::mul($g3_19, $f9_2, 26);
$f9g4_19 = self::mul($g4_19, $f9, 25);
$f9g5_38 = self::mul($g5_19, $f9_2, 26);
$f9g6_19 = self::mul($g6_19, $f9, 25);
$f9g7_38 = self::mul($g7_19, $f9_2, 26);
$f9g8_19 = self::mul($g8_19, $f9, 25);
$f9g9_38 = self::mul($g9_19, $f9_2, 26);
$h0 = $f0g0 + $f1g9_38 + $f2g8_19 + $f3g7_38 + $f4g6_19 + $f5g5_38 + $f6g4_19 + $f7g3_38 + $f8g2_19 + $f9g1_38;
$h1 = $f0g1 + $f1g0 + $f2g9_19 + $f3g8_19 + $f4g7_19 + $f5g6_19 + $f6g5_19 + $f7g4_19 + $f8g3_19 + $f9g2_19;
$h2 = $f0g2 + $f1g1_2 + $f2g0 + $f3g9_38 + $f4g8_19 + $f5g7_38 + $f6g6_19 + $f7g5_38 + $f8g4_19 + $f9g3_38;
$h3 = $f0g3 + $f1g2 + $f2g1 + $f3g0 + $f4g9_19 + $f5g8_19 + $f6g7_19 + $f7g6_19 + $f8g5_19 + $f9g4_19;
$h4 = $f0g4 + $f1g3_2 + $f2g2 + $f3g1_2 + $f4g0 + $f5g9_38 + $f6g8_19 + $f7g7_38 + $f8g6_19 + $f9g5_38;
$h5 = $f0g5 + $f1g4 + $f2g3 + $f3g2 + $f4g1 + $f5g0 + $f6g9_19 + $f7g8_19 + $f8g7_19 + $f9g6_19;
$h6 = $f0g6 + $f1g5_2 + $f2g4 + $f3g3_2 + $f4g2 + $f5g1_2 + $f6g0 + $f7g9_38 + $f8g8_19 + $f9g7_38;
$h7 = $f0g7 + $f1g6 + $f2g5 + $f3g4 + $f4g3 + $f5g2 + $f6g1 + $f7g0 + $f8g9_19 + $f9g8_19;
$h8 = $f0g8 + $f1g7_2 + $f2g6 + $f3g5_2 + $f4g4 + $f5g3_2 + $f6g2 + $f7g1_2 + $f8g0 + $f9g9_38;
$h9 = $f0g9 + $f1g8 + $f2g7 + $f3g6 + $f4g5 + $f5g4 + $f6g3 + $f7g2 + $f8g1 + $f9g0 ;
$carry0 = ($h0 + (1 << 25)) >> 26;
$h1 += $carry0;
$h0 -= $carry0 << 26;
$carry4 = ($h4 + (1 << 25)) >> 26;
$h5 += $carry4;
$h4 -= $carry4 << 26;
$carry1 = ($h1 + (1 << 24)) >> 25;
$h2 += $carry1;
$h1 -= $carry1 << 25;
$carry5 = ($h5 + (1 << 24)) >> 25;
$h6 += $carry5;
$h5 -= $carry5 << 25;
$carry2 = ($h2 + (1 << 25)) >> 26;
$h3 += $carry2;
$h2 -= $carry2 << 26;
$carry6 = ($h6 + (1 << 25)) >> 26;
$h7 += $carry6;
$h6 -= $carry6 << 26;
$carry3 = ($h3 + (1 << 24)) >> 25;
$h4 += $carry3;
$h3 -= $carry3 << 25;
$carry7 = ($h7 + (1 << 24)) >> 25;
$h8 += $carry7;
$h7 -= $carry7 << 25;
$carry4 = ($h4 + (1 << 25)) >> 26;
$h5 += $carry4;
$h4 -= $carry4 << 26;
$carry8 = ($h8 + (1 << 25)) >> 26;
$h9 += $carry8;
$h8 -= $carry8 << 26;
$carry9 = ($h9 + (1 << 24)) >> 25;
$h0 += self::mul($carry9, 19, 5);
$h9 -= $carry9 << 25;
$carry0 = ($h0 + (1 << 25)) >> 26;
$h1 += $carry0;
$h0 -= $carry0 << 26;
return self::fe_normalize(
ParagonIE_Sodium_Core_Curve25519_Fe::fromArray(
array(
(int) $h0,
(int) $h1,
(int) $h2,
(int) $h3,
(int) $h4,
(int) $h5,
(int) $h6,
(int) $h7,
(int) $h8,
(int) $h9
)
)
);
}
/**
* Get the negative values for each piece of the field element.
*
* h = -f
*
* @internal You should not use this directly from another application
*
* @param ParagonIE_Sodium_Core_Curve25519_Fe $f
* @return ParagonIE_Sodium_Core_Curve25519_Fe
* @psalm-suppress MixedAssignment
*/
public static function fe_neg(ParagonIE_Sodium_Core_Curve25519_Fe $f)
{
$h = new ParagonIE_Sodium_Core_Curve25519_Fe();
for ($i = 0; $i < 10; ++$i) {
$h[$i] = -$f[$i];
}
return self::fe_normalize($h);
}
/**
* Square a field element
*
* h = f * f
*
* @internal You should not use this directly from another application
*
* @param ParagonIE_Sodium_Core_Curve25519_Fe $f
* @return ParagonIE_Sodium_Core_Curve25519_Fe
*/
public static function fe_sq(ParagonIE_Sodium_Core_Curve25519_Fe $f)
{
$f = self::fe_normalize($f);
$f0 = (int) $f[0];
$f1 = (int) $f[1];
$f2 = (int) $f[2];
$f3 = (int) $f[3];
$f4 = (int) $f[4];
$f5 = (int) $f[5];
$f6 = (int) $f[6];
$f7 = (int) $f[7];
$f8 = (int) $f[8];
$f9 = (int) $f[9];
$f0_2 = $f0 << 1;
$f1_2 = $f1 << 1;
$f2_2 = $f2 << 1;
$f3_2 = $f3 << 1;
$f4_2 = $f4 << 1;
$f5_2 = $f5 << 1;
$f6_2 = $f6 << 1;
$f7_2 = $f7 << 1;
$f5_38 = self::mul($f5, 38, 6);
$f6_19 = self::mul($f6, 19, 5);
$f7_38 = self::mul($f7, 38, 6);
$f8_19 = self::mul($f8, 19, 5);
$f9_38 = self::mul($f9, 38, 6);
$f0f0 = self::mul($f0, $f0, 26);
$f0f1_2 = self::mul($f0_2, $f1, 26);
$f0f2_2 = self::mul($f0_2, $f2, 26);
$f0f3_2 = self::mul($f0_2, $f3, 26);
$f0f4_2 = self::mul($f0_2, $f4, 26);
$f0f5_2 = self::mul($f0_2, $f5, 26);
$f0f6_2 = self::mul($f0_2, $f6, 26);
$f0f7_2 = self::mul($f0_2, $f7, 26);
$f0f8_2 = self::mul($f0_2, $f8, 26);
$f0f9_2 = self::mul($f0_2, $f9, 26);
$f1f1_2 = self::mul($f1_2, $f1, 26);
$f1f2_2 = self::mul($f1_2, $f2, 26);
$f1f3_4 = self::mul($f1_2, $f3_2, 26);
$f1f4_2 = self::mul($f1_2, $f4, 26);
$f1f5_4 = self::mul($f1_2, $f5_2, 26);
$f1f6_2 = self::mul($f1_2, $f6, 26);
$f1f7_4 = self::mul($f1_2, $f7_2, 26);
$f1f8_2 = self::mul($f1_2, $f8, 26);
$f1f9_76 = self::mul($f9_38, $f1_2, 27);
$f2f2 = self::mul($f2, $f2, 27);
$f2f3_2 = self::mul($f2_2, $f3, 27);
$f2f4_2 = self::mul($f2_2, $f4, 27);
$f2f5_2 = self::mul($f2_2, $f5, 27);
$f2f6_2 = self::mul($f2_2, $f6, 27);
$f2f7_2 = self::mul($f2_2, $f7, 27);
$f2f8_38 = self::mul($f8_19, $f2_2, 27);
$f2f9_38 = self::mul($f9_38, $f2, 26);
$f3f3_2 = self::mul($f3_2, $f3, 26);
$f3f4_2 = self::mul($f3_2, $f4, 26);
$f3f5_4 = self::mul($f3_2, $f5_2, 26);
$f3f6_2 = self::mul($f3_2, $f6, 26);
$f3f7_76 = self::mul($f7_38, $f3_2, 26);
$f3f8_38 = self::mul($f8_19, $f3_2, 26);
$f3f9_76 = self::mul($f9_38, $f3_2, 26);
$f4f4 = self::mul($f4, $f4, 26);
$f4f5_2 = self::mul($f4_2, $f5, 26);
$f4f6_38 = self::mul($f6_19, $f4_2, 27);
$f4f7_38 = self::mul($f7_38, $f4, 26);
$f4f8_38 = self::mul($f8_19, $f4_2, 27);
$f4f9_38 = self::mul($f9_38, $f4, 26);
$f5f5_38 = self::mul($f5_38, $f5, 26);
$f5f6_38 = self::mul($f6_19, $f5_2, 26);
$f5f7_76 = self::mul($f7_38, $f5_2, 26);
$f5f8_38 = self::mul($f8_19, $f5_2, 26);
$f5f9_76 = self::mul($f9_38, $f5_2, 26);
$f6f6_19 = self::mul($f6_19, $f6, 26);
$f6f7_38 = self::mul($f7_38, $f6, 26);
$f6f8_38 = self::mul($f8_19, $f6_2, 27);
$f6f9_38 = self::mul($f9_38, $f6, 26);
$f7f7_38 = self::mul($f7_38, $f7, 26);
$f7f8_38 = self::mul($f8_19, $f7_2, 26);
$f7f9_76 = self::mul($f9_38, $f7_2, 26);
$f8f8_19 = self::mul($f8_19, $f8, 26);
$f8f9_38 = self::mul($f9_38, $f8, 26);
$f9f9_38 = self::mul($f9_38, $f9, 26);
$h0 = $f0f0 + $f1f9_76 + $f2f8_38 + $f3f7_76 + $f4f6_38 + $f5f5_38;
$h1 = $f0f1_2 + $f2f9_38 + $f3f8_38 + $f4f7_38 + $f5f6_38;
$h2 = $f0f2_2 + $f1f1_2 + $f3f9_76 + $f4f8_38 + $f5f7_76 + $f6f6_19;
$h3 = $f0f3_2 + $f1f2_2 + $f4f9_38 + $f5f8_38 + $f6f7_38;
$h4 = $f0f4_2 + $f1f3_4 + $f2f2 + $f5f9_76 + $f6f8_38 + $f7f7_38;
$h5 = $f0f5_2 + $f1f4_2 + $f2f3_2 + $f6f9_38 + $f7f8_38;
$h6 = $f0f6_2 + $f1f5_4 + $f2f4_2 + $f3f3_2 + $f7f9_76 + $f8f8_19;
$h7 = $f0f7_2 + $f1f6_2 + $f2f5_2 + $f3f4_2 + $f8f9_38;
$h8 = $f0f8_2 + $f1f7_4 + $f2f6_2 + $f3f5_4 + $f4f4 + $f9f9_38;
$h9 = $f0f9_2 + $f1f8_2 + $f2f7_2 + $f3f6_2 + $f4f5_2;
$carry0 = ($h0 + (1 << 25)) >> 26;
$h1 += $carry0;
$h0 -= $carry0 << 26;
$carry4 = ($h4 + (1 << 25)) >> 26;
$h5 += $carry4;
$h4 -= $carry4 << 26;
$carry1 = ($h1 + (1 << 24)) >> 25;
$h2 += $carry1;
$h1 -= $carry1 << 25;
$carry5 = ($h5 + (1 << 24)) >> 25;
$h6 += $carry5;
$h5 -= $carry5 << 25;
$carry2 = ($h2 + (1 << 25)) >> 26;
$h3 += $carry2;
$h2 -= $carry2 << 26;
$carry6 = ($h6 + (1 << 25)) >> 26;
$h7 += $carry6;
$h6 -= $carry6 << 26;
$carry3 = ($h3 + (1 << 24)) >> 25;
$h4 += $carry3;
$h3 -= $carry3 << 25;
$carry7 = ($h7 + (1 << 24)) >> 25;
$h8 += $carry7;
$h7 -= $carry7 << 25;
$carry4 = ($h4 + (1 << 25)) >> 26;
$h5 += $carry4;
$h4 -= $carry4 << 26;
$carry8 = ($h8 + (1 << 25)) >> 26;
$h9 += $carry8;
$h8 -= $carry8 << 26;
$carry9 = ($h9 + (1 << 24)) >> 25;
$h0 += self::mul($carry9, 19, 5);
$h9 -= $carry9 << 25;
$carry0 = ($h0 + (1 << 25)) >> 26;
$h1 += $carry0;
$h0 -= $carry0 << 26;
return self::fe_normalize(
ParagonIE_Sodium_Core_Curve25519_Fe::fromArray(
array(
(int) $h0,
(int) $h1,
(int) $h2,
(int) $h3,
(int) $h4,
(int) $h5,
(int) $h6,
(int) $h7,
(int) $h8,
(int) $h9
)
)
);
}
/**
* Square and double a field element
*
* h = 2 * f * f
*
* @internal You should not use this directly from another application
*
* @param ParagonIE_Sodium_Core_Curve25519_Fe $f
* @return ParagonIE_Sodium_Core_Curve25519_Fe
*/
public static function fe_sq2(ParagonIE_Sodium_Core_Curve25519_Fe $f)
{
$f = self::fe_normalize($f);
$f0 = (int) $f[0];
$f1 = (int) $f[1];
$f2 = (int) $f[2];
$f3 = (int) $f[3];
$f4 = (int) $f[4];
$f5 = (int) $f[5];
$f6 = (int) $f[6];
$f7 = (int) $f[7];
$f8 = (int) $f[8];
$f9 = (int) $f[9];
$f0_2 = $f0 << 1;
$f1_2 = $f1 << 1;
$f2_2 = $f2 << 1;
$f3_2 = $f3 << 1;
$f4_2 = $f4 << 1;
$f5_2 = $f5 << 1;
$f6_2 = $f6 << 1;
$f7_2 = $f7 << 1;
$f5_38 = self::mul($f5, 38, 6); /* 1.959375*2^30 */
$f6_19 = self::mul($f6, 19, 5); /* 1.959375*2^30 */
$f7_38 = self::mul($f7, 38, 6); /* 1.959375*2^30 */
$f8_19 = self::mul($f8, 19, 5); /* 1.959375*2^30 */
$f9_38 = self::mul($f9, 38, 6); /* 1.959375*2^30 */
$f0f0 = self::mul($f0, $f0, 24);
$f0f1_2 = self::mul($f0_2, $f1, 24);
$f0f2_2 = self::mul($f0_2, $f2, 24);
$f0f3_2 = self::mul($f0_2, $f3, 24);
$f0f4_2 = self::mul($f0_2, $f4, 24);
$f0f5_2 = self::mul($f0_2, $f5, 24);
$f0f6_2 = self::mul($f0_2, $f6, 24);
$f0f7_2 = self::mul($f0_2, $f7, 24);
$f0f8_2 = self::mul($f0_2, $f8, 24);
$f0f9_2 = self::mul($f0_2, $f9, 24);
$f1f1_2 = self::mul($f1_2, $f1, 24);
$f1f2_2 = self::mul($f1_2, $f2, 24);
$f1f3_4 = self::mul($f1_2, $f3_2, 24);
$f1f4_2 = self::mul($f1_2, $f4, 24);
$f1f5_4 = self::mul($f1_2, $f5_2, 24);
$f1f6_2 = self::mul($f1_2, $f6, 24);
$f1f7_4 = self::mul($f1_2, $f7_2, 24);
$f1f8_2 = self::mul($f1_2, $f8, 24);
$f1f9_76 = self::mul($f9_38, $f1_2, 24);
$f2f2 = self::mul($f2, $f2, 24);
$f2f3_2 = self::mul($f2_2, $f3, 24);
$f2f4_2 = self::mul($f2_2, $f4, 24);
$f2f5_2 = self::mul($f2_2, $f5, 24);
$f2f6_2 = self::mul($f2_2, $f6, 24);
$f2f7_2 = self::mul($f2_2, $f7, 24);
$f2f8_38 = self::mul($f8_19, $f2_2, 25);
$f2f9_38 = self::mul($f9_38, $f2, 24);
$f3f3_2 = self::mul($f3_2, $f3, 24);
$f3f4_2 = self::mul($f3_2, $f4, 24);
$f3f5_4 = self::mul($f3_2, $f5_2, 24);
$f3f6_2 = self::mul($f3_2, $f6, 24);
$f3f7_76 = self::mul($f7_38, $f3_2, 24);
$f3f8_38 = self::mul($f8_19, $f3_2, 24);
$f3f9_76 = self::mul($f9_38, $f3_2, 24);
$f4f4 = self::mul($f4, $f4, 24);
$f4f5_2 = self::mul($f4_2, $f5, 24);
$f4f6_38 = self::mul($f6_19, $f4_2, 25);
$f4f7_38 = self::mul($f7_38, $f4, 24);
$f4f8_38 = self::mul($f8_19, $f4_2, 25);
$f4f9_38 = self::mul($f9_38, $f4, 24);
$f5f5_38 = self::mul($f5_38, $f5, 24);
$f5f6_38 = self::mul($f6_19, $f5_2, 24);
$f5f7_76 = self::mul($f7_38, $f5_2, 24);
$f5f8_38 = self::mul($f8_19, $f5_2, 24);
$f5f9_76 = self::mul($f9_38, $f5_2, 24);
$f6f6_19 = self::mul($f6_19, $f6, 24);
$f6f7_38 = self::mul($f7_38, $f6, 24);
$f6f8_38 = self::mul($f8_19, $f6_2, 25);
$f6f9_38 = self::mul($f9_38, $f6, 24);
$f7f7_38 = self::mul($f7_38, $f7, 24);
$f7f8_38 = self::mul($f8_19, $f7_2, 24);
$f7f9_76 = self::mul($f9_38, $f7_2, 24);
$f8f8_19 = self::mul($f8_19, $f8, 24);
$f8f9_38 = self::mul($f9_38, $f8, 24);
$f9f9_38 = self::mul($f9_38, $f9, 24);
$h0 = (int) ($f0f0 + $f1f9_76 + $f2f8_38 + $f3f7_76 + $f4f6_38 + $f5f5_38) << 1;
$h1 = (int) ($f0f1_2 + $f2f9_38 + $f3f8_38 + $f4f7_38 + $f5f6_38) << 1;
$h2 = (int) ($f0f2_2 + $f1f1_2 + $f3f9_76 + $f4f8_38 + $f5f7_76 + $f6f6_19) << 1;
$h3 = (int) ($f0f3_2 + $f1f2_2 + $f4f9_38 + $f5f8_38 + $f6f7_38) << 1;
$h4 = (int) ($f0f4_2 + $f1f3_4 + $f2f2 + $f5f9_76 + $f6f8_38 + $f7f7_38) << 1;
$h5 = (int) ($f0f5_2 + $f1f4_2 + $f2f3_2 + $f6f9_38 + $f7f8_38) << 1;
$h6 = (int) ($f0f6_2 + $f1f5_4 + $f2f4_2 + $f3f3_2 + $f7f9_76 + $f8f8_19) << 1;
$h7 = (int) ($f0f7_2 + $f1f6_2 + $f2f5_2 + $f3f4_2 + $f8f9_38) << 1;
$h8 = (int) ($f0f8_2 + $f1f7_4 + $f2f6_2 + $f3f5_4 + $f4f4 + $f9f9_38) << 1;
$h9 = (int) ($f0f9_2 + $f1f8_2 + $f2f7_2 + $f3f6_2 + $f4f5_2) << 1;
$carry0 = ($h0 + (1 << 25)) >> 26;
$h1 += $carry0;
$h0 -= $carry0 << 26;
$carry4 = ($h4 + (1 << 25)) >> 26;
$h5 += $carry4;
$h4 -= $carry4 << 26;
$carry1 = ($h1 + (1 << 24)) >> 25;
$h2 += $carry1;
$h1 -= $carry1 << 25;
$carry5 = ($h5 + (1 << 24)) >> 25;
$h6 += $carry5;
$h5 -= $carry5 << 25;
$carry2 = ($h2 + (1 << 25)) >> 26;
$h3 += $carry2;
$h2 -= $carry2 << 26;
$carry6 = ($h6 + (1 << 25)) >> 26;
$h7 += $carry6;
$h6 -= $carry6 << 26;
$carry3 = ($h3 + (1 << 24)) >> 25;
$h4 += $carry3;
$h3 -= $carry3 << 25;
$carry7 = ($h7 + (1 << 24)) >> 25;
$h8 += $carry7;
$h7 -= $carry7 << 25;
$carry4 = ($h4 + (1 << 25)) >> 26;
$h5 += $carry4;
$h4 -= $carry4 << 26;
$carry8 = ($h8 + (1 << 25)) >> 26;
$h9 += $carry8;
$h8 -= $carry8 << 26;
$carry9 = ($h9 + (1 << 24)) >> 25;
$h0 += self::mul($carry9, 19, 5);
$h9 -= $carry9 << 25;
$carry0 = ($h0 + (1 << 25)) >> 26;
$h1 += $carry0;
$h0 -= $carry0 << 26;
return self::fe_normalize(
ParagonIE_Sodium_Core_Curve25519_Fe::fromArray(
array(
(int) $h0,
(int) $h1,
(int) $h2,
(int) $h3,
(int) $h4,
(int) $h5,
(int) $h6,
(int) $h7,
(int) $h8,
(int) $h9
)
)
);
}
/**
* @internal You should not use this directly from another application
*
* @param ParagonIE_Sodium_Core_Curve25519_Fe $Z
* @return ParagonIE_Sodium_Core_Curve25519_Fe
*/
public static function fe_invert(ParagonIE_Sodium_Core_Curve25519_Fe $Z)
{
$z = clone $Z;
$t0 = self::fe_sq($z);
$t1 = self::fe_sq($t0);
$t1 = self::fe_sq($t1);
$t1 = self::fe_mul($z, $t1);
$t0 = self::fe_mul($t0, $t1);
$t2 = self::fe_sq($t0);
$t1 = self::fe_mul($t1, $t2);
$t2 = self::fe_sq($t1);
for ($i = 1; $i < 5; ++$i) {
$t2 = self::fe_sq($t2);
}
$t1 = self::fe_mul($t2, $t1);
$t2 = self::fe_sq($t1);
for ($i = 1; $i < 10; ++$i) {
$t2 = self::fe_sq($t2);
}
$t2 = self::fe_mul($t2, $t1);
$t3 = self::fe_sq($t2);
for ($i = 1; $i < 20; ++$i) {
$t3 = self::fe_sq($t3);
}
$t2 = self::fe_mul($t3, $t2);
$t2 = self::fe_sq($t2);
for ($i = 1; $i < 10; ++$i) {
$t2 = self::fe_sq($t2);
}
$t1 = self::fe_mul($t2, $t1);
$t2 = self::fe_sq($t1);
for ($i = 1; $i < 50; ++$i) {
$t2 = self::fe_sq($t2);
}
$t2 = self::fe_mul($t2, $t1);
$t3 = self::fe_sq($t2);
for ($i = 1; $i < 100; ++$i) {
$t3 = self::fe_sq($t3);
}
$t2 = self::fe_mul($t3, $t2);
$t2 = self::fe_sq($t2);
for ($i = 1; $i < 50; ++$i) {
$t2 = self::fe_sq($t2);
}
$t1 = self::fe_mul($t2, $t1);
$t1 = self::fe_sq($t1);
for ($i = 1; $i < 5; ++$i) {
$t1 = self::fe_sq($t1);
}
return self::fe_mul($t1, $t0);
}
/**
* @internal You should not use this directly from another application
*
* @ref https://github.com/jedisct1/libsodium/blob/68564326e1e9dc57ef03746f85734232d20ca6fb/src/libsodium/crypto_core/curve25519/ref10/curve25519_ref10.c#L1054-L1106
*
* @param ParagonIE_Sodium_Core_Curve25519_Fe $z
* @return ParagonIE_Sodium_Core_Curve25519_Fe
*/
public static function fe_pow22523(ParagonIE_Sodium_Core_Curve25519_Fe $z)
{
$z = self::fe_normalize($z);
# fe_sq(t0, z);
# fe_sq(t1, t0);
# fe_sq(t1, t1);
# fe_mul(t1, z, t1);
# fe_mul(t0, t0, t1);
# fe_sq(t0, t0);
# fe_mul(t0, t1, t0);
# fe_sq(t1, t0);
$t0 = self::fe_sq($z);
$t1 = self::fe_sq($t0);
$t1 = self::fe_sq($t1);
$t1 = self::fe_mul($z, $t1);
$t0 = self::fe_mul($t0, $t1);
$t0 = self::fe_sq($t0);
$t0 = self::fe_mul($t1, $t0);
$t1 = self::fe_sq($t0);
# for (i = 1; i < 5; ++i) {
# fe_sq(t1, t1);
# }
for ($i = 1; $i < 5; ++$i) {
$t1 = self::fe_sq($t1);
}
# fe_mul(t0, t1, t0);
# fe_sq(t1, t0);
$t0 = self::fe_mul($t1, $t0);
$t1 = self::fe_sq($t0);
# for (i = 1; i < 10; ++i) {
# fe_sq(t1, t1);
# }
for ($i = 1; $i < 10; ++$i) {
$t1 = self::fe_sq($t1);
}
# fe_mul(t1, t1, t0);
# fe_sq(t2, t1);
$t1 = self::fe_mul($t1, $t0);
$t2 = self::fe_sq($t1);
# for (i = 1; i < 20; ++i) {
# fe_sq(t2, t2);
# }
for ($i = 1; $i < 20; ++$i) {
$t2 = self::fe_sq($t2);
}
# fe_mul(t1, t2, t1);
# fe_sq(t1, t1);
$t1 = self::fe_mul($t2, $t1);
$t1 = self::fe_sq($t1);
# for (i = 1; i < 10; ++i) {
# fe_sq(t1, t1);
# }
for ($i = 1; $i < 10; ++$i) {
$t1 = self::fe_sq($t1);
}
# fe_mul(t0, t1, t0);
# fe_sq(t1, t0);
$t0 = self::fe_mul($t1, $t0);
$t1 = self::fe_sq($t0);
# for (i = 1; i < 50; ++i) {
# fe_sq(t1, t1);
# }
for ($i = 1; $i < 50; ++$i) {
$t1 = self::fe_sq($t1);
}
# fe_mul(t1, t1, t0);
# fe_sq(t2, t1);
$t1 = self::fe_mul($t1, $t0);
$t2 = self::fe_sq($t1);
# for (i = 1; i < 100; ++i) {
# fe_sq(t2, t2);
# }
for ($i = 1; $i < 100; ++$i) {
$t2 = self::fe_sq($t2);
}
# fe_mul(t1, t2, t1);
# fe_sq(t1, t1);
$t1 = self::fe_mul($t2, $t1);
$t1 = self::fe_sq($t1);
# for (i = 1; i < 50; ++i) {
# fe_sq(t1, t1);
# }
for ($i = 1; $i < 50; ++$i) {
$t1 = self::fe_sq($t1);
}
# fe_mul(t0, t1, t0);
# fe_sq(t0, t0);
# fe_sq(t0, t0);
# fe_mul(out, t0, z);
$t0 = self::fe_mul($t1, $t0);
$t0 = self::fe_sq($t0);
$t0 = self::fe_sq($t0);
return self::fe_mul($t0, $z);
}
/**
* Subtract two field elements.
*
* h = f - g
*
* Preconditions:
* |f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
* |g| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
*
* Postconditions:
* |h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
*
* @internal You should not use this directly from another application
*
* @param ParagonIE_Sodium_Core_Curve25519_Fe $f
* @param ParagonIE_Sodium_Core_Curve25519_Fe $g
* @return ParagonIE_Sodium_Core_Curve25519_Fe
* @psalm-suppress MixedOperand
*/
public static function fe_sub(ParagonIE_Sodium_Core_Curve25519_Fe $f, ParagonIE_Sodium_Core_Curve25519_Fe $g)
{
return self::fe_normalize(
ParagonIE_Sodium_Core_Curve25519_Fe::fromArray(
array(
(int) ($f[0] - $g[0]),
(int) ($f[1] - $g[1]),
(int) ($f[2] - $g[2]),
(int) ($f[3] - $g[3]),
(int) ($f[4] - $g[4]),
(int) ($f[5] - $g[5]),
(int) ($f[6] - $g[6]),
(int) ($f[7] - $g[7]),
(int) ($f[8] - $g[8]),
(int) ($f[9] - $g[9])
)
)
);
}
/**
* Add two group elements.
*
* r = p + q
*
* @internal You should not use this directly from another application
*
* @param ParagonIE_Sodium_Core_Curve25519_Ge_P3 $p
* @param ParagonIE_Sodium_Core_Curve25519_Ge_Cached $q
* @return ParagonIE_Sodium_Core_Curve25519_Ge_P1p1
*/
public static function ge_add(
ParagonIE_Sodium_Core_Curve25519_Ge_P3 $p,
ParagonIE_Sodium_Core_Curve25519_Ge_Cached $q
) {
$r = new ParagonIE_Sodium_Core_Curve25519_Ge_P1p1();
$r->X = self::fe_add($p->Y, $p->X);
$r->Y = self::fe_sub($p->Y, $p->X);
$r->Z = self::fe_mul($r->X, $q->YplusX);
$r->Y = self::fe_mul($r->Y, $q->YminusX);
$r->T = self::fe_mul($q->T2d, $p->T);
$r->X = self::fe_mul($p->Z, $q->Z);
$t0 = self::fe_add($r->X, $r->X);
$r->X = self::fe_sub($r->Z, $r->Y);
$r->Y = self::fe_add($r->Z, $r->Y);
$r->Z = self::fe_add($t0, $r->T);
$r->T = self::fe_sub($t0, $r->T);
return $r;
}
/**
* @internal You should not use this directly from another application
*
* @ref https://github.com/jedisct1/libsodium/blob/157c4a80c13b117608aeae12178b2d38825f9f8f/src/libsodium/crypto_core/curve25519/ref10/curve25519_ref10.c#L1185-L1215
* @param string $a
* @return array<int, mixed>
* @throws SodiumException
* @throws TypeError
*/
public static function slide($a)
{
if (self::strlen($a) < 256) {
if (self::strlen($a) < 16) {
$a = str_pad($a, 256, '0', STR_PAD_RIGHT);
}
}
/** @var array<int, int> $r */
$r = array();
/** @var int $i */
for ($i = 0; $i < 256; ++$i) {
$r[$i] = (int) (
1 & (
self::chrToInt($a[(int) ($i >> 3)])
>>
($i & 7)
)
);
}
for ($i = 0;$i < 256;++$i) {
if ($r[$i]) {
for ($b = 1;$b <= 6 && $i + $b < 256;++$b) {
if ($r[$i + $b]) {
if ($r[$i] + ($r[$i + $b] << $b) <= 15) {
$r[$i] += $r[$i + $b] << $b;
$r[$i + $b] = 0;
} elseif ($r[$i] - ($r[$i + $b] << $b) >= -15) {
$r[$i] -= $r[$i + $b] << $b;
for ($k = $i + $b; $k < 256; ++$k) {
if (!$r[$k]) {
$r[$k] = 1;
break;
}
$r[$k] = 0;
}
} else {
break;
}
}
}
}
}
return $r;
}
/**
* @internal You should not use this directly from another application
*
* @param string $s
* @return ParagonIE_Sodium_Core_Curve25519_Ge_P3
* @throws SodiumException
* @throws TypeError
*/
public static function ge_frombytes_negate_vartime($s)
{
static $d = null;
if (!$d) {
$d = ParagonIE_Sodium_Core_Curve25519_Fe::fromArray(self::$d);
}
# fe_frombytes(h->Y,s);
# fe_1(h->Z);
$h = new ParagonIE_Sodium_Core_Curve25519_Ge_P3(
self::fe_0(),
self::fe_frombytes($s),
self::fe_1()
);
# fe_sq(u,h->Y);
# fe_mul(v,u,d);
# fe_sub(u,u,h->Z); /* u = y^2-1 */
# fe_add(v,v,h->Z); /* v = dy^2+1 */
$u = self::fe_sq($h->Y);
/** @var ParagonIE_Sodium_Core_Curve25519_Fe $d */
$v = self::fe_mul($u, $d);
$u = self::fe_sub($u, $h->Z); /* u = y^2 - 1 */
$v = self::fe_add($v, $h->Z); /* v = dy^2 + 1 */
# fe_sq(v3,v);
# fe_mul(v3,v3,v); /* v3 = v^3 */
# fe_sq(h->X,v3);
# fe_mul(h->X,h->X,v);
# fe_mul(h->X,h->X,u); /* x = uv^7 */
$v3 = self::fe_sq($v);
$v3 = self::fe_mul($v3, $v); /* v3 = v^3 */
$h->X = self::fe_sq($v3);
$h->X = self::fe_mul($h->X, $v);
$h->X = self::fe_mul($h->X, $u); /* x = uv^7 */
# fe_pow22523(h->X,h->X); /* x = (uv^7)^((q-5)/8) */
# fe_mul(h->X,h->X,v3);
# fe_mul(h->X,h->X,u); /* x = uv^3(uv^7)^((q-5)/8) */
$h->X = self::fe_pow22523($h->X); /* x = (uv^7)^((q-5)/8) */
$h->X = self::fe_mul($h->X, $v3);
$h->X = self::fe_mul($h->X, $u); /* x = uv^3(uv^7)^((q-5)/8) */
# fe_sq(vxx,h->X);
# fe_mul(vxx,vxx,v);
# fe_sub(check,vxx,u); /* vx^2-u */
$vxx = self::fe_sq($h->X);
$vxx = self::fe_mul($vxx, $v);
$check = self::fe_sub($vxx, $u); /* vx^2 - u */
# if (fe_isnonzero(check)) {
# fe_add(check,vxx,u); /* vx^2+u */
# if (fe_isnonzero(check)) {
# return -1;
# }
# fe_mul(h->X,h->X,sqrtm1);
# }
if (self::fe_isnonzero($check)) {
$check = self::fe_add($vxx, $u); /* vx^2 + u */
if (self::fe_isnonzero($check)) {
throw new RangeException('Internal check failed.');
}
$h->X = self::fe_mul(
$h->X,
ParagonIE_Sodium_Core_Curve25519_Fe::fromArray(self::$sqrtm1)
);
}
# if (fe_isnegative(h->X) == (s[31] >> 7)) {
# fe_neg(h->X,h->X);
# }
$i = self::chrToInt($s[31]);
if (self::fe_isnegative($h->X) === ($i >> 7)) {
$h->X = self::fe_neg($h->X);
}
# fe_mul(h->T,h->X,h->Y);
$h->T = self::fe_mul($h->X, $h->Y);
return $h;
}
/**
* @internal You should not use this directly from another application
*
* @param ParagonIE_Sodium_Core_Curve25519_Ge_P1p1 $R
* @param ParagonIE_Sodium_Core_Curve25519_Ge_P3 $p
* @param ParagonIE_Sodium_Core_Curve25519_Ge_Precomp $q
* @return ParagonIE_Sodium_Core_Curve25519_Ge_P1p1
*/
public static function ge_madd(
ParagonIE_Sodium_Core_Curve25519_Ge_P1p1 $R,
ParagonIE_Sodium_Core_Curve25519_Ge_P3 $p,
ParagonIE_Sodium_Core_Curve25519_Ge_Precomp $q
) {
$r = clone $R;
$r->X = self::fe_add($p->Y, $p->X);
$r->Y = self::fe_sub($p->Y, $p->X);
$r->Z = self::fe_mul($r->X, $q->yplusx);
$r->Y = self::fe_mul($r->Y, $q->yminusx);
$r->T = self::fe_mul($q->xy2d, $p->T);
$t0 = self::fe_add(clone $p->Z, clone $p->Z);
$r->X = self::fe_sub($r->Z, $r->Y);
$r->Y = self::fe_add($r->Z, $r->Y);
$r->Z = self::fe_add($t0, $r->T);
$r->T = self::fe_sub($t0, $r->T);
return $r;
}
/**
* @internal You should not use this directly from another application
*
* @param ParagonIE_Sodium_Core_Curve25519_Ge_P1p1 $R
* @param ParagonIE_Sodium_Core_Curve25519_Ge_P3 $p
* @param ParagonIE_Sodium_Core_Curve25519_Ge_Precomp $q
* @return ParagonIE_Sodium_Core_Curve25519_Ge_P1p1
*/
public static function ge_msub(
ParagonIE_Sodium_Core_Curve25519_Ge_P1p1 $R,
ParagonIE_Sodium_Core_Curve25519_Ge_P3 $p,
ParagonIE_Sodium_Core_Curve25519_Ge_Precomp $q
) {
$r = clone $R;
$r->X = self::fe_add($p->Y, $p->X);
$r->Y = self::fe_sub($p->Y, $p->X);
$r->Z = self::fe_mul($r->X, $q->yminusx);
$r->Y = self::fe_mul($r->Y, $q->yplusx);
$r->T = self::fe_mul($q->xy2d, $p->T);
$t0 = self::fe_add($p->Z, $p->Z);
$r->X = self::fe_sub($r->Z, $r->Y);
$r->Y = self::fe_add($r->Z, $r->Y);
$r->Z = self::fe_sub($t0, $r->T);
$r->T = self::fe_add($t0, $r->T);
return $r;
}
/**
* @internal You should not use this directly from another application
*
* @param ParagonIE_Sodium_Core_Curve25519_Ge_P1p1 $p
* @return ParagonIE_Sodium_Core_Curve25519_Ge_P2
*/
public static function ge_p1p1_to_p2(ParagonIE_Sodium_Core_Curve25519_Ge_P1p1 $p)
{
$r = new ParagonIE_Sodium_Core_Curve25519_Ge_P2();
$r->X = self::fe_mul($p->X, $p->T);
$r->Y = self::fe_mul($p->Y, $p->Z);
$r->Z = self::fe_mul($p->Z, $p->T);
return $r;
}
/**
* @internal You should not use this directly from another application
*
* @param ParagonIE_Sodium_Core_Curve25519_Ge_P1p1 $p
* @return ParagonIE_Sodium_Core_Curve25519_Ge_P3
*/
public static function ge_p1p1_to_p3(ParagonIE_Sodium_Core_Curve25519_Ge_P1p1 $p)
{
$r = new ParagonIE_Sodium_Core_Curve25519_Ge_P3();
$r->X = self::fe_mul($p->X, $p->T);
$r->Y = self::fe_mul($p->Y, $p->Z);
$r->Z = self::fe_mul($p->Z, $p->T);
$r->T = self::fe_mul($p->X, $p->Y);
return $r;
}
/**
* @internal You should not use this directly from another application
*
* @return ParagonIE_Sodium_Core_Curve25519_Ge_P2
*/
public static function ge_p2_0()
{
return new ParagonIE_Sodium_Core_Curve25519_Ge_P2(
self::fe_0(),
self::fe_1(),
self::fe_1()
);
}
/**
* @internal You should not use this directly from another application
*
* @param ParagonIE_Sodium_Core_Curve25519_Ge_P2 $p
* @return ParagonIE_Sodium_Core_Curve25519_Ge_P1p1
*/
public static function ge_p2_dbl(ParagonIE_Sodium_Core_Curve25519_Ge_P2 $p)
{
$r = new ParagonIE_Sodium_Core_Curve25519_Ge_P1p1();
$r->X = self::fe_sq($p->X);
$r->Z = self::fe_sq($p->Y);
$r->T = self::fe_sq2($p->Z);
$r->Y = self::fe_add($p->X, $p->Y);
$t0 = self::fe_sq($r->Y);
$r->Y = self::fe_add($r->Z, $r->X);
$r->Z = self::fe_sub($r->Z, $r->X);
$r->X = self::fe_sub($t0, $r->Y);
$r->T = self::fe_sub($r->T, $r->Z);
return $r;
}
/**
* @internal You should not use this directly from another application
*
* @return ParagonIE_Sodium_Core_Curve25519_Ge_P3
*/
public static function ge_p3_0()
{
return new ParagonIE_Sodium_Core_Curve25519_Ge_P3(
self::fe_0(),
self::fe_1(),
self::fe_1(),
self::fe_0()
);
}
/**
* @internal You should not use this directly from another application
*
* @param ParagonIE_Sodium_Core_Curve25519_Ge_P3 $p
* @return ParagonIE_Sodium_Core_Curve25519_Ge_Cached
*/
public static function ge_p3_to_cached(ParagonIE_Sodium_Core_Curve25519_Ge_P3 $p)
{
static $d2 = null;
if ($d2 === null) {
$d2 = ParagonIE_Sodium_Core_Curve25519_Fe::fromArray(self::$d2);
}
/** @var ParagonIE_Sodium_Core_Curve25519_Fe $d2 */
$r = new ParagonIE_Sodium_Core_Curve25519_Ge_Cached();
$r->YplusX = self::fe_add($p->Y, $p->X);
$r->YminusX = self::fe_sub($p->Y, $p->X);
$r->Z = self::fe_copy($p->Z);
$r->T2d = self::fe_mul($p->T, $d2);
return $r;
}
/**
* @internal You should not use this directly from another application
*
* @param ParagonIE_Sodium_Core_Curve25519_Ge_P3 $p
* @return ParagonIE_Sodium_Core_Curve25519_Ge_P2
*/
public static function ge_p3_to_p2(ParagonIE_Sodium_Core_Curve25519_Ge_P3 $p)
{
return new ParagonIE_Sodium_Core_Curve25519_Ge_P2(
self::fe_copy($p->X),
self::fe_copy($p->Y),
self::fe_copy($p->Z)
);
}
/**
* @internal You should not use this directly from another application
*
* @param ParagonIE_Sodium_Core_Curve25519_Ge_P3 $h
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function ge_p3_tobytes(ParagonIE_Sodium_Core_Curve25519_Ge_P3 $h)
{
$recip = self::fe_invert($h->Z);
$x = self::fe_mul($h->X, $recip);
$y = self::fe_mul($h->Y, $recip);
$s = self::fe_tobytes($y);
$s[31] = self::intToChr(
self::chrToInt($s[31]) ^ (self::fe_isnegative($x) << 7)
);
return $s;
}
/**
* @internal You should not use this directly from another application
*
* @param ParagonIE_Sodium_Core_Curve25519_Ge_P3 $p
* @return ParagonIE_Sodium_Core_Curve25519_Ge_P1p1
*/
public static function ge_p3_dbl(ParagonIE_Sodium_Core_Curve25519_Ge_P3 $p)
{
$q = self::ge_p3_to_p2($p);
return self::ge_p2_dbl($q);
}
/**
* @return ParagonIE_Sodium_Core_Curve25519_Ge_Precomp
*/
public static function ge_precomp_0()
{
return new ParagonIE_Sodium_Core_Curve25519_Ge_Precomp(
self::fe_1(),
self::fe_1(),
self::fe_0()
);
}
/**
* @internal You should not use this directly from another application
*
* @param int $b
* @param int $c
* @return int
*/
public static function equal($b, $c)
{
return (int) ((($b ^ $c) - 1) >> 31) & 1;
}
/**
* @internal You should not use this directly from another application
*
* @param int|string $char
* @return int (1 = yes, 0 = no)
* @throws SodiumException
* @throws TypeError
*/
public static function negative($char)
{
if (is_int($char)) {
return ($char >> 63) & 1;
}
$x = self::chrToInt(self::substr($char, 0, 1));
return (int) ($x >> 63);
}
/**
* Conditional move
*
* @internal You should not use this directly from another application
*
* @param ParagonIE_Sodium_Core_Curve25519_Ge_Precomp $t
* @param ParagonIE_Sodium_Core_Curve25519_Ge_Precomp $u
* @param int $b
* @return ParagonIE_Sodium_Core_Curve25519_Ge_Precomp
*/
public static function cmov(
ParagonIE_Sodium_Core_Curve25519_Ge_Precomp $t,
ParagonIE_Sodium_Core_Curve25519_Ge_Precomp $u,
$b
) {
if (!is_int($b)) {
throw new InvalidArgumentException('Expected an integer.');
}
return new ParagonIE_Sodium_Core_Curve25519_Ge_Precomp(
self::fe_cmov($t->yplusx, $u->yplusx, $b),
self::fe_cmov($t->yminusx, $u->yminusx, $b),
self::fe_cmov($t->xy2d, $u->xy2d, $b)
);
}
/**
* @param ParagonIE_Sodium_Core_Curve25519_Ge_Cached $t
* @param ParagonIE_Sodium_Core_Curve25519_Ge_Cached $u
* @param int $b
* @return ParagonIE_Sodium_Core_Curve25519_Ge_Cached
*/
public static function ge_cmov_cached(
ParagonIE_Sodium_Core_Curve25519_Ge_Cached $t,
ParagonIE_Sodium_Core_Curve25519_Ge_Cached $u,
$b
) {
$b &= 1;
$ret = new ParagonIE_Sodium_Core_Curve25519_Ge_Cached();
$ret->YplusX = self::fe_cmov($t->YplusX, $u->YplusX, $b);
$ret->YminusX = self::fe_cmov($t->YminusX, $u->YminusX, $b);
$ret->Z = self::fe_cmov($t->Z, $u->Z, $b);
$ret->T2d = self::fe_cmov($t->T2d, $u->T2d, $b);
return $ret;
}
/**
* @param ParagonIE_Sodium_Core_Curve25519_Ge_Cached[] $cached
* @param int $b
* @return ParagonIE_Sodium_Core_Curve25519_Ge_Cached
* @throws SodiumException
*/
public static function ge_cmov8_cached(array $cached, $b)
{
// const unsigned char bnegative = negative(b);
// const unsigned char babs = b - (((-bnegative) & b) * ((signed char) 1 << 1));
$bnegative = self::negative($b);
$babs = $b - (((-$bnegative) & $b) << 1);
// ge25519_cached_0(t);
$t = new ParagonIE_Sodium_Core_Curve25519_Ge_Cached(
self::fe_1(),
self::fe_1(),
self::fe_1(),
self::fe_0()
);
// ge25519_cmov_cached(t, &cached[0], equal(babs, 1));
// ge25519_cmov_cached(t, &cached[1], equal(babs, 2));
// ge25519_cmov_cached(t, &cached[2], equal(babs, 3));
// ge25519_cmov_cached(t, &cached[3], equal(babs, 4));
// ge25519_cmov_cached(t, &cached[4], equal(babs, 5));
// ge25519_cmov_cached(t, &cached[5], equal(babs, 6));
// ge25519_cmov_cached(t, &cached[6], equal(babs, 7));
// ge25519_cmov_cached(t, &cached[7], equal(babs, 8));
for ($x = 0; $x < 8; ++$x) {
$t = self::ge_cmov_cached($t, $cached[$x], self::equal($babs, $x + 1));
}
// fe25519_copy(minust.YplusX, t->YminusX);
// fe25519_copy(minust.YminusX, t->YplusX);
// fe25519_copy(minust.Z, t->Z);
// fe25519_neg(minust.T2d, t->T2d);
$minust = new ParagonIE_Sodium_Core_Curve25519_Ge_Cached(
self::fe_copy($t->YminusX),
self::fe_copy($t->YplusX),
self::fe_copy($t->Z),
self::fe_neg($t->T2d)
);
return self::ge_cmov_cached($t, $minust, $bnegative);
}
/**
* @internal You should not use this directly from another application
*
* @param int $pos
* @param int $b
* @return ParagonIE_Sodium_Core_Curve25519_Ge_Precomp
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArgument
* @psalm-suppress MixedArrayAccess
* @psalm-suppress MixedArrayOffset
*/
public static function ge_select($pos = 0, $b = 0)
{
static $base = null;
if ($base === null) {
$base = array();
/** @var int $i */
foreach (self::$base as $i => $bas) {
for ($j = 0; $j < 8; ++$j) {
$base[$i][$j] = new ParagonIE_Sodium_Core_Curve25519_Ge_Precomp(
ParagonIE_Sodium_Core_Curve25519_Fe::fromArray($bas[$j][0]),
ParagonIE_Sodium_Core_Curve25519_Fe::fromArray($bas[$j][1]),
ParagonIE_Sodium_Core_Curve25519_Fe::fromArray($bas[$j][2])
);
}
}
}
/** @var array<int, array<int, ParagonIE_Sodium_Core_Curve25519_Ge_Precomp>> $base */
if (!is_int($pos)) {
throw new InvalidArgumentException('Position must be an integer');
}
if ($pos < 0 || $pos > 31) {
throw new RangeException('Position is out of range [0, 31]');
}
$bnegative = self::negative($b);
$babs = $b - (((-$bnegative) & $b) << 1);
$t = self::ge_precomp_0();
for ($i = 0; $i < 8; ++$i) {
$t = self::cmov(
$t,
$base[$pos][$i],
self::equal($babs, $i + 1)
);
}
$minusT = new ParagonIE_Sodium_Core_Curve25519_Ge_Precomp(
self::fe_copy($t->yminusx),
self::fe_copy($t->yplusx),
self::fe_neg($t->xy2d)
);
return self::cmov($t, $minusT, $bnegative);
}
/**
* Subtract two group elements.
*
* r = p - q
*
* @internal You should not use this directly from another application
*
* @param ParagonIE_Sodium_Core_Curve25519_Ge_P3 $p
* @param ParagonIE_Sodium_Core_Curve25519_Ge_Cached $q
* @return ParagonIE_Sodium_Core_Curve25519_Ge_P1p1
*/
public static function ge_sub(
ParagonIE_Sodium_Core_Curve25519_Ge_P3 $p,
ParagonIE_Sodium_Core_Curve25519_Ge_Cached $q
) {
$r = new ParagonIE_Sodium_Core_Curve25519_Ge_P1p1();
$r->X = self::fe_add($p->Y, $p->X);
$r->Y = self::fe_sub($p->Y, $p->X);
$r->Z = self::fe_mul($r->X, $q->YminusX);
$r->Y = self::fe_mul($r->Y, $q->YplusX);
$r->T = self::fe_mul($q->T2d, $p->T);
$r->X = self::fe_mul($p->Z, $q->Z);
$t0 = self::fe_add($r->X, $r->X);
$r->X = self::fe_sub($r->Z, $r->Y);
$r->Y = self::fe_add($r->Z, $r->Y);
$r->Z = self::fe_sub($t0, $r->T);
$r->T = self::fe_add($t0, $r->T);
return $r;
}
/**
* Convert a group element to a byte string.
*
* @param ParagonIE_Sodium_Core_Curve25519_Ge_P2 $h
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function ge_tobytes(ParagonIE_Sodium_Core_Curve25519_Ge_P2 $h)
{
$recip = self::fe_invert($h->Z);
$x = self::fe_mul($h->X, $recip);
$y = self::fe_mul($h->Y, $recip);
$s = self::fe_tobytes($y);
$s[31] = self::intToChr(
self::chrToInt($s[31]) ^ (self::fe_isnegative($x) << 7)
);
return $s;
}
/**
* @internal You should not use this directly from another application
*
* @param string $a
* @param ParagonIE_Sodium_Core_Curve25519_Ge_P3 $A
* @param string $b
* @return ParagonIE_Sodium_Core_Curve25519_Ge_P2
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArgument
* @psalm-suppress MixedArrayAccess
*/
public static function ge_double_scalarmult_vartime(
$a,
ParagonIE_Sodium_Core_Curve25519_Ge_P3 $A,
$b
) {
/** @var array<int, ParagonIE_Sodium_Core_Curve25519_Ge_Cached> $Ai */
$Ai = array();
/** @var array<int, ParagonIE_Sodium_Core_Curve25519_Ge_Precomp> $Bi */
static $Bi = array();
if (!$Bi) {
for ($i = 0; $i < 8; ++$i) {
$Bi[$i] = new ParagonIE_Sodium_Core_Curve25519_Ge_Precomp(
ParagonIE_Sodium_Core_Curve25519_Fe::fromArray(self::$base2[$i][0]),
ParagonIE_Sodium_Core_Curve25519_Fe::fromArray(self::$base2[$i][1]),
ParagonIE_Sodium_Core_Curve25519_Fe::fromArray(self::$base2[$i][2])
);
}
}
for ($i = 0; $i < 8; ++$i) {
$Ai[$i] = new ParagonIE_Sodium_Core_Curve25519_Ge_Cached(
self::fe_0(),
self::fe_0(),
self::fe_0(),
self::fe_0()
);
}
# slide(aslide,a);
# slide(bslide,b);
/** @var array<int, int> $aslide */
$aslide = self::slide($a);
/** @var array<int, int> $bslide */
$bslide = self::slide($b);
# ge_p3_to_cached(&Ai[0],A);
# ge_p3_dbl(&t,A); ge_p1p1_to_p3(&A2,&t);
$Ai[0] = self::ge_p3_to_cached($A);
$t = self::ge_p3_dbl($A);
$A2 = self::ge_p1p1_to_p3($t);
# ge_add(&t,&A2,&Ai[0]); ge_p1p1_to_p3(&u,&t); ge_p3_to_cached(&Ai[1],&u);
# ge_add(&t,&A2,&Ai[1]); ge_p1p1_to_p3(&u,&t); ge_p3_to_cached(&Ai[2],&u);
# ge_add(&t,&A2,&Ai[2]); ge_p1p1_to_p3(&u,&t); ge_p3_to_cached(&Ai[3],&u);
# ge_add(&t,&A2,&Ai[3]); ge_p1p1_to_p3(&u,&t); ge_p3_to_cached(&Ai[4],&u);
# ge_add(&t,&A2,&Ai[4]); ge_p1p1_to_p3(&u,&t); ge_p3_to_cached(&Ai[5],&u);
# ge_add(&t,&A2,&Ai[5]); ge_p1p1_to_p3(&u,&t); ge_p3_to_cached(&Ai[6],&u);
# ge_add(&t,&A2,&Ai[6]); ge_p1p1_to_p3(&u,&t); ge_p3_to_cached(&Ai[7],&u);
for ($i = 0; $i < 7; ++$i) {
$t = self::ge_add($A2, $Ai[$i]);
$u = self::ge_p1p1_to_p3($t);
$Ai[$i + 1] = self::ge_p3_to_cached($u);
}
# ge_p2_0(r);
$r = self::ge_p2_0();
# for (i = 255;i >= 0;--i) {
# if (aslide[i] || bslide[i]) break;
# }
$i = 255;
for (; $i >= 0; --$i) {
if ($aslide[$i] || $bslide[$i]) {
break;
}
}
# for (;i >= 0;--i) {
for (; $i >= 0; --$i) {
# ge_p2_dbl(&t,r);
$t = self::ge_p2_dbl($r);
# if (aslide[i] > 0) {
if ($aslide[$i] > 0) {
# ge_p1p1_to_p3(&u,&t);
# ge_add(&t,&u,&Ai[aslide[i]/2]);
$u = self::ge_p1p1_to_p3($t);
$t = self::ge_add(
$u,
$Ai[(int) floor($aslide[$i] / 2)]
);
# } else if (aslide[i] < 0) {
} elseif ($aslide[$i] < 0) {
# ge_p1p1_to_p3(&u,&t);
# ge_sub(&t,&u,&Ai[(-aslide[i])/2]);
$u = self::ge_p1p1_to_p3($t);
$t = self::ge_sub(
$u,
$Ai[(int) floor(-$aslide[$i] / 2)]
);
}
# if (bslide[i] > 0) {
if ($bslide[$i] > 0) {
/** @var int $index */
$index = (int) floor($bslide[$i] / 2);
# ge_p1p1_to_p3(&u,&t);
# ge_madd(&t,&u,&Bi[bslide[i]/2]);
$u = self::ge_p1p1_to_p3($t);
$t = self::ge_madd($t, $u, $Bi[$index]);
# } else if (bslide[i] < 0) {
} elseif ($bslide[$i] < 0) {
/** @var int $index */
$index = (int) floor(-$bslide[$i] / 2);
# ge_p1p1_to_p3(&u,&t);
# ge_msub(&t,&u,&Bi[(-bslide[i])/2]);
$u = self::ge_p1p1_to_p3($t);
$t = self::ge_msub($t, $u, $Bi[$index]);
}
# ge_p1p1_to_p2(r,&t);
$r = self::ge_p1p1_to_p2($t);
}
return $r;
}
/**
* @internal You should not use this directly from another application
*
* @param string $a
* @param ParagonIE_Sodium_Core_Curve25519_Ge_P3 $p
* @return ParagonIE_Sodium_Core_Curve25519_Ge_P3
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedAssignment
* @psalm-suppress MixedOperand
*/
public static function ge_scalarmult($a, $p)
{
$e = array_fill(0, 64, 0);
/** @var ParagonIE_Sodium_Core_Curve25519_Ge_Cached[] $pi */
$pi = array();
// ge25519_p3_to_cached(&pi[1 - 1], p); /* p */
$pi[0] = self::ge_p3_to_cached($p);
// ge25519_p3_dbl(&t2, p);
// ge25519_p1p1_to_p3(&p2, &t2);
// ge25519_p3_to_cached(&pi[2 - 1], &p2); /* 2p = 2*p */
$t2 = self::ge_p3_dbl($p);
$p2 = self::ge_p1p1_to_p3($t2);
$pi[1] = self::ge_p3_to_cached($p2);
// ge25519_add_cached(&t3, p, &pi[2 - 1]);
// ge25519_p1p1_to_p3(&p3, &t3);
// ge25519_p3_to_cached(&pi[3 - 1], &p3); /* 3p = 2p+p */
$t3 = self::ge_add($p, $pi[1]);
$p3 = self::ge_p1p1_to_p3($t3);
$pi[2] = self::ge_p3_to_cached($p3);
// ge25519_p3_dbl(&t4, &p2);
// ge25519_p1p1_to_p3(&p4, &t4);
// ge25519_p3_to_cached(&pi[4 - 1], &p4); /* 4p = 2*2p */
$t4 = self::ge_p3_dbl($p2);
$p4 = self::ge_p1p1_to_p3($t4);
$pi[3] = self::ge_p3_to_cached($p4);
// ge25519_add_cached(&t5, p, &pi[4 - 1]);
// ge25519_p1p1_to_p3(&p5, &t5);
// ge25519_p3_to_cached(&pi[5 - 1], &p5); /* 5p = 4p+p */
$t5 = self::ge_add($p, $pi[3]);
$p5 = self::ge_p1p1_to_p3($t5);
$pi[4] = self::ge_p3_to_cached($p5);
// ge25519_p3_dbl(&t6, &p3);
// ge25519_p1p1_to_p3(&p6, &t6);
// ge25519_p3_to_cached(&pi[6 - 1], &p6); /* 6p = 2*3p */
$t6 = self::ge_p3_dbl($p3);
$p6 = self::ge_p1p1_to_p3($t6);
$pi[5] = self::ge_p3_to_cached($p6);
// ge25519_add_cached(&t7, p, &pi[6 - 1]);
// ge25519_p1p1_to_p3(&p7, &t7);
// ge25519_p3_to_cached(&pi[7 - 1], &p7); /* 7p = 6p+p */
$t7 = self::ge_add($p, $pi[5]);
$p7 = self::ge_p1p1_to_p3($t7);
$pi[6] = self::ge_p3_to_cached($p7);
// ge25519_p3_dbl(&t8, &p4);
// ge25519_p1p1_to_p3(&p8, &t8);
// ge25519_p3_to_cached(&pi[8 - 1], &p8); /* 8p = 2*4p */
$t8 = self::ge_p3_dbl($p4);
$p8 = self::ge_p1p1_to_p3($t8);
$pi[7] = self::ge_p3_to_cached($p8);
// for (i = 0; i < 32; ++i) {
// e[2 * i + 0] = (a[i] >> 0) & 15;
// e[2 * i + 1] = (a[i] >> 4) & 15;
// }
for ($i = 0; $i < 32; ++$i) {
$e[($i << 1) ] = self::chrToInt($a[$i]) & 15;
$e[($i << 1) + 1] = (self::chrToInt($a[$i]) >> 4) & 15;
}
// /* each e[i] is between 0 and 15 */
// /* e[63] is between 0 and 7 */
// carry = 0;
// for (i = 0; i < 63; ++i) {
// e[i] += carry;
// carry = e[i] + 8;
// carry >>= 4;
// e[i] -= carry * ((signed char) 1 << 4);
// }
$carry = 0;
for ($i = 0; $i < 63; ++$i) {
$e[$i] += $carry;
$carry = $e[$i] + 8;
$carry >>= 4;
$e[$i] -= $carry << 4;
}
// e[63] += carry;
// /* each e[i] is between -8 and 8 */
$e[63] += $carry;
// ge25519_p3_0(h);
$h = self::ge_p3_0();
// for (i = 63; i != 0; i--) {
for ($i = 63; $i != 0; --$i) {
// ge25519_cmov8_cached(&t, pi, e[i]);
$t = self::ge_cmov8_cached($pi, $e[$i]);
// ge25519_add_cached(&r, h, &t);
$r = self::ge_add($h, $t);
// ge25519_p1p1_to_p2(&s, &r);
// ge25519_p2_dbl(&r, &s);
// ge25519_p1p1_to_p2(&s, &r);
// ge25519_p2_dbl(&r, &s);
// ge25519_p1p1_to_p2(&s, &r);
// ge25519_p2_dbl(&r, &s);
// ge25519_p1p1_to_p2(&s, &r);
// ge25519_p2_dbl(&r, &s);
$s = self::ge_p1p1_to_p2($r);
$r = self::ge_p2_dbl($s);
$s = self::ge_p1p1_to_p2($r);
$r = self::ge_p2_dbl($s);
$s = self::ge_p1p1_to_p2($r);
$r = self::ge_p2_dbl($s);
$s = self::ge_p1p1_to_p2($r);
$r = self::ge_p2_dbl($s);
// ge25519_p1p1_to_p3(h, &r); /* *16 */
$h = self::ge_p1p1_to_p3($r); /* *16 */
}
// ge25519_cmov8_cached(&t, pi, e[i]);
// ge25519_add_cached(&r, h, &t);
// ge25519_p1p1_to_p3(h, &r);
$t = self::ge_cmov8_cached($pi, $e[0]);
$r = self::ge_add($h, $t);
return self::ge_p1p1_to_p3($r);
}
/**
* @internal You should not use this directly from another application
*
* @param string $a
* @return ParagonIE_Sodium_Core_Curve25519_Ge_P3
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedAssignment
* @psalm-suppress MixedOperand
*/
public static function ge_scalarmult_base($a)
{
/** @var array<int, int> $e */
$e = array();
$r = new ParagonIE_Sodium_Core_Curve25519_Ge_P1p1();
for ($i = 0; $i < 32; ++$i) {
$dbl = (int) $i << 1;
$e[$dbl] = (int) self::chrToInt($a[$i]) & 15;
$e[$dbl + 1] = (int) (self::chrToInt($a[$i]) >> 4) & 15;
}
$carry = 0;
for ($i = 0; $i < 63; ++$i) {
$e[$i] += $carry;
$carry = $e[$i] + 8;
$carry >>= 4;
$e[$i] -= $carry << 4;
}
$e[63] += (int) $carry;
$h = self::ge_p3_0();
for ($i = 1; $i < 64; $i += 2) {
$t = self::ge_select((int) floor($i / 2), (int) $e[$i]);
$r = self::ge_madd($r, $h, $t);
$h = self::ge_p1p1_to_p3($r);
}
$r = self::ge_p3_dbl($h);
$s = self::ge_p1p1_to_p2($r);
$r = self::ge_p2_dbl($s);
$s = self::ge_p1p1_to_p2($r);
$r = self::ge_p2_dbl($s);
$s = self::ge_p1p1_to_p2($r);
$r = self::ge_p2_dbl($s);
$h = self::ge_p1p1_to_p3($r);
for ($i = 0; $i < 64; $i += 2) {
$t = self::ge_select($i >> 1, (int) $e[$i]);
$r = self::ge_madd($r, $h, $t);
$h = self::ge_p1p1_to_p3($r);
}
return $h;
}
/**
* Calculates (ab + c) mod l
* where l = 2^252 + 27742317777372353535851937790883648493
*
* @internal You should not use this directly from another application
*
* @param string $a
* @param string $b
* @param string $c
* @return string
* @throws TypeError
*/
public static function sc_muladd($a, $b, $c)
{
$a0 = 2097151 & self::load_3(self::substr($a, 0, 3));
$a1 = 2097151 & (self::load_4(self::substr($a, 2, 4)) >> 5);
$a2 = 2097151 & (self::load_3(self::substr($a, 5, 3)) >> 2);
$a3 = 2097151 & (self::load_4(self::substr($a, 7, 4)) >> 7);
$a4 = 2097151 & (self::load_4(self::substr($a, 10, 4)) >> 4);
$a5 = 2097151 & (self::load_3(self::substr($a, 13, 3)) >> 1);
$a6 = 2097151 & (self::load_4(self::substr($a, 15, 4)) >> 6);
$a7 = 2097151 & (self::load_3(self::substr($a, 18, 3)) >> 3);
$a8 = 2097151 & self::load_3(self::substr($a, 21, 3));
$a9 = 2097151 & (self::load_4(self::substr($a, 23, 4)) >> 5);
$a10 = 2097151 & (self::load_3(self::substr($a, 26, 3)) >> 2);
$a11 = (self::load_4(self::substr($a, 28, 4)) >> 7);
$b0 = 2097151 & self::load_3(self::substr($b, 0, 3));
$b1 = 2097151 & (self::load_4(self::substr($b, 2, 4)) >> 5);
$b2 = 2097151 & (self::load_3(self::substr($b, 5, 3)) >> 2);
$b3 = 2097151 & (self::load_4(self::substr($b, 7, 4)) >> 7);
$b4 = 2097151 & (self::load_4(self::substr($b, 10, 4)) >> 4);
$b5 = 2097151 & (self::load_3(self::substr($b, 13, 3)) >> 1);
$b6 = 2097151 & (self::load_4(self::substr($b, 15, 4)) >> 6);
$b7 = 2097151 & (self::load_3(self::substr($b, 18, 3)) >> 3);
$b8 = 2097151 & self::load_3(self::substr($b, 21, 3));
$b9 = 2097151 & (self::load_4(self::substr($b, 23, 4)) >> 5);
$b10 = 2097151 & (self::load_3(self::substr($b, 26, 3)) >> 2);
$b11 = (self::load_4(self::substr($b, 28, 4)) >> 7);
$c0 = 2097151 & self::load_3(self::substr($c, 0, 3));
$c1 = 2097151 & (self::load_4(self::substr($c, 2, 4)) >> 5);
$c2 = 2097151 & (self::load_3(self::substr($c, 5, 3)) >> 2);
$c3 = 2097151 & (self::load_4(self::substr($c, 7, 4)) >> 7);
$c4 = 2097151 & (self::load_4(self::substr($c, 10, 4)) >> 4);
$c5 = 2097151 & (self::load_3(self::substr($c, 13, 3)) >> 1);
$c6 = 2097151 & (self::load_4(self::substr($c, 15, 4)) >> 6);
$c7 = 2097151 & (self::load_3(self::substr($c, 18, 3)) >> 3);
$c8 = 2097151 & self::load_3(self::substr($c, 21, 3));
$c9 = 2097151 & (self::load_4(self::substr($c, 23, 4)) >> 5);
$c10 = 2097151 & (self::load_3(self::substr($c, 26, 3)) >> 2);
$c11 = (self::load_4(self::substr($c, 28, 4)) >> 7);
/* Can't really avoid the pyramid here: */
$s0 = $c0 + self::mul($a0, $b0, 24);
$s1 = $c1 + self::mul($a0, $b1, 24) + self::mul($a1, $b0, 24);
$s2 = $c2 + self::mul($a0, $b2, 24) + self::mul($a1, $b1, 24) + self::mul($a2, $b0, 24);
$s3 = $c3 + self::mul($a0, $b3, 24) + self::mul($a1, $b2, 24) + self::mul($a2, $b1, 24) + self::mul($a3, $b0, 24);
$s4 = $c4 + self::mul($a0, $b4, 24) + self::mul($a1, $b3, 24) + self::mul($a2, $b2, 24) + self::mul($a3, $b1, 24) +
self::mul($a4, $b0, 24);
$s5 = $c5 + self::mul($a0, $b5, 24) + self::mul($a1, $b4, 24) + self::mul($a2, $b3, 24) + self::mul($a3, $b2, 24) +
self::mul($a4, $b1, 24) + self::mul($a5, $b0, 24);
$s6 = $c6 + self::mul($a0, $b6, 24) + self::mul($a1, $b5, 24) + self::mul($a2, $b4, 24) + self::mul($a3, $b3, 24) +
self::mul($a4, $b2, 24) + self::mul($a5, $b1, 24) + self::mul($a6, $b0, 24);
$s7 = $c7 + self::mul($a0, $b7, 24) + self::mul($a1, $b6, 24) + self::mul($a2, $b5, 24) + self::mul($a3, $b4, 24) +
self::mul($a4, $b3, 24) + self::mul($a5, $b2, 24) + self::mul($a6, $b1, 24) + self::mul($a7, $b0, 24);
$s8 = $c8 + self::mul($a0, $b8, 24) + self::mul($a1, $b7, 24) + self::mul($a2, $b6, 24) + self::mul($a3, $b5, 24) +
self::mul($a4, $b4, 24) + self::mul($a5, $b3, 24) + self::mul($a6, $b2, 24) + self::mul($a7, $b1, 24) +
self::mul($a8, $b0, 24);
$s9 = $c9 + self::mul($a0, $b9, 24) + self::mul($a1, $b8, 24) + self::mul($a2, $b7, 24) + self::mul($a3, $b6, 24) +
self::mul($a4, $b5, 24) + self::mul($a5, $b4, 24) + self::mul($a6, $b3, 24) + self::mul($a7, $b2, 24) +
self::mul($a8, $b1, 24) + self::mul($a9, $b0, 24);
$s10 = $c10 + self::mul($a0, $b10, 24) + self::mul($a1, $b9, 24) + self::mul($a2, $b8, 24) + self::mul($a3, $b7, 24) +
self::mul($a4, $b6, 24) + self::mul($a5, $b5, 24) + self::mul($a6, $b4, 24) + self::mul($a7, $b3, 24) +
self::mul($a8, $b2, 24) + self::mul($a9, $b1, 24) + self::mul($a10, $b0, 24);
$s11 = $c11 + self::mul($a0, $b11, 24) + self::mul($a1, $b10, 24) + self::mul($a2, $b9, 24) + self::mul($a3, $b8, 24) +
self::mul($a4, $b7, 24) + self::mul($a5, $b6, 24) + self::mul($a6, $b5, 24) + self::mul($a7, $b4, 24) +
self::mul($a8, $b3, 24) + self::mul($a9, $b2, 24) + self::mul($a10, $b1, 24) + self::mul($a11, $b0, 24);
$s12 = self::mul($a1, $b11, 24) + self::mul($a2, $b10, 24) + self::mul($a3, $b9, 24) + self::mul($a4, $b8, 24) +
self::mul($a5, $b7, 24) + self::mul($a6, $b6, 24) + self::mul($a7, $b5, 24) + self::mul($a8, $b4, 24) +
self::mul($a9, $b3, 24) + self::mul($a10, $b2, 24) + self::mul($a11, $b1, 24);
$s13 = self::mul($a2, $b11, 24) + self::mul($a3, $b10, 24) + self::mul($a4, $b9, 24) + self::mul($a5, $b8, 24) +
self::mul($a6, $b7, 24) + self::mul($a7, $b6, 24) + self::mul($a8, $b5, 24) + self::mul($a9, $b4, 24) +
self::mul($a10, $b3, 24) + self::mul($a11, $b2, 24);
$s14 = self::mul($a3, $b11, 24) + self::mul($a4, $b10, 24) + self::mul($a5, $b9, 24) + self::mul($a6, $b8, 24) +
self::mul($a7, $b7, 24) + self::mul($a8, $b6, 24) + self::mul($a9, $b5, 24) + self::mul($a10, $b4, 24) +
self::mul($a11, $b3, 24);
$s15 = self::mul($a4, $b11, 24) + self::mul($a5, $b10, 24) + self::mul($a6, $b9, 24) + self::mul($a7, $b8, 24) +
self::mul($a8, $b7, 24) + self::mul($a9, $b6, 24) + self::mul($a10, $b5, 24) + self::mul($a11, $b4, 24);
$s16 = self::mul($a5, $b11, 24) + self::mul($a6, $b10, 24) + self::mul($a7, $b9, 24) + self::mul($a8, $b8, 24) +
self::mul($a9, $b7, 24) + self::mul($a10, $b6, 24) + self::mul($a11, $b5, 24);
$s17 = self::mul($a6, $b11, 24) + self::mul($a7, $b10, 24) + self::mul($a8, $b9, 24) + self::mul($a9, $b8, 24) +
self::mul($a10, $b7, 24) + self::mul($a11, $b6, 24);
$s18 = self::mul($a7, $b11, 24) + self::mul($a8, $b10, 24) + self::mul($a9, $b9, 24) + self::mul($a10, $b8, 24) +
self::mul($a11, $b7, 24);
$s19 = self::mul($a8, $b11, 24) + self::mul($a9, $b10, 24) + self::mul($a10, $b9, 24) + self::mul($a11, $b8, 24);
$s20 = self::mul($a9, $b11, 24) + self::mul($a10, $b10, 24) + self::mul($a11, $b9, 24);
$s21 = self::mul($a10, $b11, 24) + self::mul($a11, $b10, 24);
$s22 = self::mul($a11, $b11, 24);
$s23 = 0;
$carry0 = ($s0 + (1 << 20)) >> 21;
$s1 += $carry0;
$s0 -= $carry0 << 21;
$carry2 = ($s2 + (1 << 20)) >> 21;
$s3 += $carry2;
$s2 -= $carry2 << 21;
$carry4 = ($s4 + (1 << 20)) >> 21;
$s5 += $carry4;
$s4 -= $carry4 << 21;
$carry6 = ($s6 + (1 << 20)) >> 21;
$s7 += $carry6;
$s6 -= $carry6 << 21;
$carry8 = ($s8 + (1 << 20)) >> 21;
$s9 += $carry8;
$s8 -= $carry8 << 21;
$carry10 = ($s10 + (1 << 20)) >> 21;
$s11 += $carry10;
$s10 -= $carry10 << 21;
$carry12 = ($s12 + (1 << 20)) >> 21;
$s13 += $carry12;
$s12 -= $carry12 << 21;
$carry14 = ($s14 + (1 << 20)) >> 21;
$s15 += $carry14;
$s14 -= $carry14 << 21;
$carry16 = ($s16 + (1 << 20)) >> 21;
$s17 += $carry16;
$s16 -= $carry16 << 21;
$carry18 = ($s18 + (1 << 20)) >> 21;
$s19 += $carry18;
$s18 -= $carry18 << 21;
$carry20 = ($s20 + (1 << 20)) >> 21;
$s21 += $carry20;
$s20 -= $carry20 << 21;
$carry22 = ($s22 + (1 << 20)) >> 21;
$s23 += $carry22;
$s22 -= $carry22 << 21;
$carry1 = ($s1 + (1 << 20)) >> 21;
$s2 += $carry1;
$s1 -= $carry1 << 21;
$carry3 = ($s3 + (1 << 20)) >> 21;
$s4 += $carry3;
$s3 -= $carry3 << 21;
$carry5 = ($s5 + (1 << 20)) >> 21;
$s6 += $carry5;
$s5 -= $carry5 << 21;
$carry7 = ($s7 + (1 << 20)) >> 21;
$s8 += $carry7;
$s7 -= $carry7 << 21;
$carry9 = ($s9 + (1 << 20)) >> 21;
$s10 += $carry9;
$s9 -= $carry9 << 21;
$carry11 = ($s11 + (1 << 20)) >> 21;
$s12 += $carry11;
$s11 -= $carry11 << 21;
$carry13 = ($s13 + (1 << 20)) >> 21;
$s14 += $carry13;
$s13 -= $carry13 << 21;
$carry15 = ($s15 + (1 << 20)) >> 21;
$s16 += $carry15;
$s15 -= $carry15 << 21;
$carry17 = ($s17 + (1 << 20)) >> 21;
$s18 += $carry17;
$s17 -= $carry17 << 21;
$carry19 = ($s19 + (1 << 20)) >> 21;
$s20 += $carry19;
$s19 -= $carry19 << 21;
$carry21 = ($s21 + (1 << 20)) >> 21;
$s22 += $carry21;
$s21 -= $carry21 << 21;
$s11 += self::mul($s23, 666643, 20);
$s12 += self::mul($s23, 470296, 19);
$s13 += self::mul($s23, 654183, 20);
$s14 -= self::mul($s23, 997805, 20);
$s15 += self::mul($s23, 136657, 18);
$s16 -= self::mul($s23, 683901, 20);
$s10 += self::mul($s22, 666643, 20);
$s11 += self::mul($s22, 470296, 19);
$s12 += self::mul($s22, 654183, 20);
$s13 -= self::mul($s22, 997805, 20);
$s14 += self::mul($s22, 136657, 18);
$s15 -= self::mul($s22, 683901, 20);
$s9 += self::mul($s21, 666643, 20);
$s10 += self::mul($s21, 470296, 19);
$s11 += self::mul($s21, 654183, 20);
$s12 -= self::mul($s21, 997805, 20);
$s13 += self::mul($s21, 136657, 18);
$s14 -= self::mul($s21, 683901, 20);
$s8 += self::mul($s20, 666643, 20);
$s9 += self::mul($s20, 470296, 19);
$s10 += self::mul($s20, 654183, 20);
$s11 -= self::mul($s20, 997805, 20);
$s12 += self::mul($s20, 136657, 18);
$s13 -= self::mul($s20, 683901, 20);
$s7 += self::mul($s19, 666643, 20);
$s8 += self::mul($s19, 470296, 19);
$s9 += self::mul($s19, 654183, 20);
$s10 -= self::mul($s19, 997805, 20);
$s11 += self::mul($s19, 136657, 18);
$s12 -= self::mul($s19, 683901, 20);
$s6 += self::mul($s18, 666643, 20);
$s7 += self::mul($s18, 470296, 19);
$s8 += self::mul($s18, 654183, 20);
$s9 -= self::mul($s18, 997805, 20);
$s10 += self::mul($s18, 136657, 18);
$s11 -= self::mul($s18, 683901, 20);
$carry6 = ($s6 + (1 << 20)) >> 21;
$s7 += $carry6;
$s6 -= $carry6 << 21;
$carry8 = ($s8 + (1 << 20)) >> 21;
$s9 += $carry8;
$s8 -= $carry8 << 21;
$carry10 = ($s10 + (1 << 20)) >> 21;
$s11 += $carry10;
$s10 -= $carry10 << 21;
$carry12 = ($s12 + (1 << 20)) >> 21;
$s13 += $carry12;
$s12 -= $carry12 << 21;
$carry14 = ($s14 + (1 << 20)) >> 21;
$s15 += $carry14;
$s14 -= $carry14 << 21;
$carry16 = ($s16 + (1 << 20)) >> 21;
$s17 += $carry16;
$s16 -= $carry16 << 21;
$carry7 = ($s7 + (1 << 20)) >> 21;
$s8 += $carry7;
$s7 -= $carry7 << 21;
$carry9 = ($s9 + (1 << 20)) >> 21;
$s10 += $carry9;
$s9 -= $carry9 << 21;
$carry11 = ($s11 + (1 << 20)) >> 21;
$s12 += $carry11;
$s11 -= $carry11 << 21;
$carry13 = ($s13 + (1 << 20)) >> 21;
$s14 += $carry13;
$s13 -= $carry13 << 21;
$carry15 = ($s15 + (1 << 20)) >> 21;
$s16 += $carry15;
$s15 -= $carry15 << 21;
$s5 += self::mul($s17, 666643, 20);
$s6 += self::mul($s17, 470296, 19);
$s7 += self::mul($s17, 654183, 20);
$s8 -= self::mul($s17, 997805, 20);
$s9 += self::mul($s17, 136657, 18);
$s10 -= self::mul($s17, 683901, 20);
$s4 += self::mul($s16, 666643, 20);
$s5 += self::mul($s16, 470296, 19);
$s6 += self::mul($s16, 654183, 20);
$s7 -= self::mul($s16, 997805, 20);
$s8 += self::mul($s16, 136657, 18);
$s9 -= self::mul($s16, 683901, 20);
$s3 += self::mul($s15, 666643, 20);
$s4 += self::mul($s15, 470296, 19);
$s5 += self::mul($s15, 654183, 20);
$s6 -= self::mul($s15, 997805, 20);
$s7 += self::mul($s15, 136657, 18);
$s8 -= self::mul($s15, 683901, 20);
$s2 += self::mul($s14, 666643, 20);
$s3 += self::mul($s14, 470296, 19);
$s4 += self::mul($s14, 654183, 20);
$s5 -= self::mul($s14, 997805, 20);
$s6 += self::mul($s14, 136657, 18);
$s7 -= self::mul($s14, 683901, 20);
$s1 += self::mul($s13, 666643, 20);
$s2 += self::mul($s13, 470296, 19);
$s3 += self::mul($s13, 654183, 20);
$s4 -= self::mul($s13, 997805, 20);
$s5 += self::mul($s13, 136657, 18);
$s6 -= self::mul($s13, 683901, 20);
$s0 += self::mul($s12, 666643, 20);
$s1 += self::mul($s12, 470296, 19);
$s2 += self::mul($s12, 654183, 20);
$s3 -= self::mul($s12, 997805, 20);
$s4 += self::mul($s12, 136657, 18);
$s5 -= self::mul($s12, 683901, 20);
$s12 = 0;
$carry0 = ($s0 + (1 << 20)) >> 21;
$s1 += $carry0;
$s0 -= $carry0 << 21;
$carry2 = ($s2 + (1 << 20)) >> 21;
$s3 += $carry2;
$s2 -= $carry2 << 21;
$carry4 = ($s4 + (1 << 20)) >> 21;
$s5 += $carry4;
$s4 -= $carry4 << 21;
$carry6 = ($s6 + (1 << 20)) >> 21;
$s7 += $carry6;
$s6 -= $carry6 << 21;
$carry8 = ($s8 + (1 << 20)) >> 21;
$s9 += $carry8;
$s8 -= $carry8 << 21;
$carry10 = ($s10 + (1 << 20)) >> 21;
$s11 += $carry10;
$s10 -= $carry10 << 21;
$carry1 = ($s1 + (1 << 20)) >> 21;
$s2 += $carry1;
$s1 -= $carry1 << 21;
$carry3 = ($s3 + (1 << 20)) >> 21;
$s4 += $carry3;
$s3 -= $carry3 << 21;
$carry5 = ($s5 + (1 << 20)) >> 21;
$s6 += $carry5;
$s5 -= $carry5 << 21;
$carry7 = ($s7 + (1 << 20)) >> 21;
$s8 += $carry7;
$s7 -= $carry7 << 21;
$carry9 = ($s9 + (1 << 20)) >> 21;
$s10 += $carry9;
$s9 -= $carry9 << 21;
$carry11 = ($s11 + (1 << 20)) >> 21;
$s12 += $carry11;
$s11 -= $carry11 << 21;
$s0 += self::mul($s12, 666643, 20);
$s1 += self::mul($s12, 470296, 19);
$s2 += self::mul($s12, 654183, 20);
$s3 -= self::mul($s12, 997805, 20);
$s4 += self::mul($s12, 136657, 18);
$s5 -= self::mul($s12, 683901, 20);
$s12 = 0;
$carry0 = $s0 >> 21;
$s1 += $carry0;
$s0 -= $carry0 << 21;
$carry1 = $s1 >> 21;
$s2 += $carry1;
$s1 -= $carry1 << 21;
$carry2 = $s2 >> 21;
$s3 += $carry2;
$s2 -= $carry2 << 21;
$carry3 = $s3 >> 21;
$s4 += $carry3;
$s3 -= $carry3 << 21;
$carry4 = $s4 >> 21;
$s5 += $carry4;
$s4 -= $carry4 << 21;
$carry5 = $s5 >> 21;
$s6 += $carry5;
$s5 -= $carry5 << 21;
$carry6 = $s6 >> 21;
$s7 += $carry6;
$s6 -= $carry6 << 21;
$carry7 = $s7 >> 21;
$s8 += $carry7;
$s7 -= $carry7 << 21;
$carry8 = $s8 >> 21;
$s9 += $carry8;
$s8 -= $carry8 << 21;
$carry9 = $s9 >> 21;
$s10 += $carry9;
$s9 -= $carry9 << 21;
$carry10 = $s10 >> 21;
$s11 += $carry10;
$s10 -= $carry10 << 21;
$carry11 = $s11 >> 21;
$s12 += $carry11;
$s11 -= $carry11 << 21;
$s0 += self::mul($s12, 666643, 20);
$s1 += self::mul($s12, 470296, 19);
$s2 += self::mul($s12, 654183, 20);
$s3 -= self::mul($s12, 997805, 20);
$s4 += self::mul($s12, 136657, 18);
$s5 -= self::mul($s12, 683901, 20);
$carry0 = $s0 >> 21;
$s1 += $carry0;
$s0 -= $carry0 << 21;
$carry1 = $s1 >> 21;
$s2 += $carry1;
$s1 -= $carry1 << 21;
$carry2 = $s2 >> 21;
$s3 += $carry2;
$s2 -= $carry2 << 21;
$carry3 = $s3 >> 21;
$s4 += $carry3;
$s3 -= $carry3 << 21;
$carry4 = $s4 >> 21;
$s5 += $carry4;
$s4 -= $carry4 << 21;
$carry5 = $s5 >> 21;
$s6 += $carry5;
$s5 -= $carry5 << 21;
$carry6 = $s6 >> 21;
$s7 += $carry6;
$s6 -= $carry6 << 21;
$carry7 = $s7 >> 21;
$s8 += $carry7;
$s7 -= $carry7 << 21;
$carry8 = $s8 >> 21;
$s9 += $carry8;
$s8 -= $carry8 << 21;
$carry9 = $s9 >> 21;
$s10 += $carry9;
$s9 -= $carry9 << 21;
$carry10 = $s10 >> 21;
$s11 += $carry10;
$s10 -= $carry10 << 21;
/**
* @var array<int, int>
*/
$arr = array(
(int) (0xff & ($s0 >> 0)),
(int) (0xff & ($s0 >> 8)),
(int) (0xff & (($s0 >> 16) | $s1 << 5)),
(int) (0xff & ($s1 >> 3)),
(int) (0xff & ($s1 >> 11)),
(int) (0xff & (($s1 >> 19) | $s2 << 2)),
(int) (0xff & ($s2 >> 6)),
(int) (0xff & (($s2 >> 14) | $s3 << 7)),
(int) (0xff & ($s3 >> 1)),
(int) (0xff & ($s3 >> 9)),
(int) (0xff & (($s3 >> 17) | $s4 << 4)),
(int) (0xff & ($s4 >> 4)),
(int) (0xff & ($s4 >> 12)),
(int) (0xff & (($s4 >> 20) | $s5 << 1)),
(int) (0xff & ($s5 >> 7)),
(int) (0xff & (($s5 >> 15) | $s6 << 6)),
(int) (0xff & ($s6 >> 2)),
(int) (0xff & ($s6 >> 10)),
(int) (0xff & (($s6 >> 18) | $s7 << 3)),
(int) (0xff & ($s7 >> 5)),
(int) (0xff & ($s7 >> 13)),
(int) (0xff & ($s8 >> 0)),
(int) (0xff & ($s8 >> 8)),
(int) (0xff & (($s8 >> 16) | $s9 << 5)),
(int) (0xff & ($s9 >> 3)),
(int) (0xff & ($s9 >> 11)),
(int) (0xff & (($s9 >> 19) | $s10 << 2)),
(int) (0xff & ($s10 >> 6)),
(int) (0xff & (($s10 >> 14) | $s11 << 7)),
(int) (0xff & ($s11 >> 1)),
(int) (0xff & ($s11 >> 9)),
0xff & ($s11 >> 17)
);
return self::intArrayToString($arr);
}
/**
* @internal You should not use this directly from another application
*
* @param string $s
* @return string
* @throws TypeError
*/
public static function sc_reduce($s)
{
$s0 = 2097151 & self::load_3(self::substr($s, 0, 3));
$s1 = 2097151 & (self::load_4(self::substr($s, 2, 4)) >> 5);
$s2 = 2097151 & (self::load_3(self::substr($s, 5, 3)) >> 2);
$s3 = 2097151 & (self::load_4(self::substr($s, 7, 4)) >> 7);
$s4 = 2097151 & (self::load_4(self::substr($s, 10, 4)) >> 4);
$s5 = 2097151 & (self::load_3(self::substr($s, 13, 3)) >> 1);
$s6 = 2097151 & (self::load_4(self::substr($s, 15, 4)) >> 6);
$s7 = 2097151 & (self::load_3(self::substr($s, 18, 4)) >> 3);
$s8 = 2097151 & self::load_3(self::substr($s, 21, 3));
$s9 = 2097151 & (self::load_4(self::substr($s, 23, 4)) >> 5);
$s10 = 2097151 & (self::load_3(self::substr($s, 26, 3)) >> 2);
$s11 = 2097151 & (self::load_4(self::substr($s, 28, 4)) >> 7);
$s12 = 2097151 & (self::load_4(self::substr($s, 31, 4)) >> 4);
$s13 = 2097151 & (self::load_3(self::substr($s, 34, 3)) >> 1);
$s14 = 2097151 & (self::load_4(self::substr($s, 36, 4)) >> 6);
$s15 = 2097151 & (self::load_3(self::substr($s, 39, 4)) >> 3);
$s16 = 2097151 & self::load_3(self::substr($s, 42, 3));
$s17 = 2097151 & (self::load_4(self::substr($s, 44, 4)) >> 5);
$s18 = 2097151 & (self::load_3(self::substr($s, 47, 3)) >> 2);
$s19 = 2097151 & (self::load_4(self::substr($s, 49, 4)) >> 7);
$s20 = 2097151 & (self::load_4(self::substr($s, 52, 4)) >> 4);
$s21 = 2097151 & (self::load_3(self::substr($s, 55, 3)) >> 1);
$s22 = 2097151 & (self::load_4(self::substr($s, 57, 4)) >> 6);
$s23 = 0x1fffffff & (self::load_4(self::substr($s, 60, 4)) >> 3);
$s11 += self::mul($s23, 666643, 20);
$s12 += self::mul($s23, 470296, 19);
$s13 += self::mul($s23, 654183, 20);
$s14 -= self::mul($s23, 997805, 20);
$s15 += self::mul($s23, 136657, 18);
$s16 -= self::mul($s23, 683901, 20);
$s10 += self::mul($s22, 666643, 20);
$s11 += self::mul($s22, 470296, 19);
$s12 += self::mul($s22, 654183, 20);
$s13 -= self::mul($s22, 997805, 20);
$s14 += self::mul($s22, 136657, 18);
$s15 -= self::mul($s22, 683901, 20);
$s9 += self::mul($s21, 666643, 20);
$s10 += self::mul($s21, 470296, 19);
$s11 += self::mul($s21, 654183, 20);
$s12 -= self::mul($s21, 997805, 20);
$s13 += self::mul($s21, 136657, 18);
$s14 -= self::mul($s21, 683901, 20);
$s8 += self::mul($s20, 666643, 20);
$s9 += self::mul($s20, 470296, 19);
$s10 += self::mul($s20, 654183, 20);
$s11 -= self::mul($s20, 997805, 20);
$s12 += self::mul($s20, 136657, 18);
$s13 -= self::mul($s20, 683901, 20);
$s7 += self::mul($s19, 666643, 20);
$s8 += self::mul($s19, 470296, 19);
$s9 += self::mul($s19, 654183, 20);
$s10 -= self::mul($s19, 997805, 20);
$s11 += self::mul($s19, 136657, 18);
$s12 -= self::mul($s19, 683901, 20);
$s6 += self::mul($s18, 666643, 20);
$s7 += self::mul($s18, 470296, 19);
$s8 += self::mul($s18, 654183, 20);
$s9 -= self::mul($s18, 997805, 20);
$s10 += self::mul($s18, 136657, 18);
$s11 -= self::mul($s18, 683901, 20);
$carry6 = ($s6 + (1 << 20)) >> 21;
$s7 += $carry6;
$s6 -= $carry6 << 21;
$carry8 = ($s8 + (1 << 20)) >> 21;
$s9 += $carry8;
$s8 -= $carry8 << 21;
$carry10 = ($s10 + (1 << 20)) >> 21;
$s11 += $carry10;
$s10 -= $carry10 << 21;
$carry12 = ($s12 + (1 << 20)) >> 21;
$s13 += $carry12;
$s12 -= $carry12 << 21;
$carry14 = ($s14 + (1 << 20)) >> 21;
$s15 += $carry14;
$s14 -= $carry14 << 21;
$carry16 = ($s16 + (1 << 20)) >> 21;
$s17 += $carry16;
$s16 -= $carry16 << 21;
$carry7 = ($s7 + (1 << 20)) >> 21;
$s8 += $carry7;
$s7 -= $carry7 << 21;
$carry9 = ($s9 + (1 << 20)) >> 21;
$s10 += $carry9;
$s9 -= $carry9 << 21;
$carry11 = ($s11 + (1 << 20)) >> 21;
$s12 += $carry11;
$s11 -= $carry11 << 21;
$carry13 = ($s13 + (1 << 20)) >> 21;
$s14 += $carry13;
$s13 -= $carry13 << 21;
$carry15 = ($s15 + (1 << 20)) >> 21;
$s16 += $carry15;
$s15 -= $carry15 << 21;
$s5 += self::mul($s17, 666643, 20);
$s6 += self::mul($s17, 470296, 19);
$s7 += self::mul($s17, 654183, 20);
$s8 -= self::mul($s17, 997805, 20);
$s9 += self::mul($s17, 136657, 18);
$s10 -= self::mul($s17, 683901, 20);
$s4 += self::mul($s16, 666643, 20);
$s5 += self::mul($s16, 470296, 19);
$s6 += self::mul($s16, 654183, 20);
$s7 -= self::mul($s16, 997805, 20);
$s8 += self::mul($s16, 136657, 18);
$s9 -= self::mul($s16, 683901, 20);
$s3 += self::mul($s15, 666643, 20);
$s4 += self::mul($s15, 470296, 19);
$s5 += self::mul($s15, 654183, 20);
$s6 -= self::mul($s15, 997805, 20);
$s7 += self::mul($s15, 136657, 18);
$s8 -= self::mul($s15, 683901, 20);
$s2 += self::mul($s14, 666643, 20);
$s3 += self::mul($s14, 470296, 19);
$s4 += self::mul($s14, 654183, 20);
$s5 -= self::mul($s14, 997805, 20);
$s6 += self::mul($s14, 136657, 18);
$s7 -= self::mul($s14, 683901, 20);
$s1 += self::mul($s13, 666643, 20);
$s2 += self::mul($s13, 470296, 19);
$s3 += self::mul($s13, 654183, 20);
$s4 -= self::mul($s13, 997805, 20);
$s5 += self::mul($s13, 136657, 18);
$s6 -= self::mul($s13, 683901, 20);
$s0 += self::mul($s12, 666643, 20);
$s1 += self::mul($s12, 470296, 19);
$s2 += self::mul($s12, 654183, 20);
$s3 -= self::mul($s12, 997805, 20);
$s4 += self::mul($s12, 136657, 18);
$s5 -= self::mul($s12, 683901, 20);
$s12 = 0;
$carry0 = ($s0 + (1 << 20)) >> 21;
$s1 += $carry0;
$s0 -= $carry0 << 21;
$carry2 = ($s2 + (1 << 20)) >> 21;
$s3 += $carry2;
$s2 -= $carry2 << 21;
$carry4 = ($s4 + (1 << 20)) >> 21;
$s5 += $carry4;
$s4 -= $carry4 << 21;
$carry6 = ($s6 + (1 << 20)) >> 21;
$s7 += $carry6;
$s6 -= $carry6 << 21;
$carry8 = ($s8 + (1 << 20)) >> 21;
$s9 += $carry8;
$s8 -= $carry8 << 21;
$carry10 = ($s10 + (1 << 20)) >> 21;
$s11 += $carry10;
$s10 -= $carry10 << 21;
$carry1 = ($s1 + (1 << 20)) >> 21;
$s2 += $carry1;
$s1 -= $carry1 << 21;
$carry3 = ($s3 + (1 << 20)) >> 21;
$s4 += $carry3;
$s3 -= $carry3 << 21;
$carry5 = ($s5 + (1 << 20)) >> 21;
$s6 += $carry5;
$s5 -= $carry5 << 21;
$carry7 = ($s7 + (1 << 20)) >> 21;
$s8 += $carry7;
$s7 -= $carry7 << 21;
$carry9 = ($s9 + (1 << 20)) >> 21;
$s10 += $carry9;
$s9 -= $carry9 << 21;
$carry11 = ($s11 + (1 << 20)) >> 21;
$s12 += $carry11;
$s11 -= $carry11 << 21;
$s0 += self::mul($s12, 666643, 20);
$s1 += self::mul($s12, 470296, 19);
$s2 += self::mul($s12, 654183, 20);
$s3 -= self::mul($s12, 997805, 20);
$s4 += self::mul($s12, 136657, 18);
$s5 -= self::mul($s12, 683901, 20);
$s12 = 0;
$carry0 = $s0 >> 21;
$s1 += $carry0;
$s0 -= $carry0 << 21;
$carry1 = $s1 >> 21;
$s2 += $carry1;
$s1 -= $carry1 << 21;
$carry2 = $s2 >> 21;
$s3 += $carry2;
$s2 -= $carry2 << 21;
$carry3 = $s3 >> 21;
$s4 += $carry3;
$s3 -= $carry3 << 21;
$carry4 = $s4 >> 21;
$s5 += $carry4;
$s4 -= $carry4 << 21;
$carry5 = $s5 >> 21;
$s6 += $carry5;
$s5 -= $carry5 << 21;
$carry6 = $s6 >> 21;
$s7 += $carry6;
$s6 -= $carry6 << 21;
$carry7 = $s7 >> 21;
$s8 += $carry7;
$s7 -= $carry7 << 21;
$carry8 = $s8 >> 21;
$s9 += $carry8;
$s8 -= $carry8 << 21;
$carry9 = $s9 >> 21;
$s10 += $carry9;
$s9 -= $carry9 << 21;
$carry10 = $s10 >> 21;
$s11 += $carry10;
$s10 -= $carry10 << 21;
$carry11 = $s11 >> 21;
$s12 += $carry11;
$s11 -= $carry11 << 21;
$s0 += self::mul($s12, 666643, 20);
$s1 += self::mul($s12, 470296, 19);
$s2 += self::mul($s12, 654183, 20);
$s3 -= self::mul($s12, 997805, 20);
$s4 += self::mul($s12, 136657, 18);
$s5 -= self::mul($s12, 683901, 20);
$carry0 = $s0 >> 21;
$s1 += $carry0;
$s0 -= $carry0 << 21;
$carry1 = $s1 >> 21;
$s2 += $carry1;
$s1 -= $carry1 << 21;
$carry2 = $s2 >> 21;
$s3 += $carry2;
$s2 -= $carry2 << 21;
$carry3 = $s3 >> 21;
$s4 += $carry3;
$s3 -= $carry3 << 21;
$carry4 = $s4 >> 21;
$s5 += $carry4;
$s4 -= $carry4 << 21;
$carry5 = $s5 >> 21;
$s6 += $carry5;
$s5 -= $carry5 << 21;
$carry6 = $s6 >> 21;
$s7 += $carry6;
$s6 -= $carry6 << 21;
$carry7 = $s7 >> 21;
$s8 += $carry7;
$s7 -= $carry7 << 21;
$carry8 = $s8 >> 21;
$s9 += $carry8;
$s8 -= $carry8 << 21;
$carry9 = $s9 >> 21;
$s10 += $carry9;
$s9 -= $carry9 << 21;
$carry10 = $s10 >> 21;
$s11 += $carry10;
$s10 -= $carry10 << 21;
/**
* @var array<int, int>
*/
$arr = array(
(int) ($s0 >> 0),
(int) ($s0 >> 8),
(int) (($s0 >> 16) | $s1 << 5),
(int) ($s1 >> 3),
(int) ($s1 >> 11),
(int) (($s1 >> 19) | $s2 << 2),
(int) ($s2 >> 6),
(int) (($s2 >> 14) | $s3 << 7),
(int) ($s3 >> 1),
(int) ($s3 >> 9),
(int) (($s3 >> 17) | $s4 << 4),
(int) ($s4 >> 4),
(int) ($s4 >> 12),
(int) (($s4 >> 20) | $s5 << 1),
(int) ($s5 >> 7),
(int) (($s5 >> 15) | $s6 << 6),
(int) ($s6 >> 2),
(int) ($s6 >> 10),
(int) (($s6 >> 18) | $s7 << 3),
(int) ($s7 >> 5),
(int) ($s7 >> 13),
(int) ($s8 >> 0),
(int) ($s8 >> 8),
(int) (($s8 >> 16) | $s9 << 5),
(int) ($s9 >> 3),
(int) ($s9 >> 11),
(int) (($s9 >> 19) | $s10 << 2),
(int) ($s10 >> 6),
(int) (($s10 >> 14) | $s11 << 7),
(int) ($s11 >> 1),
(int) ($s11 >> 9),
(int) $s11 >> 17
);
return self::intArrayToString($arr);
}
/**
* multiply by the order of the main subgroup l = 2^252+27742317777372353535851937790883648493
*
* @param ParagonIE_Sodium_Core_Curve25519_Ge_P3 $A
* @return ParagonIE_Sodium_Core_Curve25519_Ge_P3
*/
public static function ge_mul_l(ParagonIE_Sodium_Core_Curve25519_Ge_P3 $A)
{
$aslide = array(
13, 0, 0, 0, 0, -1, 0, 0, 0, 0, -11, 0, 0, 0, 0, 0, 0, -5, 0, 0, 0,
0, 0, 0, -3, 0, 0, 0, 0, -13, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 3, 0,
0, 0, 0, -13, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0,
0, 0, 11, 0, 0, 0, 0, -13, 0, 0, 0, 0, 0, 0, -3, 0, 0, 0, 0, 0, -1,
0, 0, 0, 0, 3, 0, 0, 0, 0, -11, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0,
0, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 7, 0, 0, 0, 0, 5, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1
);
/** @var array<int, ParagonIE_Sodium_Core_Curve25519_Ge_Cached> $Ai size 8 */
$Ai = array();
# ge_p3_to_cached(&Ai[0], A);
$Ai[0] = self::ge_p3_to_cached($A);
# ge_p3_dbl(&t, A);
$t = self::ge_p3_dbl($A);
# ge_p1p1_to_p3(&A2, &t);
$A2 = self::ge_p1p1_to_p3($t);
for ($i = 1; $i < 8; ++$i) {
# ge_add(&t, &A2, &Ai[0]);
$t = self::ge_add($A2, $Ai[$i - 1]);
# ge_p1p1_to_p3(&u, &t);
$u = self::ge_p1p1_to_p3($t);
# ge_p3_to_cached(&Ai[i], &u);
$Ai[$i] = self::ge_p3_to_cached($u);
}
$r = self::ge_p3_0();
for ($i = 252; $i >= 0; --$i) {
$t = self::ge_p3_dbl($r);
if ($aslide[$i] > 0) {
# ge_p1p1_to_p3(&u, &t);
$u = self::ge_p1p1_to_p3($t);
# ge_add(&t, &u, &Ai[aslide[i] / 2]);
$t = self::ge_add($u, $Ai[(int)($aslide[$i] / 2)]);
} elseif ($aslide[$i] < 0) {
# ge_p1p1_to_p3(&u, &t);
$u = self::ge_p1p1_to_p3($t);
# ge_sub(&t, &u, &Ai[(-aslide[i]) / 2]);
$t = self::ge_sub($u, $Ai[(int)(-$aslide[$i] / 2)]);
}
}
# ge_p1p1_to_p3(r, &t);
return self::ge_p1p1_to_p3($t);
}
/**
* @param string $a
* @param string $b
* @return string
*/
public static function sc25519_mul($a, $b)
{
// int64_t a0 = 2097151 & load_3(a);
// int64_t a1 = 2097151 & (load_4(a + 2) >> 5);
// int64_t a2 = 2097151 & (load_3(a + 5) >> 2);
// int64_t a3 = 2097151 & (load_4(a + 7) >> 7);
// int64_t a4 = 2097151 & (load_4(a + 10) >> 4);
// int64_t a5 = 2097151 & (load_3(a + 13) >> 1);
// int64_t a6 = 2097151 & (load_4(a + 15) >> 6);
// int64_t a7 = 2097151 & (load_3(a + 18) >> 3);
// int64_t a8 = 2097151 & load_3(a + 21);
// int64_t a9 = 2097151 & (load_4(a + 23) >> 5);
// int64_t a10 = 2097151 & (load_3(a + 26) >> 2);
// int64_t a11 = (load_4(a + 28) >> 7);
$a0 = 2097151 & self::load_3(self::substr($a, 0, 3));
$a1 = 2097151 & (self::load_4(self::substr($a, 2, 4)) >> 5);
$a2 = 2097151 & (self::load_3(self::substr($a, 5, 3)) >> 2);
$a3 = 2097151 & (self::load_4(self::substr($a, 7, 4)) >> 7);
$a4 = 2097151 & (self::load_4(self::substr($a, 10, 4)) >> 4);
$a5 = 2097151 & (self::load_3(self::substr($a, 13, 3)) >> 1);
$a6 = 2097151 & (self::load_4(self::substr($a, 15, 4)) >> 6);
$a7 = 2097151 & (self::load_3(self::substr($a, 18, 3)) >> 3);
$a8 = 2097151 & self::load_3(self::substr($a, 21, 3));
$a9 = 2097151 & (self::load_4(self::substr($a, 23, 4)) >> 5);
$a10 = 2097151 & (self::load_3(self::substr($a, 26, 3)) >> 2);
$a11 = (self::load_4(self::substr($a, 28, 4)) >> 7);
// int64_t b0 = 2097151 & load_3(b);
// int64_t b1 = 2097151 & (load_4(b + 2) >> 5);
// int64_t b2 = 2097151 & (load_3(b + 5) >> 2);
// int64_t b3 = 2097151 & (load_4(b + 7) >> 7);
// int64_t b4 = 2097151 & (load_4(b + 10) >> 4);
// int64_t b5 = 2097151 & (load_3(b + 13) >> 1);
// int64_t b6 = 2097151 & (load_4(b + 15) >> 6);
// int64_t b7 = 2097151 & (load_3(b + 18) >> 3);
// int64_t b8 = 2097151 & load_3(b + 21);
// int64_t b9 = 2097151 & (load_4(b + 23) >> 5);
// int64_t b10 = 2097151 & (load_3(b + 26) >> 2);
// int64_t b11 = (load_4(b + 28) >> 7);
$b0 = 2097151 & self::load_3(self::substr($b, 0, 3));
$b1 = 2097151 & (self::load_4(self::substr($b, 2, 4)) >> 5);
$b2 = 2097151 & (self::load_3(self::substr($b, 5, 3)) >> 2);
$b3 = 2097151 & (self::load_4(self::substr($b, 7, 4)) >> 7);
$b4 = 2097151 & (self::load_4(self::substr($b, 10, 4)) >> 4);
$b5 = 2097151 & (self::load_3(self::substr($b, 13, 3)) >> 1);
$b6 = 2097151 & (self::load_4(self::substr($b, 15, 4)) >> 6);
$b7 = 2097151 & (self::load_3(self::substr($b, 18, 3)) >> 3);
$b8 = 2097151 & self::load_3(self::substr($b, 21, 3));
$b9 = 2097151 & (self::load_4(self::substr($b, 23, 4)) >> 5);
$b10 = 2097151 & (self::load_3(self::substr($b, 26, 3)) >> 2);
$b11 = (self::load_4(self::substr($b, 28, 4)) >> 7);
// s0 = a0 * b0;
// s1 = a0 * b1 + a1 * b0;
// s2 = a0 * b2 + a1 * b1 + a2 * b0;
// s3 = a0 * b3 + a1 * b2 + a2 * b1 + a3 * b0;
// s4 = a0 * b4 + a1 * b3 + a2 * b2 + a3 * b1 + a4 * b0;
// s5 = a0 * b5 + a1 * b4 + a2 * b3 + a3 * b2 + a4 * b1 + a5 * b0;
// s6 = a0 * b6 + a1 * b5 + a2 * b4 + a3 * b3 + a4 * b2 + a5 * b1 + a6 * b0;
// s7 = a0 * b7 + a1 * b6 + a2 * b5 + a3 * b4 + a4 * b3 + a5 * b2 +
// a6 * b1 + a7 * b0;
// s8 = a0 * b8 + a1 * b7 + a2 * b6 + a3 * b5 + a4 * b4 + a5 * b3 +
// a6 * b2 + a7 * b1 + a8 * b0;
// s9 = a0 * b9 + a1 * b8 + a2 * b7 + a3 * b6 + a4 * b5 + a5 * b4 +
// a6 * b3 + a7 * b2 + a8 * b1 + a9 * b0;
// s10 = a0 * b10 + a1 * b9 + a2 * b8 + a3 * b7 + a4 * b6 + a5 * b5 +
// a6 * b4 + a7 * b3 + a8 * b2 + a9 * b1 + a10 * b0;
// s11 = a0 * b11 + a1 * b10 + a2 * b9 + a3 * b8 + a4 * b7 + a5 * b6 +
// a6 * b5 + a7 * b4 + a8 * b3 + a9 * b2 + a10 * b1 + a11 * b0;
// s12 = a1 * b11 + a2 * b10 + a3 * b9 + a4 * b8 + a5 * b7 + a6 * b6 +
// a7 * b5 + a8 * b4 + a9 * b3 + a10 * b2 + a11 * b1;
// s13 = a2 * b11 + a3 * b10 + a4 * b9 + a5 * b8 + a6 * b7 + a7 * b6 +
// a8 * b5 + a9 * b4 + a10 * b3 + a11 * b2;
// s14 = a3 * b11 + a4 * b10 + a5 * b9 + a6 * b8 + a7 * b7 + a8 * b6 +
// a9 * b5 + a10 * b4 + a11 * b3;
// s15 = a4 * b11 + a5 * b10 + a6 * b9 + a7 * b8 + a8 * b7 + a9 * b6 +
// a10 * b5 + a11 * b4;
// s16 =
// a5 * b11 + a6 * b10 + a7 * b9 + a8 * b8 + a9 * b7 + a10 * b6 + a11 * b5;
// s17 = a6 * b11 + a7 * b10 + a8 * b9 + a9 * b8 + a10 * b7 + a11 * b6;
// s18 = a7 * b11 + a8 * b10 + a9 * b9 + a10 * b8 + a11 * b7;
// s19 = a8 * b11 + a9 * b10 + a10 * b9 + a11 * b8;
// s20 = a9 * b11 + a10 * b10 + a11 * b9;
// s21 = a10 * b11 + a11 * b10;
// s22 = a11 * b11;
// s23 = 0;
$s0 = self::mul($a0, $b0, 22);
$s1 = self::mul($a0, $b1, 22) + self::mul($a1, $b0, 22);
$s2 = self::mul($a0, $b2, 22) + self::mul($a1, $b1, 22) + self::mul($a2, $b0, 22);
$s3 = self::mul($a0, $b3, 22) + self::mul($a1, $b2, 22) + self::mul($a2, $b1, 22) + self::mul($a3, $b0, 22);
$s4 = self::mul($a0, $b4, 22) + self::mul($a1, $b3, 22) + self::mul($a2, $b2, 22) + self::mul($a3, $b1, 22) +
self::mul($a4, $b0, 22);
$s5 = self::mul($a0, $b5, 22) + self::mul($a1, $b4, 22) + self::mul($a2, $b3, 22) + self::mul($a3, $b2, 22) +
self::mul($a4, $b1, 22) + self::mul($a5, $b0, 22);
$s6 = self::mul($a0, $b6, 22) + self::mul($a1, $b5, 22) + self::mul($a2, $b4, 22) + self::mul($a3, $b3, 22) +
self::mul($a4, $b2, 22) + self::mul($a5, $b1, 22) + self::mul($a6, $b0, 22);
$s7 = self::mul($a0, $b7, 22) + self::mul($a1, $b6, 22) + self::mul($a2, $b5, 22) + self::mul($a3, $b4, 22) +
self::mul($a4, $b3, 22) + self::mul($a5, $b2, 22) + self::mul($a6, $b1, 22) + self::mul($a7, $b0, 22);
$s8 = self::mul($a0, $b8, 22) + self::mul($a1, $b7, 22) + self::mul($a2, $b6, 22) + self::mul($a3, $b5, 22) +
self::mul($a4, $b4, 22) + self::mul($a5, $b3, 22) + self::mul($a6, $b2, 22) + self::mul($a7, $b1, 22) +
self::mul($a8, $b0, 22);
$s9 = self::mul($a0, $b9, 22) + self::mul($a1, $b8, 22) + self::mul($a2, $b7, 22) + self::mul($a3, $b6, 22) +
self::mul($a4, $b5, 22) + self::mul($a5, $b4, 22) + self::mul($a6, $b3, 22) + self::mul($a7, $b2, 22) +
self::mul($a8, $b1, 22) + self::mul($a9, $b0, 22);
$s10 = self::mul($a0, $b10, 22) + self::mul($a1, $b9, 22) + self::mul($a2, $b8, 22) + self::mul($a3, $b7, 22) +
self::mul($a4, $b6, 22) + self::mul($a5, $b5, 22) + self::mul($a6, $b4, 22) + self::mul($a7, $b3, 22) +
self::mul($a8, $b2, 22) + self::mul($a9, $b1, 22) + self::mul($a10, $b0, 22);
$s11 = self::mul($a0, $b11, 22) + self::mul($a1, $b10, 22) + self::mul($a2, $b9, 22) + self::mul($a3, $b8, 22) +
self::mul($a4, $b7, 22) + self::mul($a5, $b6, 22) + self::mul($a6, $b5, 22) + self::mul($a7, $b4, 22) +
self::mul($a8, $b3, 22) + self::mul($a9, $b2, 22) + self::mul($a10, $b1, 22) + self::mul($a11, $b0, 22);
$s12 = self::mul($a1, $b11, 22) + self::mul($a2, $b10, 22) + self::mul($a3, $b9, 22) + self::mul($a4, $b8, 22) +
self::mul($a5, $b7, 22) + self::mul($a6, $b6, 22) + self::mul($a7, $b5, 22) + self::mul($a8, $b4, 22) +
self::mul($a9, $b3, 22) + self::mul($a10, $b2, 22) + self::mul($a11, $b1, 22);
$s13 = self::mul($a2, $b11, 22) + self::mul($a3, $b10, 22) + self::mul($a4, $b9, 22) + self::mul($a5, $b8, 22) +
self::mul($a6, $b7, 22) + self::mul($a7, $b6, 22) + self::mul($a8, $b5, 22) + self::mul($a9, $b4, 22) +
self::mul($a10, $b3, 22) + self::mul($a11, $b2, 22);
$s14 = self::mul($a3, $b11, 22) + self::mul($a4, $b10, 22) + self::mul($a5, $b9, 22) + self::mul($a6, $b8, 22) +
self::mul($a7, $b7, 22) + self::mul($a8, $b6, 22) + self::mul($a9, $b5, 22) + self::mul($a10, $b4, 22) +
self::mul($a11, $b3, 22);
$s15 = self::mul($a4, $b11, 22) + self::mul($a5, $b10, 22) + self::mul($a6, $b9, 22) + self::mul($a7, $b8, 22) +
self::mul($a8, $b7, 22) + self::mul($a9, $b6, 22) + self::mul($a10, $b5, 22) + self::mul($a11, $b4, 22);
$s16 =
self::mul($a5, $b11, 22) + self::mul($a6, $b10, 22) + self::mul($a7, $b9, 22) + self::mul($a8, $b8, 22) +
self::mul($a9, $b7, 22) + self::mul($a10, $b6, 22) + self::mul($a11, $b5, 22);
$s17 = self::mul($a6, $b11, 22) + self::mul($a7, $b10, 22) + self::mul($a8, $b9, 22) + self::mul($a9, $b8, 22) +
self::mul($a10, $b7, 22) + self::mul($a11, $b6, 22);
$s18 = self::mul($a7, $b11, 22) + self::mul($a8, $b10, 22) + self::mul($a9, $b9, 22) + self::mul($a10, $b8, 22)
+ self::mul($a11, $b7, 22);
$s19 = self::mul($a8, $b11, 22) + self::mul($a9, $b10, 22) + self::mul($a10, $b9, 22) +
self::mul($a11, $b8, 22);
$s20 = self::mul($a9, $b11, 22) + self::mul($a10, $b10, 22) + self::mul($a11, $b9, 22);
$s21 = self::mul($a10, $b11, 22) + self::mul($a11, $b10, 22);
$s22 = self::mul($a11, $b11, 22);
$s23 = 0;
// carry0 = (s0 + (int64_t) (1L << 20)) >> 21;
// s1 += carry0;
// s0 -= carry0 * ((uint64_t) 1L << 21);
$carry0 = ($s0 + (1 << 20)) >> 21;
$s1 += $carry0;
$s0 -= $carry0 << 21;
// carry2 = (s2 + (int64_t) (1L << 20)) >> 21;
// s3 += carry2;
// s2 -= carry2 * ((uint64_t) 1L << 21);
$carry2 = ($s2 + (1 << 20)) >> 21;
$s3 += $carry2;
$s2 -= $carry2 << 21;
// carry4 = (s4 + (int64_t) (1L << 20)) >> 21;
// s5 += carry4;
// s4 -= carry4 * ((uint64_t) 1L << 21);
$carry4 = ($s4 + (1 << 20)) >> 21;
$s5 += $carry4;
$s4 -= $carry4 << 21;
// carry6 = (s6 + (int64_t) (1L << 20)) >> 21;
// s7 += carry6;
// s6 -= carry6 * ((uint64_t) 1L << 21);
$carry6 = ($s6 + (1 << 20)) >> 21;
$s7 += $carry6;
$s6 -= $carry6 << 21;
// carry8 = (s8 + (int64_t) (1L << 20)) >> 21;
// s9 += carry8;
// s8 -= carry8 * ((uint64_t) 1L << 21);
$carry8 = ($s8 + (1 << 20)) >> 21;
$s9 += $carry8;
$s8 -= $carry8 << 21;
// carry10 = (s10 + (int64_t) (1L << 20)) >> 21;
// s11 += carry10;
// s10 -= carry10 * ((uint64_t) 1L << 21);
$carry10 = ($s10 + (1 << 20)) >> 21;
$s11 += $carry10;
$s10 -= $carry10 << 21;
// carry12 = (s12 + (int64_t) (1L << 20)) >> 21;
// s13 += carry12;
// s12 -= carry12 * ((uint64_t) 1L << 21);
$carry12 = ($s12 + (1 << 20)) >> 21;
$s13 += $carry12;
$s12 -= $carry12 << 21;
// carry14 = (s14 + (int64_t) (1L << 20)) >> 21;
// s15 += carry14;
// s14 -= carry14 * ((uint64_t) 1L << 21);
$carry14 = ($s14 + (1 << 20)) >> 21;
$s15 += $carry14;
$s14 -= $carry14 << 21;
// carry16 = (s16 + (int64_t) (1L << 20)) >> 21;
// s17 += carry16;
// s16 -= carry16 * ((uint64_t) 1L << 21);
$carry16 = ($s16 + (1 << 20)) >> 21;
$s17 += $carry16;
$s16 -= $carry16 << 21;
// carry18 = (s18 + (int64_t) (1L << 20)) >> 21;
// s19 += carry18;
// s18 -= carry18 * ((uint64_t) 1L << 21);
$carry18 = ($s18 + (1 << 20)) >> 21;
$s19 += $carry18;
$s18 -= $carry18 << 21;
// carry20 = (s20 + (int64_t) (1L << 20)) >> 21;
// s21 += carry20;
// s20 -= carry20 * ((uint64_t) 1L << 21);
$carry20 = ($s20 + (1 << 20)) >> 21;
$s21 += $carry20;
$s20 -= $carry20 << 21;
// carry22 = (s22 + (int64_t) (1L << 20)) >> 21;
// s23 += carry22;
// s22 -= carry22 * ((uint64_t) 1L << 21);
$carry22 = ($s22 + (1 << 20)) >> 21;
$s23 += $carry22;
$s22 -= $carry22 << 21;
// carry1 = (s1 + (int64_t) (1L << 20)) >> 21;
// s2 += carry1;
// s1 -= carry1 * ((uint64_t) 1L << 21);
$carry1 = ($s1 + (1 << 20)) >> 21;
$s2 += $carry1;
$s1 -= $carry1 << 21;
// carry3 = (s3 + (int64_t) (1L << 20)) >> 21;
// s4 += carry3;
// s3 -= carry3 * ((uint64_t) 1L << 21);
$carry3 = ($s3 + (1 << 20)) >> 21;
$s4 += $carry3;
$s3 -= $carry3 << 21;
// carry5 = (s5 + (int64_t) (1L << 20)) >> 21;
// s6 += carry5;
// s5 -= carry5 * ((uint64_t) 1L << 21);
$carry5 = ($s5 + (1 << 20)) >> 21;
$s6 += $carry5;
$s5 -= $carry5 << 21;
// carry7 = (s7 + (int64_t) (1L << 20)) >> 21;
// s8 += carry7;
// s7 -= carry7 * ((uint64_t) 1L << 21);
$carry7 = ($s7 + (1 << 20)) >> 21;
$s8 += $carry7;
$s7 -= $carry7 << 21;
// carry9 = (s9 + (int64_t) (1L << 20)) >> 21;
// s10 += carry9;
// s9 -= carry9 * ((uint64_t) 1L << 21);
$carry9 = ($s9 + (1 << 20)) >> 21;
$s10 += $carry9;
$s9 -= $carry9 << 21;
// carry11 = (s11 + (int64_t) (1L << 20)) >> 21;
// s12 += carry11;
// s11 -= carry11 * ((uint64_t) 1L << 21);
$carry11 = ($s11 + (1 << 20)) >> 21;
$s12 += $carry11;
$s11 -= $carry11 << 21;
// carry13 = (s13 + (int64_t) (1L << 20)) >> 21;
// s14 += carry13;
// s13 -= carry13 * ((uint64_t) 1L << 21);
$carry13 = ($s13 + (1 << 20)) >> 21;
$s14 += $carry13;
$s13 -= $carry13 << 21;
// carry15 = (s15 + (int64_t) (1L << 20)) >> 21;
// s16 += carry15;
// s15 -= carry15 * ((uint64_t) 1L << 21);
$carry15 = ($s15 + (1 << 20)) >> 21;
$s16 += $carry15;
$s15 -= $carry15 << 21;
// carry17 = (s17 + (int64_t) (1L << 20)) >> 21;
// s18 += carry17;
// s17 -= carry17 * ((uint64_t) 1L << 21);
$carry17 = ($s17 + (1 << 20)) >> 21;
$s18 += $carry17;
$s17 -= $carry17 << 21;
// carry19 = (s19 + (int64_t) (1L << 20)) >> 21;
// s20 += carry19;
// s19 -= carry19 * ((uint64_t) 1L << 21);
$carry19 = ($s19 + (1 << 20)) >> 21;
$s20 += $carry19;
$s19 -= $carry19 << 21;
// carry21 = (s21 + (int64_t) (1L << 20)) >> 21;
// s22 += carry21;
// s21 -= carry21 * ((uint64_t) 1L << 21);
$carry21 = ($s21 + (1 << 20)) >> 21;
$s22 += $carry21;
$s21 -= $carry21 << 21;
// s11 += s23 * 666643;
// s12 += s23 * 470296;
// s13 += s23 * 654183;
// s14 -= s23 * 997805;
// s15 += s23 * 136657;
// s16 -= s23 * 683901;
$s11 += self::mul($s23, 666643, 20);
$s12 += self::mul($s23, 470296, 19);
$s13 += self::mul($s23, 654183, 20);
$s14 -= self::mul($s23, 997805, 20);
$s15 += self::mul($s23, 136657, 18);
$s16 -= self::mul($s23, 683901, 20);
// s10 += s22 * 666643;
// s11 += s22 * 470296;
// s12 += s22 * 654183;
// s13 -= s22 * 997805;
// s14 += s22 * 136657;
// s15 -= s22 * 683901;
$s10 += self::mul($s22, 666643, 20);
$s11 += self::mul($s22, 470296, 19);
$s12 += self::mul($s22, 654183, 20);
$s13 -= self::mul($s22, 997805, 20);
$s14 += self::mul($s22, 136657, 18);
$s15 -= self::mul($s22, 683901, 20);
// s9 += s21 * 666643;
// s10 += s21 * 470296;
// s11 += s21 * 654183;
// s12 -= s21 * 997805;
// s13 += s21 * 136657;
// s14 -= s21 * 683901;
$s9 += self::mul($s21, 666643, 20);
$s10 += self::mul($s21, 470296, 19);
$s11 += self::mul($s21, 654183, 20);
$s12 -= self::mul($s21, 997805, 20);
$s13 += self::mul($s21, 136657, 18);
$s14 -= self::mul($s21, 683901, 20);
// s8 += s20 * 666643;
// s9 += s20 * 470296;
// s10 += s20 * 654183;
// s11 -= s20 * 997805;
// s12 += s20 * 136657;
// s13 -= s20 * 683901;
$s8 += self::mul($s20, 666643, 20);
$s9 += self::mul($s20, 470296, 19);
$s10 += self::mul($s20, 654183, 20);
$s11 -= self::mul($s20, 997805, 20);
$s12 += self::mul($s20, 136657, 18);
$s13 -= self::mul($s20, 683901, 20);
// s7 += s19 * 666643;
// s8 += s19 * 470296;
// s9 += s19 * 654183;
// s10 -= s19 * 997805;
// s11 += s19 * 136657;
// s12 -= s19 * 683901;
$s7 += self::mul($s19, 666643, 20);
$s8 += self::mul($s19, 470296, 19);
$s9 += self::mul($s19, 654183, 20);
$s10 -= self::mul($s19, 997805, 20);
$s11 += self::mul($s19, 136657, 18);
$s12 -= self::mul($s19, 683901, 20);
// s6 += s18 * 666643;
// s7 += s18 * 470296;
// s8 += s18 * 654183;
// s9 -= s18 * 997805;
// s10 += s18 * 136657;
// s11 -= s18 * 683901;
$s6 += self::mul($s18, 666643, 20);
$s7 += self::mul($s18, 470296, 19);
$s8 += self::mul($s18, 654183, 20);
$s9 -= self::mul($s18, 997805, 20);
$s10 += self::mul($s18, 136657, 18);
$s11 -= self::mul($s18, 683901, 20);
// carry6 = (s6 + (int64_t) (1L << 20)) >> 21;
// s7 += carry6;
// s6 -= carry6 * ((uint64_t) 1L << 21);
$carry6 = ($s6 + (1 << 20)) >> 21;
$s7 += $carry6;
$s6 -= $carry6 << 21;
// carry8 = (s8 + (int64_t) (1L << 20)) >> 21;
// s9 += carry8;
// s8 -= carry8 * ((uint64_t) 1L << 21);
$carry8 = ($s8 + (1 << 20)) >> 21;
$s9 += $carry8;
$s8 -= $carry8 << 21;
// carry10 = (s10 + (int64_t) (1L << 20)) >> 21;
// s11 += carry10;
// s10 -= carry10 * ((uint64_t) 1L << 21);
$carry10 = ($s10 + (1 << 20)) >> 21;
$s11 += $carry10;
$s10 -= $carry10 << 21;
// carry12 = (s12 + (int64_t) (1L << 20)) >> 21;
// s13 += carry12;
// s12 -= carry12 * ((uint64_t) 1L << 21);
$carry12 = ($s12 + (1 << 20)) >> 21;
$s13 += $carry12;
$s12 -= $carry12 << 21;
// carry14 = (s14 + (int64_t) (1L << 20)) >> 21;
// s15 += carry14;
// s14 -= carry14 * ((uint64_t) 1L << 21);
$carry14 = ($s14 + (1 << 20)) >> 21;
$s15 += $carry14;
$s14 -= $carry14 << 21;
// carry16 = (s16 + (int64_t) (1L << 20)) >> 21;
// s17 += carry16;
// s16 -= carry16 * ((uint64_t) 1L << 21);
$carry16 = ($s16 + (1 << 20)) >> 21;
$s17 += $carry16;
$s16 -= $carry16 << 21;
// carry7 = (s7 + (int64_t) (1L << 20)) >> 21;
// s8 += carry7;
// s7 -= carry7 * ((uint64_t) 1L << 21);
$carry7 = ($s7 + (1 << 20)) >> 21;
$s8 += $carry7;
$s7 -= $carry7 << 21;
// carry9 = (s9 + (int64_t) (1L << 20)) >> 21;
// s10 += carry9;
// s9 -= carry9 * ((uint64_t) 1L << 21);
$carry9 = ($s9 + (1 << 20)) >> 21;
$s10 += $carry9;
$s9 -= $carry9 << 21;
// carry11 = (s11 + (int64_t) (1L << 20)) >> 21;
// s12 += carry11;
// s11 -= carry11 * ((uint64_t) 1L << 21);
$carry11 = ($s11 + (1 << 20)) >> 21;
$s12 += $carry11;
$s11 -= $carry11 << 21;
// carry13 = (s13 + (int64_t) (1L << 20)) >> 21;
// s14 += carry13;
// s13 -= carry13 * ((uint64_t) 1L << 21);
$carry13 = ($s13 + (1 << 20)) >> 21;
$s14 += $carry13;
$s13 -= $carry13 << 21;
// carry15 = (s15 + (int64_t) (1L << 20)) >> 21;
// s16 += carry15;
// s15 -= carry15 * ((uint64_t) 1L << 21);
$carry15 = ($s15 + (1 << 20)) >> 21;
$s16 += $carry15;
$s15 -= $carry15 << 21;
// s5 += s17 * 666643;
// s6 += s17 * 470296;
// s7 += s17 * 654183;
// s8 -= s17 * 997805;
// s9 += s17 * 136657;
// s10 -= s17 * 683901;
$s5 += self::mul($s17, 666643, 20);
$s6 += self::mul($s17, 470296, 19);
$s7 += self::mul($s17, 654183, 20);
$s8 -= self::mul($s17, 997805, 20);
$s9 += self::mul($s17, 136657, 18);
$s10 -= self::mul($s17, 683901, 20);
// s4 += s16 * 666643;
// s5 += s16 * 470296;
// s6 += s16 * 654183;
// s7 -= s16 * 997805;
// s8 += s16 * 136657;
// s9 -= s16 * 683901;
$s4 += self::mul($s16, 666643, 20);
$s5 += self::mul($s16, 470296, 19);
$s6 += self::mul($s16, 654183, 20);
$s7 -= self::mul($s16, 997805, 20);
$s8 += self::mul($s16, 136657, 18);
$s9 -= self::mul($s16, 683901, 20);
// s3 += s15 * 666643;
// s4 += s15 * 470296;
// s5 += s15 * 654183;
// s6 -= s15 * 997805;
// s7 += s15 * 136657;
// s8 -= s15 * 683901;
$s3 += self::mul($s15, 666643, 20);
$s4 += self::mul($s15, 470296, 19);
$s5 += self::mul($s15, 654183, 20);
$s6 -= self::mul($s15, 997805, 20);
$s7 += self::mul($s15, 136657, 18);
$s8 -= self::mul($s15, 683901, 20);
// s2 += s14 * 666643;
// s3 += s14 * 470296;
// s4 += s14 * 654183;
// s5 -= s14 * 997805;
// s6 += s14 * 136657;
// s7 -= s14 * 683901;
$s2 += self::mul($s14, 666643, 20);
$s3 += self::mul($s14, 470296, 19);
$s4 += self::mul($s14, 654183, 20);
$s5 -= self::mul($s14, 997805, 20);
$s6 += self::mul($s14, 136657, 18);
$s7 -= self::mul($s14, 683901, 20);
// s1 += s13 * 666643;
// s2 += s13 * 470296;
// s3 += s13 * 654183;
// s4 -= s13 * 997805;
// s5 += s13 * 136657;
// s6 -= s13 * 683901;
$s1 += self::mul($s13, 666643, 20);
$s2 += self::mul($s13, 470296, 19);
$s3 += self::mul($s13, 654183, 20);
$s4 -= self::mul($s13, 997805, 20);
$s5 += self::mul($s13, 136657, 18);
$s6 -= self::mul($s13, 683901, 20);
// s0 += s12 * 666643;
// s1 += s12 * 470296;
// s2 += s12 * 654183;
// s3 -= s12 * 997805;
// s4 += s12 * 136657;
// s5 -= s12 * 683901;
// s12 = 0;
$s0 += self::mul($s12, 666643, 20);
$s1 += self::mul($s12, 470296, 19);
$s2 += self::mul($s12, 654183, 20);
$s3 -= self::mul($s12, 997805, 20);
$s4 += self::mul($s12, 136657, 18);
$s5 -= self::mul($s12, 683901, 20);
$s12 = 0;
// carry0 = (s0 + (int64_t) (1L << 20)) >> 21;
// s1 += carry0;
// s0 -= carry0 * ((uint64_t) 1L << 21);
$carry0 = ($s0 + (1 << 20)) >> 21;
$s1 += $carry0;
$s0 -= $carry0 << 21;
// carry2 = (s2 + (int64_t) (1L << 20)) >> 21;
// s3 += carry2;
// s2 -= carry2 * ((uint64_t) 1L << 21);
$carry2 = ($s2 + (1 << 20)) >> 21;
$s3 += $carry2;
$s2 -= $carry2 << 21;
// carry4 = (s4 + (int64_t) (1L << 20)) >> 21;
// s5 += carry4;
// s4 -= carry4 * ((uint64_t) 1L << 21);
$carry4 = ($s4 + (1 << 20)) >> 21;
$s5 += $carry4;
$s4 -= $carry4 << 21;
// carry6 = (s6 + (int64_t) (1L << 20)) >> 21;
// s7 += carry6;
// s6 -= carry6 * ((uint64_t) 1L << 21);
$carry6 = ($s6 + (1 << 20)) >> 21;
$s7 += $carry6;
$s6 -= $carry6 << 21;
// carry8 = (s8 + (int64_t) (1L << 20)) >> 21;
// s9 += carry8;
// s8 -= carry8 * ((uint64_t) 1L << 21);
$carry8 = ($s8 + (1 << 20)) >> 21;
$s9 += $carry8;
$s8 -= $carry8 << 21;
// carry10 = (s10 + (int64_t) (1L << 20)) >> 21;
// s11 += carry10;
// s10 -= carry10 * ((uint64_t) 1L << 21);
$carry10 = ($s10 + (1 << 20)) >> 21;
$s11 += $carry10;
$s10 -= $carry10 << 21;
// carry1 = (s1 + (int64_t) (1L << 20)) >> 21;
// s2 += carry1;
// s1 -= carry1 * ((uint64_t) 1L << 21);
$carry1 = ($s1 + (1 << 20)) >> 21;
$s2 += $carry1;
$s1 -= $carry1 << 21;
// carry3 = (s3 + (int64_t) (1L << 20)) >> 21;
// s4 += carry3;
// s3 -= carry3 * ((uint64_t) 1L << 21);
$carry3 = ($s3 + (1 << 20)) >> 21;
$s4 += $carry3;
$s3 -= $carry3 << 21;
// carry5 = (s5 + (int64_t) (1L << 20)) >> 21;
// s6 += carry5;
// s5 -= carry5 * ((uint64_t) 1L << 21);
$carry5 = ($s5 + (1 << 20)) >> 21;
$s6 += $carry5;
$s5 -= $carry5 << 21;
// carry7 = (s7 + (int64_t) (1L << 20)) >> 21;
// s8 += carry7;
// s7 -= carry7 * ((uint64_t) 1L << 21);
$carry7 = ($s7 + (1 << 20)) >> 21;
$s8 += $carry7;
$s7 -= $carry7 << 21;
// carry9 = (s9 + (int64_t) (1L << 20)) >> 21;
// s10 += carry9;
// s9 -= carry9 * ((uint64_t) 1L << 21);
$carry9 = ($s9 + (1 << 20)) >> 21;
$s10 += $carry9;
$s9 -= $carry9 << 21;
// carry11 = (s11 + (int64_t) (1L << 20)) >> 21;
// s12 += carry11;
// s11 -= carry11 * ((uint64_t) 1L << 21);
$carry11 = ($s11 + (1 << 20)) >> 21;
$s12 += $carry11;
$s11 -= $carry11 << 21;
// s0 += s12 * 666643;
// s1 += s12 * 470296;
// s2 += s12 * 654183;
// s3 -= s12 * 997805;
// s4 += s12 * 136657;
// s5 -= s12 * 683901;
// s12 = 0;
$s0 += self::mul($s12, 666643, 20);
$s1 += self::mul($s12, 470296, 19);
$s2 += self::mul($s12, 654183, 20);
$s3 -= self::mul($s12, 997805, 20);
$s4 += self::mul($s12, 136657, 18);
$s5 -= self::mul($s12, 683901, 20);
$s12 = 0;
// carry0 = s0 >> 21;
// s1 += carry0;
// s0 -= carry0 * ((uint64_t) 1L << 21);
$carry0 = $s0 >> 21;
$s1 += $carry0;
$s0 -= $carry0 << 21;
// carry1 = s1 >> 21;
// s2 += carry1;
// s1 -= carry1 * ((uint64_t) 1L << 21);
$carry1 = $s1 >> 21;
$s2 += $carry1;
$s1 -= $carry1 << 21;
// carry2 = s2 >> 21;
// s3 += carry2;
// s2 -= carry2 * ((uint64_t) 1L << 21);
$carry2 = $s2 >> 21;
$s3 += $carry2;
$s2 -= $carry2 << 21;
// carry3 = s3 >> 21;
// s4 += carry3;
// s3 -= carry3 * ((uint64_t) 1L << 21);
$carry3 = $s3 >> 21;
$s4 += $carry3;
$s3 -= $carry3 << 21;
// carry4 = s4 >> 21;
// s5 += carry4;
// s4 -= carry4 * ((uint64_t) 1L << 21);
$carry4 = $s4 >> 21;
$s5 += $carry4;
$s4 -= $carry4 << 21;
// carry5 = s5 >> 21;
// s6 += carry5;
// s5 -= carry5 * ((uint64_t) 1L << 21);
$carry5 = $s5 >> 21;
$s6 += $carry5;
$s5 -= $carry5 << 21;
// carry6 = s6 >> 21;
// s7 += carry6;
// s6 -= carry6 * ((uint64_t) 1L << 21);
$carry6 = $s6 >> 21;
$s7 += $carry6;
$s6 -= $carry6 << 21;
// carry7 = s7 >> 21;
// s8 += carry7;
// s7 -= carry7 * ((uint64_t) 1L << 21);
$carry7 = $s7 >> 21;
$s8 += $carry7;
$s7 -= $carry7 << 21;
// carry8 = s8 >> 21;
// s9 += carry8;
// s8 -= carry8 * ((uint64_t) 1L << 21);
$carry8 = $s8 >> 21;
$s9 += $carry8;
$s8 -= $carry8 << 21;
// carry9 = s9 >> 21;
// s10 += carry9;
// s9 -= carry9 * ((uint64_t) 1L << 21);
$carry9 = $s9 >> 21;
$s10 += $carry9;
$s9 -= $carry9 << 21;
// carry10 = s10 >> 21;
// s11 += carry10;
// s10 -= carry10 * ((uint64_t) 1L << 21);
$carry10 = $s10 >> 21;
$s11 += $carry10;
$s10 -= $carry10 << 21;
// carry11 = s11 >> 21;
// s12 += carry11;
// s11 -= carry11 * ((uint64_t) 1L << 21);
$carry11 = $s11 >> 21;
$s12 += $carry11;
$s11 -= $carry11 << 21;
// s0 += s12 * 666643;
// s1 += s12 * 470296;
// s2 += s12 * 654183;
// s3 -= s12 * 997805;
// s4 += s12 * 136657;
// s5 -= s12 * 683901;
$s0 += self::mul($s12, 666643, 20);
$s1 += self::mul($s12, 470296, 19);
$s2 += self::mul($s12, 654183, 20);
$s3 -= self::mul($s12, 997805, 20);
$s4 += self::mul($s12, 136657, 18);
$s5 -= self::mul($s12, 683901, 20);
// carry0 = s0 >> 21;
// s1 += carry0;
// s0 -= carry0 * ((uint64_t) 1L << 21);
$carry0 = $s0 >> 21;
$s1 += $carry0;
$s0 -= $carry0 << 21;
// carry1 = s1 >> 21;
// s2 += carry1;
// s1 -= carry1 * ((uint64_t) 1L << 21);
$carry1 = $s1 >> 21;
$s2 += $carry1;
$s1 -= $carry1 << 21;
// carry2 = s2 >> 21;
// s3 += carry2;
// s2 -= carry2 * ((uint64_t) 1L << 21);
$carry2 = $s2 >> 21;
$s3 += $carry2;
$s2 -= $carry2 << 21;
// carry3 = s3 >> 21;
// s4 += carry3;
// s3 -= carry3 * ((uint64_t) 1L << 21);
$carry3 = $s3 >> 21;
$s4 += $carry3;
$s3 -= $carry3 << 21;
// carry4 = s4 >> 21;
// s5 += carry4;
// s4 -= carry4 * ((uint64_t) 1L << 21);
$carry4 = $s4 >> 21;
$s5 += $carry4;
$s4 -= $carry4 << 21;
// carry5 = s5 >> 21;
// s6 += carry5;
// s5 -= carry5 * ((uint64_t) 1L << 21);
$carry5 = $s5 >> 21;
$s6 += $carry5;
$s5 -= $carry5 << 21;
// carry6 = s6 >> 21;
// s7 += carry6;
// s6 -= carry6 * ((uint64_t) 1L << 21);
$carry6 = $s6 >> 21;
$s7 += $carry6;
$s6 -= $carry6 << 21;
// carry7 = s7 >> 21;
// s8 += carry7;
// s7 -= carry7 * ((uint64_t) 1L << 21);
$carry7 = $s7 >> 21;
$s8 += $carry7;
$s7 -= $carry7 << 21;
// carry8 = s8 >> 21;
// s9 += carry8;
// s8 -= carry8 * ((uint64_t) 1L << 21);
$carry8 = $s8 >> 21;
$s9 += $carry8;
$s8 -= $carry8 << 21;
// carry9 = s9 >> 21;
// s10 += carry9;
// s9 -= carry9 * ((uint64_t) 1L << 21);
$carry9 = $s9 >> 21;
$s10 += $carry9;
$s9 -= $carry9 << 21;
// carry10 = s10 >> 21;
// s11 += carry10;
// s10 -= carry10 * ((uint64_t) 1L << 21);
$carry10 = $s10 >> 21;
$s11 += $carry10;
$s10 -= $carry10 << 21;
$s = array_fill(0, 32, 0);
// s[0] = s0 >> 0;
$s[0] = $s0 >> 0;
// s[1] = s0 >> 8;
$s[1] = $s0 >> 8;
// s[2] = (s0 >> 16) | (s1 * ((uint64_t) 1 << 5));
$s[2] = ($s0 >> 16) | ($s1 << 5);
// s[3] = s1 >> 3;
$s[3] = $s1 >> 3;
// s[4] = s1 >> 11;
$s[4] = $s1 >> 11;
// s[5] = (s1 >> 19) | (s2 * ((uint64_t) 1 << 2));
$s[5] = ($s1 >> 19) | ($s2 << 2);
// s[6] = s2 >> 6;
$s[6] = $s2 >> 6;
// s[7] = (s2 >> 14) | (s3 * ((uint64_t) 1 << 7));
$s[7] = ($s2 >> 14) | ($s3 << 7);
// s[8] = s3 >> 1;
$s[8] = $s3 >> 1;
// s[9] = s3 >> 9;
$s[9] = $s3 >> 9;
// s[10] = (s3 >> 17) | (s4 * ((uint64_t) 1 << 4));
$s[10] = ($s3 >> 17) | ($s4 << 4);
// s[11] = s4 >> 4;
$s[11] = $s4 >> 4;
// s[12] = s4 >> 12;
$s[12] = $s4 >> 12;
// s[13] = (s4 >> 20) | (s5 * ((uint64_t) 1 << 1));
$s[13] = ($s4 >> 20) | ($s5 << 1);
// s[14] = s5 >> 7;
$s[14] = $s5 >> 7;
// s[15] = (s5 >> 15) | (s6 * ((uint64_t) 1 << 6));
$s[15] = ($s5 >> 15) | ($s6 << 6);
// s[16] = s6 >> 2;
$s[16] = $s6 >> 2;
// s[17] = s6 >> 10;
$s[17] = $s6 >> 10;
// s[18] = (s6 >> 18) | (s7 * ((uint64_t) 1 << 3));
$s[18] = ($s6 >> 18) | ($s7 << 3);
// s[19] = s7 >> 5;
$s[19] = $s7 >> 5;
// s[20] = s7 >> 13;
$s[20] = $s7 >> 13;
// s[21] = s8 >> 0;
$s[21] = $s8 >> 0;
// s[22] = s8 >> 8;
$s[22] = $s8 >> 8;
// s[23] = (s8 >> 16) | (s9 * ((uint64_t) 1 << 5));
$s[23] = ($s8 >> 16) | ($s9 << 5);
// s[24] = s9 >> 3;
$s[24] = $s9 >> 3;
// s[25] = s9 >> 11;
$s[25] = $s9 >> 11;
// s[26] = (s9 >> 19) | (s10 * ((uint64_t) 1 << 2));
$s[26] = ($s9 >> 19) | ($s10 << 2);
// s[27] = s10 >> 6;
$s[27] = $s10 >> 6;
// s[28] = (s10 >> 14) | (s11 * ((uint64_t) 1 << 7));
$s[28] = ($s10 >> 14) | ($s11 << 7);
// s[29] = s11 >> 1;
$s[29] = $s11 >> 1;
// s[30] = s11 >> 9;
$s[30] = $s11 >> 9;
// s[31] = s11 >> 17;
$s[31] = $s11 >> 17;
return self::intArrayToString($s);
}
/**
* @param string $s
* @return string
*/
public static function sc25519_sq($s)
{
return self::sc25519_mul($s, $s);
}
/**
* @param string $s
* @param int $n
* @param string $a
* @return string
*/
public static function sc25519_sqmul($s, $n, $a)
{
for ($i = 0; $i < $n; ++$i) {
$s = self::sc25519_sq($s);
}
return self::sc25519_mul($s, $a);
}
/**
* @param string $s
* @return string
*/
public static function sc25519_invert($s)
{
$_10 = self::sc25519_sq($s);
$_11 = self::sc25519_mul($s, $_10);
$_100 = self::sc25519_mul($s, $_11);
$_1000 = self::sc25519_sq($_100);
$_1010 = self::sc25519_mul($_10, $_1000);
$_1011 = self::sc25519_mul($s, $_1010);
$_10000 = self::sc25519_sq($_1000);
$_10110 = self::sc25519_sq($_1011);
$_100000 = self::sc25519_mul($_1010, $_10110);
$_100110 = self::sc25519_mul($_10000, $_10110);
$_1000000 = self::sc25519_sq($_100000);
$_1010000 = self::sc25519_mul($_10000, $_1000000);
$_1010011 = self::sc25519_mul($_11, $_1010000);
$_1100011 = self::sc25519_mul($_10000, $_1010011);
$_1100111 = self::sc25519_mul($_100, $_1100011);
$_1101011 = self::sc25519_mul($_100, $_1100111);
$_10010011 = self::sc25519_mul($_1000000, $_1010011);
$_10010111 = self::sc25519_mul($_100, $_10010011);
$_10111101 = self::sc25519_mul($_100110, $_10010111);
$_11010011 = self::sc25519_mul($_10110, $_10111101);
$_11100111 = self::sc25519_mul($_1010000, $_10010111);
$_11101011 = self::sc25519_mul($_100, $_11100111);
$_11110101 = self::sc25519_mul($_1010, $_11101011);
$recip = self::sc25519_mul($_1011, $_11110101);
$recip = self::sc25519_sqmul($recip, 126, $_1010011);
$recip = self::sc25519_sqmul($recip, 9, $_10);
$recip = self::sc25519_mul($recip, $_11110101);
$recip = self::sc25519_sqmul($recip, 7, $_1100111);
$recip = self::sc25519_sqmul($recip, 9, $_11110101);
$recip = self::sc25519_sqmul($recip, 11, $_10111101);
$recip = self::sc25519_sqmul($recip, 8, $_11100111);
$recip = self::sc25519_sqmul($recip, 9, $_1101011);
$recip = self::sc25519_sqmul($recip, 6, $_1011);
$recip = self::sc25519_sqmul($recip, 14, $_10010011);
$recip = self::sc25519_sqmul($recip, 10, $_1100011);
$recip = self::sc25519_sqmul($recip, 9, $_10010111);
$recip = self::sc25519_sqmul($recip, 10, $_11110101);
$recip = self::sc25519_sqmul($recip, 8, $_11010011);
return self::sc25519_sqmul($recip, 8, $_11101011);
}
/**
* @param string $s
* @return string
*/
public static function clamp($s)
{
$s_ = self::stringToIntArray($s);
$s_[0] &= 248;
$s_[31] |= 64;
$s_[31] &= 128;
return self::intArrayToString($s_);
}
/**
* Ensure limbs are less than 28 bits long to prevent float promotion.
*
* This uses a constant-time conditional swap under the hood.
*
* @param ParagonIE_Sodium_Core_Curve25519_Fe $f
* @return ParagonIE_Sodium_Core_Curve25519_Fe
*/
public static function fe_normalize(ParagonIE_Sodium_Core_Curve25519_Fe $f)
{
$x = (PHP_INT_SIZE << 3) - 1; // 31 or 63
$g = self::fe_copy($f);
for ($i = 0; $i < 10; ++$i) {
$mask = -(($g[$i] >> $x) & 1);
/*
* Get two candidate normalized values for $g[$i], depending on the sign of $g[$i]:
*/
$a = $g[$i] & 0x7ffffff;
$b = -((-$g[$i]) & 0x7ffffff);
/*
* Return the appropriate candidate value, based on the sign of the original input:
*
* The following is equivalent to this ternary:
*
* $g[$i] = (($g[$i] >> $x) & 1) ? $a : $b;
*
* Except what's written doesn't contain timing leaks.
*/
$g[$i] = ($a ^ (($a ^ $b) & $mask));
}
return $g;
}
}
Core/AES.php 0000644 00000037015 15133021213 0006552 0 ustar 00 <?php
if (class_exists('ParagonIE_Sodium_Core_AES', false)) {
return;
}
/**
* Bitsliced implementation of the AES block cipher.
*
* Based on the implementation provided by BearSSL.
*
* @internal This should only be used by sodium_compat
*/
class ParagonIE_Sodium_Core_AES extends ParagonIE_Sodium_Core_Util
{
/**
* @var int[] AES round constants
*/
private static $Rcon = array(
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36
);
/**
* Mutates the values of $q!
*
* @param ParagonIE_Sodium_Core_AES_Block $q
* @return void
*/
public static function sbox(ParagonIE_Sodium_Core_AES_Block $q)
{
/**
* @var int $x0
* @var int $x1
* @var int $x2
* @var int $x3
* @var int $x4
* @var int $x5
* @var int $x6
* @var int $x7
*/
$x0 = $q[7] & self::U32_MAX;
$x1 = $q[6] & self::U32_MAX;
$x2 = $q[5] & self::U32_MAX;
$x3 = $q[4] & self::U32_MAX;
$x4 = $q[3] & self::U32_MAX;
$x5 = $q[2] & self::U32_MAX;
$x6 = $q[1] & self::U32_MAX;
$x7 = $q[0] & self::U32_MAX;
$y14 = $x3 ^ $x5;
$y13 = $x0 ^ $x6;
$y9 = $x0 ^ $x3;
$y8 = $x0 ^ $x5;
$t0 = $x1 ^ $x2;
$y1 = $t0 ^ $x7;
$y4 = $y1 ^ $x3;
$y12 = $y13 ^ $y14;
$y2 = $y1 ^ $x0;
$y5 = $y1 ^ $x6;
$y3 = $y5 ^ $y8;
$t1 = $x4 ^ $y12;
$y15 = $t1 ^ $x5;
$y20 = $t1 ^ $x1;
$y6 = $y15 ^ $x7;
$y10 = $y15 ^ $t0;
$y11 = $y20 ^ $y9;
$y7 = $x7 ^ $y11;
$y17 = $y10 ^ $y11;
$y19 = $y10 ^ $y8;
$y16 = $t0 ^ $y11;
$y21 = $y13 ^ $y16;
$y18 = $x0 ^ $y16;
/*
* Non-linear section.
*/
$t2 = $y12 & $y15;
$t3 = $y3 & $y6;
$t4 = $t3 ^ $t2;
$t5 = $y4 & $x7;
$t6 = $t5 ^ $t2;
$t7 = $y13 & $y16;
$t8 = $y5 & $y1;
$t9 = $t8 ^ $t7;
$t10 = $y2 & $y7;
$t11 = $t10 ^ $t7;
$t12 = $y9 & $y11;
$t13 = $y14 & $y17;
$t14 = $t13 ^ $t12;
$t15 = $y8 & $y10;
$t16 = $t15 ^ $t12;
$t17 = $t4 ^ $t14;
$t18 = $t6 ^ $t16;
$t19 = $t9 ^ $t14;
$t20 = $t11 ^ $t16;
$t21 = $t17 ^ $y20;
$t22 = $t18 ^ $y19;
$t23 = $t19 ^ $y21;
$t24 = $t20 ^ $y18;
$t25 = $t21 ^ $t22;
$t26 = $t21 & $t23;
$t27 = $t24 ^ $t26;
$t28 = $t25 & $t27;
$t29 = $t28 ^ $t22;
$t30 = $t23 ^ $t24;
$t31 = $t22 ^ $t26;
$t32 = $t31 & $t30;
$t33 = $t32 ^ $t24;
$t34 = $t23 ^ $t33;
$t35 = $t27 ^ $t33;
$t36 = $t24 & $t35;
$t37 = $t36 ^ $t34;
$t38 = $t27 ^ $t36;
$t39 = $t29 & $t38;
$t40 = $t25 ^ $t39;
$t41 = $t40 ^ $t37;
$t42 = $t29 ^ $t33;
$t43 = $t29 ^ $t40;
$t44 = $t33 ^ $t37;
$t45 = $t42 ^ $t41;
$z0 = $t44 & $y15;
$z1 = $t37 & $y6;
$z2 = $t33 & $x7;
$z3 = $t43 & $y16;
$z4 = $t40 & $y1;
$z5 = $t29 & $y7;
$z6 = $t42 & $y11;
$z7 = $t45 & $y17;
$z8 = $t41 & $y10;
$z9 = $t44 & $y12;
$z10 = $t37 & $y3;
$z11 = $t33 & $y4;
$z12 = $t43 & $y13;
$z13 = $t40 & $y5;
$z14 = $t29 & $y2;
$z15 = $t42 & $y9;
$z16 = $t45 & $y14;
$z17 = $t41 & $y8;
/*
* Bottom linear transformation.
*/
$t46 = $z15 ^ $z16;
$t47 = $z10 ^ $z11;
$t48 = $z5 ^ $z13;
$t49 = $z9 ^ $z10;
$t50 = $z2 ^ $z12;
$t51 = $z2 ^ $z5;
$t52 = $z7 ^ $z8;
$t53 = $z0 ^ $z3;
$t54 = $z6 ^ $z7;
$t55 = $z16 ^ $z17;
$t56 = $z12 ^ $t48;
$t57 = $t50 ^ $t53;
$t58 = $z4 ^ $t46;
$t59 = $z3 ^ $t54;
$t60 = $t46 ^ $t57;
$t61 = $z14 ^ $t57;
$t62 = $t52 ^ $t58;
$t63 = $t49 ^ $t58;
$t64 = $z4 ^ $t59;
$t65 = $t61 ^ $t62;
$t66 = $z1 ^ $t63;
$s0 = $t59 ^ $t63;
$s6 = $t56 ^ ~$t62;
$s7 = $t48 ^ ~$t60;
$t67 = $t64 ^ $t65;
$s3 = $t53 ^ $t66;
$s4 = $t51 ^ $t66;
$s5 = $t47 ^ $t65;
$s1 = $t64 ^ ~$s3;
$s2 = $t55 ^ ~$t67;
$q[7] = $s0 & self::U32_MAX;
$q[6] = $s1 & self::U32_MAX;
$q[5] = $s2 & self::U32_MAX;
$q[4] = $s3 & self::U32_MAX;
$q[3] = $s4 & self::U32_MAX;
$q[2] = $s5 & self::U32_MAX;
$q[1] = $s6 & self::U32_MAX;
$q[0] = $s7 & self::U32_MAX;
}
/**
* Mutates the values of $q!
*
* @param ParagonIE_Sodium_Core_AES_Block $q
* @return void
*/
public static function invSbox(ParagonIE_Sodium_Core_AES_Block $q)
{
self::processInversion($q);
self::sbox($q);
self::processInversion($q);
}
/**
* This is some boilerplate code needed to invert an S-box. Rather than repeat the code
* twice, I moved it to a protected method.
*
* Mutates $q
*
* @param ParagonIE_Sodium_Core_AES_Block $q
* @return void
*/
protected static function processInversion(ParagonIE_Sodium_Core_AES_Block $q)
{
$q0 = (~$q[0]) & self::U32_MAX;
$q1 = (~$q[1]) & self::U32_MAX;
$q2 = $q[2] & self::U32_MAX;
$q3 = $q[3] & self::U32_MAX;
$q4 = $q[4] & self::U32_MAX;
$q5 = (~$q[5]) & self::U32_MAX;
$q6 = (~$q[6]) & self::U32_MAX;
$q7 = $q[7] & self::U32_MAX;
$q[7] = ($q1 ^ $q4 ^ $q6) & self::U32_MAX;
$q[6] = ($q0 ^ $q3 ^ $q5) & self::U32_MAX;
$q[5] = ($q7 ^ $q2 ^ $q4) & self::U32_MAX;
$q[4] = ($q6 ^ $q1 ^ $q3) & self::U32_MAX;
$q[3] = ($q5 ^ $q0 ^ $q2) & self::U32_MAX;
$q[2] = ($q4 ^ $q7 ^ $q1) & self::U32_MAX;
$q[1] = ($q3 ^ $q6 ^ $q0) & self::U32_MAX;
$q[0] = ($q2 ^ $q5 ^ $q7) & self::U32_MAX;
}
/**
* @param int $x
* @return int
*/
public static function subWord($x)
{
$q = ParagonIE_Sodium_Core_AES_Block::fromArray(
array($x, $x, $x, $x, $x, $x, $x, $x)
);
$q->orthogonalize();
self::sbox($q);
$q->orthogonalize();
return $q[0] & self::U32_MAX;
}
/**
* Calculate the key schedule from a given random key
*
* @param string $key
* @return ParagonIE_Sodium_Core_AES_KeySchedule
* @throws SodiumException
*/
public static function keySchedule($key)
{
$key_len = self::strlen($key);
switch ($key_len) {
case 16:
$num_rounds = 10;
break;
case 24:
$num_rounds = 12;
break;
case 32:
$num_rounds = 14;
break;
default:
throw new SodiumException('Invalid key length: ' . $key_len);
}
$skey = array();
$comp_skey = array();
$nk = $key_len >> 2;
$nkf = ($num_rounds + 1) << 2;
$tmp = 0;
for ($i = 0; $i < $nk; ++$i) {
$tmp = self::load_4(self::substr($key, $i << 2, 4));
$skey[($i << 1)] = $tmp;
$skey[($i << 1) + 1] = $tmp;
}
for ($i = $nk, $j = 0, $k = 0; $i < $nkf; ++$i) {
if ($j === 0) {
$tmp = (($tmp & 0xff) << 24) | ($tmp >> 8);
$tmp = (self::subWord($tmp) ^ self::$Rcon[$k]) & self::U32_MAX;
} elseif ($nk > 6 && $j === 4) {
$tmp = self::subWord($tmp);
}
$tmp ^= $skey[($i - $nk) << 1];
$skey[($i << 1)] = $tmp & self::U32_MAX;
$skey[($i << 1) + 1] = $tmp & self::U32_MAX;
if (++$j === $nk) {
/** @psalm-suppress LoopInvalidation */
$j = 0;
++$k;
}
}
for ($i = 0; $i < $nkf; $i += 4) {
$q = ParagonIE_Sodium_Core_AES_Block::fromArray(
array_slice($skey, $i << 1, 8)
);
$q->orthogonalize();
// We have to overwrite $skey since we're not using C pointers like BearSSL did
for ($j = 0; $j < 8; ++$j) {
$skey[($i << 1) + $j] = $q[$j];
}
}
for ($i = 0, $j = 0; $i < $nkf; ++$i, $j += 2) {
$comp_skey[$i] = ($skey[$j] & 0x55555555)
| ($skey[$j + 1] & 0xAAAAAAAA);
}
return new ParagonIE_Sodium_Core_AES_KeySchedule($comp_skey, $num_rounds);
}
/**
* Mutates $q
*
* @param ParagonIE_Sodium_Core_AES_KeySchedule $skey
* @param ParagonIE_Sodium_Core_AES_Block $q
* @param int $offset
* @return void
*/
public static function addRoundKey(
ParagonIE_Sodium_Core_AES_Block $q,
ParagonIE_Sodium_Core_AES_KeySchedule $skey,
$offset = 0
) {
$block = $skey->getRoundKey($offset);
for ($j = 0; $j < 8; ++$j) {
$q[$j] = ($q[$j] ^ $block[$j]) & ParagonIE_Sodium_Core_Util::U32_MAX;
}
}
/**
* This mainly exists for testing, as we need the round key features for AEGIS.
*
* @param string $message
* @param string $key
* @return string
* @throws SodiumException
*/
public static function decryptBlockECB($message, $key)
{
if (self::strlen($message) !== 16) {
throw new SodiumException('decryptBlockECB() expects a 16 byte message');
}
$skey = self::keySchedule($key)->expand();
$q = ParagonIE_Sodium_Core_AES_Block::init();
$q[0] = self::load_4(self::substr($message, 0, 4));
$q[2] = self::load_4(self::substr($message, 4, 4));
$q[4] = self::load_4(self::substr($message, 8, 4));
$q[6] = self::load_4(self::substr($message, 12, 4));
$q->orthogonalize();
self::bitsliceDecryptBlock($skey, $q);
$q->orthogonalize();
return self::store32_le($q[0]) .
self::store32_le($q[2]) .
self::store32_le($q[4]) .
self::store32_le($q[6]);
}
/**
* This mainly exists for testing, as we need the round key features for AEGIS.
*
* @param string $message
* @param string $key
* @return string
* @throws SodiumException
*/
public static function encryptBlockECB($message, $key)
{
if (self::strlen($message) !== 16) {
throw new SodiumException('encryptBlockECB() expects a 16 byte message');
}
$comp_skey = self::keySchedule($key);
$skey = $comp_skey->expand();
$q = ParagonIE_Sodium_Core_AES_Block::init();
$q[0] = self::load_4(self::substr($message, 0, 4));
$q[2] = self::load_4(self::substr($message, 4, 4));
$q[4] = self::load_4(self::substr($message, 8, 4));
$q[6] = self::load_4(self::substr($message, 12, 4));
$q->orthogonalize();
self::bitsliceEncryptBlock($skey, $q);
$q->orthogonalize();
return self::store32_le($q[0]) .
self::store32_le($q[2]) .
self::store32_le($q[4]) .
self::store32_le($q[6]);
}
/**
* Mutates $q
*
* @param ParagonIE_Sodium_Core_AES_Expanded $skey
* @param ParagonIE_Sodium_Core_AES_Block $q
* @return void
*/
public static function bitsliceEncryptBlock(
ParagonIE_Sodium_Core_AES_Expanded $skey,
ParagonIE_Sodium_Core_AES_Block $q
) {
self::addRoundKey($q, $skey);
for ($u = 1; $u < $skey->getNumRounds(); ++$u) {
self::sbox($q);
$q->shiftRows();
$q->mixColumns();
self::addRoundKey($q, $skey, ($u << 3));
}
self::sbox($q);
$q->shiftRows();
self::addRoundKey($q, $skey, ($skey->getNumRounds() << 3));
}
/**
* @param string $x
* @param string $y
* @return string
*/
public static function aesRound($x, $y)
{
$q = ParagonIE_Sodium_Core_AES_Block::init();
$q[0] = self::load_4(self::substr($x, 0, 4));
$q[2] = self::load_4(self::substr($x, 4, 4));
$q[4] = self::load_4(self::substr($x, 8, 4));
$q[6] = self::load_4(self::substr($x, 12, 4));
$rk = ParagonIE_Sodium_Core_AES_Block::init();
$rk[0] = $rk[1] = self::load_4(self::substr($y, 0, 4));
$rk[2] = $rk[3] = self::load_4(self::substr($y, 4, 4));
$rk[4] = $rk[5] = self::load_4(self::substr($y, 8, 4));
$rk[6] = $rk[7] = self::load_4(self::substr($y, 12, 4));
$q->orthogonalize();
self::sbox($q);
$q->shiftRows();
$q->mixColumns();
$q->orthogonalize();
// add round key without key schedule:
for ($i = 0; $i < 8; ++$i) {
$q[$i] ^= $rk[$i];
}
return self::store32_le($q[0]) .
self::store32_le($q[2]) .
self::store32_le($q[4]) .
self::store32_le($q[6]);
}
/**
* Process two AES blocks in one shot.
*
* @param string $b0 First AES block
* @param string $rk0 First round key
* @param string $b1 Second AES block
* @param string $rk1 Second round key
* @return string[]
*/
public static function doubleRound($b0, $rk0, $b1, $rk1)
{
$q = ParagonIE_Sodium_Core_AES_Block::init();
// First block
$q[0] = self::load_4(self::substr($b0, 0, 4));
$q[2] = self::load_4(self::substr($b0, 4, 4));
$q[4] = self::load_4(self::substr($b0, 8, 4));
$q[6] = self::load_4(self::substr($b0, 12, 4));
// Second block
$q[1] = self::load_4(self::substr($b1, 0, 4));
$q[3] = self::load_4(self::substr($b1, 4, 4));
$q[5] = self::load_4(self::substr($b1, 8, 4));
$q[7] = self::load_4(self::substr($b1, 12, 4));;
$rk = ParagonIE_Sodium_Core_AES_Block::init();
// First round key
$rk[0] = self::load_4(self::substr($rk0, 0, 4));
$rk[2] = self::load_4(self::substr($rk0, 4, 4));
$rk[4] = self::load_4(self::substr($rk0, 8, 4));
$rk[6] = self::load_4(self::substr($rk0, 12, 4));
// Second round key
$rk[1] = self::load_4(self::substr($rk1, 0, 4));
$rk[3] = self::load_4(self::substr($rk1, 4, 4));
$rk[5] = self::load_4(self::substr($rk1, 8, 4));
$rk[7] = self::load_4(self::substr($rk1, 12, 4));
$q->orthogonalize();
self::sbox($q);
$q->shiftRows();
$q->mixColumns();
$q->orthogonalize();
// add round key without key schedule:
for ($i = 0; $i < 8; ++$i) {
$q[$i] ^= $rk[$i];
}
return array(
self::store32_le($q[0]) . self::store32_le($q[2]) . self::store32_le($q[4]) . self::store32_le($q[6]),
self::store32_le($q[1]) . self::store32_le($q[3]) . self::store32_le($q[5]) . self::store32_le($q[7]),
);
}
/**
* @param ParagonIE_Sodium_Core_AES_Expanded $skey
* @param ParagonIE_Sodium_Core_AES_Block $q
* @return void
*/
public static function bitsliceDecryptBlock(
ParagonIE_Sodium_Core_AES_Expanded $skey,
ParagonIE_Sodium_Core_AES_Block $q
) {
self::addRoundKey($q, $skey, ($skey->getNumRounds() << 3));
for ($u = $skey->getNumRounds() - 1; $u > 0; --$u) {
$q->inverseShiftRows();
self::invSbox($q);
self::addRoundKey($q, $skey, ($u << 3));
$q->inverseMixColumns();
}
$q->inverseShiftRows();
self::invSbox($q);
self::addRoundKey($q, $skey, ($u << 3));
}
}
Core/XChaCha20.php 0000604 00000006452 15133021213 0007540 0 ustar 00 <?php
if (class_exists('ParagonIE_Sodium_Core_XChaCha20', false)) {
return;
}
/**
* Class ParagonIE_Sodium_Core_XChaCha20
*/
class ParagonIE_Sodium_Core_XChaCha20 extends ParagonIE_Sodium_Core_HChaCha20
{
/**
* @internal You should not use this directly from another application
*
* @param int $len
* @param string $nonce
* @param string $key
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function stream($len = 64, $nonce = '', $key = '')
{
if (self::strlen($nonce) !== 24) {
throw new SodiumException('Nonce must be 24 bytes long');
}
return self::encryptBytes(
new ParagonIE_Sodium_Core_ChaCha20_Ctx(
self::hChaCha20(
self::substr($nonce, 0, 16),
$key
),
self::substr($nonce, 16, 8)
),
str_repeat("\x00", $len)
);
}
/**
* @internal You should not use this directly from another application
*
* @param int $len
* @param string $nonce
* @param string $key
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function ietfStream($len = 64, $nonce = '', $key = '')
{
if (self::strlen($nonce) !== 24) {
throw new SodiumException('Nonce must be 24 bytes long');
}
return self::encryptBytes(
new ParagonIE_Sodium_Core_ChaCha20_IetfCtx(
self::hChaCha20(
self::substr($nonce, 0, 16),
$key
),
"\x00\x00\x00\x00" . self::substr($nonce, 16, 8)
),
str_repeat("\x00", $len)
);
}
/**
* @internal You should not use this directly from another application
*
* @param string $message
* @param string $nonce
* @param string $key
* @param string $ic
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function streamXorIc($message, $nonce = '', $key = '', $ic = '')
{
if (self::strlen($nonce) !== 24) {
throw new SodiumException('Nonce must be 24 bytes long');
}
return self::encryptBytes(
new ParagonIE_Sodium_Core_ChaCha20_Ctx(
self::hChaCha20(self::substr($nonce, 0, 16), $key),
self::substr($nonce, 16, 8),
$ic
),
$message
);
}
/**
* @internal You should not use this directly from another application
*
* @param string $message
* @param string $nonce
* @param string $key
* @param string $ic
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function ietfStreamXorIc($message, $nonce = '', $key = '', $ic = '')
{
if (self::strlen($nonce) !== 24) {
throw new SodiumException('Nonce must be 24 bytes long');
}
return self::encryptBytes(
new ParagonIE_Sodium_Core_ChaCha20_IetfCtx(
self::hChaCha20(self::substr($nonce, 0, 16), $key),
"\x00\x00\x00\x00" . self::substr($nonce, 16, 8),
$ic
),
$message
);
}
}
Core/BLAKE2b.php 0000644 00000057200 15133021213 0007202 0 ustar 00 <?php
if (class_exists('ParagonIE_Sodium_Core_BLAKE2b', false)) {
return;
}
/**
* Class ParagonIE_Sodium_Core_BLAKE2b
*
* Based on the work of Devi Mandiri in devi/salt.
*/
abstract class ParagonIE_Sodium_Core_BLAKE2b extends ParagonIE_Sodium_Core_Util
{
/**
* @var SplFixedArray
*/
protected static $iv;
/**
* @var array<int, array<int, int>>
*/
protected static $sigma = array(
array( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15),
array( 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3),
array( 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4),
array( 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8),
array( 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13),
array( 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9),
array( 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11),
array( 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10),
array( 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5),
array( 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0),
array( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15),
array( 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3)
);
const BLOCKBYTES = 128;
const OUTBYTES = 64;
const KEYBYTES = 64;
/**
* Turn two 32-bit integers into a fixed array representing a 64-bit integer.
*
* @internal You should not use this directly from another application
*
* @param int $high
* @param int $low
* @return SplFixedArray
* @psalm-suppress MixedAssignment
*/
public static function new64($high, $low)
{
if (PHP_INT_SIZE === 4) {
throw new SodiumException("Error, use 32-bit");
}
$i64 = new SplFixedArray(2);
$i64[0] = $high & 0xffffffff;
$i64[1] = $low & 0xffffffff;
return $i64;
}
/**
* Convert an arbitrary number into an SplFixedArray of two 32-bit integers
* that represents a 64-bit integer.
*
* @internal You should not use this directly from another application
*
* @param int $num
* @return SplFixedArray
*/
protected static function to64($num)
{
list($hi, $lo) = self::numericTo64BitInteger($num);
return self::new64($hi, $lo);
}
/**
* Adds two 64-bit integers together, returning their sum as a SplFixedArray
* containing two 32-bit integers (representing a 64-bit integer).
*
* @internal You should not use this directly from another application
*
* @param SplFixedArray $x
* @param SplFixedArray $y
* @return SplFixedArray
* @psalm-suppress MixedArgument
* @psalm-suppress MixedAssignment
* @psalm-suppress MixedOperand
*/
protected static function add64($x, $y)
{
if (PHP_INT_SIZE === 4) {
throw new SodiumException("Error, use 32-bit");
}
$l = ($x[1] + $y[1]) & 0xffffffff;
return self::new64(
(int) ($x[0] + $y[0] + (
($l < $x[1]) ? 1 : 0
)),
(int) $l
);
}
/**
* @internal You should not use this directly from another application
*
* @param SplFixedArray $x
* @param SplFixedArray $y
* @param SplFixedArray $z
* @return SplFixedArray
*/
protected static function add364($x, $y, $z)
{
return self::add64($x, self::add64($y, $z));
}
/**
* @internal You should not use this directly from another application
*
* @param SplFixedArray $x
* @param SplFixedArray $y
* @return SplFixedArray
* @throws SodiumException
* @throws TypeError
*/
protected static function xor64(SplFixedArray $x, SplFixedArray $y)
{
if (PHP_INT_SIZE === 4) {
throw new SodiumException("Error, use 32-bit");
}
if (!is_numeric($x[0])) {
throw new SodiumException('x[0] is not an integer');
}
if (!is_numeric($x[1])) {
throw new SodiumException('x[1] is not an integer');
}
if (!is_numeric($y[0])) {
throw new SodiumException('y[0] is not an integer');
}
if (!is_numeric($y[1])) {
throw new SodiumException('y[1] is not an integer');
}
return self::new64(
(int) (($x[0] ^ $y[0]) & 0xffffffff),
(int) (($x[1] ^ $y[1]) & 0xffffffff)
);
}
/**
* @internal You should not use this directly from another application
*
* @param SplFixedArray $x
* @param int $c
* @return SplFixedArray
* @psalm-suppress MixedAssignment
*/
public static function rotr64($x, $c)
{
if (PHP_INT_SIZE === 4) {
throw new SodiumException("Error, use 32-bit");
}
if ($c >= 64) {
$c %= 64;
}
if ($c >= 32) {
/** @var int $tmp */
$tmp = $x[0];
$x[0] = $x[1];
$x[1] = $tmp;
$c -= 32;
}
if ($c === 0) {
return $x;
}
$l0 = 0;
$c = 64 - $c;
/** @var int $c */
if ($c < 32) {
$h0 = ((int) ($x[0]) << $c) | (
(
(int) ($x[1]) & ((1 << $c) - 1)
<<
(32 - $c)
) >> (32 - $c)
);
$l0 = (int) ($x[1]) << $c;
} else {
$h0 = (int) ($x[1]) << ($c - 32);
}
$h1 = 0;
$c1 = 64 - $c;
if ($c1 < 32) {
$h1 = (int) ($x[0]) >> $c1;
$l1 = ((int) ($x[1]) >> $c1) | ((int) ($x[0]) & ((1 << $c1) - 1)) << (32 - $c1);
} else {
$l1 = (int) ($x[0]) >> ($c1 - 32);
}
return self::new64($h0 | $h1, $l0 | $l1);
}
/**
* @internal You should not use this directly from another application
*
* @param SplFixedArray $x
* @return int
* @psalm-suppress MixedOperand
*/
protected static function flatten64($x)
{
return (int) ($x[0] * 4294967296 + $x[1]);
}
/**
* @internal You should not use this directly from another application
*
* @param SplFixedArray $x
* @param int $i
* @return SplFixedArray
* @psalm-suppress MixedArgument
* @psalm-suppress MixedArrayOffset
*/
protected static function load64(SplFixedArray $x, $i)
{
/** @var int $l */
$l = (int) ($x[$i])
| ((int) ($x[$i+1]) << 8)
| ((int) ($x[$i+2]) << 16)
| ((int) ($x[$i+3]) << 24);
/** @var int $h */
$h = (int) ($x[$i+4])
| ((int) ($x[$i+5]) << 8)
| ((int) ($x[$i+6]) << 16)
| ((int) ($x[$i+7]) << 24);
return self::new64($h, $l);
}
/**
* @internal You should not use this directly from another application
*
* @param SplFixedArray $x
* @param int $i
* @param SplFixedArray $u
* @return void
* @psalm-suppress MixedAssignment
*/
protected static function store64(SplFixedArray $x, $i, SplFixedArray $u)
{
$maxLength = $x->getSize() - 1;
for ($j = 0; $j < 8; ++$j) {
/*
[0, 1, 2, 3, 4, 5, 6, 7]
... becomes ...
[0, 0, 0, 0, 1, 1, 1, 1]
*/
/** @var int $uIdx */
$uIdx = ((7 - $j) & 4) >> 2;
$x[$i] = ((int) ($u[$uIdx]) & 0xff);
if (++$i > $maxLength) {
return;
}
/** @psalm-suppress MixedOperand */
$u[$uIdx] >>= 8;
}
}
/**
* This just sets the $iv static variable.
*
* @internal You should not use this directly from another application
*
* @return void
*/
public static function pseudoConstructor()
{
static $called = false;
if ($called) {
return;
}
self::$iv = new SplFixedArray(8);
self::$iv[0] = self::new64(0x6a09e667, 0xf3bcc908);
self::$iv[1] = self::new64(0xbb67ae85, 0x84caa73b);
self::$iv[2] = self::new64(0x3c6ef372, 0xfe94f82b);
self::$iv[3] = self::new64(0xa54ff53a, 0x5f1d36f1);
self::$iv[4] = self::new64(0x510e527f, 0xade682d1);
self::$iv[5] = self::new64(0x9b05688c, 0x2b3e6c1f);
self::$iv[6] = self::new64(0x1f83d9ab, 0xfb41bd6b);
self::$iv[7] = self::new64(0x5be0cd19, 0x137e2179);
$called = true;
}
/**
* Returns a fresh BLAKE2 context.
*
* @internal You should not use this directly from another application
*
* @return SplFixedArray
* @psalm-suppress MixedAssignment
* @psalm-suppress MixedArrayAccess
* @psalm-suppress MixedArrayAssignment
*/
protected static function context()
{
$ctx = new SplFixedArray(6);
$ctx[0] = new SplFixedArray(8); // h
$ctx[1] = new SplFixedArray(2); // t
$ctx[2] = new SplFixedArray(2); // f
$ctx[3] = new SplFixedArray(256); // buf
$ctx[4] = 0; // buflen
$ctx[5] = 0; // last_node (uint8_t)
for ($i = 8; $i--;) {
$ctx[0][$i] = self::$iv[$i];
}
for ($i = 256; $i--;) {
$ctx[3][$i] = 0;
}
$zero = self::new64(0, 0);
$ctx[1][0] = $zero;
$ctx[1][1] = $zero;
$ctx[2][0] = $zero;
$ctx[2][1] = $zero;
return $ctx;
}
/**
* @internal You should not use this directly from another application
*
* @param SplFixedArray $ctx
* @param SplFixedArray $buf
* @return void
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArgument
* @psalm-suppress MixedAssignment
* @psalm-suppress MixedArrayAccess
* @psalm-suppress MixedArrayAssignment
* @psalm-suppress MixedArrayOffset
*/
protected static function compress(SplFixedArray $ctx, SplFixedArray $buf)
{
$m = new SplFixedArray(16);
$v = new SplFixedArray(16);
for ($i = 16; $i--;) {
$m[$i] = self::load64($buf, $i << 3);
}
for ($i = 8; $i--;) {
$v[$i] = $ctx[0][$i];
}
$v[ 8] = self::$iv[0];
$v[ 9] = self::$iv[1];
$v[10] = self::$iv[2];
$v[11] = self::$iv[3];
$v[12] = self::xor64($ctx[1][0], self::$iv[4]);
$v[13] = self::xor64($ctx[1][1], self::$iv[5]);
$v[14] = self::xor64($ctx[2][0], self::$iv[6]);
$v[15] = self::xor64($ctx[2][1], self::$iv[7]);
for ($r = 0; $r < 12; ++$r) {
$v = self::G($r, 0, 0, 4, 8, 12, $v, $m);
$v = self::G($r, 1, 1, 5, 9, 13, $v, $m);
$v = self::G($r, 2, 2, 6, 10, 14, $v, $m);
$v = self::G($r, 3, 3, 7, 11, 15, $v, $m);
$v = self::G($r, 4, 0, 5, 10, 15, $v, $m);
$v = self::G($r, 5, 1, 6, 11, 12, $v, $m);
$v = self::G($r, 6, 2, 7, 8, 13, $v, $m);
$v = self::G($r, 7, 3, 4, 9, 14, $v, $m);
}
for ($i = 8; $i--;) {
$ctx[0][$i] = self::xor64(
$ctx[0][$i], self::xor64($v[$i], $v[$i+8])
);
}
}
/**
* @internal You should not use this directly from another application
*
* @param int $r
* @param int $i
* @param int $a
* @param int $b
* @param int $c
* @param int $d
* @param SplFixedArray $v
* @param SplFixedArray $m
* @return SplFixedArray
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArgument
* @psalm-suppress MixedArrayOffset
*/
public static function G($r, $i, $a, $b, $c, $d, SplFixedArray $v, SplFixedArray $m)
{
$v[$a] = self::add364($v[$a], $v[$b], $m[self::$sigma[$r][$i << 1]]);
$v[$d] = self::rotr64(self::xor64($v[$d], $v[$a]), 32);
$v[$c] = self::add64($v[$c], $v[$d]);
$v[$b] = self::rotr64(self::xor64($v[$b], $v[$c]), 24);
$v[$a] = self::add364($v[$a], $v[$b], $m[self::$sigma[$r][($i << 1) + 1]]);
$v[$d] = self::rotr64(self::xor64($v[$d], $v[$a]), 16);
$v[$c] = self::add64($v[$c], $v[$d]);
$v[$b] = self::rotr64(self::xor64($v[$b], $v[$c]), 63);
return $v;
}
/**
* @internal You should not use this directly from another application
*
* @param SplFixedArray $ctx
* @param int $inc
* @return void
* @throws SodiumException
* @psalm-suppress MixedArgument
* @psalm-suppress MixedArrayAccess
* @psalm-suppress MixedArrayAssignment
*/
public static function increment_counter($ctx, $inc)
{
if ($inc < 0) {
throw new SodiumException('Increasing by a negative number makes no sense.');
}
$t = self::to64($inc);
# S->t is $ctx[1] in our implementation
# S->t[0] = ( uint64_t )( t >> 0 );
$ctx[1][0] = self::add64($ctx[1][0], $t);
# S->t[1] += ( S->t[0] < inc );
if (self::flatten64($ctx[1][0]) < $inc) {
$ctx[1][1] = self::add64($ctx[1][1], self::to64(1));
}
}
/**
* @internal You should not use this directly from another application
*
* @param SplFixedArray $ctx
* @param SplFixedArray $p
* @param int $plen
* @return void
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArgument
* @psalm-suppress MixedAssignment
* @psalm-suppress MixedArrayAccess
* @psalm-suppress MixedArrayAssignment
* @psalm-suppress MixedArrayOffset
* @psalm-suppress MixedOperand
*/
public static function update(SplFixedArray $ctx, SplFixedArray $p, $plen)
{
self::pseudoConstructor();
$offset = 0;
while ($plen > 0) {
$left = $ctx[4];
$fill = 256 - $left;
if ($plen > $fill) {
# memcpy( S->buf + left, in, fill ); /* Fill buffer */
for ($i = $fill; $i--;) {
$ctx[3][$i + $left] = $p[$i + $offset];
}
# S->buflen += fill;
$ctx[4] += $fill;
# blake2b_increment_counter( S, BLAKE2B_BLOCKBYTES );
self::increment_counter($ctx, 128);
# blake2b_compress( S, S->buf ); /* Compress */
self::compress($ctx, $ctx[3]);
# memcpy( S->buf, S->buf + BLAKE2B_BLOCKBYTES, BLAKE2B_BLOCKBYTES ); /* Shift buffer left */
for ($i = 128; $i--;) {
$ctx[3][$i] = $ctx[3][$i + 128];
}
# S->buflen -= BLAKE2B_BLOCKBYTES;
$ctx[4] -= 128;
# in += fill;
$offset += $fill;
# inlen -= fill;
$plen -= $fill;
} else {
for ($i = $plen; $i--;) {
$ctx[3][$i + $left] = $p[$i + $offset];
}
$ctx[4] += $plen;
$offset += $plen;
$plen -= $plen;
}
}
}
/**
* @internal You should not use this directly from another application
*
* @param SplFixedArray $ctx
* @param SplFixedArray $out
* @return SplFixedArray
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArgument
* @psalm-suppress MixedAssignment
* @psalm-suppress MixedArrayAccess
* @psalm-suppress MixedArrayAssignment
* @psalm-suppress MixedArrayOffset
* @psalm-suppress MixedOperand
*/
public static function finish(SplFixedArray $ctx, SplFixedArray $out)
{
self::pseudoConstructor();
if ($ctx[4] > 128) {
self::increment_counter($ctx, 128);
self::compress($ctx, $ctx[3]);
$ctx[4] -= 128;
if ($ctx[4] > 128) {
throw new SodiumException('Failed to assert that buflen <= 128 bytes');
}
for ($i = $ctx[4]; $i--;) {
$ctx[3][$i] = $ctx[3][$i + 128];
}
}
self::increment_counter($ctx, $ctx[4]);
$ctx[2][0] = self::new64(0xffffffff, 0xffffffff);
for ($i = 256 - $ctx[4]; $i--;) {
$ctx[3][$i+$ctx[4]] = 0;
}
self::compress($ctx, $ctx[3]);
$i = (int) (($out->getSize() - 1) / 8);
for (; $i >= 0; --$i) {
self::store64($out, $i << 3, $ctx[0][$i]);
}
return $out;
}
/**
* @internal You should not use this directly from another application
*
* @param SplFixedArray|null $key
* @param int $outlen
* @param SplFixedArray|null $salt
* @param SplFixedArray|null $personal
* @return SplFixedArray
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArgument
* @psalm-suppress MixedAssignment
* @psalm-suppress MixedArrayAccess
* @psalm-suppress MixedArrayAssignment
* @psalm-suppress MixedArrayOffset
*/
public static function init(
$key = null,
$outlen = 64,
$salt = null,
$personal = null
) {
self::pseudoConstructor();
$klen = 0;
if ($key !== null) {
if (count($key) > 64) {
throw new SodiumException('Invalid key size');
}
$klen = count($key);
}
if ($outlen > 64) {
throw new SodiumException('Invalid output size');
}
$ctx = self::context();
$p = new SplFixedArray(64);
// Zero our param buffer...
for ($i = 64; --$i;) {
$p[$i] = 0;
}
$p[0] = $outlen; // digest_length
$p[1] = $klen; // key_length
$p[2] = 1; // fanout
$p[3] = 1; // depth
if ($salt instanceof SplFixedArray) {
// salt: [32] through [47]
for ($i = 0; $i < 16; ++$i) {
$p[32 + $i] = (int) $salt[$i];
}
}
if ($personal instanceof SplFixedArray) {
// personal: [48] through [63]
for ($i = 0; $i < 16; ++$i) {
$p[48 + $i] = (int) $personal[$i];
}
}
$ctx[0][0] = self::xor64(
$ctx[0][0],
self::load64($p, 0)
);
if ($salt instanceof SplFixedArray || $personal instanceof SplFixedArray) {
// We need to do what blake2b_init_param() does:
for ($i = 1; $i < 8; ++$i) {
$ctx[0][$i] = self::xor64(
$ctx[0][$i],
self::load64($p, $i << 3)
);
}
}
if ($klen > 0 && $key instanceof SplFixedArray) {
$block = new SplFixedArray(128);
for ($i = 128; $i--;) {
$block[$i] = 0;
}
for ($i = $klen; $i--;) {
$block[$i] = $key[$i];
}
self::update($ctx, $block, 128);
$ctx[4] = 128;
}
return $ctx;
}
/**
* Convert a string into an SplFixedArray of integers
*
* @internal You should not use this directly from another application
*
* @param string $str
* @return SplFixedArray
* @psalm-suppress MixedArgumentTypeCoercion
*/
public static function stringToSplFixedArray($str = '')
{
$values = unpack('C*', $str);
return SplFixedArray::fromArray(array_values($values));
}
/**
* Convert an SplFixedArray of integers into a string
*
* @internal You should not use this directly from another application
*
* @param SplFixedArray $a
* @return string
* @throws TypeError
*/
public static function SplFixedArrayToString(SplFixedArray $a)
{
/**
* @var array<int, int|string> $arr
*/
$arr = $a->toArray();
$c = $a->count();
array_unshift($arr, str_repeat('C', $c));
return (string) (call_user_func_array('pack', $arr));
}
/**
* @internal You should not use this directly from another application
*
* @param SplFixedArray $ctx
* @return string
* @throws TypeError
* @psalm-suppress MixedArgument
* @psalm-suppress MixedAssignment
* @psalm-suppress MixedArrayAccess
* @psalm-suppress MixedArrayAssignment
* @psalm-suppress MixedArrayOffset
* @psalm-suppress MixedMethodCall
*/
public static function contextToString(SplFixedArray $ctx)
{
$str = '';
/** @var array<int, array<int, int>> $ctxA */
$ctxA = $ctx[0]->toArray();
# uint64_t h[8];
for ($i = 0; $i < 8; ++$i) {
$str .= self::store32_le($ctxA[$i][1]);
$str .= self::store32_le($ctxA[$i][0]);
}
# uint64_t t[2];
# uint64_t f[2];
for ($i = 1; $i < 3; ++$i) {
$ctxA = $ctx[$i]->toArray();
$str .= self::store32_le($ctxA[0][1]);
$str .= self::store32_le($ctxA[0][0]);
$str .= self::store32_le($ctxA[1][1]);
$str .= self::store32_le($ctxA[1][0]);
}
# uint8_t buf[2 * 128];
$str .= self::SplFixedArrayToString($ctx[3]);
/** @var int $ctx4 */
$ctx4 = (int) $ctx[4];
# size_t buflen;
$str .= implode('', array(
self::intToChr($ctx4 & 0xff),
self::intToChr(($ctx4 >> 8) & 0xff),
self::intToChr(($ctx4 >> 16) & 0xff),
self::intToChr(($ctx4 >> 24) & 0xff),
self::intToChr(($ctx4 >> 32) & 0xff),
self::intToChr(($ctx4 >> 40) & 0xff),
self::intToChr(($ctx4 >> 48) & 0xff),
self::intToChr(($ctx4 >> 56) & 0xff)
));
# uint8_t last_node;
return $str . self::intToChr($ctx[5]) . str_repeat("\x00", 23);
}
/**
* Creates an SplFixedArray containing other SplFixedArray elements, from
* a string (compatible with \Sodium\crypto_generichash_{init, update, final})
*
* @internal You should not use this directly from another application
*
* @param string $string
* @return SplFixedArray
* @throws SodiumException
* @throws TypeError
* @psalm-suppress MixedArrayAssignment
*/
public static function stringToContext($string)
{
$ctx = self::context();
# uint64_t h[8];
for ($i = 0; $i < 8; ++$i) {
$ctx[0][$i] = SplFixedArray::fromArray(
array(
self::load_4(
self::substr($string, (($i << 3) + 4), 4)
),
self::load_4(
self::substr($string, (($i << 3) + 0), 4)
)
)
);
}
# uint64_t t[2];
# uint64_t f[2];
for ($i = 1; $i < 3; ++$i) {
$ctx[$i][1] = SplFixedArray::fromArray(
array(
self::load_4(self::substr($string, 76 + (($i - 1) << 4), 4)),
self::load_4(self::substr($string, 72 + (($i - 1) << 4), 4))
)
);
$ctx[$i][0] = SplFixedArray::fromArray(
array(
self::load_4(self::substr($string, 68 + (($i - 1) << 4), 4)),
self::load_4(self::substr($string, 64 + (($i - 1) << 4), 4))
)
);
}
# uint8_t buf[2 * 128];
$ctx[3] = self::stringToSplFixedArray(self::substr($string, 96, 256));
# uint8_t buf[2 * 128];
$int = 0;
for ($i = 0; $i < 8; ++$i) {
$int |= self::chrToInt($string[352 + $i]) << ($i << 3);
}
$ctx[4] = $int;
return $ctx;
}
}
Core/AES/Expanded.php 0000644 00000000460 15133021213 0010274 0 ustar 00 <?php
if (class_exists('ParagonIE_Sodium_Core_AES_Expanded', false)) {
return;
}
/**
* @internal This should only be used by sodium_compat
*/
class ParagonIE_Sodium_Core_AES_Expanded extends ParagonIE_Sodium_Core_AES_KeySchedule
{
/** @var bool $expanded */
protected $expanded = true;
}
Core/AES/Block.php 0000644 00000024342 15133021213 0007603 0 ustar 00 <?php
if (class_exists('ParagonIE_Sodium_Core_AES_Block', false)) {
return;
}
/**
* @internal This should only be used by sodium_compat
*/
class ParagonIE_Sodium_Core_AES_Block extends SplFixedArray
{
/**
* @var array<int, int>
*/
protected $values = array();
/**
* @var int
*/
protected $size;
/**
* @param int $size
*/
public function __construct($size = 8)
{
parent::__construct($size);
$this->size = $size;
$this->values = array_fill(0, $size, 0);
}
/**
* @return self
*/
public static function init()
{
return new self(8);
}
/**
* @internal You should not use this directly from another application
*
* @param array<int, int> $array
* @param bool $save_indexes
* @return self
*
* @psalm-suppress MethodSignatureMismatch
*/
#[ReturnTypeWillChange]
public static function fromArray($array, $save_indexes = null)
{
$count = count($array);
if ($save_indexes) {
$keys = array_keys($array);
} else {
$keys = range(0, $count - 1);
}
$array = array_values($array);
/** @var array<int, int> $keys */
$obj = new ParagonIE_Sodium_Core_AES_Block();
if ($save_indexes) {
for ($i = 0; $i < $count; ++$i) {
$obj->offsetSet($keys[$i], $array[$i]);
}
} else {
for ($i = 0; $i < $count; ++$i) {
$obj->offsetSet($i, $array[$i]);
}
}
return $obj;
}
/**
* @internal You should not use this directly from another application
*
* @param int|null $offset
* @param int $value
* @return void
*
* @psalm-suppress MethodSignatureMismatch
* @psalm-suppress MixedArrayOffset
*/
#[ReturnTypeWillChange]
public function offsetSet($offset, $value)
{
if (!is_int($value)) {
throw new InvalidArgumentException('Expected an integer');
}
if (is_null($offset)) {
$this->values[] = $value;
} else {
$this->values[$offset] = $value;
}
}
/**
* @internal You should not use this directly from another application
*
* @param int $offset
* @return bool
*
* @psalm-suppress MethodSignatureMismatch
* @psalm-suppress MixedArrayOffset
*/
#[ReturnTypeWillChange]
public function offsetExists($offset)
{
return isset($this->values[$offset]);
}
/**
* @internal You should not use this directly from another application
*
* @param int $offset
* @return void
*
* @psalm-suppress MethodSignatureMismatch
* @psalm-suppress MixedArrayOffset
*/
#[ReturnTypeWillChange]
public function offsetUnset($offset)
{
unset($this->values[$offset]);
}
/**
* @internal You should not use this directly from another application
*
* @param int $offset
* @return int
*
* @psalm-suppress MethodSignatureMismatch
* @psalm-suppress MixedArrayOffset
*/
#[ReturnTypeWillChange]
public function offsetGet($offset)
{
if (!isset($this->values[$offset])) {
$this->values[$offset] = 0;
}
return (int) ($this->values[$offset]);
}
/**
* @internal You should not use this directly from another application
*
* @return array
*/
public function __debugInfo()
{
$out = array();
foreach ($this->values as $v) {
$out[] = str_pad(dechex($v), 8, '0', STR_PAD_LEFT);
}
return array(implode(', ', $out));
/*
return array(implode(', ', $this->values));
*/
}
/**
* @param int $cl low bit mask
* @param int $ch high bit mask
* @param int $s shift
* @param int $x index 1
* @param int $y index 2
* @return self
*/
public function swapN($cl, $ch, $s, $x, $y)
{
static $u32mask = ParagonIE_Sodium_Core_Util::U32_MAX;
$a = $this->values[$x] & $u32mask;
$b = $this->values[$y] & $u32mask;
// (x) = (a & cl) | ((b & cl) << (s));
$this->values[$x] = ($a & $cl) | ((($b & $cl) << $s) & $u32mask);
// (y) = ((a & ch) >> (s)) | (b & ch);
$this->values[$y] = ((($a & $ch) & $u32mask) >> $s) | ($b & $ch);
return $this;
}
/**
* @param int $x index 1
* @param int $y index 2
* @return self
*/
public function swap2($x, $y)
{
return $this->swapN(0x55555555, 0xAAAAAAAA, 1, $x, $y);
}
/**
* @param int $x index 1
* @param int $y index 2
* @return self
*/
public function swap4($x, $y)
{
return $this->swapN(0x33333333, 0xCCCCCCCC, 2, $x, $y);
}
/**
* @param int $x index 1
* @param int $y index 2
* @return self
*/
public function swap8($x, $y)
{
return $this->swapN(0x0F0F0F0F, 0xF0F0F0F0, 4, $x, $y);
}
/**
* @return self
*/
public function orthogonalize()
{
return $this
->swap2(0, 1)
->swap2(2, 3)
->swap2(4, 5)
->swap2(6, 7)
->swap4(0, 2)
->swap4(1, 3)
->swap4(4, 6)
->swap4(5, 7)
->swap8(0, 4)
->swap8(1, 5)
->swap8(2, 6)
->swap8(3, 7);
}
/**
* @return self
*/
public function shiftRows()
{
for ($i = 0; $i < 8; ++$i) {
$x = $this->values[$i] & ParagonIE_Sodium_Core_Util::U32_MAX;
$this->values[$i] = (
($x & 0x000000FF)
| (($x & 0x0000FC00) >> 2) | (($x & 0x00000300) << 6)
| (($x & 0x00F00000) >> 4) | (($x & 0x000F0000) << 4)
| (($x & 0xC0000000) >> 6) | (($x & 0x3F000000) << 2)
) & ParagonIE_Sodium_Core_Util::U32_MAX;
}
return $this;
}
/**
* @param int $x
* @return int
*/
public static function rotr16($x)
{
return (($x << 16) & ParagonIE_Sodium_Core_Util::U32_MAX) | ($x >> 16);
}
/**
* @return self
*/
public function mixColumns()
{
$q0 = $this->values[0];
$q1 = $this->values[1];
$q2 = $this->values[2];
$q3 = $this->values[3];
$q4 = $this->values[4];
$q5 = $this->values[5];
$q6 = $this->values[6];
$q7 = $this->values[7];
$r0 = (($q0 >> 8) | ($q0 << 24)) & ParagonIE_Sodium_Core_Util::U32_MAX;
$r1 = (($q1 >> 8) | ($q1 << 24)) & ParagonIE_Sodium_Core_Util::U32_MAX;
$r2 = (($q2 >> 8) | ($q2 << 24)) & ParagonIE_Sodium_Core_Util::U32_MAX;
$r3 = (($q3 >> 8) | ($q3 << 24)) & ParagonIE_Sodium_Core_Util::U32_MAX;
$r4 = (($q4 >> 8) | ($q4 << 24)) & ParagonIE_Sodium_Core_Util::U32_MAX;
$r5 = (($q5 >> 8) | ($q5 << 24)) & ParagonIE_Sodium_Core_Util::U32_MAX;
$r6 = (($q6 >> 8) | ($q6 << 24)) & ParagonIE_Sodium_Core_Util::U32_MAX;
$r7 = (($q7 >> 8) | ($q7 << 24)) & ParagonIE_Sodium_Core_Util::U32_MAX;
$this->values[0] = $q7 ^ $r7 ^ $r0 ^ self::rotr16($q0 ^ $r0);
$this->values[1] = $q0 ^ $r0 ^ $q7 ^ $r7 ^ $r1 ^ self::rotr16($q1 ^ $r1);
$this->values[2] = $q1 ^ $r1 ^ $r2 ^ self::rotr16($q2 ^ $r2);
$this->values[3] = $q2 ^ $r2 ^ $q7 ^ $r7 ^ $r3 ^ self::rotr16($q3 ^ $r3);
$this->values[4] = $q3 ^ $r3 ^ $q7 ^ $r7 ^ $r4 ^ self::rotr16($q4 ^ $r4);
$this->values[5] = $q4 ^ $r4 ^ $r5 ^ self::rotr16($q5 ^ $r5);
$this->values[6] = $q5 ^ $r5 ^ $r6 ^ self::rotr16($q6 ^ $r6);
$this->values[7] = $q6 ^ $r6 ^ $r7 ^ self::rotr16($q7 ^ $r7);
return $this;
}
/**
* @return self
*/
public function inverseMixColumns()
{
$q0 = $this->values[0];
$q1 = $this->values[1];
$q2 = $this->values[2];
$q3 = $this->values[3];
$q4 = $this->values[4];
$q5 = $this->values[5];
$q6 = $this->values[6];
$q7 = $this->values[7];
$r0 = (($q0 >> 8) | ($q0 << 24)) & ParagonIE_Sodium_Core_Util::U32_MAX;
$r1 = (($q1 >> 8) | ($q1 << 24)) & ParagonIE_Sodium_Core_Util::U32_MAX;
$r2 = (($q2 >> 8) | ($q2 << 24)) & ParagonIE_Sodium_Core_Util::U32_MAX;
$r3 = (($q3 >> 8) | ($q3 << 24)) & ParagonIE_Sodium_Core_Util::U32_MAX;
$r4 = (($q4 >> 8) | ($q4 << 24)) & ParagonIE_Sodium_Core_Util::U32_MAX;
$r5 = (($q5 >> 8) | ($q5 << 24)) & ParagonIE_Sodium_Core_Util::U32_MAX;
$r6 = (($q6 >> 8) | ($q6 << 24)) & ParagonIE_Sodium_Core_Util::U32_MAX;
$r7 = (($q7 >> 8) | ($q7 << 24)) & ParagonIE_Sodium_Core_Util::U32_MAX;
$this->values[0] = $q5 ^ $q6 ^ $q7 ^ $r0 ^ $r5 ^ $r7 ^ self::rotr16($q0 ^ $q5 ^ $q6 ^ $r0 ^ $r5);
$this->values[1] = $q0 ^ $q5 ^ $r0 ^ $r1 ^ $r5 ^ $r6 ^ $r7 ^ self::rotr16($q1 ^ $q5 ^ $q7 ^ $r1 ^ $r5 ^ $r6);
$this->values[2] = $q0 ^ $q1 ^ $q6 ^ $r1 ^ $r2 ^ $r6 ^ $r7 ^ self::rotr16($q0 ^ $q2 ^ $q6 ^ $r2 ^ $r6 ^ $r7);
$this->values[3] = $q0 ^ $q1 ^ $q2 ^ $q5 ^ $q6 ^ $r0 ^ $r2 ^ $r3 ^ $r5 ^ self::rotr16($q0 ^ $q1 ^ $q3 ^ $q5 ^ $q6 ^ $q7 ^ $r0 ^ $r3 ^ $r5 ^ $r7);
$this->values[4] = $q1 ^ $q2 ^ $q3 ^ $q5 ^ $r1 ^ $r3 ^ $r4 ^ $r5 ^ $r6 ^ $r7 ^ self::rotr16($q1 ^ $q2 ^ $q4 ^ $q5 ^ $q7 ^ $r1 ^ $r4 ^ $r5 ^ $r6);
$this->values[5] = $q2 ^ $q3 ^ $q4 ^ $q6 ^ $r2 ^ $r4 ^ $r5 ^ $r6 ^ $r7 ^ self::rotr16($q2 ^ $q3 ^ $q5 ^ $q6 ^ $r2 ^ $r5 ^ $r6 ^ $r7);
$this->values[6] = $q3 ^ $q4 ^ $q5 ^ $q7 ^ $r3 ^ $r5 ^ $r6 ^ $r7 ^ self::rotr16($q3 ^ $q4 ^ $q6 ^ $q7 ^ $r3 ^ $r6 ^ $r7);
$this->values[7] = $q4 ^ $q5 ^ $q6 ^ $r4 ^ $r6 ^ $r7 ^ self::rotr16($q4 ^ $q5 ^ $q7 ^ $r4 ^ $r7);
return $this;
}
/**
* @return self
*/
public function inverseShiftRows()
{
for ($i = 0; $i < 8; ++$i) {
$x = $this->values[$i];
$this->values[$i] = ParagonIE_Sodium_Core_Util::U32_MAX & (
($x & 0x000000FF)
| (($x & 0x00003F00) << 2) | (($x & 0x0000C000) >> 6)
| (($x & 0x000F0000) << 4) | (($x & 0x00F00000) >> 4)
| (($x & 0x03000000) << 6) | (($x & 0xFC000000) >> 2)
);
}
return $this;
}
}
Core/AES/KeySchedule.php 0000644 00000003531 15133021213 0010753 0 ustar 00 <?php
if (class_exists('ParagonIE_Sodium_Core_AES_KeySchedule', false)) {
return;
}
/**
* @internal This should only be used by sodium_compat
*/
class ParagonIE_Sodium_Core_AES_KeySchedule
{
/** @var array<int, int> $skey -- has size 120 */
protected $skey;
/** @var bool $expanded */
protected $expanded = false;
/** @var int $numRounds */
private $numRounds;
/**
* @param array $skey
* @param int $numRounds
*/
public function __construct(array $skey, $numRounds = 10)
{
$this->skey = $skey;
$this->numRounds = $numRounds;
}
/**
* Get a value at an arbitrary index. Mostly used for unit testing.
*
* @param int $i
* @return int
*/
public function get($i)
{
return $this->skey[$i];
}
/**
* @return int
*/
public function getNumRounds()
{
return $this->numRounds;
}
/**
* @param int $offset
* @return ParagonIE_Sodium_Core_AES_Block
*/
public function getRoundKey($offset)
{
return ParagonIE_Sodium_Core_AES_Block::fromArray(
array_slice($this->skey, $offset, 8)
);
}
/**
* Return an expanded key schedule
*
* @return ParagonIE_Sodium_Core_AES_Expanded
*/
public function expand()
{
$exp = new ParagonIE_Sodium_Core_AES_Expanded(
array_fill(0, 120, 0),
$this->numRounds
);
$n = ($exp->numRounds + 1) << 2;
for ($u = 0, $v = 0; $u < $n; ++$u, $v += 2) {
$x = $y = $this->skey[$u];
$x &= 0x55555555;
$exp->skey[$v] = ($x | ($x << 1)) & ParagonIE_Sodium_Core_Util::U32_MAX;
$y &= 0xAAAAAAAA;
$exp->skey[$v + 1] = ($y | ($y >> 1)) & ParagonIE_Sodium_Core_Util::U32_MAX;
}
return $exp;
}
}
Core/AEGIS256.php 0000644 00000007016 15133021213 0007225 0 ustar 00 <?php
if (!defined('SODIUM_COMPAT_AEGIS_C0')) {
define('SODIUM_COMPAT_AEGIS_C0', "\x00\x01\x01\x02\x03\x05\x08\x0d\x15\x22\x37\x59\x90\xe9\x79\x62");
}
if (!defined('SODIUM_COMPAT_AEGIS_C1')) {
define('SODIUM_COMPAT_AEGIS_C1', "\xdb\x3d\x18\x55\x6d\xc2\x2f\xf1\x20\x11\x31\x42\x73\xb5\x28\xdd");
}
class ParagonIE_Sodium_Core_AEGIS256 extends ParagonIE_Sodium_Core_AES
{
/**
* @param string $ct
* @param string $tag
* @param string $ad
* @param string $key
* @param string $nonce
* @return string
* @throws SodiumException
*/
public static function decrypt($ct, $tag, $ad, $key, $nonce)
{
$state = self::init($key, $nonce);
// ad_blocks = Split(ZeroPad(ad, 128), 128)
$ad_blocks = (self::strlen($ad) + 15) >> 4;
// for ai in ad_blocks:
// Absorb(ai)
for ($i = 0; $i < $ad_blocks; ++$i) {
$ai = self::substr($ad, $i << 4, 16);
if (self::strlen($ai) < 16) {
$ai = str_pad($ai, 16, "\0", STR_PAD_RIGHT);
}
$state->absorb($ai);
}
$msg = '';
$cn = self::strlen($ct) & 15;
$ct_blocks = self::strlen($ct) >> 4;
// ct_blocks = Split(ZeroPad(ct, 128), 128)
// cn = Tail(ct, |ct| mod 128)
for ($i = 0; $i < $ct_blocks; ++$i) {
$msg .= $state->dec(self::substr($ct, $i << 4, 16));
}
// if cn is not empty:
// msg = msg || DecPartial(cn)
if ($cn) {
$start = $ct_blocks << 4;
$msg .= $state->decPartial(self::substr($ct, $start, $cn));
}
$expected_tag = $state->finalize(
self::strlen($ad) << 3,
self::strlen($msg) << 3
);
if (!self::hashEquals($expected_tag, $tag)) {
try {
// The RFC says to erase msg, so we shall try:
ParagonIE_Sodium_Compat::memzero($msg);
} catch (SodiumException $ex) {
// Do nothing if we cannot memzero
}
throw new SodiumException('verification failed');
}
return $msg;
}
/**
* @param string $msg
* @param string $ad
* @param string $key
* @param string $nonce
* @return array
* @throws SodiumException
*/
public static function encrypt($msg, $ad, $key, $nonce)
{
$state = self::init($key, $nonce);
$ad_len = self::strlen($ad);
$msg_len = self::strlen($msg);
$ad_blocks = ($ad_len + 15) >> 4;
for ($i = 0; $i < $ad_blocks; ++$i) {
$ai = self::substr($ad, $i << 4, 16);
if (self::strlen($ai) < 16) {
$ai = str_pad($ai, 16, "\0", STR_PAD_RIGHT);
}
$state->absorb($ai);
}
$ct = '';
$msg_blocks = ($msg_len + 15) >> 4;
for ($i = 0; $i < $msg_blocks; ++$i) {
$xi = self::substr($msg, $i << 4, 16);
if (self::strlen($xi) < 16) {
$xi = str_pad($xi, 16, "\0", STR_PAD_RIGHT);
}
$ct .= $state->enc($xi);
}
$tag = $state->finalize(
$ad_len << 3,
$msg_len << 3
);
return array(
self::substr($ct, 0, $msg_len),
$tag
);
}
/**
* @param string $key
* @param string $nonce
* @return ParagonIE_Sodium_Core_AEGIS_State256
*/
public static function init($key, $nonce)
{
return ParagonIE_Sodium_Core_AEGIS_State256::init($key, $nonce);
}
}
Core/Salsa20.php 0000604 00000020051 15133021213 0007333 0 ustar 00 <?php
if (class_exists('ParagonIE_Sodium_Core_Salsa20', false)) {
return;
}
/**
* Class ParagonIE_Sodium_Core_Salsa20
*/
abstract class ParagonIE_Sodium_Core_Salsa20 extends ParagonIE_Sodium_Core_Util
{
const ROUNDS = 20;
/**
* Calculate an salsa20 hash of a single block
*
* @internal You should not use this directly from another application
*
* @param string $in
* @param string $k
* @param string|null $c
* @return string
* @throws TypeError
*/
public static function core_salsa20($in, $k, $c = null)
{
if (self::strlen($k) < 32) {
throw new RangeException('Key must be 32 bytes long');
}
if ($c === null) {
$j0 = $x0 = 0x61707865;
$j5 = $x5 = 0x3320646e;
$j10 = $x10 = 0x79622d32;
$j15 = $x15 = 0x6b206574;
} else {
$j0 = $x0 = self::load_4(self::substr($c, 0, 4));
$j5 = $x5 = self::load_4(self::substr($c, 4, 4));
$j10 = $x10 = self::load_4(self::substr($c, 8, 4));
$j15 = $x15 = self::load_4(self::substr($c, 12, 4));
}
$j1 = $x1 = self::load_4(self::substr($k, 0, 4));
$j2 = $x2 = self::load_4(self::substr($k, 4, 4));
$j3 = $x3 = self::load_4(self::substr($k, 8, 4));
$j4 = $x4 = self::load_4(self::substr($k, 12, 4));
$j6 = $x6 = self::load_4(self::substr($in, 0, 4));
$j7 = $x7 = self::load_4(self::substr($in, 4, 4));
$j8 = $x8 = self::load_4(self::substr($in, 8, 4));
$j9 = $x9 = self::load_4(self::substr($in, 12, 4));
$j11 = $x11 = self::load_4(self::substr($k, 16, 4));
$j12 = $x12 = self::load_4(self::substr($k, 20, 4));
$j13 = $x13 = self::load_4(self::substr($k, 24, 4));
$j14 = $x14 = self::load_4(self::substr($k, 28, 4));
for ($i = self::ROUNDS; $i > 0; $i -= 2) {
$x4 ^= self::rotate($x0 + $x12, 7);
$x8 ^= self::rotate($x4 + $x0, 9);
$x12 ^= self::rotate($x8 + $x4, 13);
$x0 ^= self::rotate($x12 + $x8, 18);
$x9 ^= self::rotate($x5 + $x1, 7);
$x13 ^= self::rotate($x9 + $x5, 9);
$x1 ^= self::rotate($x13 + $x9, 13);
$x5 ^= self::rotate($x1 + $x13, 18);
$x14 ^= self::rotate($x10 + $x6, 7);
$x2 ^= self::rotate($x14 + $x10, 9);
$x6 ^= self::rotate($x2 + $x14, 13);
$x10 ^= self::rotate($x6 + $x2, 18);
$x3 ^= self::rotate($x15 + $x11, 7);
$x7 ^= self::rotate($x3 + $x15, 9);
$x11 ^= self::rotate($x7 + $x3, 13);
$x15 ^= self::rotate($x11 + $x7, 18);
$x1 ^= self::rotate($x0 + $x3, 7);
$x2 ^= self::rotate($x1 + $x0, 9);
$x3 ^= self::rotate($x2 + $x1, 13);
$x0 ^= self::rotate($x3 + $x2, 18);
$x6 ^= self::rotate($x5 + $x4, 7);
$x7 ^= self::rotate($x6 + $x5, 9);
$x4 ^= self::rotate($x7 + $x6, 13);
$x5 ^= self::rotate($x4 + $x7, 18);
$x11 ^= self::rotate($x10 + $x9, 7);
$x8 ^= self::rotate($x11 + $x10, 9);
$x9 ^= self::rotate($x8 + $x11, 13);
$x10 ^= self::rotate($x9 + $x8, 18);
$x12 ^= self::rotate($x15 + $x14, 7);
$x13 ^= self::rotate($x12 + $x15, 9);
$x14 ^= self::rotate($x13 + $x12, 13);
$x15 ^= self::rotate($x14 + $x13, 18);
}
$x0 += $j0;
$x1 += $j1;
$x2 += $j2;
$x3 += $j3;
$x4 += $j4;
$x5 += $j5;
$x6 += $j6;
$x7 += $j7;
$x8 += $j8;
$x9 += $j9;
$x10 += $j10;
$x11 += $j11;
$x12 += $j12;
$x13 += $j13;
$x14 += $j14;
$x15 += $j15;
return self::store32_le($x0) .
self::store32_le($x1) .
self::store32_le($x2) .
self::store32_le($x3) .
self::store32_le($x4) .
self::store32_le($x5) .
self::store32_le($x6) .
self::store32_le($x7) .
self::store32_le($x8) .
self::store32_le($x9) .
self::store32_le($x10) .
self::store32_le($x11) .
self::store32_le($x12) .
self::store32_le($x13) .
self::store32_le($x14) .
self::store32_le($x15);
}
/**
* @internal You should not use this directly from another application
*
* @param int $len
* @param string $nonce
* @param string $key
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function salsa20($len, $nonce, $key)
{
if (self::strlen($key) !== 32) {
throw new RangeException('Key must be 32 bytes long');
}
$kcopy = '' . $key;
$in = self::substr($nonce, 0, 8) . str_repeat("\0", 8);
$c = '';
while ($len >= 64) {
$c .= self::core_salsa20($in, $kcopy, null);
$u = 1;
// Internal counter.
for ($i = 8; $i < 16; ++$i) {
$u += self::chrToInt($in[$i]);
$in[$i] = self::intToChr($u & 0xff);
$u >>= 8;
}
$len -= 64;
}
if ($len > 0) {
$c .= self::substr(
self::core_salsa20($in, $kcopy, null),
0,
$len
);
}
try {
ParagonIE_Sodium_Compat::memzero($kcopy);
} catch (SodiumException $ex) {
$kcopy = null;
}
return $c;
}
/**
* @internal You should not use this directly from another application
*
* @param string $m
* @param string $n
* @param int $ic
* @param string $k
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function salsa20_xor_ic($m, $n, $ic, $k)
{
$mlen = self::strlen($m);
if ($mlen < 1) {
return '';
}
$kcopy = self::substr($k, 0, 32);
$in = self::substr($n, 0, 8);
// Initialize the counter
$in .= ParagonIE_Sodium_Core_Util::store64_le($ic);
$c = '';
while ($mlen >= 64) {
$block = self::core_salsa20($in, $kcopy, null);
$c .= self::xorStrings(
self::substr($m, 0, 64),
self::substr($block, 0, 64)
);
$u = 1;
for ($i = 8; $i < 16; ++$i) {
$u += self::chrToInt($in[$i]);
$in[$i] = self::intToChr($u & 0xff);
$u >>= 8;
}
$mlen -= 64;
$m = self::substr($m, 64);
}
if ($mlen) {
$block = self::core_salsa20($in, $kcopy, null);
$c .= self::xorStrings(
self::substr($m, 0, $mlen),
self::substr($block, 0, $mlen)
);
}
try {
ParagonIE_Sodium_Compat::memzero($block);
ParagonIE_Sodium_Compat::memzero($kcopy);
} catch (SodiumException $ex) {
$block = null;
$kcopy = null;
}
return $c;
}
/**
* @internal You should not use this directly from another application
*
* @param string $message
* @param string $nonce
* @param string $key
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function salsa20_xor($message, $nonce, $key)
{
return self::xorStrings(
$message,
self::salsa20(
self::strlen($message),
$nonce,
$key
)
);
}
/**
* @internal You should not use this directly from another application
*
* @param int $u
* @param int $c
* @return int
*/
public static function rotate($u, $c)
{
$u &= 0xffffffff;
$c %= 32;
return (int) (0xffffffff & (
($u << $c)
|
($u >> (32 - $c))
)
);
}
}
Core/ChaCha20/Ctx.php 0000644 00000007546 15133021213 0010157 0 ustar 00 <?php
if (class_exists('ParagonIE_Sodium_Core_ChaCha20_Ctx', false)) {
return;
}
/**
* Class ParagonIE_Sodium_Core_ChaCha20_Ctx
*/
class ParagonIE_Sodium_Core_ChaCha20_Ctx extends ParagonIE_Sodium_Core_Util implements ArrayAccess
{
/**
* @var SplFixedArray internally, <int, int>
*/
protected $container;
/**
* ParagonIE_Sodium_Core_ChaCha20_Ctx constructor.
*
* @internal You should not use this directly from another application
*
* @param string $key ChaCha20 key.
* @param string $iv Initialization Vector (a.k.a. nonce).
* @param string $counter The initial counter value.
* Defaults to 8 0x00 bytes.
* @throws InvalidArgumentException
* @throws TypeError
*/
public function __construct($key = '', $iv = '', $counter = '')
{
if (self::strlen($key) !== 32) {
throw new InvalidArgumentException('ChaCha20 expects a 256-bit key.');
}
if (self::strlen($iv) !== 8) {
throw new InvalidArgumentException('ChaCha20 expects a 64-bit nonce.');
}
$this->container = new SplFixedArray(16);
/* "expand 32-byte k" as per ChaCha20 spec */
$this->container[0] = 0x61707865;
$this->container[1] = 0x3320646e;
$this->container[2] = 0x79622d32;
$this->container[3] = 0x6b206574;
$this->container[4] = self::load_4(self::substr($key, 0, 4));
$this->container[5] = self::load_4(self::substr($key, 4, 4));
$this->container[6] = self::load_4(self::substr($key, 8, 4));
$this->container[7] = self::load_4(self::substr($key, 12, 4));
$this->container[8] = self::load_4(self::substr($key, 16, 4));
$this->container[9] = self::load_4(self::substr($key, 20, 4));
$this->container[10] = self::load_4(self::substr($key, 24, 4));
$this->container[11] = self::load_4(self::substr($key, 28, 4));
if (empty($counter)) {
$this->container[12] = 0;
$this->container[13] = 0;
} else {
$this->container[12] = self::load_4(self::substr($counter, 0, 4));
$this->container[13] = self::load_4(self::substr($counter, 4, 4));
}
$this->container[14] = self::load_4(self::substr($iv, 0, 4));
$this->container[15] = self::load_4(self::substr($iv, 4, 4));
}
/**
* @internal You should not use this directly from another application
*
* @param int $offset
* @param int $value
* @return void
* @psalm-suppress MixedArrayOffset
*/
#[ReturnTypeWillChange]
public function offsetSet($offset, $value)
{
if (!is_int($offset)) {
throw new InvalidArgumentException('Expected an integer');
}
if (!is_int($value)) {
throw new InvalidArgumentException('Expected an integer');
}
$this->container[$offset] = $value;
}
/**
* @internal You should not use this directly from another application
*
* @param int $offset
* @return bool
*/
#[ReturnTypeWillChange]
public function offsetExists($offset)
{
return isset($this->container[$offset]);
}
/**
* @internal You should not use this directly from another application
*
* @param int $offset
* @return void
* @psalm-suppress MixedArrayOffset
*/
#[ReturnTypeWillChange]
public function offsetUnset($offset)
{
unset($this->container[$offset]);
}
/**
* @internal You should not use this directly from another application
*
* @param int $offset
* @return mixed|null
* @psalm-suppress MixedArrayOffset
*/
#[ReturnTypeWillChange]
public function offsetGet($offset)
{
return isset($this->container[$offset])
? $this->container[$offset]
: null;
}
}
Core/ChaCha20/IetfCtx.php 0000604 00000002452 15133021213 0010752 0 ustar 00 <?php
if (class_exists('ParagonIE_Sodium_Core_ChaCha20_IetfCtx', false)) {
return;
}
/**
* Class ParagonIE_Sodium_Core_ChaCha20_IetfCtx
*/
class ParagonIE_Sodium_Core_ChaCha20_IetfCtx extends ParagonIE_Sodium_Core_ChaCha20_Ctx
{
/**
* ParagonIE_Sodium_Core_ChaCha20_IetfCtx constructor.
*
* @internal You should not use this directly from another application
*
* @param string $key ChaCha20 key.
* @param string $iv Initialization Vector (a.k.a. nonce).
* @param string $counter The initial counter value.
* Defaults to 4 0x00 bytes.
* @throws InvalidArgumentException
* @throws TypeError
*/
public function __construct($key = '', $iv = '', $counter = '')
{
if (self::strlen($iv) !== 12) {
throw new InvalidArgumentException('ChaCha20 expects a 96-bit nonce in IETF mode.');
}
parent::__construct($key, self::substr($iv, 0, 8), $counter);
if (!empty($counter)) {
$this->container[12] = self::load_4(self::substr($counter, 0, 4));
}
$this->container[13] = self::load_4(self::substr($iv, 0, 4));
$this->container[14] = self::load_4(self::substr($iv, 4, 4));
$this->container[15] = self::load_4(self::substr($iv, 8, 4));
}
}
File.php 0000644 00000147501 15133021213 0006133 0 ustar 00 <?php
if (class_exists('ParagonIE_Sodium_File', false)) {
return;
}
/**
* Class ParagonIE_Sodium_File
*/
class ParagonIE_Sodium_File extends ParagonIE_Sodium_Core_Util
{
/* PHP's default buffer size is 8192 for fread()/fwrite(). */
const BUFFER_SIZE = 8192;
/**
* Box a file (rather than a string). Uses less memory than
* ParagonIE_Sodium_Compat::crypto_box(), but produces
* the same result.
*
* @param string $inputFile Absolute path to a file on the filesystem
* @param string $outputFile Absolute path to a file on the filesystem
* @param string $nonce Number to be used only once
* @param string $keyPair ECDH secret key and ECDH public key concatenated
*
* @return bool
* @throws SodiumException
* @throws TypeError
*/
public static function box($inputFile, $outputFile, $nonce, $keyPair)
{
/* Type checks: */
if (!is_string($inputFile)) {
throw new TypeError('Argument 1 must be a string, ' . gettype($inputFile) . ' given.');
}
if (!is_string($outputFile)) {
throw new TypeError('Argument 2 must be a string, ' . gettype($outputFile) . ' given.');
}
if (!is_string($nonce)) {
throw new TypeError('Argument 3 must be a string, ' . gettype($nonce) . ' given.');
}
/* Input validation: */
if (!is_string($keyPair)) {
throw new TypeError('Argument 4 must be a string, ' . gettype($keyPair) . ' given.');
}
if (self::strlen($nonce) !== ParagonIE_Sodium_Compat::CRYPTO_BOX_NONCEBYTES) {
throw new TypeError('Argument 3 must be CRYPTO_BOX_NONCEBYTES bytes');
}
if (self::strlen($keyPair) !== ParagonIE_Sodium_Compat::CRYPTO_BOX_KEYPAIRBYTES) {
throw new TypeError('Argument 4 must be CRYPTO_BOX_KEYPAIRBYTES bytes');
}
/** @var int $size */
$size = filesize($inputFile);
if (!is_int($size)) {
throw new SodiumException('Could not obtain the file size');
}
/** @var resource $ifp */
$ifp = fopen($inputFile, 'rb');
if (!is_resource($ifp)) {
throw new SodiumException('Could not open input file for reading');
}
/** @var resource $ofp */
$ofp = fopen($outputFile, 'wb');
if (!is_resource($ofp)) {
fclose($ifp);
throw new SodiumException('Could not open output file for writing');
}
$res = self::box_encrypt($ifp, $ofp, $size, $nonce, $keyPair);
fclose($ifp);
fclose($ofp);
return $res;
}
/**
* Open a boxed file (rather than a string). Uses less memory than
* ParagonIE_Sodium_Compat::crypto_box_open(), but produces
* the same result.
*
* Warning: Does not protect against TOCTOU attacks. You should
* just load the file into memory and use crypto_box_open() if
* you are worried about those.
*
* @param string $inputFile
* @param string $outputFile
* @param string $nonce
* @param string $keypair
* @return bool
* @throws SodiumException
* @throws TypeError
*/
public static function box_open($inputFile, $outputFile, $nonce, $keypair)
{
/* Type checks: */
if (!is_string($inputFile)) {
throw new TypeError('Argument 1 must be a string, ' . gettype($inputFile) . ' given.');
}
if (!is_string($outputFile)) {
throw new TypeError('Argument 2 must be a string, ' . gettype($outputFile) . ' given.');
}
if (!is_string($nonce)) {
throw new TypeError('Argument 3 must be a string, ' . gettype($nonce) . ' given.');
}
if (!is_string($keypair)) {
throw new TypeError('Argument 4 must be a string, ' . gettype($keypair) . ' given.');
}
/* Input validation: */
if (self::strlen($nonce) !== ParagonIE_Sodium_Compat::CRYPTO_BOX_NONCEBYTES) {
throw new TypeError('Argument 4 must be CRYPTO_BOX_NONCEBYTES bytes');
}
if (self::strlen($keypair) !== ParagonIE_Sodium_Compat::CRYPTO_BOX_KEYPAIRBYTES) {
throw new TypeError('Argument 4 must be CRYPTO_BOX_KEYPAIRBYTES bytes');
}
/** @var int $size */
$size = filesize($inputFile);
if (!is_int($size)) {
throw new SodiumException('Could not obtain the file size');
}
/** @var resource $ifp */
$ifp = fopen($inputFile, 'rb');
if (!is_resource($ifp)) {
throw new SodiumException('Could not open input file for reading');
}
/** @var resource $ofp */
$ofp = fopen($outputFile, 'wb');
if (!is_resource($ofp)) {
fclose($ifp);
throw new SodiumException('Could not open output file for writing');
}
$res = self::box_decrypt($ifp, $ofp, $size, $nonce, $keypair);
fclose($ifp);
fclose($ofp);
try {
ParagonIE_Sodium_Compat::memzero($nonce);
ParagonIE_Sodium_Compat::memzero($ephKeypair);
} catch (SodiumException $ex) {
if (isset($ephKeypair)) {
unset($ephKeypair);
}
}
return $res;
}
/**
* Seal a file (rather than a string). Uses less memory than
* ParagonIE_Sodium_Compat::crypto_box_seal(), but produces
* the same result.
*
* @param string $inputFile Absolute path to a file on the filesystem
* @param string $outputFile Absolute path to a file on the filesystem
* @param string $publicKey ECDH public key
*
* @return bool
* @throws SodiumException
* @throws TypeError
*/
public static function box_seal($inputFile, $outputFile, $publicKey)
{
/* Type checks: */
if (!is_string($inputFile)) {
throw new TypeError('Argument 1 must be a string, ' . gettype($inputFile) . ' given.');
}
if (!is_string($outputFile)) {
throw new TypeError('Argument 2 must be a string, ' . gettype($outputFile) . ' given.');
}
if (!is_string($publicKey)) {
throw new TypeError('Argument 3 must be a string, ' . gettype($publicKey) . ' given.');
}
/* Input validation: */
if (self::strlen($publicKey) !== ParagonIE_Sodium_Compat::CRYPTO_BOX_PUBLICKEYBYTES) {
throw new TypeError('Argument 3 must be CRYPTO_BOX_PUBLICKEYBYTES bytes');
}
/** @var int $size */
$size = filesize($inputFile);
if (!is_int($size)) {
throw new SodiumException('Could not obtain the file size');
}
/** @var resource $ifp */
$ifp = fopen($inputFile, 'rb');
if (!is_resource($ifp)) {
throw new SodiumException('Could not open input file for reading');
}
/** @var resource $ofp */
$ofp = fopen($outputFile, 'wb');
if (!is_resource($ofp)) {
fclose($ifp);
throw new SodiumException('Could not open output file for writing');
}
/** @var string $ephKeypair */
$ephKeypair = ParagonIE_Sodium_Compat::crypto_box_keypair();
/** @var string $msgKeypair */
$msgKeypair = ParagonIE_Sodium_Compat::crypto_box_keypair_from_secretkey_and_publickey(
ParagonIE_Sodium_Compat::crypto_box_secretkey($ephKeypair),
$publicKey
);
/** @var string $ephemeralPK */
$ephemeralPK = ParagonIE_Sodium_Compat::crypto_box_publickey($ephKeypair);
/** @var string $nonce */
$nonce = ParagonIE_Sodium_Compat::crypto_generichash(
$ephemeralPK . $publicKey,
'',
24
);
/** @var int $firstWrite */
$firstWrite = fwrite(
$ofp,
$ephemeralPK,
ParagonIE_Sodium_Compat::CRYPTO_BOX_PUBLICKEYBYTES
);
if (!is_int($firstWrite)) {
fclose($ifp);
fclose($ofp);
ParagonIE_Sodium_Compat::memzero($ephKeypair);
throw new SodiumException('Could not write to output file');
}
if ($firstWrite !== ParagonIE_Sodium_Compat::CRYPTO_BOX_PUBLICKEYBYTES) {
ParagonIE_Sodium_Compat::memzero($ephKeypair);
fclose($ifp);
fclose($ofp);
throw new SodiumException('Error writing public key to output file');
}
$res = self::box_encrypt($ifp, $ofp, $size, $nonce, $msgKeypair);
fclose($ifp);
fclose($ofp);
try {
ParagonIE_Sodium_Compat::memzero($nonce);
ParagonIE_Sodium_Compat::memzero($ephKeypair);
} catch (SodiumException $ex) {
/** @psalm-suppress PossiblyUndefinedVariable */
unset($ephKeypair);
}
return $res;
}
/**
* Open a sealed file (rather than a string). Uses less memory than
* ParagonIE_Sodium_Compat::crypto_box_seal_open(), but produces
* the same result.
*
* Warning: Does not protect against TOCTOU attacks. You should
* just load the file into memory and use crypto_box_seal_open() if
* you are worried about those.
*
* @param string $inputFile
* @param string $outputFile
* @param string $ecdhKeypair
* @return bool
* @throws SodiumException
* @throws TypeError
*/
public static function box_seal_open($inputFile, $outputFile, $ecdhKeypair)
{
/* Type checks: */
if (!is_string($inputFile)) {
throw new TypeError('Argument 1 must be a string, ' . gettype($inputFile) . ' given.');
}
if (!is_string($outputFile)) {
throw new TypeError('Argument 2 must be a string, ' . gettype($outputFile) . ' given.');
}
if (!is_string($ecdhKeypair)) {
throw new TypeError('Argument 3 must be a string, ' . gettype($ecdhKeypair) . ' given.');
}
/* Input validation: */
if (self::strlen($ecdhKeypair) !== ParagonIE_Sodium_Compat::CRYPTO_BOX_KEYPAIRBYTES) {
throw new TypeError('Argument 3 must be CRYPTO_BOX_KEYPAIRBYTES bytes');
}
$publicKey = ParagonIE_Sodium_Compat::crypto_box_publickey($ecdhKeypair);
/** @var int $size */
$size = filesize($inputFile);
if (!is_int($size)) {
throw new SodiumException('Could not obtain the file size');
}
/** @var resource $ifp */
$ifp = fopen($inputFile, 'rb');
if (!is_resource($ifp)) {
throw new SodiumException('Could not open input file for reading');
}
/** @var resource $ofp */
$ofp = fopen($outputFile, 'wb');
if (!is_resource($ofp)) {
fclose($ifp);
throw new SodiumException('Could not open output file for writing');
}
$ephemeralPK = fread($ifp, ParagonIE_Sodium_Compat::CRYPTO_BOX_PUBLICKEYBYTES);
if (!is_string($ephemeralPK)) {
throw new SodiumException('Could not read input file');
}
if (self::strlen($ephemeralPK) !== ParagonIE_Sodium_Compat::CRYPTO_BOX_PUBLICKEYBYTES) {
fclose($ifp);
fclose($ofp);
throw new SodiumException('Could not read public key from sealed file');
}
$nonce = ParagonIE_Sodium_Compat::crypto_generichash(
$ephemeralPK . $publicKey,
'',
24
);
$msgKeypair = ParagonIE_Sodium_Compat::crypto_box_keypair_from_secretkey_and_publickey(
ParagonIE_Sodium_Compat::crypto_box_secretkey($ecdhKeypair),
$ephemeralPK
);
$res = self::box_decrypt($ifp, $ofp, $size, $nonce, $msgKeypair);
fclose($ifp);
fclose($ofp);
try {
ParagonIE_Sodium_Compat::memzero($nonce);
ParagonIE_Sodium_Compat::memzero($ephKeypair);
} catch (SodiumException $ex) {
if (isset($ephKeypair)) {
unset($ephKeypair);
}
}
return $res;
}
/**
* Calculate the BLAKE2b hash of a file.
*
* @param string $filePath Absolute path to a file on the filesystem
* @param string|null $key BLAKE2b key
* @param int $outputLength Length of hash output
*
* @return string BLAKE2b hash
* @throws SodiumException
* @throws TypeError
* @psalm-suppress FailedTypeResolution
*/
public static function generichash($filePath, $key = '', $outputLength = 32)
{
/* Type checks: */
if (!is_string($filePath)) {
throw new TypeError('Argument 1 must be a string, ' . gettype($filePath) . ' given.');
}
if (!is_string($key)) {
if (is_null($key)) {
$key = '';
} else {
throw new TypeError('Argument 2 must be a string, ' . gettype($key) . ' given.');
}
}
if (!is_int($outputLength)) {
if (!is_numeric($outputLength)) {
throw new TypeError('Argument 3 must be an integer, ' . gettype($outputLength) . ' given.');
}
$outputLength = (int) $outputLength;
}
/* Input validation: */
if (!empty($key)) {
if (self::strlen($key) < ParagonIE_Sodium_Compat::CRYPTO_GENERICHASH_KEYBYTES_MIN) {
throw new TypeError('Argument 2 must be at least CRYPTO_GENERICHASH_KEYBYTES_MIN bytes');
}
if (self::strlen($key) > ParagonIE_Sodium_Compat::CRYPTO_GENERICHASH_KEYBYTES_MAX) {
throw new TypeError('Argument 2 must be at most CRYPTO_GENERICHASH_KEYBYTES_MAX bytes');
}
}
if ($outputLength < ParagonIE_Sodium_Compat::CRYPTO_GENERICHASH_BYTES_MIN) {
throw new SodiumException('Argument 3 must be at least CRYPTO_GENERICHASH_BYTES_MIN');
}
if ($outputLength > ParagonIE_Sodium_Compat::CRYPTO_GENERICHASH_BYTES_MAX) {
throw new SodiumException('Argument 3 must be at least CRYPTO_GENERICHASH_BYTES_MAX');
}
/** @var int $size */
$size = filesize($filePath);
if (!is_int($size)) {
throw new SodiumException('Could not obtain the file size');
}
/** @var resource $fp */
$fp = fopen($filePath, 'rb');
if (!is_resource($fp)) {
throw new SodiumException('Could not open input file for reading');
}
$ctx = ParagonIE_Sodium_Compat::crypto_generichash_init($key, $outputLength);
while ($size > 0) {
$blockSize = $size > 64
? 64
: $size;
$read = fread($fp, $blockSize);
if (!is_string($read)) {
throw new SodiumException('Could not read input file');
}
ParagonIE_Sodium_Compat::crypto_generichash_update($ctx, $read);
$size -= $blockSize;
}
fclose($fp);
return ParagonIE_Sodium_Compat::crypto_generichash_final($ctx, $outputLength);
}
/**
* Encrypt a file (rather than a string). Uses less memory than
* ParagonIE_Sodium_Compat::crypto_secretbox(), but produces
* the same result.
*
* @param string $inputFile Absolute path to a file on the filesystem
* @param string $outputFile Absolute path to a file on the filesystem
* @param string $nonce Number to be used only once
* @param string $key Encryption key
*
* @return bool
* @throws SodiumException
* @throws TypeError
*/
public static function secretbox($inputFile, $outputFile, $nonce, $key)
{
/* Type checks: */
if (!is_string($inputFile)) {
throw new TypeError('Argument 1 must be a string, ' . gettype($inputFile) . ' given..');
}
if (!is_string($outputFile)) {
throw new TypeError('Argument 2 must be a string, ' . gettype($outputFile) . ' given.');
}
if (!is_string($nonce)) {
throw new TypeError('Argument 3 must be a string, ' . gettype($nonce) . ' given.');
}
/* Input validation: */
if (self::strlen($nonce) !== ParagonIE_Sodium_Compat::CRYPTO_SECRETBOX_NONCEBYTES) {
throw new TypeError('Argument 3 must be CRYPTO_SECRETBOX_NONCEBYTES bytes');
}
if (!is_string($key)) {
throw new TypeError('Argument 4 must be a string, ' . gettype($key) . ' given.');
}
if (self::strlen($key) !== ParagonIE_Sodium_Compat::CRYPTO_SECRETBOX_KEYBYTES) {
throw new TypeError('Argument 4 must be CRYPTO_SECRETBOX_KEYBYTES bytes');
}
/** @var int $size */
$size = filesize($inputFile);
if (!is_int($size)) {
throw new SodiumException('Could not obtain the file size');
}
/** @var resource $ifp */
$ifp = fopen($inputFile, 'rb');
if (!is_resource($ifp)) {
throw new SodiumException('Could not open input file for reading');
}
/** @var resource $ofp */
$ofp = fopen($outputFile, 'wb');
if (!is_resource($ofp)) {
fclose($ifp);
throw new SodiumException('Could not open output file for writing');
}
$res = self::secretbox_encrypt($ifp, $ofp, $size, $nonce, $key);
fclose($ifp);
fclose($ofp);
return $res;
}
/**
* Seal a file (rather than a string). Uses less memory than
* ParagonIE_Sodium_Compat::crypto_secretbox_open(), but produces
* the same result.
*
* Warning: Does not protect against TOCTOU attacks. You should
* just load the file into memory and use crypto_secretbox_open() if
* you are worried about those.
*
* @param string $inputFile
* @param string $outputFile
* @param string $nonce
* @param string $key
* @return bool
* @throws SodiumException
* @throws TypeError
*/
public static function secretbox_open($inputFile, $outputFile, $nonce, $key)
{
/* Type checks: */
if (!is_string($inputFile)) {
throw new TypeError('Argument 1 must be a string, ' . gettype($inputFile) . ' given.');
}
if (!is_string($outputFile)) {
throw new TypeError('Argument 2 must be a string, ' . gettype($outputFile) . ' given.');
}
if (!is_string($nonce)) {
throw new TypeError('Argument 3 must be a string, ' . gettype($nonce) . ' given.');
}
if (!is_string($key)) {
throw new TypeError('Argument 4 must be a string, ' . gettype($key) . ' given.');
}
/* Input validation: */
if (self::strlen($nonce) !== ParagonIE_Sodium_Compat::CRYPTO_SECRETBOX_NONCEBYTES) {
throw new TypeError('Argument 4 must be CRYPTO_SECRETBOX_NONCEBYTES bytes');
}
if (self::strlen($key) !== ParagonIE_Sodium_Compat::CRYPTO_SECRETBOX_KEYBYTES) {
throw new TypeError('Argument 4 must be CRYPTO_SECRETBOXBOX_KEYBYTES bytes');
}
/** @var int $size */
$size = filesize($inputFile);
if (!is_int($size)) {
throw new SodiumException('Could not obtain the file size');
}
/** @var resource $ifp */
$ifp = fopen($inputFile, 'rb');
if (!is_resource($ifp)) {
throw new SodiumException('Could not open input file for reading');
}
/** @var resource $ofp */
$ofp = fopen($outputFile, 'wb');
if (!is_resource($ofp)) {
fclose($ifp);
throw new SodiumException('Could not open output file for writing');
}
$res = self::secretbox_decrypt($ifp, $ofp, $size, $nonce, $key);
fclose($ifp);
fclose($ofp);
try {
ParagonIE_Sodium_Compat::memzero($key);
} catch (SodiumException $ex) {
/** @psalm-suppress PossiblyUndefinedVariable */
unset($key);
}
return $res;
}
/**
* Sign a file (rather than a string). Uses less memory than
* ParagonIE_Sodium_Compat::crypto_sign_detached(), but produces
* the same result.
*
* @param string $filePath Absolute path to a file on the filesystem
* @param string $secretKey Secret signing key
*
* @return string Ed25519 signature
* @throws SodiumException
* @throws TypeError
*/
public static function sign($filePath, $secretKey)
{
/* Type checks: */
if (!is_string($filePath)) {
throw new TypeError('Argument 1 must be a string, ' . gettype($filePath) . ' given.');
}
if (!is_string($secretKey)) {
throw new TypeError('Argument 2 must be a string, ' . gettype($secretKey) . ' given.');
}
/* Input validation: */
if (self::strlen($secretKey) !== ParagonIE_Sodium_Compat::CRYPTO_SIGN_SECRETKEYBYTES) {
throw new TypeError('Argument 2 must be CRYPTO_SIGN_SECRETKEYBYTES bytes');
}
if (PHP_INT_SIZE === 4) {
return self::sign_core32($filePath, $secretKey);
}
/** @var int $size */
$size = filesize($filePath);
if (!is_int($size)) {
throw new SodiumException('Could not obtain the file size');
}
/** @var resource $fp */
$fp = fopen($filePath, 'rb');
if (!is_resource($fp)) {
throw new SodiumException('Could not open input file for reading');
}
/** @var string $az */
$az = hash('sha512', self::substr($secretKey, 0, 32), true);
$az[0] = self::intToChr(self::chrToInt($az[0]) & 248);
$az[31] = self::intToChr((self::chrToInt($az[31]) & 63) | 64);
$hs = hash_init('sha512');
self::hash_update($hs, self::substr($az, 32, 32));
/** @var resource $hs */
$hs = self::updateHashWithFile($hs, $fp, $size);
/** @var string $nonceHash */
$nonceHash = hash_final($hs, true);
/** @var string $pk */
$pk = self::substr($secretKey, 32, 32);
/** @var string $nonce */
$nonce = ParagonIE_Sodium_Core_Ed25519::sc_reduce($nonceHash) . self::substr($nonceHash, 32);
/** @var string $sig */
$sig = ParagonIE_Sodium_Core_Ed25519::ge_p3_tobytes(
ParagonIE_Sodium_Core_Ed25519::ge_scalarmult_base($nonce)
);
$hs = hash_init('sha512');
self::hash_update($hs, self::substr($sig, 0, 32));
self::hash_update($hs, self::substr($pk, 0, 32));
/** @var resource $hs */
$hs = self::updateHashWithFile($hs, $fp, $size);
/** @var string $hramHash */
$hramHash = hash_final($hs, true);
/** @var string $hram */
$hram = ParagonIE_Sodium_Core_Ed25519::sc_reduce($hramHash);
/** @var string $sigAfter */
$sigAfter = ParagonIE_Sodium_Core_Ed25519::sc_muladd($hram, $az, $nonce);
/** @var string $sig */
$sig = self::substr($sig, 0, 32) . self::substr($sigAfter, 0, 32);
try {
ParagonIE_Sodium_Compat::memzero($az);
} catch (SodiumException $ex) {
$az = null;
}
fclose($fp);
return $sig;
}
/**
* Verify a file (rather than a string). Uses less memory than
* ParagonIE_Sodium_Compat::crypto_sign_verify_detached(), but
* produces the same result.
*
* @param string $sig Ed25519 signature
* @param string $filePath Absolute path to a file on the filesystem
* @param string $publicKey Signing public key
*
* @return bool
* @throws SodiumException
* @throws TypeError
* @throws Exception
*/
public static function verify($sig, $filePath, $publicKey)
{
/* Type checks: */
if (!is_string($sig)) {
throw new TypeError('Argument 1 must be a string, ' . gettype($sig) . ' given.');
}
if (!is_string($filePath)) {
throw new TypeError('Argument 2 must be a string, ' . gettype($filePath) . ' given.');
}
if (!is_string($publicKey)) {
throw new TypeError('Argument 3 must be a string, ' . gettype($publicKey) . ' given.');
}
/* Input validation: */
if (self::strlen($sig) !== ParagonIE_Sodium_Compat::CRYPTO_SIGN_BYTES) {
throw new TypeError('Argument 1 must be CRYPTO_SIGN_BYTES bytes');
}
if (self::strlen($publicKey) !== ParagonIE_Sodium_Compat::CRYPTO_SIGN_PUBLICKEYBYTES) {
throw new TypeError('Argument 3 must be CRYPTO_SIGN_PUBLICKEYBYTES bytes');
}
if (self::strlen($sig) < 64) {
throw new SodiumException('Signature is too short');
}
if (PHP_INT_SIZE === 4) {
return self::verify_core32($sig, $filePath, $publicKey);
}
/* Security checks */
if (
(ParagonIE_Sodium_Core_Ed25519::chrToInt($sig[63]) & 240)
&&
ParagonIE_Sodium_Core_Ed25519::check_S_lt_L(self::substr($sig, 32, 32))
) {
throw new SodiumException('S < L - Invalid signature');
}
if (ParagonIE_Sodium_Core_Ed25519::small_order($sig)) {
throw new SodiumException('Signature is on too small of an order');
}
if ((self::chrToInt($sig[63]) & 224) !== 0) {
throw new SodiumException('Invalid signature');
}
$d = 0;
for ($i = 0; $i < 32; ++$i) {
$d |= self::chrToInt($publicKey[$i]);
}
if ($d === 0) {
throw new SodiumException('All zero public key');
}
/** @var int $size */
$size = filesize($filePath);
if (!is_int($size)) {
throw new SodiumException('Could not obtain the file size');
}
/** @var resource $fp */
$fp = fopen($filePath, 'rb');
if (!is_resource($fp)) {
throw new SodiumException('Could not open input file for reading');
}
/** @var bool The original value of ParagonIE_Sodium_Compat::$fastMult */
$orig = ParagonIE_Sodium_Compat::$fastMult;
// Set ParagonIE_Sodium_Compat::$fastMult to true to speed up verification.
ParagonIE_Sodium_Compat::$fastMult = true;
/** @var ParagonIE_Sodium_Core_Curve25519_Ge_P3 $A */
$A = ParagonIE_Sodium_Core_Ed25519::ge_frombytes_negate_vartime($publicKey);
$hs = hash_init('sha512');
self::hash_update($hs, self::substr($sig, 0, 32));
self::hash_update($hs, self::substr($publicKey, 0, 32));
/** @var resource $hs */
$hs = self::updateHashWithFile($hs, $fp, $size);
/** @var string $hDigest */
$hDigest = hash_final($hs, true);
/** @var string $h */
$h = ParagonIE_Sodium_Core_Ed25519::sc_reduce($hDigest) . self::substr($hDigest, 32);
/** @var ParagonIE_Sodium_Core_Curve25519_Ge_P2 $R */
$R = ParagonIE_Sodium_Core_Ed25519::ge_double_scalarmult_vartime(
$h,
$A,
self::substr($sig, 32)
);
/** @var string $rcheck */
$rcheck = ParagonIE_Sodium_Core_Ed25519::ge_tobytes($R);
// Close the file handle
fclose($fp);
// Reset ParagonIE_Sodium_Compat::$fastMult to what it was before.
ParagonIE_Sodium_Compat::$fastMult = $orig;
return self::verify_32($rcheck, self::substr($sig, 0, 32));
}
/**
* @param resource $ifp
* @param resource $ofp
* @param int $mlen
* @param string $nonce
* @param string $boxKeypair
* @return bool
* @throws SodiumException
* @throws TypeError
*/
protected static function box_encrypt($ifp, $ofp, $mlen, $nonce, $boxKeypair)
{
if (PHP_INT_SIZE === 4) {
return self::secretbox_encrypt(
$ifp,
$ofp,
$mlen,
$nonce,
ParagonIE_Sodium_Crypto32::box_beforenm(
ParagonIE_Sodium_Crypto32::box_secretkey($boxKeypair),
ParagonIE_Sodium_Crypto32::box_publickey($boxKeypair)
)
);
}
return self::secretbox_encrypt(
$ifp,
$ofp,
$mlen,
$nonce,
ParagonIE_Sodium_Crypto::box_beforenm(
ParagonIE_Sodium_Crypto::box_secretkey($boxKeypair),
ParagonIE_Sodium_Crypto::box_publickey($boxKeypair)
)
);
}
/**
* @param resource $ifp
* @param resource $ofp
* @param int $mlen
* @param string $nonce
* @param string $boxKeypair
* @return bool
* @throws SodiumException
* @throws TypeError
*/
protected static function box_decrypt($ifp, $ofp, $mlen, $nonce, $boxKeypair)
{
if (PHP_INT_SIZE === 4) {
return self::secretbox_decrypt(
$ifp,
$ofp,
$mlen,
$nonce,
ParagonIE_Sodium_Crypto32::box_beforenm(
ParagonIE_Sodium_Crypto32::box_secretkey($boxKeypair),
ParagonIE_Sodium_Crypto32::box_publickey($boxKeypair)
)
);
}
return self::secretbox_decrypt(
$ifp,
$ofp,
$mlen,
$nonce,
ParagonIE_Sodium_Crypto::box_beforenm(
ParagonIE_Sodium_Crypto::box_secretkey($boxKeypair),
ParagonIE_Sodium_Crypto::box_publickey($boxKeypair)
)
);
}
/**
* Encrypt a file
*
* @param resource $ifp
* @param resource $ofp
* @param int $mlen
* @param string $nonce
* @param string $key
* @return bool
* @throws SodiumException
* @throws TypeError
*/
protected static function secretbox_encrypt($ifp, $ofp, $mlen, $nonce, $key)
{
if (PHP_INT_SIZE === 4) {
return self::secretbox_encrypt_core32($ifp, $ofp, $mlen, $nonce, $key);
}
$plaintext = fread($ifp, 32);
if (!is_string($plaintext)) {
throw new SodiumException('Could not read input file');
}
$first32 = self::ftell($ifp);
/** @var string $subkey */
$subkey = ParagonIE_Sodium_Core_HSalsa20::hsalsa20($nonce, $key);
/** @var string $realNonce */
$realNonce = ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8);
/** @var string $block0 */
$block0 = str_repeat("\x00", 32);
/** @var int $mlen - Length of the plaintext message */
$mlen0 = $mlen;
if ($mlen0 > 64 - ParagonIE_Sodium_Crypto::secretbox_xsalsa20poly1305_ZEROBYTES) {
$mlen0 = 64 - ParagonIE_Sodium_Crypto::secretbox_xsalsa20poly1305_ZEROBYTES;
}
$block0 .= ParagonIE_Sodium_Core_Util::substr($plaintext, 0, $mlen0);
/** @var string $block0 */
$block0 = ParagonIE_Sodium_Core_Salsa20::salsa20_xor(
$block0,
$realNonce,
$subkey
);
$state = new ParagonIE_Sodium_Core_Poly1305_State(
ParagonIE_Sodium_Core_Util::substr(
$block0,
0,
ParagonIE_Sodium_Crypto::onetimeauth_poly1305_KEYBYTES
)
);
// Pre-write 16 blank bytes for the Poly1305 tag
$start = self::ftell($ofp);
fwrite($ofp, str_repeat("\x00", 16));
/** @var string $c */
$cBlock = ParagonIE_Sodium_Core_Util::substr(
$block0,
ParagonIE_Sodium_Crypto::secretbox_xsalsa20poly1305_ZEROBYTES
);
$state->update($cBlock);
fwrite($ofp, $cBlock);
$mlen -= 32;
/** @var int $iter */
$iter = 1;
/** @var int $incr */
$incr = self::BUFFER_SIZE >> 6;
/*
* Set the cursor to the end of the first half-block. All future bytes will
* generated from salsa20_xor_ic, starting from 1 (second block).
*/
fseek($ifp, $first32, SEEK_SET);
while ($mlen > 0) {
$blockSize = $mlen > self::BUFFER_SIZE
? self::BUFFER_SIZE
: $mlen;
$plaintext = fread($ifp, $blockSize);
if (!is_string($plaintext)) {
throw new SodiumException('Could not read input file');
}
$cBlock = ParagonIE_Sodium_Core_Salsa20::salsa20_xor_ic(
$plaintext,
$realNonce,
$iter,
$subkey
);
fwrite($ofp, $cBlock, $blockSize);
$state->update($cBlock);
$mlen -= $blockSize;
$iter += $incr;
}
try {
ParagonIE_Sodium_Compat::memzero($block0);
ParagonIE_Sodium_Compat::memzero($subkey);
} catch (SodiumException $ex) {
$block0 = null;
$subkey = null;
}
$end = self::ftell($ofp);
/*
* Write the Poly1305 authentication tag that provides integrity
* over the ciphertext (encrypt-then-MAC)
*/
fseek($ofp, $start, SEEK_SET);
fwrite($ofp, $state->finish(), ParagonIE_Sodium_Compat::CRYPTO_SECRETBOX_MACBYTES);
fseek($ofp, $end, SEEK_SET);
unset($state);
return true;
}
/**
* Decrypt a file
*
* @param resource $ifp
* @param resource $ofp
* @param int $mlen
* @param string $nonce
* @param string $key
* @return bool
* @throws SodiumException
* @throws TypeError
*/
protected static function secretbox_decrypt($ifp, $ofp, $mlen, $nonce, $key)
{
if (PHP_INT_SIZE === 4) {
return self::secretbox_decrypt_core32($ifp, $ofp, $mlen, $nonce, $key);
}
$tag = fread($ifp, 16);
if (!is_string($tag)) {
throw new SodiumException('Could not read input file');
}
/** @var string $subkey */
$subkey = ParagonIE_Sodium_Core_HSalsa20::hsalsa20($nonce, $key);
/** @var string $realNonce */
$realNonce = ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8);
/** @var string $block0 */
$block0 = ParagonIE_Sodium_Core_Salsa20::salsa20(
64,
ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8),
$subkey
);
/* Verify the Poly1305 MAC -before- attempting to decrypt! */
$state = new ParagonIE_Sodium_Core_Poly1305_State(self::substr($block0, 0, 32));
if (!self::onetimeauth_verify($state, $ifp, $tag, $mlen)) {
throw new SodiumException('Invalid MAC');
}
/*
* Set the cursor to the end of the first half-block. All future bytes will
* generated from salsa20_xor_ic, starting from 1 (second block).
*/
$first32 = fread($ifp, 32);
if (!is_string($first32)) {
throw new SodiumException('Could not read input file');
}
$first32len = self::strlen($first32);
fwrite(
$ofp,
self::xorStrings(
self::substr($block0, 32, $first32len),
self::substr($first32, 0, $first32len)
)
);
$mlen -= 32;
/** @var int $iter */
$iter = 1;
/** @var int $incr */
$incr = self::BUFFER_SIZE >> 6;
/* Decrypts ciphertext, writes to output file. */
while ($mlen > 0) {
$blockSize = $mlen > self::BUFFER_SIZE
? self::BUFFER_SIZE
: $mlen;
$ciphertext = fread($ifp, $blockSize);
if (!is_string($ciphertext)) {
throw new SodiumException('Could not read input file');
}
$pBlock = ParagonIE_Sodium_Core_Salsa20::salsa20_xor_ic(
$ciphertext,
$realNonce,
$iter,
$subkey
);
fwrite($ofp, $pBlock, $blockSize);
$mlen -= $blockSize;
$iter += $incr;
}
return true;
}
/**
* @param ParagonIE_Sodium_Core_Poly1305_State $state
* @param resource $ifp
* @param string $tag
* @param int $mlen
* @return bool
* @throws SodiumException
* @throws TypeError
*/
protected static function onetimeauth_verify(
ParagonIE_Sodium_Core_Poly1305_State $state,
$ifp,
$tag = '',
$mlen = 0
) {
/** @var int $pos */
$pos = self::ftell($ifp);
/** @var int $iter */
$iter = 1;
/** @var int $incr */
$incr = self::BUFFER_SIZE >> 6;
while ($mlen > 0) {
$blockSize = $mlen > self::BUFFER_SIZE
? self::BUFFER_SIZE
: $mlen;
$ciphertext = fread($ifp, $blockSize);
if (!is_string($ciphertext)) {
throw new SodiumException('Could not read input file');
}
$state->update($ciphertext);
$mlen -= $blockSize;
$iter += $incr;
}
$res = ParagonIE_Sodium_Core_Util::verify_16($tag, $state->finish());
fseek($ifp, $pos, SEEK_SET);
return $res;
}
/**
* Update a hash context with the contents of a file, without
* loading the entire file into memory.
*
* @param resource|HashContext $hash
* @param resource $fp
* @param int $size
* @return resource|object Resource on PHP < 7.2, HashContext object on PHP >= 7.2
* @throws SodiumException
* @throws TypeError
* @psalm-suppress PossiblyInvalidArgument
* PHP 7.2 changes from a resource to an object,
* which causes Psalm to complain about an error.
* @psalm-suppress TypeCoercion
* Ditto.
*/
public static function updateHashWithFile($hash, $fp, $size = 0)
{
/* Type checks: */
if (PHP_VERSION_ID < 70200) {
if (!is_resource($hash)) {
throw new TypeError('Argument 1 must be a resource, ' . gettype($hash) . ' given.');
}
} else {
if (!is_object($hash)) {
throw new TypeError('Argument 1 must be an object (PHP 7.2+), ' . gettype($hash) . ' given.');
}
}
if (!is_resource($fp)) {
throw new TypeError('Argument 2 must be a resource, ' . gettype($fp) . ' given.');
}
if (!is_int($size)) {
throw new TypeError('Argument 3 must be an integer, ' . gettype($size) . ' given.');
}
/** @var int $originalPosition */
$originalPosition = self::ftell($fp);
// Move file pointer to beginning of file
fseek($fp, 0, SEEK_SET);
for ($i = 0; $i < $size; $i += self::BUFFER_SIZE) {
/** @var string|bool $message */
$message = fread(
$fp,
($size - $i) > self::BUFFER_SIZE
? $size - $i
: self::BUFFER_SIZE
);
if (!is_string($message)) {
throw new SodiumException('Unexpected error reading from file.');
}
/** @var string $message */
/** @psalm-suppress InvalidArgument */
self::hash_update($hash, $message);
}
// Reset file pointer's position
fseek($fp, $originalPosition, SEEK_SET);
return $hash;
}
/**
* Sign a file (rather than a string). Uses less memory than
* ParagonIE_Sodium_Compat::crypto_sign_detached(), but produces
* the same result. (32-bit)
*
* @param string $filePath Absolute path to a file on the filesystem
* @param string $secretKey Secret signing key
*
* @return string Ed25519 signature
* @throws SodiumException
* @throws TypeError
*/
private static function sign_core32($filePath, $secretKey)
{
$size = filesize($filePath);
if (!is_int($size)) {
throw new SodiumException('Could not obtain the file size');
}
$fp = fopen($filePath, 'rb');
if (!is_resource($fp)) {
throw new SodiumException('Could not open input file for reading');
}
/** @var string $az */
$az = hash('sha512', self::substr($secretKey, 0, 32), true);
$az[0] = self::intToChr(self::chrToInt($az[0]) & 248);
$az[31] = self::intToChr((self::chrToInt($az[31]) & 63) | 64);
$hs = hash_init('sha512');
self::hash_update($hs, self::substr($az, 32, 32));
/** @var resource $hs */
$hs = self::updateHashWithFile($hs, $fp, $size);
$nonceHash = hash_final($hs, true);
$pk = self::substr($secretKey, 32, 32);
$nonce = ParagonIE_Sodium_Core32_Ed25519::sc_reduce($nonceHash) . self::substr($nonceHash, 32);
$sig = ParagonIE_Sodium_Core32_Ed25519::ge_p3_tobytes(
ParagonIE_Sodium_Core32_Ed25519::ge_scalarmult_base($nonce)
);
$hs = hash_init('sha512');
self::hash_update($hs, self::substr($sig, 0, 32));
self::hash_update($hs, self::substr($pk, 0, 32));
/** @var resource $hs */
$hs = self::updateHashWithFile($hs, $fp, $size);
$hramHash = hash_final($hs, true);
$hram = ParagonIE_Sodium_Core32_Ed25519::sc_reduce($hramHash);
$sigAfter = ParagonIE_Sodium_Core32_Ed25519::sc_muladd($hram, $az, $nonce);
/** @var string $sig */
$sig = self::substr($sig, 0, 32) . self::substr($sigAfter, 0, 32);
try {
ParagonIE_Sodium_Compat::memzero($az);
} catch (SodiumException $ex) {
$az = null;
}
fclose($fp);
return $sig;
}
/**
*
* Verify a file (rather than a string). Uses less memory than
* ParagonIE_Sodium_Compat::crypto_sign_verify_detached(), but
* produces the same result. (32-bit)
*
* @param string $sig Ed25519 signature
* @param string $filePath Absolute path to a file on the filesystem
* @param string $publicKey Signing public key
*
* @return bool
* @throws SodiumException
* @throws Exception
*/
public static function verify_core32($sig, $filePath, $publicKey)
{
/* Security checks */
if (ParagonIE_Sodium_Core32_Ed25519::check_S_lt_L(self::substr($sig, 32, 32))) {
throw new SodiumException('S < L - Invalid signature');
}
if (ParagonIE_Sodium_Core32_Ed25519::small_order($sig)) {
throw new SodiumException('Signature is on too small of an order');
}
if ((self::chrToInt($sig[63]) & 224) !== 0) {
throw new SodiumException('Invalid signature');
}
$d = 0;
for ($i = 0; $i < 32; ++$i) {
$d |= self::chrToInt($publicKey[$i]);
}
if ($d === 0) {
throw new SodiumException('All zero public key');
}
/** @var int|bool $size */
$size = filesize($filePath);
if (!is_int($size)) {
throw new SodiumException('Could not obtain the file size');
}
/** @var int $size */
/** @var resource|bool $fp */
$fp = fopen($filePath, 'rb');
if (!is_resource($fp)) {
throw new SodiumException('Could not open input file for reading');
}
/** @var resource $fp */
/** @var bool The original value of ParagonIE_Sodium_Compat::$fastMult */
$orig = ParagonIE_Sodium_Compat::$fastMult;
// Set ParagonIE_Sodium_Compat::$fastMult to true to speed up verification.
ParagonIE_Sodium_Compat::$fastMult = true;
/** @var ParagonIE_Sodium_Core32_Curve25519_Ge_P3 $A */
$A = ParagonIE_Sodium_Core32_Ed25519::ge_frombytes_negate_vartime($publicKey);
$hs = hash_init('sha512');
self::hash_update($hs, self::substr($sig, 0, 32));
self::hash_update($hs, self::substr($publicKey, 0, 32));
/** @var resource $hs */
$hs = self::updateHashWithFile($hs, $fp, $size);
/** @var string $hDigest */
$hDigest = hash_final($hs, true);
/** @var string $h */
$h = ParagonIE_Sodium_Core32_Ed25519::sc_reduce($hDigest) . self::substr($hDigest, 32);
/** @var ParagonIE_Sodium_Core32_Curve25519_Ge_P2 $R */
$R = ParagonIE_Sodium_Core32_Ed25519::ge_double_scalarmult_vartime(
$h,
$A,
self::substr($sig, 32)
);
/** @var string $rcheck */
$rcheck = ParagonIE_Sodium_Core32_Ed25519::ge_tobytes($R);
// Close the file handle
fclose($fp);
// Reset ParagonIE_Sodium_Compat::$fastMult to what it was before.
ParagonIE_Sodium_Compat::$fastMult = $orig;
return self::verify_32($rcheck, self::substr($sig, 0, 32));
}
/**
* Encrypt a file (32-bit)
*
* @param resource $ifp
* @param resource $ofp
* @param int $mlen
* @param string $nonce
* @param string $key
* @return bool
* @throws SodiumException
* @throws TypeError
*/
protected static function secretbox_encrypt_core32($ifp, $ofp, $mlen, $nonce, $key)
{
$plaintext = fread($ifp, 32);
if (!is_string($plaintext)) {
throw new SodiumException('Could not read input file');
}
$first32 = self::ftell($ifp);
/** @var string $subkey */
$subkey = ParagonIE_Sodium_Core32_HSalsa20::hsalsa20($nonce, $key);
/** @var string $realNonce */
$realNonce = ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8);
/** @var string $block0 */
$block0 = str_repeat("\x00", 32);
/** @var int $mlen - Length of the plaintext message */
$mlen0 = $mlen;
if ($mlen0 > 64 - ParagonIE_Sodium_Crypto::secretbox_xsalsa20poly1305_ZEROBYTES) {
$mlen0 = 64 - ParagonIE_Sodium_Crypto::secretbox_xsalsa20poly1305_ZEROBYTES;
}
$block0 .= ParagonIE_Sodium_Core32_Util::substr($plaintext, 0, $mlen0);
/** @var string $block0 */
$block0 = ParagonIE_Sodium_Core32_Salsa20::salsa20_xor(
$block0,
$realNonce,
$subkey
);
$state = new ParagonIE_Sodium_Core32_Poly1305_State(
ParagonIE_Sodium_Core32_Util::substr(
$block0,
0,
ParagonIE_Sodium_Crypto::onetimeauth_poly1305_KEYBYTES
)
);
// Pre-write 16 blank bytes for the Poly1305 tag
$start = self::ftell($ofp);
fwrite($ofp, str_repeat("\x00", 16));
/** @var string $c */
$cBlock = ParagonIE_Sodium_Core32_Util::substr(
$block0,
ParagonIE_Sodium_Crypto::secretbox_xsalsa20poly1305_ZEROBYTES
);
$state->update($cBlock);
fwrite($ofp, $cBlock);
$mlen -= 32;
/** @var int $iter */
$iter = 1;
/** @var int $incr */
$incr = self::BUFFER_SIZE >> 6;
/*
* Set the cursor to the end of the first half-block. All future bytes will
* generated from salsa20_xor_ic, starting from 1 (second block).
*/
fseek($ifp, $first32, SEEK_SET);
while ($mlen > 0) {
$blockSize = $mlen > self::BUFFER_SIZE
? self::BUFFER_SIZE
: $mlen;
$plaintext = fread($ifp, $blockSize);
if (!is_string($plaintext)) {
throw new SodiumException('Could not read input file');
}
$cBlock = ParagonIE_Sodium_Core32_Salsa20::salsa20_xor_ic(
$plaintext,
$realNonce,
$iter,
$subkey
);
fwrite($ofp, $cBlock, $blockSize);
$state->update($cBlock);
$mlen -= $blockSize;
$iter += $incr;
}
try {
ParagonIE_Sodium_Compat::memzero($block0);
ParagonIE_Sodium_Compat::memzero($subkey);
} catch (SodiumException $ex) {
$block0 = null;
$subkey = null;
}
$end = self::ftell($ofp);
/*
* Write the Poly1305 authentication tag that provides integrity
* over the ciphertext (encrypt-then-MAC)
*/
fseek($ofp, $start, SEEK_SET);
fwrite($ofp, $state->finish(), ParagonIE_Sodium_Compat::CRYPTO_SECRETBOX_MACBYTES);
fseek($ofp, $end, SEEK_SET);
unset($state);
return true;
}
/**
* Decrypt a file (32-bit)
*
* @param resource $ifp
* @param resource $ofp
* @param int $mlen
* @param string $nonce
* @param string $key
* @return bool
* @throws SodiumException
* @throws TypeError
*/
protected static function secretbox_decrypt_core32($ifp, $ofp, $mlen, $nonce, $key)
{
$tag = fread($ifp, 16);
if (!is_string($tag)) {
throw new SodiumException('Could not read input file');
}
/** @var string $subkey */
$subkey = ParagonIE_Sodium_Core32_HSalsa20::hsalsa20($nonce, $key);
/** @var string $realNonce */
$realNonce = ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8);
/** @var string $block0 */
$block0 = ParagonIE_Sodium_Core32_Salsa20::salsa20(
64,
ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8),
$subkey
);
/* Verify the Poly1305 MAC -before- attempting to decrypt! */
$state = new ParagonIE_Sodium_Core32_Poly1305_State(self::substr($block0, 0, 32));
if (!self::onetimeauth_verify_core32($state, $ifp, $tag, $mlen)) {
throw new SodiumException('Invalid MAC');
}
/*
* Set the cursor to the end of the first half-block. All future bytes will
* generated from salsa20_xor_ic, starting from 1 (second block).
*/
$first32 = fread($ifp, 32);
if (!is_string($first32)) {
throw new SodiumException('Could not read input file');
}
$first32len = self::strlen($first32);
fwrite(
$ofp,
self::xorStrings(
self::substr($block0, 32, $first32len),
self::substr($first32, 0, $first32len)
)
);
$mlen -= 32;
/** @var int $iter */
$iter = 1;
/** @var int $incr */
$incr = self::BUFFER_SIZE >> 6;
/* Decrypts ciphertext, writes to output file. */
while ($mlen > 0) {
$blockSize = $mlen > self::BUFFER_SIZE
? self::BUFFER_SIZE
: $mlen;
$ciphertext = fread($ifp, $blockSize);
if (!is_string($ciphertext)) {
throw new SodiumException('Could not read input file');
}
$pBlock = ParagonIE_Sodium_Core32_Salsa20::salsa20_xor_ic(
$ciphertext,
$realNonce,
$iter,
$subkey
);
fwrite($ofp, $pBlock, $blockSize);
$mlen -= $blockSize;
$iter += $incr;
}
return true;
}
/**
* One-time message authentication for 32-bit systems
*
* @param ParagonIE_Sodium_Core32_Poly1305_State $state
* @param resource $ifp
* @param string $tag
* @param int $mlen
* @return bool
* @throws SodiumException
* @throws TypeError
*/
protected static function onetimeauth_verify_core32(
ParagonIE_Sodium_Core32_Poly1305_State $state,
$ifp,
$tag = '',
$mlen = 0
) {
/** @var int $pos */
$pos = self::ftell($ifp);
while ($mlen > 0) {
$blockSize = $mlen > self::BUFFER_SIZE
? self::BUFFER_SIZE
: $mlen;
$ciphertext = fread($ifp, $blockSize);
if (!is_string($ciphertext)) {
throw new SodiumException('Could not read input file');
}
$state->update($ciphertext);
$mlen -= $blockSize;
}
$res = ParagonIE_Sodium_Core32_Util::verify_16($tag, $state->finish());
fseek($ifp, $pos, SEEK_SET);
return $res;
}
/**
* @param resource $resource
* @return int
* @throws SodiumException
*/
private static function ftell($resource)
{
$return = ftell($resource);
if (!is_int($return)) {
throw new SodiumException('ftell() returned false');
}
return (int) $return;
}
}
Crypto.php 0000604 00000153032 15133021213 0006524 0 ustar 00 <?php
if (class_exists('ParagonIE_Sodium_Crypto', false)) {
return;
}
/**
* Class ParagonIE_Sodium_Crypto
*
* ATTENTION!
*
* If you are using this library, you should be using
* ParagonIE_Sodium_Compat in your code, not this class.
*/
abstract class ParagonIE_Sodium_Crypto
{
const aead_chacha20poly1305_KEYBYTES = 32;
const aead_chacha20poly1305_NSECBYTES = 0;
const aead_chacha20poly1305_NPUBBYTES = 8;
const aead_chacha20poly1305_ABYTES = 16;
const aead_chacha20poly1305_IETF_KEYBYTES = 32;
const aead_chacha20poly1305_IETF_NSECBYTES = 0;
const aead_chacha20poly1305_IETF_NPUBBYTES = 12;
const aead_chacha20poly1305_IETF_ABYTES = 16;
const aead_xchacha20poly1305_IETF_KEYBYTES = 32;
const aead_xchacha20poly1305_IETF_NSECBYTES = 0;
const aead_xchacha20poly1305_IETF_NPUBBYTES = 24;
const aead_xchacha20poly1305_IETF_ABYTES = 16;
const box_curve25519xsalsa20poly1305_SEEDBYTES = 32;
const box_curve25519xsalsa20poly1305_PUBLICKEYBYTES = 32;
const box_curve25519xsalsa20poly1305_SECRETKEYBYTES = 32;
const box_curve25519xsalsa20poly1305_BEFORENMBYTES = 32;
const box_curve25519xsalsa20poly1305_NONCEBYTES = 24;
const box_curve25519xsalsa20poly1305_MACBYTES = 16;
const box_curve25519xsalsa20poly1305_BOXZEROBYTES = 16;
const box_curve25519xsalsa20poly1305_ZEROBYTES = 32;
const onetimeauth_poly1305_BYTES = 16;
const onetimeauth_poly1305_KEYBYTES = 32;
const secretbox_xsalsa20poly1305_KEYBYTES = 32;
const secretbox_xsalsa20poly1305_NONCEBYTES = 24;
const secretbox_xsalsa20poly1305_MACBYTES = 16;
const secretbox_xsalsa20poly1305_BOXZEROBYTES = 16;
const secretbox_xsalsa20poly1305_ZEROBYTES = 32;
const secretbox_xchacha20poly1305_KEYBYTES = 32;
const secretbox_xchacha20poly1305_NONCEBYTES = 24;
const secretbox_xchacha20poly1305_MACBYTES = 16;
const secretbox_xchacha20poly1305_BOXZEROBYTES = 16;
const secretbox_xchacha20poly1305_ZEROBYTES = 32;
const stream_salsa20_KEYBYTES = 32;
/**
* AEAD Decryption with ChaCha20-Poly1305
*
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
*
* @param string $message
* @param string $ad
* @param string $nonce
* @param string $key
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function aead_chacha20poly1305_decrypt(
$message = '',
$ad = '',
$nonce = '',
$key = ''
) {
/** @var int $len - Length of message (ciphertext + MAC) */
$len = ParagonIE_Sodium_Core_Util::strlen($message);
/** @var int $clen - Length of ciphertext */
$clen = $len - self::aead_chacha20poly1305_ABYTES;
/** @var int $adlen - Length of associated data */
$adlen = ParagonIE_Sodium_Core_Util::strlen($ad);
/** @var string $mac - Message authentication code */
$mac = ParagonIE_Sodium_Core_Util::substr(
$message,
$clen,
self::aead_chacha20poly1305_ABYTES
);
/** @var string $ciphertext - The encrypted message (sans MAC) */
$ciphertext = ParagonIE_Sodium_Core_Util::substr($message, 0, $clen);
/** @var string The first block of the chacha20 keystream, used as a poly1305 key */
$block0 = ParagonIE_Sodium_Core_ChaCha20::stream(
32,
$nonce,
$key
);
/* Recalculate the Poly1305 authentication tag (MAC): */
$state = new ParagonIE_Sodium_Core_Poly1305_State($block0);
try {
ParagonIE_Sodium_Compat::memzero($block0);
} catch (SodiumException $ex) {
$block0 = null;
}
$state->update($ad);
$state->update(ParagonIE_Sodium_Core_Util::store64_le($adlen));
$state->update($ciphertext);
$state->update(ParagonIE_Sodium_Core_Util::store64_le($clen));
$computed_mac = $state->finish();
/* Compare the given MAC with the recalculated MAC: */
if (!ParagonIE_Sodium_Core_Util::verify_16($computed_mac, $mac)) {
throw new SodiumException('Invalid MAC');
}
// Here, we know that the MAC is valid, so we decrypt and return the plaintext
return ParagonIE_Sodium_Core_ChaCha20::streamXorIc(
$ciphertext,
$nonce,
$key,
ParagonIE_Sodium_Core_Util::store64_le(1)
);
}
/**
* AEAD Encryption with ChaCha20-Poly1305
*
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
*
* @param string $message
* @param string $ad
* @param string $nonce
* @param string $key
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function aead_chacha20poly1305_encrypt(
$message = '',
$ad = '',
$nonce = '',
$key = ''
) {
/** @var int $len - Length of the plaintext message */
$len = ParagonIE_Sodium_Core_Util::strlen($message);
/** @var int $adlen - Length of the associated data */
$adlen = ParagonIE_Sodium_Core_Util::strlen($ad);
/** @var string The first block of the chacha20 keystream, used as a poly1305 key */
$block0 = ParagonIE_Sodium_Core_ChaCha20::stream(
32,
$nonce,
$key
);
$state = new ParagonIE_Sodium_Core_Poly1305_State($block0);
try {
ParagonIE_Sodium_Compat::memzero($block0);
} catch (SodiumException $ex) {
$block0 = null;
}
/** @var string $ciphertext - Raw encrypted data */
$ciphertext = ParagonIE_Sodium_Core_ChaCha20::streamXorIc(
$message,
$nonce,
$key,
ParagonIE_Sodium_Core_Util::store64_le(1)
);
$state->update($ad);
$state->update(ParagonIE_Sodium_Core_Util::store64_le($adlen));
$state->update($ciphertext);
$state->update(ParagonIE_Sodium_Core_Util::store64_le($len));
return $ciphertext . $state->finish();
}
/**
* AEAD Decryption with ChaCha20-Poly1305, IETF mode (96-bit nonce)
*
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
*
* @param string $message
* @param string $ad
* @param string $nonce
* @param string $key
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function aead_chacha20poly1305_ietf_decrypt(
$message = '',
$ad = '',
$nonce = '',
$key = ''
) {
/** @var int $adlen - Length of associated data */
$adlen = ParagonIE_Sodium_Core_Util::strlen($ad);
/** @var int $len - Length of message (ciphertext + MAC) */
$len = ParagonIE_Sodium_Core_Util::strlen($message);
/** @var int $clen - Length of ciphertext */
$clen = $len - self::aead_chacha20poly1305_IETF_ABYTES;
/** @var string The first block of the chacha20 keystream, used as a poly1305 key */
$block0 = ParagonIE_Sodium_Core_ChaCha20::ietfStream(
32,
$nonce,
$key
);
/** @var string $mac - Message authentication code */
$mac = ParagonIE_Sodium_Core_Util::substr(
$message,
$len - self::aead_chacha20poly1305_IETF_ABYTES,
self::aead_chacha20poly1305_IETF_ABYTES
);
/** @var string $ciphertext - The encrypted message (sans MAC) */
$ciphertext = ParagonIE_Sodium_Core_Util::substr(
$message,
0,
$len - self::aead_chacha20poly1305_IETF_ABYTES
);
/* Recalculate the Poly1305 authentication tag (MAC): */
$state = new ParagonIE_Sodium_Core_Poly1305_State($block0);
try {
ParagonIE_Sodium_Compat::memzero($block0);
} catch (SodiumException $ex) {
$block0 = null;
}
$state->update($ad);
$state->update(str_repeat("\x00", ((0x10 - $adlen) & 0xf)));
$state->update($ciphertext);
$state->update(str_repeat("\x00", (0x10 - $clen) & 0xf));
$state->update(ParagonIE_Sodium_Core_Util::store64_le($adlen));
$state->update(ParagonIE_Sodium_Core_Util::store64_le($clen));
$computed_mac = $state->finish();
/* Compare the given MAC with the recalculated MAC: */
if (!ParagonIE_Sodium_Core_Util::verify_16($computed_mac, $mac)) {
throw new SodiumException('Invalid MAC');
}
// Here, we know that the MAC is valid, so we decrypt and return the plaintext
return ParagonIE_Sodium_Core_ChaCha20::ietfStreamXorIc(
$ciphertext,
$nonce,
$key,
ParagonIE_Sodium_Core_Util::store64_le(1)
);
}
/**
* AEAD Encryption with ChaCha20-Poly1305, IETF mode (96-bit nonce)
*
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
*
* @param string $message
* @param string $ad
* @param string $nonce
* @param string $key
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function aead_chacha20poly1305_ietf_encrypt(
$message = '',
$ad = '',
$nonce = '',
$key = ''
) {
/** @var int $len - Length of the plaintext message */
$len = ParagonIE_Sodium_Core_Util::strlen($message);
/** @var int $adlen - Length of the associated data */
$adlen = ParagonIE_Sodium_Core_Util::strlen($ad);
/** @var string The first block of the chacha20 keystream, used as a poly1305 key */
$block0 = ParagonIE_Sodium_Core_ChaCha20::ietfStream(
32,
$nonce,
$key
);
$state = new ParagonIE_Sodium_Core_Poly1305_State($block0);
try {
ParagonIE_Sodium_Compat::memzero($block0);
} catch (SodiumException $ex) {
$block0 = null;
}
/** @var string $ciphertext - Raw encrypted data */
$ciphertext = ParagonIE_Sodium_Core_ChaCha20::ietfStreamXorIc(
$message,
$nonce,
$key,
ParagonIE_Sodium_Core_Util::store64_le(1)
);
$state->update($ad);
$state->update(str_repeat("\x00", ((0x10 - $adlen) & 0xf)));
$state->update($ciphertext);
$state->update(str_repeat("\x00", ((0x10 - $len) & 0xf)));
$state->update(ParagonIE_Sodium_Core_Util::store64_le($adlen));
$state->update(ParagonIE_Sodium_Core_Util::store64_le($len));
return $ciphertext . $state->finish();
}
/**
* AEAD Decryption with ChaCha20-Poly1305, IETF mode (96-bit nonce)
*
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
*
* @param string $message
* @param string $ad
* @param string $nonce
* @param string $key
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function aead_xchacha20poly1305_ietf_decrypt(
$message = '',
$ad = '',
$nonce = '',
$key = ''
) {
$subkey = ParagonIE_Sodium_Core_HChaCha20::hChaCha20(
ParagonIE_Sodium_Core_Util::substr($nonce, 0, 16),
$key
);
$nonceLast = "\x00\x00\x00\x00" .
ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8);
return self::aead_chacha20poly1305_ietf_decrypt($message, $ad, $nonceLast, $subkey);
}
/**
* AEAD Encryption with ChaCha20-Poly1305, IETF mode (96-bit nonce)
*
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
*
* @param string $message
* @param string $ad
* @param string $nonce
* @param string $key
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function aead_xchacha20poly1305_ietf_encrypt(
$message = '',
$ad = '',
$nonce = '',
$key = ''
) {
$subkey = ParagonIE_Sodium_Core_HChaCha20::hChaCha20(
ParagonIE_Sodium_Core_Util::substr($nonce, 0, 16),
$key
);
$nonceLast = "\x00\x00\x00\x00" .
ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8);
return self::aead_chacha20poly1305_ietf_encrypt($message, $ad, $nonceLast, $subkey);
}
/**
* HMAC-SHA-512-256 (a.k.a. the leftmost 256 bits of HMAC-SHA-512)
*
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
*
* @param string $message
* @param string $key
* @return string
* @throws TypeError
*/
public static function auth($message, $key)
{
return ParagonIE_Sodium_Core_Util::substr(
hash_hmac('sha512', $message, $key, true),
0,
32
);
}
/**
* HMAC-SHA-512-256 validation. Constant-time via hash_equals().
*
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
*
* @param string $mac
* @param string $message
* @param string $key
* @return bool
* @throws SodiumException
* @throws TypeError
*/
public static function auth_verify($mac, $message, $key)
{
return ParagonIE_Sodium_Core_Util::hashEquals(
$mac,
self::auth($message, $key)
);
}
/**
* X25519 key exchange followed by XSalsa20Poly1305 symmetric encryption
*
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
*
* @param string $plaintext
* @param string $nonce
* @param string $keypair
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function box($plaintext, $nonce, $keypair)
{
$c = self::secretbox(
$plaintext,
$nonce,
self::box_beforenm(
self::box_secretkey($keypair),
self::box_publickey($keypair)
)
);
return $c;
}
/**
* X25519-XSalsa20-Poly1305 with one ephemeral X25519 keypair.
*
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
*
* @param string $message
* @param string $publicKey
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function box_seal($message, $publicKey)
{
/** @var string $ephemeralKeypair */
$ephemeralKeypair = self::box_keypair();
/** @var string $ephemeralSK */
$ephemeralSK = self::box_secretkey($ephemeralKeypair);
/** @var string $ephemeralPK */
$ephemeralPK = self::box_publickey($ephemeralKeypair);
/** @var string $nonce */
$nonce = self::generichash(
$ephemeralPK . $publicKey,
'',
24
);
/** @var string $keypair - The combined keypair used in crypto_box() */
$keypair = self::box_keypair_from_secretkey_and_publickey($ephemeralSK, $publicKey);
/** @var string $ciphertext Ciphertext + MAC from crypto_box */
$ciphertext = self::box($message, $nonce, $keypair);
try {
ParagonIE_Sodium_Compat::memzero($ephemeralKeypair);
ParagonIE_Sodium_Compat::memzero($ephemeralSK);
ParagonIE_Sodium_Compat::memzero($nonce);
} catch (SodiumException $ex) {
$ephemeralKeypair = null;
$ephemeralSK = null;
$nonce = null;
}
return $ephemeralPK . $ciphertext;
}
/**
* Opens a message encrypted via box_seal().
*
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
*
* @param string $message
* @param string $keypair
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function box_seal_open($message, $keypair)
{
/** @var string $ephemeralPK */
$ephemeralPK = ParagonIE_Sodium_Core_Util::substr($message, 0, 32);
/** @var string $ciphertext (ciphertext + MAC) */
$ciphertext = ParagonIE_Sodium_Core_Util::substr($message, 32);
/** @var string $secretKey */
$secretKey = self::box_secretkey($keypair);
/** @var string $publicKey */
$publicKey = self::box_publickey($keypair);
/** @var string $nonce */
$nonce = self::generichash(
$ephemeralPK . $publicKey,
'',
24
);
/** @var string $keypair */
$keypair = self::box_keypair_from_secretkey_and_publickey($secretKey, $ephemeralPK);
/** @var string $m */
$m = self::box_open($ciphertext, $nonce, $keypair);
try {
ParagonIE_Sodium_Compat::memzero($secretKey);
ParagonIE_Sodium_Compat::memzero($ephemeralPK);
ParagonIE_Sodium_Compat::memzero($nonce);
} catch (SodiumException $ex) {
$secretKey = null;
$ephemeralPK = null;
$nonce = null;
}
return $m;
}
/**
* Used by crypto_box() to get the crypto_secretbox() key.
*
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
*
* @param string $sk
* @param string $pk
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function box_beforenm($sk, $pk)
{
return ParagonIE_Sodium_Core_HSalsa20::hsalsa20(
str_repeat("\x00", 16),
self::scalarmult($sk, $pk)
);
}
/**
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
*
* @return string
* @throws Exception
* @throws SodiumException
* @throws TypeError
*/
public static function box_keypair()
{
$sKey = random_bytes(32);
$pKey = self::scalarmult_base($sKey);
return $sKey . $pKey;
}
/**
* @param string $seed
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function box_seed_keypair($seed)
{
$sKey = ParagonIE_Sodium_Core_Util::substr(
hash('sha512', $seed, true),
0,
32
);
$pKey = self::scalarmult_base($sKey);
return $sKey . $pKey;
}
/**
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
*
* @param string $sKey
* @param string $pKey
* @return string
* @throws TypeError
*/
public static function box_keypair_from_secretkey_and_publickey($sKey, $pKey)
{
return ParagonIE_Sodium_Core_Util::substr($sKey, 0, 32) .
ParagonIE_Sodium_Core_Util::substr($pKey, 0, 32);
}
/**
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
*
* @param string $keypair
* @return string
* @throws RangeException
* @throws TypeError
*/
public static function box_secretkey($keypair)
{
if (ParagonIE_Sodium_Core_Util::strlen($keypair) !== 64) {
throw new RangeException(
'Must be ParagonIE_Sodium_Compat::CRYPTO_BOX_KEYPAIRBYTES bytes long.'
);
}
return ParagonIE_Sodium_Core_Util::substr($keypair, 0, 32);
}
/**
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
*
* @param string $keypair
* @return string
* @throws RangeException
* @throws TypeError
*/
public static function box_publickey($keypair)
{
if (ParagonIE_Sodium_Core_Util::strlen($keypair) !== ParagonIE_Sodium_Compat::CRYPTO_BOX_KEYPAIRBYTES) {
throw new RangeException(
'Must be ParagonIE_Sodium_Compat::CRYPTO_BOX_KEYPAIRBYTES bytes long.'
);
}
return ParagonIE_Sodium_Core_Util::substr($keypair, 32, 32);
}
/**
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
*
* @param string $sKey
* @return string
* @throws RangeException
* @throws SodiumException
* @throws TypeError
*/
public static function box_publickey_from_secretkey($sKey)
{
if (ParagonIE_Sodium_Core_Util::strlen($sKey) !== ParagonIE_Sodium_Compat::CRYPTO_BOX_SECRETKEYBYTES) {
throw new RangeException(
'Must be ParagonIE_Sodium_Compat::CRYPTO_BOX_SECRETKEYBYTES bytes long.'
);
}
return self::scalarmult_base($sKey);
}
/**
* Decrypt a message encrypted with box().
*
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
*
* @param string $ciphertext
* @param string $nonce
* @param string $keypair
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function box_open($ciphertext, $nonce, $keypair)
{
return self::secretbox_open(
$ciphertext,
$nonce,
self::box_beforenm(
self::box_secretkey($keypair),
self::box_publickey($keypair)
)
);
}
/**
* Calculate a BLAKE2b hash.
*
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
*
* @param string $message
* @param string|null $key
* @param int $outlen
* @return string
* @throws RangeException
* @throws SodiumException
* @throws TypeError
*/
public static function generichash($message, $key = '', $outlen = 32)
{
// This ensures that ParagonIE_Sodium_Core_BLAKE2b::$iv is initialized
ParagonIE_Sodium_Core_BLAKE2b::pseudoConstructor();
$k = null;
if (!empty($key)) {
/** @var SplFixedArray $k */
$k = ParagonIE_Sodium_Core_BLAKE2b::stringToSplFixedArray($key);
if ($k->count() > ParagonIE_Sodium_Core_BLAKE2b::KEYBYTES) {
throw new RangeException('Invalid key size');
}
}
/** @var SplFixedArray $in */
$in = ParagonIE_Sodium_Core_BLAKE2b::stringToSplFixedArray($message);
/** @var SplFixedArray $ctx */
$ctx = ParagonIE_Sodium_Core_BLAKE2b::init($k, $outlen);
ParagonIE_Sodium_Core_BLAKE2b::update($ctx, $in, $in->count());
/** @var SplFixedArray $out */
$out = new SplFixedArray($outlen);
$out = ParagonIE_Sodium_Core_BLAKE2b::finish($ctx, $out);
/** @var array<int, int> */
$outArray = $out->toArray();
return ParagonIE_Sodium_Core_Util::intArrayToString($outArray);
}
/**
* Finalize a BLAKE2b hashing context, returning the hash.
*
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
*
* @param string $ctx
* @param int $outlen
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function generichash_final($ctx, $outlen = 32)
{
if (!is_string($ctx)) {
throw new TypeError('Context must be a string');
}
$out = new SplFixedArray($outlen);
/** @var SplFixedArray $context */
$context = ParagonIE_Sodium_Core_BLAKE2b::stringToContext($ctx);
/** @var SplFixedArray $out */
$out = ParagonIE_Sodium_Core_BLAKE2b::finish($context, $out);
/** @var array<int, int> */
$outArray = $out->toArray();
return ParagonIE_Sodium_Core_Util::intArrayToString($outArray);
}
/**
* Initialize a hashing context for BLAKE2b.
*
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
*
* @param string $key
* @param int $outputLength
* @return string
* @throws RangeException
* @throws SodiumException
* @throws TypeError
*/
public static function generichash_init($key = '', $outputLength = 32)
{
// This ensures that ParagonIE_Sodium_Core_BLAKE2b::$iv is initialized
ParagonIE_Sodium_Core_BLAKE2b::pseudoConstructor();
$k = null;
if (!empty($key)) {
$k = ParagonIE_Sodium_Core_BLAKE2b::stringToSplFixedArray($key);
if ($k->count() > ParagonIE_Sodium_Core_BLAKE2b::KEYBYTES) {
throw new RangeException('Invalid key size');
}
}
/** @var SplFixedArray $ctx */
$ctx = ParagonIE_Sodium_Core_BLAKE2b::init($k, $outputLength);
return ParagonIE_Sodium_Core_BLAKE2b::contextToString($ctx);
}
/**
* Initialize a hashing context for BLAKE2b.
*
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
*
* @param string $key
* @param int $outputLength
* @param string $salt
* @param string $personal
* @return string
* @throws RangeException
* @throws SodiumException
* @throws TypeError
*/
public static function generichash_init_salt_personal(
$key = '',
$outputLength = 32,
$salt = '',
$personal = ''
) {
// This ensures that ParagonIE_Sodium_Core_BLAKE2b::$iv is initialized
ParagonIE_Sodium_Core_BLAKE2b::pseudoConstructor();
$k = null;
if (!empty($key)) {
$k = ParagonIE_Sodium_Core_BLAKE2b::stringToSplFixedArray($key);
if ($k->count() > ParagonIE_Sodium_Core_BLAKE2b::KEYBYTES) {
throw new RangeException('Invalid key size');
}
}
if (!empty($salt)) {
$s = ParagonIE_Sodium_Core_BLAKE2b::stringToSplFixedArray($salt);
} else {
$s = null;
}
if (!empty($salt)) {
$p = ParagonIE_Sodium_Core_BLAKE2b::stringToSplFixedArray($personal);
} else {
$p = null;
}
/** @var SplFixedArray $ctx */
$ctx = ParagonIE_Sodium_Core_BLAKE2b::init($k, $outputLength, $s, $p);
return ParagonIE_Sodium_Core_BLAKE2b::contextToString($ctx);
}
/**
* Update a hashing context for BLAKE2b with $message
*
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
*
* @param string $ctx
* @param string $message
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function generichash_update($ctx, $message)
{
// This ensures that ParagonIE_Sodium_Core_BLAKE2b::$iv is initialized
ParagonIE_Sodium_Core_BLAKE2b::pseudoConstructor();
/** @var SplFixedArray $context */
$context = ParagonIE_Sodium_Core_BLAKE2b::stringToContext($ctx);
/** @var SplFixedArray $in */
$in = ParagonIE_Sodium_Core_BLAKE2b::stringToSplFixedArray($message);
ParagonIE_Sodium_Core_BLAKE2b::update($context, $in, $in->count());
return ParagonIE_Sodium_Core_BLAKE2b::contextToString($context);
}
/**
* Libsodium's crypto_kx().
*
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
*
* @param string $my_sk
* @param string $their_pk
* @param string $client_pk
* @param string $server_pk
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function keyExchange($my_sk, $their_pk, $client_pk, $server_pk)
{
return ParagonIE_Sodium_Compat::crypto_generichash(
ParagonIE_Sodium_Compat::crypto_scalarmult($my_sk, $their_pk) .
$client_pk .
$server_pk
);
}
/**
* ECDH over Curve25519
*
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
*
* @param string $sKey
* @param string $pKey
* @return string
*
* @throws SodiumException
* @throws TypeError
*/
public static function scalarmult($sKey, $pKey)
{
$q = ParagonIE_Sodium_Core_X25519::crypto_scalarmult_curve25519_ref10($sKey, $pKey);
self::scalarmult_throw_if_zero($q);
return $q;
}
/**
* ECDH over Curve25519, using the basepoint.
* Used to get a secret key from a public key.
*
* @param string $secret
* @return string
*
* @throws SodiumException
* @throws TypeError
*/
public static function scalarmult_base($secret)
{
$q = ParagonIE_Sodium_Core_X25519::crypto_scalarmult_curve25519_ref10_base($secret);
self::scalarmult_throw_if_zero($q);
return $q;
}
/**
* This throws an Error if a zero public key was passed to the function.
*
* @param string $q
* @return void
* @throws SodiumException
* @throws TypeError
*/
protected static function scalarmult_throw_if_zero($q)
{
$d = 0;
for ($i = 0; $i < self::box_curve25519xsalsa20poly1305_SECRETKEYBYTES; ++$i) {
$d |= ParagonIE_Sodium_Core_Util::chrToInt($q[$i]);
}
/* branch-free variant of === 0 */
if (-(1 & (($d - 1) >> 8))) {
throw new SodiumException('Zero public key is not allowed');
}
}
/**
* XSalsa20-Poly1305 authenticated symmetric-key encryption.
*
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
*
* @param string $plaintext
* @param string $nonce
* @param string $key
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function secretbox($plaintext, $nonce, $key)
{
/** @var string $subkey */
$subkey = ParagonIE_Sodium_Core_HSalsa20::hsalsa20($nonce, $key);
/** @var string $block0 */
$block0 = str_repeat("\x00", 32);
/** @var int $mlen - Length of the plaintext message */
$mlen = ParagonIE_Sodium_Core_Util::strlen($plaintext);
$mlen0 = $mlen;
if ($mlen0 > 64 - self::secretbox_xsalsa20poly1305_ZEROBYTES) {
$mlen0 = 64 - self::secretbox_xsalsa20poly1305_ZEROBYTES;
}
$block0 .= ParagonIE_Sodium_Core_Util::substr($plaintext, 0, $mlen0);
/** @var string $block0 */
$block0 = ParagonIE_Sodium_Core_Salsa20::salsa20_xor(
$block0,
ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8),
$subkey
);
/** @var string $c */
$c = ParagonIE_Sodium_Core_Util::substr(
$block0,
self::secretbox_xsalsa20poly1305_ZEROBYTES
);
if ($mlen > $mlen0) {
$c .= ParagonIE_Sodium_Core_Salsa20::salsa20_xor_ic(
ParagonIE_Sodium_Core_Util::substr(
$plaintext,
self::secretbox_xsalsa20poly1305_ZEROBYTES
),
ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8),
1,
$subkey
);
}
$state = new ParagonIE_Sodium_Core_Poly1305_State(
ParagonIE_Sodium_Core_Util::substr(
$block0,
0,
self::onetimeauth_poly1305_KEYBYTES
)
);
try {
ParagonIE_Sodium_Compat::memzero($block0);
ParagonIE_Sodium_Compat::memzero($subkey);
} catch (SodiumException $ex) {
$block0 = null;
$subkey = null;
}
$state->update($c);
/** @var string $c - MAC || ciphertext */
$c = $state->finish() . $c;
unset($state);
return $c;
}
/**
* Decrypt a ciphertext generated via secretbox().
*
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
*
* @param string $ciphertext
* @param string $nonce
* @param string $key
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function secretbox_open($ciphertext, $nonce, $key)
{
/** @var string $mac */
$mac = ParagonIE_Sodium_Core_Util::substr(
$ciphertext,
0,
self::secretbox_xsalsa20poly1305_MACBYTES
);
/** @var string $c */
$c = ParagonIE_Sodium_Core_Util::substr(
$ciphertext,
self::secretbox_xsalsa20poly1305_MACBYTES
);
/** @var int $clen */
$clen = ParagonIE_Sodium_Core_Util::strlen($c);
/** @var string $subkey */
$subkey = ParagonIE_Sodium_Core_HSalsa20::hsalsa20($nonce, $key);
/** @var string $block0 */
$block0 = ParagonIE_Sodium_Core_Salsa20::salsa20(
64,
ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8),
$subkey
);
$verified = ParagonIE_Sodium_Core_Poly1305::onetimeauth_verify(
$mac,
$c,
ParagonIE_Sodium_Core_Util::substr($block0, 0, 32)
);
if (!$verified) {
try {
ParagonIE_Sodium_Compat::memzero($subkey);
} catch (SodiumException $ex) {
$subkey = null;
}
throw new SodiumException('Invalid MAC');
}
/** @var string $m - Decrypted message */
$m = ParagonIE_Sodium_Core_Util::xorStrings(
ParagonIE_Sodium_Core_Util::substr($block0, self::secretbox_xsalsa20poly1305_ZEROBYTES),
ParagonIE_Sodium_Core_Util::substr($c, 0, self::secretbox_xsalsa20poly1305_ZEROBYTES)
);
if ($clen > self::secretbox_xsalsa20poly1305_ZEROBYTES) {
// We had more than 1 block, so let's continue to decrypt the rest.
$m .= ParagonIE_Sodium_Core_Salsa20::salsa20_xor_ic(
ParagonIE_Sodium_Core_Util::substr(
$c,
self::secretbox_xsalsa20poly1305_ZEROBYTES
),
ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8),
1,
(string) $subkey
);
}
return $m;
}
/**
* XChaCha20-Poly1305 authenticated symmetric-key encryption.
*
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
*
* @param string $plaintext
* @param string $nonce
* @param string $key
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function secretbox_xchacha20poly1305($plaintext, $nonce, $key)
{
/** @var string $subkey */
$subkey = ParagonIE_Sodium_Core_HChaCha20::hChaCha20(
ParagonIE_Sodium_Core_Util::substr($nonce, 0, 16),
$key
);
$nonceLast = ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8);
/** @var string $block0 */
$block0 = str_repeat("\x00", 32);
/** @var int $mlen - Length of the plaintext message */
$mlen = ParagonIE_Sodium_Core_Util::strlen($plaintext);
$mlen0 = $mlen;
if ($mlen0 > 64 - self::secretbox_xchacha20poly1305_ZEROBYTES) {
$mlen0 = 64 - self::secretbox_xchacha20poly1305_ZEROBYTES;
}
$block0 .= ParagonIE_Sodium_Core_Util::substr($plaintext, 0, $mlen0);
/** @var string $block0 */
$block0 = ParagonIE_Sodium_Core_ChaCha20::streamXorIc(
$block0,
$nonceLast,
$subkey
);
/** @var string $c */
$c = ParagonIE_Sodium_Core_Util::substr(
$block0,
self::secretbox_xchacha20poly1305_ZEROBYTES
);
if ($mlen > $mlen0) {
$c .= ParagonIE_Sodium_Core_ChaCha20::streamXorIc(
ParagonIE_Sodium_Core_Util::substr(
$plaintext,
self::secretbox_xchacha20poly1305_ZEROBYTES
),
$nonceLast,
$subkey,
ParagonIE_Sodium_Core_Util::store64_le(1)
);
}
$state = new ParagonIE_Sodium_Core_Poly1305_State(
ParagonIE_Sodium_Core_Util::substr(
$block0,
0,
self::onetimeauth_poly1305_KEYBYTES
)
);
try {
ParagonIE_Sodium_Compat::memzero($block0);
ParagonIE_Sodium_Compat::memzero($subkey);
} catch (SodiumException $ex) {
$block0 = null;
$subkey = null;
}
$state->update($c);
/** @var string $c - MAC || ciphertext */
$c = $state->finish() . $c;
unset($state);
return $c;
}
/**
* Decrypt a ciphertext generated via secretbox_xchacha20poly1305().
*
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
*
* @param string $ciphertext
* @param string $nonce
* @param string $key
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function secretbox_xchacha20poly1305_open($ciphertext, $nonce, $key)
{
/** @var string $mac */
$mac = ParagonIE_Sodium_Core_Util::substr(
$ciphertext,
0,
self::secretbox_xchacha20poly1305_MACBYTES
);
/** @var string $c */
$c = ParagonIE_Sodium_Core_Util::substr(
$ciphertext,
self::secretbox_xchacha20poly1305_MACBYTES
);
/** @var int $clen */
$clen = ParagonIE_Sodium_Core_Util::strlen($c);
/** @var string $subkey */
$subkey = ParagonIE_Sodium_Core_HChaCha20::hchacha20($nonce, $key);
/** @var string $block0 */
$block0 = ParagonIE_Sodium_Core_ChaCha20::stream(
64,
ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8),
$subkey
);
$verified = ParagonIE_Sodium_Core_Poly1305::onetimeauth_verify(
$mac,
$c,
ParagonIE_Sodium_Core_Util::substr($block0, 0, 32)
);
if (!$verified) {
try {
ParagonIE_Sodium_Compat::memzero($subkey);
} catch (SodiumException $ex) {
$subkey = null;
}
throw new SodiumException('Invalid MAC');
}
/** @var string $m - Decrypted message */
$m = ParagonIE_Sodium_Core_Util::xorStrings(
ParagonIE_Sodium_Core_Util::substr($block0, self::secretbox_xchacha20poly1305_ZEROBYTES),
ParagonIE_Sodium_Core_Util::substr($c, 0, self::secretbox_xchacha20poly1305_ZEROBYTES)
);
if ($clen > self::secretbox_xchacha20poly1305_ZEROBYTES) {
// We had more than 1 block, so let's continue to decrypt the rest.
$m .= ParagonIE_Sodium_Core_ChaCha20::streamXorIc(
ParagonIE_Sodium_Core_Util::substr(
$c,
self::secretbox_xchacha20poly1305_ZEROBYTES
),
ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8),
(string) $subkey,
ParagonIE_Sodium_Core_Util::store64_le(1)
);
}
return $m;
}
/**
* @param string $key
* @return array<int, string> Returns a state and a header.
* @throws Exception
* @throws SodiumException
*/
public static function secretstream_xchacha20poly1305_init_push($key)
{
# randombytes_buf(out, crypto_secretstream_xchacha20poly1305_HEADERBYTES);
$out = random_bytes(24);
# crypto_core_hchacha20(state->k, out, k, NULL);
$subkey = ParagonIE_Sodium_Core_HChaCha20::hChaCha20($out, $key);
$state = new ParagonIE_Sodium_Core_SecretStream_State(
$subkey,
ParagonIE_Sodium_Core_Util::substr($out, 16, 8) . str_repeat("\0", 4)
);
# _crypto_secretstream_xchacha20poly1305_counter_reset(state);
$state->counterReset();
# memcpy(STATE_INONCE(state), out + crypto_core_hchacha20_INPUTBYTES,
# crypto_secretstream_xchacha20poly1305_INONCEBYTES);
# memset(state->_pad, 0, sizeof state->_pad);
return array(
$state->toString(),
$out
);
}
/**
* @param string $key
* @param string $header
* @return string Returns a state.
* @throws Exception
*/
public static function secretstream_xchacha20poly1305_init_pull($key, $header)
{
# crypto_core_hchacha20(state->k, in, k, NULL);
$subkey = ParagonIE_Sodium_Core_HChaCha20::hChaCha20(
ParagonIE_Sodium_Core_Util::substr($header, 0, 16),
$key
);
$state = new ParagonIE_Sodium_Core_SecretStream_State(
$subkey,
ParagonIE_Sodium_Core_Util::substr($header, 16)
);
$state->counterReset();
# memcpy(STATE_INONCE(state), in + crypto_core_hchacha20_INPUTBYTES,
# crypto_secretstream_xchacha20poly1305_INONCEBYTES);
# memset(state->_pad, 0, sizeof state->_pad);
# return 0;
return $state->toString();
}
/**
* @param string $state
* @param string $msg
* @param string $aad
* @param int $tag
* @return string
* @throws SodiumException
*/
public static function secretstream_xchacha20poly1305_push(&$state, $msg, $aad = '', $tag = 0)
{
$st = ParagonIE_Sodium_Core_SecretStream_State::fromString($state);
# crypto_onetimeauth_poly1305_state poly1305_state;
# unsigned char block[64U];
# unsigned char slen[8U];
# unsigned char *c;
# unsigned char *mac;
$msglen = ParagonIE_Sodium_Core_Util::strlen($msg);
$aadlen = ParagonIE_Sodium_Core_Util::strlen($aad);
if ((($msglen + 63) >> 6) > 0xfffffffe) {
throw new SodiumException(
'message cannot be larger than SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_MESSAGEBYTES_MAX bytes'
);
}
# if (outlen_p != NULL) {
# *outlen_p = 0U;
# }
# if (mlen > crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX) {
# sodium_misuse();
# }
# crypto_stream_chacha20_ietf(block, sizeof block, state->nonce, state->k);
# crypto_onetimeauth_poly1305_init(&poly1305_state, block);
# sodium_memzero(block, sizeof block);
$auth = new ParagonIE_Sodium_Core_Poly1305_State(
ParagonIE_Sodium_Core_ChaCha20::ietfStream(32, $st->getCombinedNonce(), $st->getKey())
);
# crypto_onetimeauth_poly1305_update(&poly1305_state, ad, adlen);
$auth->update($aad);
# crypto_onetimeauth_poly1305_update(&poly1305_state, _pad0,
# (0x10 - adlen) & 0xf);
$auth->update(str_repeat("\0", ((0x10 - $aadlen) & 0xf)));
# memset(block, 0, sizeof block);
# block[0] = tag;
# crypto_stream_chacha20_ietf_xor_ic(block, block, sizeof block,
# state->nonce, 1U, state->k);
$block = ParagonIE_Sodium_Core_ChaCha20::ietfStreamXorIc(
ParagonIE_Sodium_Core_Util::intToChr($tag) . str_repeat("\0", 63),
$st->getCombinedNonce(),
$st->getKey(),
ParagonIE_Sodium_Core_Util::store64_le(1)
);
# crypto_onetimeauth_poly1305_update(&poly1305_state, block, sizeof block);
$auth->update($block);
# out[0] = block[0];
$out = $block[0];
# c = out + (sizeof tag);
# crypto_stream_chacha20_ietf_xor_ic(c, m, mlen, state->nonce, 2U, state->k);
$cipher = ParagonIE_Sodium_Core_ChaCha20::ietfStreamXorIc(
$msg,
$st->getCombinedNonce(),
$st->getKey(),
ParagonIE_Sodium_Core_Util::store64_le(2)
);
# crypto_onetimeauth_poly1305_update(&poly1305_state, c, mlen);
$auth->update($cipher);
$out .= $cipher;
unset($cipher);
# crypto_onetimeauth_poly1305_update
# (&poly1305_state, _pad0, (0x10 - (sizeof block) + mlen) & 0xf);
$auth->update(str_repeat("\0", ((0x10 - 64 + $msglen) & 0xf)));
# STORE64_LE(slen, (uint64_t) adlen);
$slen = ParagonIE_Sodium_Core_Util::store64_le($aadlen);
# crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen);
$auth->update($slen);
# STORE64_LE(slen, (sizeof block) + mlen);
$slen = ParagonIE_Sodium_Core_Util::store64_le(64 + $msglen);
# crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen);
$auth->update($slen);
# mac = c + mlen;
# crypto_onetimeauth_poly1305_final(&poly1305_state, mac);
$mac = $auth->finish();
$out .= $mac;
# sodium_memzero(&poly1305_state, sizeof poly1305_state);
unset($auth);
# XOR_BUF(STATE_INONCE(state), mac,
# crypto_secretstream_xchacha20poly1305_INONCEBYTES);
$st->xorNonce($mac);
# sodium_increment(STATE_COUNTER(state),
# crypto_secretstream_xchacha20poly1305_COUNTERBYTES);
$st->incrementCounter();
// Overwrite by reference:
$state = $st->toString();
/** @var bool $rekey */
$rekey = ($tag & ParagonIE_Sodium_Compat::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_REKEY) !== 0;
# if ((tag & crypto_secretstream_xchacha20poly1305_TAG_REKEY) != 0 ||
# sodium_is_zero(STATE_COUNTER(state),
# crypto_secretstream_xchacha20poly1305_COUNTERBYTES)) {
# crypto_secretstream_xchacha20poly1305_rekey(state);
# }
if ($rekey || $st->needsRekey()) {
// DO REKEY
self::secretstream_xchacha20poly1305_rekey($state);
}
# if (outlen_p != NULL) {
# *outlen_p = crypto_secretstream_xchacha20poly1305_ABYTES + mlen;
# }
return $out;
}
/**
* @param string $state
* @param string $cipher
* @param string $aad
* @return bool|array{0: string, 1: int}
* @throws SodiumException
*/
public static function secretstream_xchacha20poly1305_pull(&$state, $cipher, $aad = '')
{
$st = ParagonIE_Sodium_Core_SecretStream_State::fromString($state);
$cipherlen = ParagonIE_Sodium_Core_Util::strlen($cipher);
# mlen = inlen - crypto_secretstream_xchacha20poly1305_ABYTES;
$msglen = $cipherlen - ParagonIE_Sodium_Compat::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_ABYTES;
$aadlen = ParagonIE_Sodium_Core_Util::strlen($aad);
# if (mlen > crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX) {
# sodium_misuse();
# }
if ((($msglen + 63) >> 6) > 0xfffffffe) {
throw new SodiumException(
'message cannot be larger than SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_MESSAGEBYTES_MAX bytes'
);
}
# crypto_stream_chacha20_ietf(block, sizeof block, state->nonce, state->k);
# crypto_onetimeauth_poly1305_init(&poly1305_state, block);
# sodium_memzero(block, sizeof block);
$auth = new ParagonIE_Sodium_Core_Poly1305_State(
ParagonIE_Sodium_Core_ChaCha20::ietfStream(32, $st->getCombinedNonce(), $st->getKey())
);
# crypto_onetimeauth_poly1305_update(&poly1305_state, ad, adlen);
$auth->update($aad);
# crypto_onetimeauth_poly1305_update(&poly1305_state, _pad0,
# (0x10 - adlen) & 0xf);
$auth->update(str_repeat("\0", ((0x10 - $aadlen) & 0xf)));
# memset(block, 0, sizeof block);
# block[0] = in[0];
# crypto_stream_chacha20_ietf_xor_ic(block, block, sizeof block,
# state->nonce, 1U, state->k);
$block = ParagonIE_Sodium_Core_ChaCha20::ietfStreamXorIc(
$cipher[0] . str_repeat("\0", 63),
$st->getCombinedNonce(),
$st->getKey(),
ParagonIE_Sodium_Core_Util::store64_le(1)
);
# tag = block[0];
# block[0] = in[0];
# crypto_onetimeauth_poly1305_update(&poly1305_state, block, sizeof block);
$tag = ParagonIE_Sodium_Core_Util::chrToInt($block[0]);
$block[0] = $cipher[0];
$auth->update($block);
# c = in + (sizeof tag);
# crypto_onetimeauth_poly1305_update(&poly1305_state, c, mlen);
$auth->update(ParagonIE_Sodium_Core_Util::substr($cipher, 1, $msglen));
# crypto_onetimeauth_poly1305_update
# (&poly1305_state, _pad0, (0x10 - (sizeof block) + mlen) & 0xf);
$auth->update(str_repeat("\0", ((0x10 - 64 + $msglen) & 0xf)));
# STORE64_LE(slen, (uint64_t) adlen);
# crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen);
$slen = ParagonIE_Sodium_Core_Util::store64_le($aadlen);
$auth->update($slen);
# STORE64_LE(slen, (sizeof block) + mlen);
# crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen);
$slen = ParagonIE_Sodium_Core_Util::store64_le(64 + $msglen);
$auth->update($slen);
# crypto_onetimeauth_poly1305_final(&poly1305_state, mac);
# sodium_memzero(&poly1305_state, sizeof poly1305_state);
$mac = $auth->finish();
# stored_mac = c + mlen;
# if (sodium_memcmp(mac, stored_mac, sizeof mac) != 0) {
# sodium_memzero(mac, sizeof mac);
# return -1;
# }
$stored = ParagonIE_Sodium_Core_Util::substr($cipher, $msglen + 1, 16);
if (!ParagonIE_Sodium_Core_Util::hashEquals($mac, $stored)) {
return false;
}
# crypto_stream_chacha20_ietf_xor_ic(m, c, mlen, state->nonce, 2U, state->k);
$out = ParagonIE_Sodium_Core_ChaCha20::ietfStreamXorIc(
ParagonIE_Sodium_Core_Util::substr($cipher, 1, $msglen),
$st->getCombinedNonce(),
$st->getKey(),
ParagonIE_Sodium_Core_Util::store64_le(2)
);
# XOR_BUF(STATE_INONCE(state), mac,
# crypto_secretstream_xchacha20poly1305_INONCEBYTES);
$st->xorNonce($mac);
# sodium_increment(STATE_COUNTER(state),
# crypto_secretstream_xchacha20poly1305_COUNTERBYTES);
$st->incrementCounter();
# if ((tag & crypto_secretstream_xchacha20poly1305_TAG_REKEY) != 0 ||
# sodium_is_zero(STATE_COUNTER(state),
# crypto_secretstream_xchacha20poly1305_COUNTERBYTES)) {
# crypto_secretstream_xchacha20poly1305_rekey(state);
# }
// Overwrite by reference:
$state = $st->toString();
/** @var bool $rekey */
$rekey = ($tag & ParagonIE_Sodium_Compat::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_REKEY) !== 0;
if ($rekey || $st->needsRekey()) {
// DO REKEY
self::secretstream_xchacha20poly1305_rekey($state);
}
return array($out, $tag);
}
/**
* @param string $state
* @return void
* @throws SodiumException
*/
public static function secretstream_xchacha20poly1305_rekey(&$state)
{
$st = ParagonIE_Sodium_Core_SecretStream_State::fromString($state);
# unsigned char new_key_and_inonce[crypto_stream_chacha20_ietf_KEYBYTES +
# crypto_secretstream_xchacha20poly1305_INONCEBYTES];
# size_t i;
# for (i = 0U; i < crypto_stream_chacha20_ietf_KEYBYTES; i++) {
# new_key_and_inonce[i] = state->k[i];
# }
$new_key_and_inonce = $st->getKey();
# for (i = 0U; i < crypto_secretstream_xchacha20poly1305_INONCEBYTES; i++) {
# new_key_and_inonce[crypto_stream_chacha20_ietf_KEYBYTES + i] =
# STATE_INONCE(state)[i];
# }
$new_key_and_inonce .= ParagonIE_Sodium_Core_Util::substR($st->getNonce(), 0, 8);
# crypto_stream_chacha20_ietf_xor(new_key_and_inonce, new_key_and_inonce,
# sizeof new_key_and_inonce,
# state->nonce, state->k);
$st->rekey(ParagonIE_Sodium_Core_ChaCha20::ietfStreamXorIc(
$new_key_and_inonce,
$st->getCombinedNonce(),
$st->getKey(),
ParagonIE_Sodium_Core_Util::store64_le(0)
));
# for (i = 0U; i < crypto_stream_chacha20_ietf_KEYBYTES; i++) {
# state->k[i] = new_key_and_inonce[i];
# }
# for (i = 0U; i < crypto_secretstream_xchacha20poly1305_INONCEBYTES; i++) {
# STATE_INONCE(state)[i] =
# new_key_and_inonce[crypto_stream_chacha20_ietf_KEYBYTES + i];
# }
# _crypto_secretstream_xchacha20poly1305_counter_reset(state);
$st->counterReset();
$state = $st->toString();
}
/**
* Detached Ed25519 signature.
*
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
*
* @param string $message
* @param string $sk
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function sign_detached($message, $sk)
{
return ParagonIE_Sodium_Core_Ed25519::sign_detached($message, $sk);
}
/**
* Attached Ed25519 signature. (Returns a signed message.)
*
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
*
* @param string $message
* @param string $sk
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function sign($message, $sk)
{
return ParagonIE_Sodium_Core_Ed25519::sign($message, $sk);
}
/**
* Opens a signed message. If valid, returns the message.
*
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
*
* @param string $signedMessage
* @param string $pk
* @return string
* @throws SodiumException
* @throws TypeError
*/
public static function sign_open($signedMessage, $pk)
{
return ParagonIE_Sodium_Core_Ed25519::sign_open($signedMessage, $pk);
}
/**
* Verify a detached signature of a given message and public key.
*
* @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
*
* @param string $signature
* @param string $message
* @param string $pk
* @return bool
* @throws SodiumException
* @throws TypeError
*/
public static function sign_verify_detached($signature, $message, $pk)
{
return ParagonIE_Sodium_Core_Ed25519::verify_detached($signature, $message, $pk);
}
}
Decode/HTML/Entities.php 0000644 00000041531 15133170420 0010770 0 ustar 00 <?php
/**
* SimplePie
*
* A PHP-Based RSS and Atom Feed Framework.
* Takes the hard work out of managing a complete RSS/Atom solution.
*
* Copyright (c) 2004-2016, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* * Neither the name of the SimplePie Team nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
* AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package SimplePie
* @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
* @author Ryan Parman
* @author Sam Sneddon
* @author Ryan McCue
* @link http://simplepie.org/ SimplePie
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
/**
* Decode HTML Entities
*
* This implements HTML5 as of revision 967 (2007-06-28)
*
* @deprecated Use DOMDocument instead!
* @package SimplePie
*/
class SimplePie_Decode_HTML_Entities
{
/**
* Data to be parsed
*
* @access private
* @var string
*/
var $data = '';
/**
* Currently consumed bytes
*
* @access private
* @var string
*/
var $consumed = '';
/**
* Position of the current byte being parsed
*
* @access private
* @var int
*/
var $position = 0;
/**
* Create an instance of the class with the input data
*
* @access public
* @param string $data Input data
*/
public function __construct($data)
{
$this->data = $data;
}
/**
* Parse the input data
*
* @access public
* @return string Output data
*/
public function parse()
{
while (($this->position = strpos($this->data, '&', $this->position)) !== false)
{
$this->consume();
$this->entity();
$this->consumed = '';
}
return $this->data;
}
/**
* Consume the next byte
*
* @access private
* @return mixed The next byte, or false, if there is no more data
*/
public function consume()
{
if (isset($this->data[$this->position]))
{
$this->consumed .= $this->data[$this->position];
return $this->data[$this->position++];
}
return false;
}
/**
* Consume a range of characters
*
* @access private
* @param string $chars Characters to consume
* @return mixed A series of characters that match the range, or false
*/
public function consume_range($chars)
{
if ($len = strspn($this->data, $chars, $this->position))
{
$data = substr($this->data, $this->position, $len);
$this->consumed .= $data;
$this->position += $len;
return $data;
}
return false;
}
/**
* Unconsume one byte
*
* @access private
*/
public function unconsume()
{
$this->consumed = substr($this->consumed, 0, -1);
$this->position--;
}
/**
* Decode an entity
*
* @access private
*/
public function entity()
{
switch ($this->consume())
{
case "\x09":
case "\x0A":
case "\x0B":
case "\x0C":
case "\x20":
case "\x3C":
case "\x26":
case false:
break;
case "\x23":
switch ($this->consume())
{
case "\x78":
case "\x58":
$range = '0123456789ABCDEFabcdef';
$hex = true;
break;
default:
$range = '0123456789';
$hex = false;
$this->unconsume();
break;
}
if ($codepoint = $this->consume_range($range))
{
static $windows_1252_specials = array(0x0D => "\x0A", 0x80 => "\xE2\x82\xAC", 0x81 => "\xEF\xBF\xBD", 0x82 => "\xE2\x80\x9A", 0x83 => "\xC6\x92", 0x84 => "\xE2\x80\x9E", 0x85 => "\xE2\x80\xA6", 0x86 => "\xE2\x80\xA0", 0x87 => "\xE2\x80\xA1", 0x88 => "\xCB\x86", 0x89 => "\xE2\x80\xB0", 0x8A => "\xC5\xA0", 0x8B => "\xE2\x80\xB9", 0x8C => "\xC5\x92", 0x8D => "\xEF\xBF\xBD", 0x8E => "\xC5\xBD", 0x8F => "\xEF\xBF\xBD", 0x90 => "\xEF\xBF\xBD", 0x91 => "\xE2\x80\x98", 0x92 => "\xE2\x80\x99", 0x93 => "\xE2\x80\x9C", 0x94 => "\xE2\x80\x9D", 0x95 => "\xE2\x80\xA2", 0x96 => "\xE2\x80\x93", 0x97 => "\xE2\x80\x94", 0x98 => "\xCB\x9C", 0x99 => "\xE2\x84\xA2", 0x9A => "\xC5\xA1", 0x9B => "\xE2\x80\xBA", 0x9C => "\xC5\x93", 0x9D => "\xEF\xBF\xBD", 0x9E => "\xC5\xBE", 0x9F => "\xC5\xB8");
if ($hex)
{
$codepoint = hexdec($codepoint);
}
else
{
$codepoint = intval($codepoint);
}
if (isset($windows_1252_specials[$codepoint]))
{
$replacement = $windows_1252_specials[$codepoint];
}
else
{
$replacement = SimplePie_Misc::codepoint_to_utf8($codepoint);
}
if (!in_array($this->consume(), array(';', false), true))
{
$this->unconsume();
}
$consumed_length = strlen($this->consumed);
$this->data = substr_replace($this->data, $replacement, $this->position - $consumed_length, $consumed_length);
$this->position += strlen($replacement) - $consumed_length;
}
break;
default:
static $entities = array(
'Aacute' => "\xC3\x81",
'aacute' => "\xC3\xA1",
'Aacute;' => "\xC3\x81",
'aacute;' => "\xC3\xA1",
'Acirc' => "\xC3\x82",
'acirc' => "\xC3\xA2",
'Acirc;' => "\xC3\x82",
'acirc;' => "\xC3\xA2",
'acute' => "\xC2\xB4",
'acute;' => "\xC2\xB4",
'AElig' => "\xC3\x86",
'aelig' => "\xC3\xA6",
'AElig;' => "\xC3\x86",
'aelig;' => "\xC3\xA6",
'Agrave' => "\xC3\x80",
'agrave' => "\xC3\xA0",
'Agrave;' => "\xC3\x80",
'agrave;' => "\xC3\xA0",
'alefsym;' => "\xE2\x84\xB5",
'Alpha;' => "\xCE\x91",
'alpha;' => "\xCE\xB1",
'AMP' => "\x26",
'amp' => "\x26",
'AMP;' => "\x26",
'amp;' => "\x26",
'and;' => "\xE2\x88\xA7",
'ang;' => "\xE2\x88\xA0",
'apos;' => "\x27",
'Aring' => "\xC3\x85",
'aring' => "\xC3\xA5",
'Aring;' => "\xC3\x85",
'aring;' => "\xC3\xA5",
'asymp;' => "\xE2\x89\x88",
'Atilde' => "\xC3\x83",
'atilde' => "\xC3\xA3",
'Atilde;' => "\xC3\x83",
'atilde;' => "\xC3\xA3",
'Auml' => "\xC3\x84",
'auml' => "\xC3\xA4",
'Auml;' => "\xC3\x84",
'auml;' => "\xC3\xA4",
'bdquo;' => "\xE2\x80\x9E",
'Beta;' => "\xCE\x92",
'beta;' => "\xCE\xB2",
'brvbar' => "\xC2\xA6",
'brvbar;' => "\xC2\xA6",
'bull;' => "\xE2\x80\xA2",
'cap;' => "\xE2\x88\xA9",
'Ccedil' => "\xC3\x87",
'ccedil' => "\xC3\xA7",
'Ccedil;' => "\xC3\x87",
'ccedil;' => "\xC3\xA7",
'cedil' => "\xC2\xB8",
'cedil;' => "\xC2\xB8",
'cent' => "\xC2\xA2",
'cent;' => "\xC2\xA2",
'Chi;' => "\xCE\xA7",
'chi;' => "\xCF\x87",
'circ;' => "\xCB\x86",
'clubs;' => "\xE2\x99\xA3",
'cong;' => "\xE2\x89\x85",
'COPY' => "\xC2\xA9",
'copy' => "\xC2\xA9",
'COPY;' => "\xC2\xA9",
'copy;' => "\xC2\xA9",
'crarr;' => "\xE2\x86\xB5",
'cup;' => "\xE2\x88\xAA",
'curren' => "\xC2\xA4",
'curren;' => "\xC2\xA4",
'Dagger;' => "\xE2\x80\xA1",
'dagger;' => "\xE2\x80\xA0",
'dArr;' => "\xE2\x87\x93",
'darr;' => "\xE2\x86\x93",
'deg' => "\xC2\xB0",
'deg;' => "\xC2\xB0",
'Delta;' => "\xCE\x94",
'delta;' => "\xCE\xB4",
'diams;' => "\xE2\x99\xA6",
'divide' => "\xC3\xB7",
'divide;' => "\xC3\xB7",
'Eacute' => "\xC3\x89",
'eacute' => "\xC3\xA9",
'Eacute;' => "\xC3\x89",
'eacute;' => "\xC3\xA9",
'Ecirc' => "\xC3\x8A",
'ecirc' => "\xC3\xAA",
'Ecirc;' => "\xC3\x8A",
'ecirc;' => "\xC3\xAA",
'Egrave' => "\xC3\x88",
'egrave' => "\xC3\xA8",
'Egrave;' => "\xC3\x88",
'egrave;' => "\xC3\xA8",
'empty;' => "\xE2\x88\x85",
'emsp;' => "\xE2\x80\x83",
'ensp;' => "\xE2\x80\x82",
'Epsilon;' => "\xCE\x95",
'epsilon;' => "\xCE\xB5",
'equiv;' => "\xE2\x89\xA1",
'Eta;' => "\xCE\x97",
'eta;' => "\xCE\xB7",
'ETH' => "\xC3\x90",
'eth' => "\xC3\xB0",
'ETH;' => "\xC3\x90",
'eth;' => "\xC3\xB0",
'Euml' => "\xC3\x8B",
'euml' => "\xC3\xAB",
'Euml;' => "\xC3\x8B",
'euml;' => "\xC3\xAB",
'euro;' => "\xE2\x82\xAC",
'exist;' => "\xE2\x88\x83",
'fnof;' => "\xC6\x92",
'forall;' => "\xE2\x88\x80",
'frac12' => "\xC2\xBD",
'frac12;' => "\xC2\xBD",
'frac14' => "\xC2\xBC",
'frac14;' => "\xC2\xBC",
'frac34' => "\xC2\xBE",
'frac34;' => "\xC2\xBE",
'frasl;' => "\xE2\x81\x84",
'Gamma;' => "\xCE\x93",
'gamma;' => "\xCE\xB3",
'ge;' => "\xE2\x89\xA5",
'GT' => "\x3E",
'gt' => "\x3E",
'GT;' => "\x3E",
'gt;' => "\x3E",
'hArr;' => "\xE2\x87\x94",
'harr;' => "\xE2\x86\x94",
'hearts;' => "\xE2\x99\xA5",
'hellip;' => "\xE2\x80\xA6",
'Iacute' => "\xC3\x8D",
'iacute' => "\xC3\xAD",
'Iacute;' => "\xC3\x8D",
'iacute;' => "\xC3\xAD",
'Icirc' => "\xC3\x8E",
'icirc' => "\xC3\xAE",
'Icirc;' => "\xC3\x8E",
'icirc;' => "\xC3\xAE",
'iexcl' => "\xC2\xA1",
'iexcl;' => "\xC2\xA1",
'Igrave' => "\xC3\x8C",
'igrave' => "\xC3\xAC",
'Igrave;' => "\xC3\x8C",
'igrave;' => "\xC3\xAC",
'image;' => "\xE2\x84\x91",
'infin;' => "\xE2\x88\x9E",
'int;' => "\xE2\x88\xAB",
'Iota;' => "\xCE\x99",
'iota;' => "\xCE\xB9",
'iquest' => "\xC2\xBF",
'iquest;' => "\xC2\xBF",
'isin;' => "\xE2\x88\x88",
'Iuml' => "\xC3\x8F",
'iuml' => "\xC3\xAF",
'Iuml;' => "\xC3\x8F",
'iuml;' => "\xC3\xAF",
'Kappa;' => "\xCE\x9A",
'kappa;' => "\xCE\xBA",
'Lambda;' => "\xCE\x9B",
'lambda;' => "\xCE\xBB",
'lang;' => "\xE3\x80\x88",
'laquo' => "\xC2\xAB",
'laquo;' => "\xC2\xAB",
'lArr;' => "\xE2\x87\x90",
'larr;' => "\xE2\x86\x90",
'lceil;' => "\xE2\x8C\x88",
'ldquo;' => "\xE2\x80\x9C",
'le;' => "\xE2\x89\xA4",
'lfloor;' => "\xE2\x8C\x8A",
'lowast;' => "\xE2\x88\x97",
'loz;' => "\xE2\x97\x8A",
'lrm;' => "\xE2\x80\x8E",
'lsaquo;' => "\xE2\x80\xB9",
'lsquo;' => "\xE2\x80\x98",
'LT' => "\x3C",
'lt' => "\x3C",
'LT;' => "\x3C",
'lt;' => "\x3C",
'macr' => "\xC2\xAF",
'macr;' => "\xC2\xAF",
'mdash;' => "\xE2\x80\x94",
'micro' => "\xC2\xB5",
'micro;' => "\xC2\xB5",
'middot' => "\xC2\xB7",
'middot;' => "\xC2\xB7",
'minus;' => "\xE2\x88\x92",
'Mu;' => "\xCE\x9C",
'mu;' => "\xCE\xBC",
'nabla;' => "\xE2\x88\x87",
'nbsp' => "\xC2\xA0",
'nbsp;' => "\xC2\xA0",
'ndash;' => "\xE2\x80\x93",
'ne;' => "\xE2\x89\xA0",
'ni;' => "\xE2\x88\x8B",
'not' => "\xC2\xAC",
'not;' => "\xC2\xAC",
'notin;' => "\xE2\x88\x89",
'nsub;' => "\xE2\x8A\x84",
'Ntilde' => "\xC3\x91",
'ntilde' => "\xC3\xB1",
'Ntilde;' => "\xC3\x91",
'ntilde;' => "\xC3\xB1",
'Nu;' => "\xCE\x9D",
'nu;' => "\xCE\xBD",
'Oacute' => "\xC3\x93",
'oacute' => "\xC3\xB3",
'Oacute;' => "\xC3\x93",
'oacute;' => "\xC3\xB3",
'Ocirc' => "\xC3\x94",
'ocirc' => "\xC3\xB4",
'Ocirc;' => "\xC3\x94",
'ocirc;' => "\xC3\xB4",
'OElig;' => "\xC5\x92",
'oelig;' => "\xC5\x93",
'Ograve' => "\xC3\x92",
'ograve' => "\xC3\xB2",
'Ograve;' => "\xC3\x92",
'ograve;' => "\xC3\xB2",
'oline;' => "\xE2\x80\xBE",
'Omega;' => "\xCE\xA9",
'omega;' => "\xCF\x89",
'Omicron;' => "\xCE\x9F",
'omicron;' => "\xCE\xBF",
'oplus;' => "\xE2\x8A\x95",
'or;' => "\xE2\x88\xA8",
'ordf' => "\xC2\xAA",
'ordf;' => "\xC2\xAA",
'ordm' => "\xC2\xBA",
'ordm;' => "\xC2\xBA",
'Oslash' => "\xC3\x98",
'oslash' => "\xC3\xB8",
'Oslash;' => "\xC3\x98",
'oslash;' => "\xC3\xB8",
'Otilde' => "\xC3\x95",
'otilde' => "\xC3\xB5",
'Otilde;' => "\xC3\x95",
'otilde;' => "\xC3\xB5",
'otimes;' => "\xE2\x8A\x97",
'Ouml' => "\xC3\x96",
'ouml' => "\xC3\xB6",
'Ouml;' => "\xC3\x96",
'ouml;' => "\xC3\xB6",
'para' => "\xC2\xB6",
'para;' => "\xC2\xB6",
'part;' => "\xE2\x88\x82",
'permil;' => "\xE2\x80\xB0",
'perp;' => "\xE2\x8A\xA5",
'Phi;' => "\xCE\xA6",
'phi;' => "\xCF\x86",
'Pi;' => "\xCE\xA0",
'pi;' => "\xCF\x80",
'piv;' => "\xCF\x96",
'plusmn' => "\xC2\xB1",
'plusmn;' => "\xC2\xB1",
'pound' => "\xC2\xA3",
'pound;' => "\xC2\xA3",
'Prime;' => "\xE2\x80\xB3",
'prime;' => "\xE2\x80\xB2",
'prod;' => "\xE2\x88\x8F",
'prop;' => "\xE2\x88\x9D",
'Psi;' => "\xCE\xA8",
'psi;' => "\xCF\x88",
'QUOT' => "\x22",
'quot' => "\x22",
'QUOT;' => "\x22",
'quot;' => "\x22",
'radic;' => "\xE2\x88\x9A",
'rang;' => "\xE3\x80\x89",
'raquo' => "\xC2\xBB",
'raquo;' => "\xC2\xBB",
'rArr;' => "\xE2\x87\x92",
'rarr;' => "\xE2\x86\x92",
'rceil;' => "\xE2\x8C\x89",
'rdquo;' => "\xE2\x80\x9D",
'real;' => "\xE2\x84\x9C",
'REG' => "\xC2\xAE",
'reg' => "\xC2\xAE",
'REG;' => "\xC2\xAE",
'reg;' => "\xC2\xAE",
'rfloor;' => "\xE2\x8C\x8B",
'Rho;' => "\xCE\xA1",
'rho;' => "\xCF\x81",
'rlm;' => "\xE2\x80\x8F",
'rsaquo;' => "\xE2\x80\xBA",
'rsquo;' => "\xE2\x80\x99",
'sbquo;' => "\xE2\x80\x9A",
'Scaron;' => "\xC5\xA0",
'scaron;' => "\xC5\xA1",
'sdot;' => "\xE2\x8B\x85",
'sect' => "\xC2\xA7",
'sect;' => "\xC2\xA7",
'shy' => "\xC2\xAD",
'shy;' => "\xC2\xAD",
'Sigma;' => "\xCE\xA3",
'sigma;' => "\xCF\x83",
'sigmaf;' => "\xCF\x82",
'sim;' => "\xE2\x88\xBC",
'spades;' => "\xE2\x99\xA0",
'sub;' => "\xE2\x8A\x82",
'sube;' => "\xE2\x8A\x86",
'sum;' => "\xE2\x88\x91",
'sup;' => "\xE2\x8A\x83",
'sup1' => "\xC2\xB9",
'sup1;' => "\xC2\xB9",
'sup2' => "\xC2\xB2",
'sup2;' => "\xC2\xB2",
'sup3' => "\xC2\xB3",
'sup3;' => "\xC2\xB3",
'supe;' => "\xE2\x8A\x87",
'szlig' => "\xC3\x9F",
'szlig;' => "\xC3\x9F",
'Tau;' => "\xCE\xA4",
'tau;' => "\xCF\x84",
'there4;' => "\xE2\x88\xB4",
'Theta;' => "\xCE\x98",
'theta;' => "\xCE\xB8",
'thetasym;' => "\xCF\x91",
'thinsp;' => "\xE2\x80\x89",
'THORN' => "\xC3\x9E",
'thorn' => "\xC3\xBE",
'THORN;' => "\xC3\x9E",
'thorn;' => "\xC3\xBE",
'tilde;' => "\xCB\x9C",
'times' => "\xC3\x97",
'times;' => "\xC3\x97",
'TRADE;' => "\xE2\x84\xA2",
'trade;' => "\xE2\x84\xA2",
'Uacute' => "\xC3\x9A",
'uacute' => "\xC3\xBA",
'Uacute;' => "\xC3\x9A",
'uacute;' => "\xC3\xBA",
'uArr;' => "\xE2\x87\x91",
'uarr;' => "\xE2\x86\x91",
'Ucirc' => "\xC3\x9B",
'ucirc' => "\xC3\xBB",
'Ucirc;' => "\xC3\x9B",
'ucirc;' => "\xC3\xBB",
'Ugrave' => "\xC3\x99",
'ugrave' => "\xC3\xB9",
'Ugrave;' => "\xC3\x99",
'ugrave;' => "\xC3\xB9",
'uml' => "\xC2\xA8",
'uml;' => "\xC2\xA8",
'upsih;' => "\xCF\x92",
'Upsilon;' => "\xCE\xA5",
'upsilon;' => "\xCF\x85",
'Uuml' => "\xC3\x9C",
'uuml' => "\xC3\xBC",
'Uuml;' => "\xC3\x9C",
'uuml;' => "\xC3\xBC",
'weierp;' => "\xE2\x84\x98",
'Xi;' => "\xCE\x9E",
'xi;' => "\xCE\xBE",
'Yacute' => "\xC3\x9D",
'yacute' => "\xC3\xBD",
'Yacute;' => "\xC3\x9D",
'yacute;' => "\xC3\xBD",
'yen' => "\xC2\xA5",
'yen;' => "\xC2\xA5",
'yuml' => "\xC3\xBF",
'Yuml;' => "\xC5\xB8",
'yuml;' => "\xC3\xBF",
'Zeta;' => "\xCE\x96",
'zeta;' => "\xCE\xB6",
'zwj;' => "\xE2\x80\x8D",
'zwnj;' => "\xE2\x80\x8C"
);
for ($i = 0, $match = null; $i < 9 && $this->consume() !== false; $i++)
{
$consumed = substr($this->consumed, 1);
if (isset($entities[$consumed]))
{
$match = $consumed;
}
}
if ($match !== null)
{
$this->data = substr_replace($this->data, $entities[$match], $this->position - strlen($consumed) - 1, strlen($match) + 1);
$this->position += strlen($entities[$match]) - strlen($consumed) - 1;
}
break;
}
}
}
Exception.php 0000604 00000002132 15133170420 0007201 0 ustar 00 <?php
/**
* Exception for HTTP requests
*
* @package Requests\Exceptions
*/
namespace WpOrg\Requests;
use Exception as PHPException;
/**
* Exception for HTTP requests
*
* @package Requests\Exceptions
*/
class Exception extends PHPException {
/**
* Type of exception
*
* @var string
*/
protected $type;
/**
* Data associated with the exception
*
* @var mixed
*/
protected $data;
/**
* Create a new exception
*
* @param string $message Exception message
* @param string $type Exception type
* @param mixed $data Associated data
* @param integer $code Exception numerical code, if applicable
*/
public function __construct($message, $type, $data = null, $code = 0) {
parent::__construct($message, $code);
$this->type = $type;
$this->data = $data;
}
/**
* Like {@see \Exception::getCode()}, but a string code.
*
* @codeCoverageIgnore
* @return string
*/
public function getType() {
return $this->type;
}
/**
* Gives any relevant data
*
* @codeCoverageIgnore
* @return mixed
*/
public function getData() {
return $this->data;
}
}
Cache.php 0000644 00000011363 15133170420 0006260 0 ustar 00 <?php
/**
* SimplePie
*
* A PHP-Based RSS and Atom Feed Framework.
* Takes the hard work out of managing a complete RSS/Atom solution.
*
* Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* * Neither the name of the SimplePie Team nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
* AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package SimplePie
* @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
* @author Ryan Parman
* @author Sam Sneddon
* @author Ryan McCue
* @link http://simplepie.org/ SimplePie
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
namespace SimplePie;
use SimplePie\Cache\Base;
/**
* Used to create cache objects
*
* This class can be overloaded with {@see SimplePie::set_cache_class()},
* although the preferred way is to create your own handler
* via {@see register()}
*
* @package SimplePie
* @subpackage Caching
* @deprecated since SimplePie 1.8.0, use "SimplePie\SimplePie::set_cache()" instead
*/
class Cache
{
/**
* Cache handler classes
*
* These receive 3 parameters to their constructor, as documented in
* {@see register()}
* @var array
*/
protected static $handlers = [
'mysql' => 'SimplePie\Cache\MySQL',
'memcache' => 'SimplePie\Cache\Memcache',
'memcached' => 'SimplePie\Cache\Memcached',
'redis' => 'SimplePie\Cache\Redis'
];
/**
* Don't call the constructor. Please.
*/
private function __construct()
{
}
/**
* Create a new SimplePie\Cache object
*
* @param string $location URL location (scheme is used to determine handler)
* @param string $filename Unique identifier for cache object
* @param Base::TYPE_FEED|Base::TYPE_IMAGE $extension 'spi' or 'spc'
* @return Base Type of object depends on scheme of `$location`
*/
public static function get_handler($location, $filename, $extension)
{
$type = explode(':', $location, 2);
$type = $type[0];
if (!empty(self::$handlers[$type])) {
$class = self::$handlers[$type];
return new $class($location, $filename, $extension);
}
return new \SimplePie\Cache\File($location, $filename, $extension);
}
/**
* Create a new SimplePie\Cache object
*
* @deprecated since SimplePie 1.3.1, use {@see get_handler()} instead
*/
public function create($location, $filename, $extension)
{
trigger_error('Cache::create() has been replaced with Cache::get_handler() since SimplePie 1.3.1, use the registry system instead.', \E_USER_DEPRECATED);
return self::get_handler($location, $filename, $extension);
}
/**
* Register a handler
*
* @param string $type DSN type to register for
* @param class-string<Base> $class Name of handler class. Must implement Base
*/
public static function register($type, $class)
{
self::$handlers[$type] = $class;
}
/**
* Parse a URL into an array
*
* @param string $url
* @return array
*/
public static function parse_URL($url)
{
$params = parse_url($url);
$params['extras'] = [];
if (isset($params['query'])) {
parse_str($params['query'], $params['extras']);
}
return $params;
}
}
class_alias('SimplePie\Cache', 'SimplePie_Cache');
Author.php 0000644 00000007441 15133170420 0006521 0 ustar 00 <?php
/**
* SimplePie
*
* A PHP-Based RSS and Atom Feed Framework.
* Takes the hard work out of managing a complete RSS/Atom solution.
*
* Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* * Neither the name of the SimplePie Team nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
* AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package SimplePie
* @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
* @author Ryan Parman
* @author Sam Sneddon
* @author Ryan McCue
* @link http://simplepie.org/ SimplePie
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
namespace SimplePie;
/**
* Manages all author-related data
*
* Used by {@see Item::get_author()} and {@see SimplePie::get_authors()}
*
* This class can be overloaded with {@see SimplePie::set_author_class()}
*
* @package SimplePie
* @subpackage API
*/
class Author
{
/**
* Author's name
*
* @var string
* @see get_name()
*/
public $name;
/**
* Author's link
*
* @var string
* @see get_link()
*/
public $link;
/**
* Author's email address
*
* @var string
* @see get_email()
*/
public $email;
/**
* Constructor, used to input the data
*
* @param string $name
* @param string $link
* @param string $email
*/
public function __construct($name = null, $link = null, $email = null)
{
$this->name = $name;
$this->link = $link;
$this->email = $email;
}
/**
* String-ified version
*
* @return string
*/
public function __toString()
{
// There is no $this->data here
return md5(serialize($this));
}
/**
* Author's name
*
* @return string|null
*/
public function get_name()
{
if ($this->name !== null) {
return $this->name;
}
return null;
}
/**
* Author's link
*
* @return string|null
*/
public function get_link()
{
if ($this->link !== null) {
return $this->link;
}
return null;
}
/**
* Author's email address
*
* @return string|null
*/
public function get_email()
{
if ($this->email !== null) {
return $this->email;
}
return null;
}
}
class_alias('SimplePie\Author', 'SimplePie_Author');
Sanitize.php 0000644 00000061065 15133170420 0007047 0 ustar 00 <?php
/**
* SimplePie
*
* A PHP-Based RSS and Atom Feed Framework.
* Takes the hard work out of managing a complete RSS/Atom solution.
*
* Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* * Neither the name of the SimplePie Team nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
* AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package SimplePie
* @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
* @author Ryan Parman
* @author Sam Sneddon
* @author Ryan McCue
* @link http://simplepie.org/ SimplePie
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
namespace SimplePie;
use InvalidArgumentException;
use SimplePie\Cache\Base;
use SimplePie\Cache\BaseDataCache;
use SimplePie\Cache\CallableNameFilter;
use SimplePie\Cache\DataCache;
use SimplePie\Cache\NameFilter;
/**
* Used for data cleanup and post-processing
*
*
* This class can be overloaded with {@see \SimplePie\SimplePie::set_sanitize_class()}
*
* @package SimplePie
* @todo Move to using an actual HTML parser (this will allow tags to be properly stripped, and to switch between HTML and XHTML), this will also make it easier to shorten a string while preserving HTML tags
*/
class Sanitize implements RegistryAware
{
// Private vars
public $base;
// Options
public $remove_div = true;
public $image_handler = '';
public $strip_htmltags = ['base', 'blink', 'body', 'doctype', 'embed', 'font', 'form', 'frame', 'frameset', 'html', 'iframe', 'input', 'marquee', 'meta', 'noscript', 'object', 'param', 'script', 'style'];
public $encode_instead_of_strip = false;
public $strip_attributes = ['bgsound', 'expr', 'id', 'style', 'onclick', 'onerror', 'onfinish', 'onmouseover', 'onmouseout', 'onfocus', 'onblur', 'lowsrc', 'dynsrc'];
public $rename_attributes = [];
public $add_attributes = ['audio' => ['preload' => 'none'], 'iframe' => ['sandbox' => 'allow-scripts allow-same-origin'], 'video' => ['preload' => 'none']];
public $strip_comments = false;
public $output_encoding = 'UTF-8';
public $enable_cache = true;
public $cache_location = './cache';
public $cache_name_function = 'md5';
/**
* @var NameFilter
*/
private $cache_namefilter;
public $timeout = 10;
public $useragent = '';
public $force_fsockopen = false;
public $replace_url_attributes = null;
public $registry;
/**
* @var DataCache|null
*/
private $cache = null;
/**
* @var int Cache duration (in seconds)
*/
private $cache_duration = 3600;
/**
* List of domains for which to force HTTPS.
* @see \SimplePie\Sanitize::set_https_domains()
* Array is a tree split at DNS levels. Example:
* array('biz' => true, 'com' => array('example' => true), 'net' => array('example' => array('www' => true)))
*/
public $https_domains = [];
public function __construct()
{
// Set defaults
$this->set_url_replacements(null);
}
public function remove_div($enable = true)
{
$this->remove_div = (bool) $enable;
}
public function set_image_handler($page = false)
{
if ($page) {
$this->image_handler = (string) $page;
} else {
$this->image_handler = false;
}
}
public function set_registry(\SimplePie\Registry $registry)/* : void */
{
$this->registry = $registry;
}
public function pass_cache_data($enable_cache = true, $cache_location = './cache', $cache_name_function = 'md5', $cache_class = 'SimplePie\Cache', ?DataCache $cache = null)
{
if (isset($enable_cache)) {
$this->enable_cache = (bool) $enable_cache;
}
if ($cache_location) {
$this->cache_location = (string) $cache_location;
}
if (!is_string($cache_name_function) && !is_object($cache_name_function) && !$cache_name_function instanceof NameFilter) {
throw new InvalidArgumentException(sprintf(
'%s(): Argument #3 ($cache_name_function) must be of type %s',
__METHOD__,
NameFilter::class
), 1);
}
// BC: $cache_name_function could be a callable as string
if (is_string($cache_name_function)) {
// trigger_error(sprintf('Providing $cache_name_function as string in "%s()" is deprecated since SimplePie 1.8.0, provide as "%s" instead.', __METHOD__, NameFilter::class), \E_USER_DEPRECATED);
$this->cache_name_function = (string) $cache_name_function;
$cache_name_function = new CallableNameFilter($cache_name_function);
}
$this->cache_namefilter = $cache_name_function;
if ($cache !== null) {
$this->cache = $cache;
}
}
public function pass_file_data($file_class = 'SimplePie\File', $timeout = 10, $useragent = '', $force_fsockopen = false)
{
if ($timeout) {
$this->timeout = (string) $timeout;
}
if ($useragent) {
$this->useragent = (string) $useragent;
}
if ($force_fsockopen) {
$this->force_fsockopen = (string) $force_fsockopen;
}
}
public function strip_htmltags($tags = ['base', 'blink', 'body', 'doctype', 'embed', 'font', 'form', 'frame', 'frameset', 'html', 'iframe', 'input', 'marquee', 'meta', 'noscript', 'object', 'param', 'script', 'style'])
{
if ($tags) {
if (is_array($tags)) {
$this->strip_htmltags = $tags;
} else {
$this->strip_htmltags = explode(',', $tags);
}
} else {
$this->strip_htmltags = false;
}
}
public function encode_instead_of_strip($encode = false)
{
$this->encode_instead_of_strip = (bool) $encode;
}
public function rename_attributes($attribs = [])
{
if ($attribs) {
if (is_array($attribs)) {
$this->rename_attributes = $attribs;
} else {
$this->rename_attributes = explode(',', $attribs);
}
} else {
$this->rename_attributes = false;
}
}
public function strip_attributes($attribs = ['bgsound', 'expr', 'id', 'style', 'onclick', 'onerror', 'onfinish', 'onmouseover', 'onmouseout', 'onfocus', 'onblur', 'lowsrc', 'dynsrc'])
{
if ($attribs) {
if (is_array($attribs)) {
$this->strip_attributes = $attribs;
} else {
$this->strip_attributes = explode(',', $attribs);
}
} else {
$this->strip_attributes = false;
}
}
public function add_attributes($attribs = ['audio' => ['preload' => 'none'], 'iframe' => ['sandbox' => 'allow-scripts allow-same-origin'], 'video' => ['preload' => 'none']])
{
if ($attribs) {
if (is_array($attribs)) {
$this->add_attributes = $attribs;
} else {
$this->add_attributes = explode(',', $attribs);
}
} else {
$this->add_attributes = false;
}
}
public function strip_comments($strip = false)
{
$this->strip_comments = (bool) $strip;
}
public function set_output_encoding($encoding = 'UTF-8')
{
$this->output_encoding = (string) $encoding;
}
/**
* Set element/attribute key/value pairs of HTML attributes
* containing URLs that need to be resolved relative to the feed
*
* Defaults to |a|@href, |area|@href, |audio|@src, |blockquote|@cite,
* |del|@cite, |form|@action, |img|@longdesc, |img|@src, |input|@src,
* |ins|@cite, |q|@cite, |source|@src, |video|@src
*
* @since 1.0
* @param array|null $element_attribute Element/attribute key/value pairs, null for default
*/
public function set_url_replacements($element_attribute = null)
{
if ($element_attribute === null) {
$element_attribute = [
'a' => 'href',
'area' => 'href',
'audio' => 'src',
'blockquote' => 'cite',
'del' => 'cite',
'form' => 'action',
'img' => [
'longdesc',
'src'
],
'input' => 'src',
'ins' => 'cite',
'q' => 'cite',
'source' => 'src',
'video' => [
'poster',
'src'
]
];
}
$this->replace_url_attributes = (array) $element_attribute;
}
/**
* Set the list of domains for which to force HTTPS.
* @see \SimplePie\Misc::https_url()
* Example array('biz', 'example.com', 'example.org', 'www.example.net');
*/
public function set_https_domains($domains)
{
$this->https_domains = [];
foreach ($domains as $domain) {
$domain = trim($domain, ". \t\n\r\0\x0B");
$segments = array_reverse(explode('.', $domain));
$node = &$this->https_domains;
foreach ($segments as $segment) {//Build a tree
if ($node === true) {
break;
}
if (!isset($node[$segment])) {
$node[$segment] = [];
}
$node = &$node[$segment];
}
$node = true;
}
}
/**
* Check if the domain is in the list of forced HTTPS.
*/
protected function is_https_domain($domain)
{
$domain = trim($domain, '. ');
$segments = array_reverse(explode('.', $domain));
$node = &$this->https_domains;
foreach ($segments as $segment) {//Explore the tree
if (isset($node[$segment])) {
$node = &$node[$segment];
} else {
break;
}
}
return $node === true;
}
/**
* Force HTTPS for selected Web sites.
*/
public function https_url($url)
{
return (strtolower(substr($url, 0, 7)) === 'http://') &&
$this->is_https_domain(parse_url($url, PHP_URL_HOST)) ?
substr_replace($url, 's', 4, 0) : //Add the 's' to HTTPS
$url;
}
public function sanitize($data, $type, $base = '')
{
$data = trim($data);
if ($data !== '' || $type & \SimplePie\SimplePie::CONSTRUCT_IRI) {
if ($type & \SimplePie\SimplePie::CONSTRUCT_MAYBE_HTML) {
if (preg_match('/(&(#(x[0-9a-fA-F]+|[0-9]+)|[a-zA-Z0-9]+)|<\/[A-Za-z][^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3E]*' . \SimplePie\SimplePie::PCRE_HTML_ATTRIBUTE . '>)/', $data)) {
$type |= \SimplePie\SimplePie::CONSTRUCT_HTML;
} else {
$type |= \SimplePie\SimplePie::CONSTRUCT_TEXT;
}
}
if ($type & \SimplePie\SimplePie::CONSTRUCT_BASE64) {
$data = base64_decode($data);
}
if ($type & (\SimplePie\SimplePie::CONSTRUCT_HTML | \SimplePie\SimplePie::CONSTRUCT_XHTML)) {
if (!class_exists('DOMDocument')) {
throw new \SimplePie\Exception('DOMDocument not found, unable to use sanitizer');
}
$document = new \DOMDocument();
$document->encoding = 'UTF-8';
$data = $this->preprocess($data, $type);
set_error_handler(['SimplePie\Misc', 'silence_errors']);
$document->loadHTML($data);
restore_error_handler();
$xpath = new \DOMXPath($document);
// Strip comments
if ($this->strip_comments) {
$comments = $xpath->query('//comment()');
foreach ($comments as $comment) {
$comment->parentNode->removeChild($comment);
}
}
// Strip out HTML tags and attributes that might cause various security problems.
// Based on recommendations by Mark Pilgrim at:
// http://diveintomark.org/archives/2003/06/12/how_to_consume_rss_safely
if ($this->strip_htmltags) {
foreach ($this->strip_htmltags as $tag) {
$this->strip_tag($tag, $document, $xpath, $type);
}
}
if ($this->rename_attributes) {
foreach ($this->rename_attributes as $attrib) {
$this->rename_attr($attrib, $xpath);
}
}
if ($this->strip_attributes) {
foreach ($this->strip_attributes as $attrib) {
$this->strip_attr($attrib, $xpath);
}
}
if ($this->add_attributes) {
foreach ($this->add_attributes as $tag => $valuePairs) {
$this->add_attr($tag, $valuePairs, $document);
}
}
// Replace relative URLs
$this->base = $base;
foreach ($this->replace_url_attributes as $element => $attributes) {
$this->replace_urls($document, $element, $attributes);
}
// If image handling (caching, etc.) is enabled, cache and rewrite all the image tags.
if (isset($this->image_handler) && ((string) $this->image_handler) !== '' && $this->enable_cache) {
$images = $document->getElementsByTagName('img');
foreach ($images as $img) {
if ($img->hasAttribute('src')) {
$image_url = $this->cache_namefilter->filter($img->getAttribute('src'));
$cache = $this->get_cache($image_url);
if ($cache->get_data($image_url, false)) {
$img->setAttribute('src', $this->image_handler . $image_url);
} else {
$file = $this->registry->create(File::class, [$img->getAttribute('src'), $this->timeout, 5, ['X-FORWARDED-FOR' => $_SERVER['REMOTE_ADDR']], $this->useragent, $this->force_fsockopen]);
$headers = $file->headers;
if ($file->success && ($file->method & \SimplePie\SimplePie::FILE_SOURCE_REMOTE === 0 || ($file->status_code === 200 || $file->status_code > 206 && $file->status_code < 300))) {
if ($cache->set_data($image_url, ['headers' => $file->headers, 'body' => $file->body], $this->cache_duration)) {
$img->setAttribute('src', $this->image_handler . $image_url);
} else {
trigger_error("$this->cache_location is not writable. Make sure you've set the correct relative or absolute path, and that the location is server-writable.", E_USER_WARNING);
}
}
}
}
}
}
// Get content node
$div = $document->getElementsByTagName('body')->item(0)->firstChild;
// Finally, convert to a HTML string
$data = trim($document->saveHTML($div));
if ($this->remove_div) {
$data = preg_replace('/^<div' . \SimplePie\SimplePie::PCRE_XML_ATTRIBUTE . '>/', '', $data);
$data = preg_replace('/<\/div>$/', '', $data);
} else {
$data = preg_replace('/^<div' . \SimplePie\SimplePie::PCRE_XML_ATTRIBUTE . '>/', '<div>', $data);
}
$data = str_replace('</source>', '', $data);
}
if ($type & \SimplePie\SimplePie::CONSTRUCT_IRI) {
$absolute = $this->registry->call(Misc::class, 'absolutize_url', [$data, $base]);
if ($absolute !== false) {
$data = $absolute;
}
}
if ($type & (\SimplePie\SimplePie::CONSTRUCT_TEXT | \SimplePie\SimplePie::CONSTRUCT_IRI)) {
$data = htmlspecialchars($data, ENT_COMPAT, 'UTF-8');
}
if ($this->output_encoding !== 'UTF-8') {
$data = $this->registry->call(Misc::class, 'change_encoding', [$data, 'UTF-8', $this->output_encoding]);
}
}
return $data;
}
protected function preprocess($html, $type)
{
$ret = '';
$html = preg_replace('%</?(?:html|body)[^>]*?'.'>%is', '', $html);
if ($type & ~\SimplePie\SimplePie::CONSTRUCT_XHTML) {
// Atom XHTML constructs are wrapped with a div by default
// Note: No protection if $html contains a stray </div>!
$html = '<div>' . $html . '</div>';
$ret .= '<!DOCTYPE html>';
$content_type = 'text/html';
} else {
$ret .= '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">';
$content_type = 'application/xhtml+xml';
}
$ret .= '<html><head>';
$ret .= '<meta http-equiv="Content-Type" content="' . $content_type . '; charset=utf-8" />';
$ret .= '</head><body>' . $html . '</body></html>';
return $ret;
}
public function replace_urls($document, $tag, $attributes)
{
if (!is_array($attributes)) {
$attributes = [$attributes];
}
if (!is_array($this->strip_htmltags) || !in_array($tag, $this->strip_htmltags)) {
$elements = $document->getElementsByTagName($tag);
foreach ($elements as $element) {
foreach ($attributes as $attribute) {
if ($element->hasAttribute($attribute)) {
$value = $this->registry->call(Misc::class, 'absolutize_url', [$element->getAttribute($attribute), $this->base]);
if ($value !== false) {
$value = $this->https_url($value);
$element->setAttribute($attribute, $value);
}
}
}
}
}
}
public function do_strip_htmltags($match)
{
if ($this->encode_instead_of_strip) {
if (isset($match[4]) && !in_array(strtolower($match[1]), ['script', 'style'])) {
$match[1] = htmlspecialchars($match[1], ENT_COMPAT, 'UTF-8');
$match[2] = htmlspecialchars($match[2], ENT_COMPAT, 'UTF-8');
return "<$match[1]$match[2]>$match[3]</$match[1]>";
} else {
return htmlspecialchars($match[0], ENT_COMPAT, 'UTF-8');
}
} elseif (isset($match[4]) && !in_array(strtolower($match[1]), ['script', 'style'])) {
return $match[4];
} else {
return '';
}
}
protected function strip_tag($tag, $document, $xpath, $type)
{
$elements = $xpath->query('body//' . $tag);
if ($this->encode_instead_of_strip) {
foreach ($elements as $element) {
$fragment = $document->createDocumentFragment();
// For elements which aren't script or style, include the tag itself
if (!in_array($tag, ['script', 'style'])) {
$text = '<' . $tag;
if ($element->hasAttributes()) {
$attrs = [];
foreach ($element->attributes as $name => $attr) {
$value = $attr->value;
// In XHTML, empty values should never exist, so we repeat the value
if (empty($value) && ($type & \SimplePie\SimplePie::CONSTRUCT_XHTML)) {
$value = $name;
}
// For HTML, empty is fine
elseif (empty($value) && ($type & \SimplePie\SimplePie::CONSTRUCT_HTML)) {
$attrs[] = $name;
continue;
}
// Standard attribute text
$attrs[] = $name . '="' . $attr->value . '"';
}
$text .= ' ' . implode(' ', $attrs);
}
$text .= '>';
$fragment->appendChild(new \DOMText($text));
}
$number = $element->childNodes->length;
for ($i = $number; $i > 0; $i--) {
$child = $element->childNodes->item(0);
$fragment->appendChild($child);
}
if (!in_array($tag, ['script', 'style'])) {
$fragment->appendChild(new \DOMText('</' . $tag . '>'));
}
$element->parentNode->replaceChild($fragment, $element);
}
return;
} elseif (in_array($tag, ['script', 'style'])) {
foreach ($elements as $element) {
$element->parentNode->removeChild($element);
}
return;
} else {
foreach ($elements as $element) {
$fragment = $document->createDocumentFragment();
$number = $element->childNodes->length;
for ($i = $number; $i > 0; $i--) {
$child = $element->childNodes->item(0);
$fragment->appendChild($child);
}
$element->parentNode->replaceChild($fragment, $element);
}
}
}
protected function strip_attr($attrib, $xpath)
{
$elements = $xpath->query('//*[@' . $attrib . ']');
foreach ($elements as $element) {
$element->removeAttribute($attrib);
}
}
protected function rename_attr($attrib, $xpath)
{
$elements = $xpath->query('//*[@' . $attrib . ']');
foreach ($elements as $element) {
$element->setAttribute('data-sanitized-' . $attrib, $element->getAttribute($attrib));
$element->removeAttribute($attrib);
}
}
protected function add_attr($tag, $valuePairs, $document)
{
$elements = $document->getElementsByTagName($tag);
foreach ($elements as $element) {
foreach ($valuePairs as $attrib => $value) {
$element->setAttribute($attrib, $value);
}
}
}
/**
* Get a DataCache
*
* @param string $image_url Only needed for BC, can be removed in SimplePie 2.0.0
*
* @return DataCache
*/
private function get_cache($image_url = '')
{
if ($this->cache === null) {
// @trigger_error(sprintf('Not providing as PSR-16 cache implementation is deprecated since SimplePie 1.8.0, please use "SimplePie\SimplePie::set_cache()".'), \E_USER_DEPRECATED);
$cache = $this->registry->call(Cache::class, 'get_handler', [
$this->cache_location,
$image_url,
Base::TYPE_IMAGE
]);
return new BaseDataCache($cache);
}
return $this->cache;
}
}
class_alias('SimplePie\Sanitize', 'SimplePie_Sanitize');
Item.php 0000644 00000400572 15133170420 0006157 0 ustar 00 <?php
/**
* SimplePie
*
* A PHP-Based RSS and Atom Feed Framework.
* Takes the hard work out of managing a complete RSS/Atom solution.
*
* Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* * Neither the name of the SimplePie Team nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
* AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package SimplePie
* @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
* @author Ryan Parman
* @author Sam Sneddon
* @author Ryan McCue
* @link http://simplepie.org/ SimplePie
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
namespace SimplePie;
/**
* Manages all item-related data
*
* Used by {@see \SimplePie\SimplePie::get_item()} and {@see \SimplePie\SimplePie::get_items()}
*
* This class can be overloaded with {@see \SimplePie\SimplePie::set_item_class()}
*
* @package \SimplePie\SimplePie
* @subpackage API
*/
class Item implements RegistryAware
{
/**
* Parent feed
*
* @access private
* @var \SimplePie\SimplePie
*/
public $feed;
/**
* Raw data
*
* @access private
* @var array
*/
public $data = [];
/**
* Registry object
*
* @see set_registry
* @var \SimplePie\Registry
*/
protected $registry;
/**
* Create a new item object
*
* This is usually used by {@see \SimplePie\SimplePie::get_items} and
* {@see \SimplePie\SimplePie::get_item}. Avoid creating this manually.
*
* @param \SimplePie\SimplePie $feed Parent feed
* @param array $data Raw data
*/
public function __construct($feed, $data)
{
$this->feed = $feed;
$this->data = $data;
}
/**
* Set the registry handler
*
* This is usually used by {@see \SimplePie\Registry::create}
*
* @since 1.3
* @param \SimplePie\Registry $registry
*/
public function set_registry(\SimplePie\Registry $registry)/* : void */
{
$this->registry = $registry;
}
/**
* Get a string representation of the item
*
* @return string
*/
public function __toString()
{
return md5(serialize($this->data));
}
/**
* Remove items that link back to this before destroying this object
*/
public function __destruct()
{
if (!gc_enabled()) {
unset($this->feed);
}
}
/**
* Get data for an item-level element
*
* This method allows you to get access to ANY element/attribute that is a
* sub-element of the item/entry tag.
*
* See {@see \SimplePie\SimplePie::get_feed_tags()} for a description of the return value
*
* @since 1.0
* @see http://simplepie.org/wiki/faq/supported_xml_namespaces
* @param string $namespace The URL of the XML namespace of the elements you're trying to access
* @param string $tag Tag name
* @return array
*/
public function get_item_tags($namespace, $tag)
{
if (isset($this->data['child'][$namespace][$tag])) {
return $this->data['child'][$namespace][$tag];
}
return null;
}
/**
* Get the base URL value.
* Uses `<xml:base>`, or item link, or feed base URL.
*
* @param array $element
* @return string
*/
public function get_base($element = [])
{
if (!empty($element['xml_base_explicit']) && isset($element['xml_base'])) {
return $element['xml_base'];
}
$link = $this->get_permalink();
if ($link != null) {
return $link;
}
return $this->feed->get_base($element);
}
/**
* Sanitize feed data
*
* @access private
* @see \SimplePie\SimplePie::sanitize()
* @param string $data Data to sanitize
* @param int $type One of the \SimplePie\SimplePie::CONSTRUCT_* constants
* @param string $base Base URL to resolve URLs against
* @return string Sanitized data
*/
public function sanitize($data, $type, $base = '')
{
return $this->feed->sanitize($data, $type, $base);
}
/**
* Get the parent feed
*
* Note: this may not work as you think for multifeeds!
*
* @link http://simplepie.org/faq/typical_multifeed_gotchas#missing_data_from_feed
* @since 1.0
* @return \SimplePie\SimplePie
*/
public function get_feed()
{
return $this->feed;
}
/**
* Get the unique identifier for the item
*
* This is usually used when writing code to check for new items in a feed.
*
* Uses `<atom:id>`, `<guid>`, `<dc:identifier>` or the `about` attribute
* for RDF. If none of these are supplied (or `$hash` is true), creates an
* MD5 hash based on the permalink, title and content.
*
* @since Beta 2
* @param boolean $hash Should we force using a hash instead of the supplied ID?
* @param string|false $fn User-supplied function to generate an hash
* @return string|null
*/
public function get_id($hash = false, $fn = 'md5')
{
if (!$hash) {
if ($return = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_ATOM_10, 'id')) {
return $this->sanitize($return[0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
} elseif ($return = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_ATOM_03, 'id')) {
return $this->sanitize($return[0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
} elseif ($return = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_RSS_20, 'guid')) {
return $this->sanitize($return[0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
} elseif ($return = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_DC_11, 'identifier')) {
return $this->sanitize($return[0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
} elseif ($return = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_DC_10, 'identifier')) {
return $this->sanitize($return[0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
} elseif (isset($this->data['attribs'][\SimplePie\SimplePie::NAMESPACE_RDF]['about'])) {
return $this->sanitize($this->data['attribs'][\SimplePie\SimplePie::NAMESPACE_RDF]['about'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
}
if ($fn === false) {
return null;
} elseif (!is_callable($fn)) {
trigger_error('User-supplied function $fn must be callable', E_USER_WARNING);
$fn = 'md5';
}
return call_user_func(
$fn,
$this->get_permalink().$this->get_title().$this->get_content()
);
}
/**
* Get the title of the item
*
* Uses `<atom:title>`, `<title>` or `<dc:title>`
*
* @since Beta 2 (previously called `get_item_title` since 0.8)
* @return string|null
*/
public function get_title()
{
if (!isset($this->data['title'])) {
if ($return = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_ATOM_10, 'title')) {
$this->data['title'] = $this->sanitize($return[0]['data'], $this->registry->call(Misc::class, 'atom_10_construct_type', [$return[0]['attribs']]), $this->get_base($return[0]));
} elseif ($return = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_ATOM_03, 'title')) {
$this->data['title'] = $this->sanitize($return[0]['data'], $this->registry->call(Misc::class, 'atom_03_construct_type', [$return[0]['attribs']]), $this->get_base($return[0]));
} elseif ($return = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_RSS_10, 'title')) {
$this->data['title'] = $this->sanitize($return[0]['data'], \SimplePie\SimplePie::CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
} elseif ($return = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_RSS_090, 'title')) {
$this->data['title'] = $this->sanitize($return[0]['data'], \SimplePie\SimplePie::CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
} elseif ($return = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_RSS_20, 'title')) {
$this->data['title'] = $this->sanitize($return[0]['data'], \SimplePie\SimplePie::CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
} elseif ($return = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_DC_11, 'title')) {
$this->data['title'] = $this->sanitize($return[0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
} elseif ($return = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_DC_10, 'title')) {
$this->data['title'] = $this->sanitize($return[0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
} else {
$this->data['title'] = null;
}
}
return $this->data['title'];
}
/**
* Get the content for the item
*
* Prefers summaries over full content , but will return full content if a
* summary does not exist.
*
* To prefer full content instead, use {@see get_content}
*
* Uses `<atom:summary>`, `<description>`, `<dc:description>` or
* `<itunes:subtitle>`
*
* @since 0.8
* @param boolean $description_only Should we avoid falling back to the content?
* @return string|null
*/
public function get_description($description_only = false)
{
if (($tags = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_ATOM_10, 'summary')) &&
($return = $this->sanitize($tags[0]['data'], $this->registry->call(Misc::class, 'atom_10_construct_type', [$tags[0]['attribs']]), $this->get_base($tags[0])))) {
return $return;
} elseif (($tags = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_ATOM_03, 'summary')) &&
($return = $this->sanitize($tags[0]['data'], $this->registry->call(Misc::class, 'atom_03_construct_type', [$tags[0]['attribs']]), $this->get_base($tags[0])))) {
return $return;
} elseif (($tags = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_RSS_10, 'description')) &&
($return = $this->sanitize($tags[0]['data'], \SimplePie\SimplePie::CONSTRUCT_MAYBE_HTML, $this->get_base($tags[0])))) {
return $return;
} elseif (($tags = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_RSS_20, 'description')) &&
($return = $this->sanitize($tags[0]['data'], \SimplePie\SimplePie::CONSTRUCT_HTML, $this->get_base($tags[0])))) {
return $return;
} elseif (($tags = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_DC_11, 'description')) &&
($return = $this->sanitize($tags[0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT))) {
return $return;
} elseif (($tags = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_DC_10, 'description')) &&
($return = $this->sanitize($tags[0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT))) {
return $return;
} elseif (($tags = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_ITUNES, 'summary')) &&
($return = $this->sanitize($tags[0]['data'], \SimplePie\SimplePie::CONSTRUCT_HTML, $this->get_base($tags[0])))) {
return $return;
} elseif (($tags = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_ITUNES, 'subtitle')) &&
($return = $this->sanitize($tags[0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT))) {
return $return;
} elseif (($tags = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_RSS_090, 'description')) &&
($return = $this->sanitize($tags[0]['data'], \SimplePie\SimplePie::CONSTRUCT_HTML))) {
return $return;
} elseif (!$description_only) {
return $this->get_content(true);
}
return null;
}
/**
* Get the content for the item
*
* Prefers full content over summaries, but will return a summary if full
* content does not exist.
*
* To prefer summaries instead, use {@see get_description}
*
* Uses `<atom:content>` or `<content:encoded>` (RSS 1.0 Content Module)
*
* @since 1.0
* @param boolean $content_only Should we avoid falling back to the description?
* @return string|null
*/
public function get_content($content_only = false)
{
if (($tags = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_ATOM_10, 'content')) &&
($return = $this->sanitize($tags[0]['data'], $this->registry->call(Misc::class, 'atom_10_content_construct_type', [$tags[0]['attribs']]), $this->get_base($tags[0])))) {
return $return;
} elseif (($tags = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_ATOM_03, 'content')) &&
($return = $this->sanitize($tags[0]['data'], $this->registry->call(Misc::class, 'atom_03_construct_type', [$tags[0]['attribs']]), $this->get_base($tags[0])))) {
return $return;
} elseif (($tags = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_RSS_10_MODULES_CONTENT, 'encoded')) &&
($return = $this->sanitize($tags[0]['data'], \SimplePie\SimplePie::CONSTRUCT_HTML, $this->get_base($tags[0])))) {
return $return;
} elseif (!$content_only) {
return $this->get_description(true);
}
return null;
}
/**
* Get the media:thumbnail of the item
*
* Uses `<media:thumbnail>`
*
*
* @return array|null
*/
public function get_thumbnail()
{
if (!isset($this->data['thumbnail'])) {
if ($return = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_MEDIARSS, 'thumbnail')) {
$thumbnail = $return[0]['attribs'][''];
if (empty($thumbnail['url'])) {
$this->data['thumbnail'] = null;
} else {
$thumbnail['url'] = $this->sanitize($thumbnail['url'], \SimplePie\SimplePie::CONSTRUCT_IRI, $this->get_base($return[0]));
$this->data['thumbnail'] = $thumbnail;
}
} else {
$this->data['thumbnail'] = null;
}
}
return $this->data['thumbnail'];
}
/**
* Get a category for the item
*
* @since Beta 3 (previously called `get_categories()` since Beta 2)
* @param int $key The category that you want to return. Remember that arrays begin with 0, not 1
* @return \SimplePie\Category|null
*/
public function get_category($key = 0)
{
$categories = $this->get_categories();
if (isset($categories[$key])) {
return $categories[$key];
}
return null;
}
/**
* Get all categories for the item
*
* Uses `<atom:category>`, `<category>` or `<dc:subject>`
*
* @since Beta 3
* @return \SimplePie\Category[]|null List of {@see \SimplePie\Category} objects
*/
public function get_categories()
{
$categories = [];
$type = 'category';
foreach ((array) $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_ATOM_10, $type) as $category) {
$term = null;
$scheme = null;
$label = null;
if (isset($category['attribs']['']['term'])) {
$term = $this->sanitize($category['attribs']['']['term'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($category['attribs']['']['scheme'])) {
$scheme = $this->sanitize($category['attribs']['']['scheme'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($category['attribs']['']['label'])) {
$label = $this->sanitize($category['attribs']['']['label'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
$categories[] = $this->registry->create(Category::class, [$term, $scheme, $label, $type]);
}
foreach ((array) $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_RSS_20, $type) as $category) {
// This is really the label, but keep this as the term also for BC.
// Label will also work on retrieving because that falls back to term.
$term = $this->sanitize($category['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
if (isset($category['attribs']['']['domain'])) {
$scheme = $this->sanitize($category['attribs']['']['domain'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
} else {
$scheme = null;
}
$categories[] = $this->registry->create(Category::class, [$term, $scheme, null, $type]);
}
$type = 'subject';
foreach ((array) $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_DC_11, $type) as $category) {
$categories[] = $this->registry->create(Category::class, [$this->sanitize($category['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT), null, null, $type]);
}
foreach ((array) $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_DC_10, $type) as $category) {
$categories[] = $this->registry->create(Category::class, [$this->sanitize($category['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT), null, null, $type]);
}
if (!empty($categories)) {
return array_unique($categories);
}
return null;
}
/**
* Get an author for the item
*
* @since Beta 2
* @param int $key The author that you want to return. Remember that arrays begin with 0, not 1
* @return \SimplePie\Author|null
*/
public function get_author($key = 0)
{
$authors = $this->get_authors();
if (isset($authors[$key])) {
return $authors[$key];
}
return null;
}
/**
* Get a contributor for the item
*
* @since 1.1
* @param int $key The contrbutor that you want to return. Remember that arrays begin with 0, not 1
* @return \SimplePie\Author|null
*/
public function get_contributor($key = 0)
{
$contributors = $this->get_contributors();
if (isset($contributors[$key])) {
return $contributors[$key];
}
return null;
}
/**
* Get all contributors for the item
*
* Uses `<atom:contributor>`
*
* @since 1.1
* @return \SimplePie\Author[]|null List of {@see \SimplePie\Author} objects
*/
public function get_contributors()
{
$contributors = [];
foreach ((array) $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_ATOM_10, 'contributor') as $contributor) {
$name = null;
$uri = null;
$email = null;
if (isset($contributor['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_10]['name'][0]['data'])) {
$name = $this->sanitize($contributor['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_10]['name'][0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($contributor['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_10]['uri'][0]['data'])) {
$uri = $this->sanitize($contributor['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_10]['uri'][0]['data'], \SimplePie\SimplePie::CONSTRUCT_IRI, $this->get_base($contributor['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_10]['uri'][0]));
}
if (isset($contributor['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_10]['email'][0]['data'])) {
$email = $this->sanitize($contributor['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_10]['email'][0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if ($name !== null || $email !== null || $uri !== null) {
$contributors[] = $this->registry->create(Author::class, [$name, $uri, $email]);
}
}
foreach ((array) $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_ATOM_03, 'contributor') as $contributor) {
$name = null;
$url = null;
$email = null;
if (isset($contributor['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_03]['name'][0]['data'])) {
$name = $this->sanitize($contributor['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_03]['name'][0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($contributor['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_03]['url'][0]['data'])) {
$url = $this->sanitize($contributor['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_03]['url'][0]['data'], \SimplePie\SimplePie::CONSTRUCT_IRI, $this->get_base($contributor['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_03]['url'][0]));
}
if (isset($contributor['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_03]['email'][0]['data'])) {
$email = $this->sanitize($contributor['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_03]['email'][0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if ($name !== null || $email !== null || $url !== null) {
$contributors[] = $this->registry->create(Author::class, [$name, $url, $email]);
}
}
if (!empty($contributors)) {
return array_unique($contributors);
}
return null;
}
/**
* Get all authors for the item
*
* Uses `<atom:author>`, `<author>`, `<dc:creator>` or `<itunes:author>`
*
* @since Beta 2
* @return \SimplePie\Author[]|null List of {@see \SimplePie\Author} objects
*/
public function get_authors()
{
$authors = [];
foreach ((array) $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_ATOM_10, 'author') as $author) {
$name = null;
$uri = null;
$email = null;
if (isset($author['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_10]['name'][0]['data'])) {
$name = $this->sanitize($author['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_10]['name'][0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($author['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_10]['uri'][0]['data'])) {
$uri = $this->sanitize($author['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_10]['uri'][0]['data'], \SimplePie\SimplePie::CONSTRUCT_IRI, $this->get_base($author['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_10]['uri'][0]));
}
if (isset($author['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_10]['email'][0]['data'])) {
$email = $this->sanitize($author['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_10]['email'][0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if ($name !== null || $email !== null || $uri !== null) {
$authors[] = $this->registry->create(Author::class, [$name, $uri, $email]);
}
}
if ($author = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_ATOM_03, 'author')) {
$name = null;
$url = null;
$email = null;
if (isset($author[0]['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_03]['name'][0]['data'])) {
$name = $this->sanitize($author[0]['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_03]['name'][0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($author[0]['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_03]['url'][0]['data'])) {
$url = $this->sanitize($author[0]['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_03]['url'][0]['data'], \SimplePie\SimplePie::CONSTRUCT_IRI, $this->get_base($author[0]['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_03]['url'][0]));
}
if (isset($author[0]['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_03]['email'][0]['data'])) {
$email = $this->sanitize($author[0]['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_03]['email'][0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if ($name !== null || $email !== null || $url !== null) {
$authors[] = $this->registry->create(Author::class, [$name, $url, $email]);
}
}
if ($author = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_RSS_20, 'author')) {
$authors[] = $this->registry->create(Author::class, [null, null, $this->sanitize($author[0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT)]);
}
foreach ((array) $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_DC_11, 'creator') as $author) {
$authors[] = $this->registry->create(Author::class, [$this->sanitize($author['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT), null, null]);
}
foreach ((array) $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_DC_10, 'creator') as $author) {
$authors[] = $this->registry->create(Author::class, [$this->sanitize($author['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT), null, null]);
}
foreach ((array) $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_ITUNES, 'author') as $author) {
$authors[] = $this->registry->create(Author::class, [$this->sanitize($author['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT), null, null]);
}
if (!empty($authors)) {
return array_unique($authors);
} elseif (($source = $this->get_source()) && ($authors = $source->get_authors())) {
return $authors;
} elseif ($authors = $this->feed->get_authors()) {
return $authors;
}
return null;
}
/**
* Get the copyright info for the item
*
* Uses `<atom:rights>` or `<dc:rights>`
*
* @since 1.1
* @return string
*/
public function get_copyright()
{
if ($return = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_ATOM_10, 'rights')) {
return $this->sanitize($return[0]['data'], $this->registry->call(Misc::class, 'atom_10_construct_type', [$return[0]['attribs']]), $this->get_base($return[0]));
} elseif ($return = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_DC_11, 'rights')) {
return $this->sanitize($return[0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
} elseif ($return = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_DC_10, 'rights')) {
return $this->sanitize($return[0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
return null;
}
/**
* Get the posting date/time for the item
*
* Uses `<atom:published>`, `<atom:updated>`, `<atom:issued>`,
* `<atom:modified>`, `<pubDate>` or `<dc:date>`
*
* Note: obeys PHP's timezone setting. To get a UTC date/time, use
* {@see get_gmdate}
*
* @since Beta 2 (previously called `get_item_date` since 0.8)
*
* @param string $date_format Supports any PHP date format from {@see http://php.net/date} (empty for the raw data)
* @return int|string|null
*/
public function get_date($date_format = 'j F Y, g:i a')
{
if (!isset($this->data['date'])) {
if ($return = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_ATOM_10, 'published')) {
$this->data['date']['raw'] = $return[0]['data'];
} elseif ($return = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_RSS_20, 'pubDate')) {
$this->data['date']['raw'] = $return[0]['data'];
} elseif ($return = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_DC_11, 'date')) {
$this->data['date']['raw'] = $return[0]['data'];
} elseif ($return = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_DC_10, 'date')) {
$this->data['date']['raw'] = $return[0]['data'];
} elseif ($return = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_ATOM_10, 'updated')) {
$this->data['date']['raw'] = $return[0]['data'];
} elseif ($return = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_ATOM_03, 'issued')) {
$this->data['date']['raw'] = $return[0]['data'];
} elseif ($return = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_ATOM_03, 'created')) {
$this->data['date']['raw'] = $return[0]['data'];
} elseif ($return = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_ATOM_03, 'modified')) {
$this->data['date']['raw'] = $return[0]['data'];
}
if (!empty($this->data['date']['raw'])) {
$parser = $this->registry->call(Parse\Date::class, 'get');
$this->data['date']['parsed'] = $parser->parse($this->data['date']['raw']) ?: null;
} else {
$this->data['date'] = null;
}
}
if ($this->data['date']) {
$date_format = (string) $date_format;
switch ($date_format) {
case '':
return $this->sanitize($this->data['date']['raw'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
case 'U':
return $this->data['date']['parsed'];
default:
return date($date_format, $this->data['date']['parsed']);
}
}
return null;
}
/**
* Get the update date/time for the item
*
* Uses `<atom:updated>`
*
* Note: obeys PHP's timezone setting. To get a UTC date/time, use
* {@see get_gmdate}
*
* @param string $date_format Supports any PHP date format from {@see http://php.net/date} (empty for the raw data)
* @return int|string|null
*/
public function get_updated_date($date_format = 'j F Y, g:i a')
{
if (!isset($this->data['updated'])) {
if ($return = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_ATOM_10, 'updated')) {
$this->data['updated']['raw'] = $return[0]['data'];
}
if (!empty($this->data['updated']['raw'])) {
$parser = $this->registry->call(Parse\Date::class, 'get');
$this->data['updated']['parsed'] = $parser->parse($this->data['updated']['raw']) ?: null;
} else {
$this->data['updated'] = null;
}
}
if ($this->data['updated']) {
$date_format = (string) $date_format;
switch ($date_format) {
case '':
return $this->sanitize($this->data['updated']['raw'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
case 'U':
return $this->data['updated']['parsed'];
default:
return date($date_format, $this->data['updated']['parsed']);
}
}
return null;
}
/**
* Get the localized posting date/time for the item
*
* Returns the date formatted in the localized language. To display in
* languages other than the server's default, you need to change the locale
* with {@link http://php.net/setlocale setlocale()}. The available
* localizations depend on which ones are installed on your web server.
*
* @since 1.0
*
* @param string $date_format Supports any PHP date format from {@see http://php.net/strftime} (empty for the raw data)
* @return int|string|null
*/
public function get_local_date($date_format = '%c')
{
if (!$date_format) {
return $this->sanitize($this->get_date(''), \SimplePie\SimplePie::CONSTRUCT_TEXT);
} elseif (($date = $this->get_date('U')) !== null && $date !== false) {
return strftime($date_format, $date);
}
return null;
}
/**
* Get the posting date/time for the item (UTC time)
*
* @see get_date
* @param string $date_format Supports any PHP date format from {@see http://php.net/date}
* @return int|string|null
*/
public function get_gmdate($date_format = 'j F Y, g:i a')
{
$date = $this->get_date('U');
if ($date === null) {
return null;
}
return gmdate($date_format, $date);
}
/**
* Get the update date/time for the item (UTC time)
*
* @see get_updated_date
* @param string $date_format Supports any PHP date format from {@see http://php.net/date}
* @return int|string|null
*/
public function get_updated_gmdate($date_format = 'j F Y, g:i a')
{
$date = $this->get_updated_date('U');
if ($date === null) {
return null;
}
return gmdate($date_format, $date);
}
/**
* Get the permalink for the item
*
* Returns the first link available with a relationship of "alternate".
* Identical to {@see get_link()} with key 0
*
* @see get_link
* @since 0.8
* @return string|null Permalink URL
*/
public function get_permalink()
{
$link = $this->get_link();
$enclosure = $this->get_enclosure(0);
if ($link !== null) {
return $link;
} elseif ($enclosure !== null) {
return $enclosure->get_link();
}
return null;
}
/**
* Get a single link for the item
*
* @since Beta 3
* @param int $key The link that you want to return. Remember that arrays begin with 0, not 1
* @param string $rel The relationship of the link to return
* @return string|null Link URL
*/
public function get_link($key = 0, $rel = 'alternate')
{
$links = $this->get_links($rel);
if ($links && $links[$key] !== null) {
return $links[$key];
}
return null;
}
/**
* Get all links for the item
*
* Uses `<atom:link>`, `<link>` or `<guid>`
*
* @since Beta 2
* @param string $rel The relationship of links to return
* @return array|null Links found for the item (strings)
*/
public function get_links($rel = 'alternate')
{
if (!isset($this->data['links'])) {
$this->data['links'] = [];
foreach ((array) $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_ATOM_10, 'link') as $link) {
if (isset($link['attribs']['']['href'])) {
$link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate';
$this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], \SimplePie\SimplePie::CONSTRUCT_IRI, $this->get_base($link));
}
}
foreach ((array) $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_ATOM_03, 'link') as $link) {
if (isset($link['attribs']['']['href'])) {
$link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate';
$this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], \SimplePie\SimplePie::CONSTRUCT_IRI, $this->get_base($link));
}
}
if ($links = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_RSS_10, 'link')) {
$this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], \SimplePie\SimplePie::CONSTRUCT_IRI, $this->get_base($links[0]));
}
if ($links = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_RSS_090, 'link')) {
$this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], \SimplePie\SimplePie::CONSTRUCT_IRI, $this->get_base($links[0]));
}
if ($links = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_RSS_20, 'link')) {
$this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], \SimplePie\SimplePie::CONSTRUCT_IRI, $this->get_base($links[0]));
}
if ($links = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_RSS_20, 'guid')) {
if (!isset($links[0]['attribs']['']['isPermaLink']) || strtolower(trim($links[0]['attribs']['']['isPermaLink'])) === 'true') {
$this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], \SimplePie\SimplePie::CONSTRUCT_IRI, $this->get_base($links[0]));
}
}
$keys = array_keys($this->data['links']);
foreach ($keys as $key) {
if ($this->registry->call(Misc::class, 'is_isegment_nz_nc', [$key])) {
if (isset($this->data['links'][\SimplePie\SimplePie::IANA_LINK_RELATIONS_REGISTRY . $key])) {
$this->data['links'][\SimplePie\SimplePie::IANA_LINK_RELATIONS_REGISTRY . $key] = array_merge($this->data['links'][$key], $this->data['links'][\SimplePie\SimplePie::IANA_LINK_RELATIONS_REGISTRY . $key]);
$this->data['links'][$key] = &$this->data['links'][\SimplePie\SimplePie::IANA_LINK_RELATIONS_REGISTRY . $key];
} else {
$this->data['links'][\SimplePie\SimplePie::IANA_LINK_RELATIONS_REGISTRY . $key] = &$this->data['links'][$key];
}
} elseif (substr($key, 0, 41) === \SimplePie\SimplePie::IANA_LINK_RELATIONS_REGISTRY) {
$this->data['links'][substr($key, 41)] = &$this->data['links'][$key];
}
$this->data['links'][$key] = array_unique($this->data['links'][$key]);
}
}
if (isset($this->data['links'][$rel])) {
return $this->data['links'][$rel];
}
return null;
}
/**
* Get an enclosure from the item
*
* Supports the <enclosure> RSS tag, as well as Media RSS and iTunes RSS.
*
* @since Beta 2
* @todo Add ability to prefer one type of content over another (in a media group).
* @param int $key The enclosure that you want to return. Remember that arrays begin with 0, not 1
* @return \SimplePie\Enclosure|null
*/
public function get_enclosure($key = 0, $prefer = null)
{
$enclosures = $this->get_enclosures();
if (isset($enclosures[$key])) {
return $enclosures[$key];
}
return null;
}
/**
* Get all available enclosures (podcasts, etc.)
*
* Supports the <enclosure> RSS tag, as well as Media RSS and iTunes RSS.
*
* At this point, we're pretty much assuming that all enclosures for an item
* are the same content. Anything else is too complicated to
* properly support.
*
* @since Beta 2
* @todo Add support for end-user defined sorting of enclosures by type/handler (so we can prefer the faster-loading FLV over MP4).
* @todo If an element exists at a level, but its value is empty, we should fall back to the value from the parent (if it exists).
* @return \SimplePie\Enclosure[]|null List of \SimplePie\Enclosure items
*/
public function get_enclosures()
{
if (!isset($this->data['enclosures'])) {
$this->data['enclosures'] = [];
// Elements
$captions_parent = null;
$categories_parent = null;
$copyrights_parent = null;
$credits_parent = null;
$description_parent = null;
$duration_parent = null;
$hashes_parent = null;
$keywords_parent = null;
$player_parent = null;
$ratings_parent = null;
$restrictions_parent = null;
$thumbnails_parent = null;
$title_parent = null;
// Let's do the channel and item-level ones first, and just re-use them if we need to.
$parent = $this->get_feed();
// CAPTIONS
if ($captions = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_MEDIARSS, 'text')) {
foreach ($captions as $caption) {
$caption_type = null;
$caption_lang = null;
$caption_startTime = null;
$caption_endTime = null;
$caption_text = null;
if (isset($caption['attribs']['']['type'])) {
$caption_type = $this->sanitize($caption['attribs']['']['type'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($caption['attribs']['']['lang'])) {
$caption_lang = $this->sanitize($caption['attribs']['']['lang'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($caption['attribs']['']['start'])) {
$caption_startTime = $this->sanitize($caption['attribs']['']['start'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($caption['attribs']['']['end'])) {
$caption_endTime = $this->sanitize($caption['attribs']['']['end'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($caption['data'])) {
$caption_text = $this->sanitize($caption['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
$captions_parent[] = $this->registry->create(Caption::class, [$caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text]);
}
} elseif ($captions = $parent->get_channel_tags(\SimplePie\SimplePie::NAMESPACE_MEDIARSS, 'text')) {
foreach ($captions as $caption) {
$caption_type = null;
$caption_lang = null;
$caption_startTime = null;
$caption_endTime = null;
$caption_text = null;
if (isset($caption['attribs']['']['type'])) {
$caption_type = $this->sanitize($caption['attribs']['']['type'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($caption['attribs']['']['lang'])) {
$caption_lang = $this->sanitize($caption['attribs']['']['lang'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($caption['attribs']['']['start'])) {
$caption_startTime = $this->sanitize($caption['attribs']['']['start'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($caption['attribs']['']['end'])) {
$caption_endTime = $this->sanitize($caption['attribs']['']['end'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($caption['data'])) {
$caption_text = $this->sanitize($caption['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
$captions_parent[] = $this->registry->create(Caption::class, [$caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text]);
}
}
if (is_array($captions_parent)) {
$captions_parent = array_values(array_unique($captions_parent));
}
// CATEGORIES
foreach ((array) $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_MEDIARSS, 'category') as $category) {
$term = null;
$scheme = null;
$label = null;
if (isset($category['data'])) {
$term = $this->sanitize($category['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($category['attribs']['']['scheme'])) {
$scheme = $this->sanitize($category['attribs']['']['scheme'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
} else {
$scheme = 'http://search.yahoo.com/mrss/category_schema';
}
if (isset($category['attribs']['']['label'])) {
$label = $this->sanitize($category['attribs']['']['label'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
$categories_parent[] = $this->registry->create(Category::class, [$term, $scheme, $label]);
}
foreach ((array) $parent->get_channel_tags(\SimplePie\SimplePie::NAMESPACE_MEDIARSS, 'category') as $category) {
$term = null;
$scheme = null;
$label = null;
if (isset($category['data'])) {
$term = $this->sanitize($category['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($category['attribs']['']['scheme'])) {
$scheme = $this->sanitize($category['attribs']['']['scheme'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
} else {
$scheme = 'http://search.yahoo.com/mrss/category_schema';
}
if (isset($category['attribs']['']['label'])) {
$label = $this->sanitize($category['attribs']['']['label'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
$categories_parent[] = $this->registry->create(Category::class, [$term, $scheme, $label]);
}
foreach ((array) $parent->get_channel_tags(\SimplePie\SimplePie::NAMESPACE_ITUNES, 'category') as $category) {
$term = null;
$scheme = 'http://www.itunes.com/dtds/podcast-1.0.dtd';
$label = null;
if (isset($category['attribs']['']['text'])) {
$label = $this->sanitize($category['attribs']['']['text'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
$categories_parent[] = $this->registry->create(Category::class, [$term, $scheme, $label]);
if (isset($category['child'][\SimplePie\SimplePie::NAMESPACE_ITUNES]['category'])) {
foreach ((array) $category['child'][\SimplePie\SimplePie::NAMESPACE_ITUNES]['category'] as $subcategory) {
if (isset($subcategory['attribs']['']['text'])) {
$label = $this->sanitize($subcategory['attribs']['']['text'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
$categories_parent[] = $this->registry->create(Category::class, [$term, $scheme, $label]);
}
}
}
if (is_array($categories_parent)) {
$categories_parent = array_values(array_unique($categories_parent));
}
// COPYRIGHT
if ($copyright = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_MEDIARSS, 'copyright')) {
$copyright_url = null;
$copyright_label = null;
if (isset($copyright[0]['attribs']['']['url'])) {
$copyright_url = $this->sanitize($copyright[0]['attribs']['']['url'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($copyright[0]['data'])) {
$copyright_label = $this->sanitize($copyright[0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
$copyrights_parent = $this->registry->create(Copyright::class, [$copyright_url, $copyright_label]);
} elseif ($copyright = $parent->get_channel_tags(\SimplePie\SimplePie::NAMESPACE_MEDIARSS, 'copyright')) {
$copyright_url = null;
$copyright_label = null;
if (isset($copyright[0]['attribs']['']['url'])) {
$copyright_url = $this->sanitize($copyright[0]['attribs']['']['url'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($copyright[0]['data'])) {
$copyright_label = $this->sanitize($copyright[0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
$copyrights_parent = $this->registry->create(Copyright::class, [$copyright_url, $copyright_label]);
}
// CREDITS
if ($credits = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_MEDIARSS, 'credit')) {
foreach ($credits as $credit) {
$credit_role = null;
$credit_scheme = null;
$credit_name = null;
if (isset($credit['attribs']['']['role'])) {
$credit_role = $this->sanitize($credit['attribs']['']['role'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($credit['attribs']['']['scheme'])) {
$credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
} else {
$credit_scheme = 'urn:ebu';
}
if (isset($credit['data'])) {
$credit_name = $this->sanitize($credit['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
$credits_parent[] = $this->registry->create(Credit::class, [$credit_role, $credit_scheme, $credit_name]);
}
} elseif ($credits = $parent->get_channel_tags(\SimplePie\SimplePie::NAMESPACE_MEDIARSS, 'credit')) {
foreach ($credits as $credit) {
$credit_role = null;
$credit_scheme = null;
$credit_name = null;
if (isset($credit['attribs']['']['role'])) {
$credit_role = $this->sanitize($credit['attribs']['']['role'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($credit['attribs']['']['scheme'])) {
$credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
} else {
$credit_scheme = 'urn:ebu';
}
if (isset($credit['data'])) {
$credit_name = $this->sanitize($credit['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
$credits_parent[] = $this->registry->create(Credit::class, [$credit_role, $credit_scheme, $credit_name]);
}
}
if (is_array($credits_parent)) {
$credits_parent = array_values(array_unique($credits_parent));
}
// DESCRIPTION
if ($description_parent = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_MEDIARSS, 'description')) {
if (isset($description_parent[0]['data'])) {
$description_parent = $this->sanitize($description_parent[0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
} elseif ($description_parent = $parent->get_channel_tags(\SimplePie\SimplePie::NAMESPACE_MEDIARSS, 'description')) {
if (isset($description_parent[0]['data'])) {
$description_parent = $this->sanitize($description_parent[0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
}
// DURATION
if ($duration_parent = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_ITUNES, 'duration')) {
$seconds = null;
$minutes = null;
$hours = null;
if (isset($duration_parent[0]['data'])) {
$temp = explode(':', $this->sanitize($duration_parent[0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT));
if (sizeof($temp) > 0) {
$seconds = (int) array_pop($temp);
}
if (sizeof($temp) > 0) {
$minutes = (int) array_pop($temp);
$seconds += $minutes * 60;
}
if (sizeof($temp) > 0) {
$hours = (int) array_pop($temp);
$seconds += $hours * 3600;
}
unset($temp);
$duration_parent = $seconds;
}
}
// HASHES
if ($hashes_iterator = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_MEDIARSS, 'hash')) {
foreach ($hashes_iterator as $hash) {
$value = null;
$algo = null;
if (isset($hash['data'])) {
$value = $this->sanitize($hash['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($hash['attribs']['']['algo'])) {
$algo = $this->sanitize($hash['attribs']['']['algo'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
} else {
$algo = 'md5';
}
$hashes_parent[] = $algo.':'.$value;
}
} elseif ($hashes_iterator = $parent->get_channel_tags(\SimplePie\SimplePie::NAMESPACE_MEDIARSS, 'hash')) {
foreach ($hashes_iterator as $hash) {
$value = null;
$algo = null;
if (isset($hash['data'])) {
$value = $this->sanitize($hash['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($hash['attribs']['']['algo'])) {
$algo = $this->sanitize($hash['attribs']['']['algo'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
} else {
$algo = 'md5';
}
$hashes_parent[] = $algo.':'.$value;
}
}
if (is_array($hashes_parent)) {
$hashes_parent = array_values(array_unique($hashes_parent));
}
// KEYWORDS
if ($keywords = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_MEDIARSS, 'keywords')) {
if (isset($keywords[0]['data'])) {
$temp = explode(',', $this->sanitize($keywords[0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT));
foreach ($temp as $word) {
$keywords_parent[] = trim($word);
}
}
unset($temp);
} elseif ($keywords = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_ITUNES, 'keywords')) {
if (isset($keywords[0]['data'])) {
$temp = explode(',', $this->sanitize($keywords[0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT));
foreach ($temp as $word) {
$keywords_parent[] = trim($word);
}
}
unset($temp);
} elseif ($keywords = $parent->get_channel_tags(\SimplePie\SimplePie::NAMESPACE_MEDIARSS, 'keywords')) {
if (isset($keywords[0]['data'])) {
$temp = explode(',', $this->sanitize($keywords[0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT));
foreach ($temp as $word) {
$keywords_parent[] = trim($word);
}
}
unset($temp);
} elseif ($keywords = $parent->get_channel_tags(\SimplePie\SimplePie::NAMESPACE_ITUNES, 'keywords')) {
if (isset($keywords[0]['data'])) {
$temp = explode(',', $this->sanitize($keywords[0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT));
foreach ($temp as $word) {
$keywords_parent[] = trim($word);
}
}
unset($temp);
}
if (is_array($keywords_parent)) {
$keywords_parent = array_values(array_unique($keywords_parent));
}
// PLAYER
if ($player_parent = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_MEDIARSS, 'player')) {
if (isset($player_parent[0]['attribs']['']['url'])) {
$player_parent = $this->sanitize($player_parent[0]['attribs']['']['url'], \SimplePie\SimplePie::CONSTRUCT_IRI);
}
} elseif ($player_parent = $parent->get_channel_tags(\SimplePie\SimplePie::NAMESPACE_MEDIARSS, 'player')) {
if (isset($player_parent[0]['attribs']['']['url'])) {
$player_parent = $this->sanitize($player_parent[0]['attribs']['']['url'], \SimplePie\SimplePie::CONSTRUCT_IRI);
}
}
// RATINGS
if ($ratings = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_MEDIARSS, 'rating')) {
foreach ($ratings as $rating) {
$rating_scheme = null;
$rating_value = null;
if (isset($rating['attribs']['']['scheme'])) {
$rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
} else {
$rating_scheme = 'urn:simple';
}
if (isset($rating['data'])) {
$rating_value = $this->sanitize($rating['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
$ratings_parent[] = $this->registry->create(Rating::class, [$rating_scheme, $rating_value]);
}
} elseif ($ratings = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_ITUNES, 'explicit')) {
foreach ($ratings as $rating) {
$rating_scheme = 'urn:itunes';
$rating_value = null;
if (isset($rating['data'])) {
$rating_value = $this->sanitize($rating['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
$ratings_parent[] = $this->registry->create(Rating::class, [$rating_scheme, $rating_value]);
}
} elseif ($ratings = $parent->get_channel_tags(\SimplePie\SimplePie::NAMESPACE_MEDIARSS, 'rating')) {
foreach ($ratings as $rating) {
$rating_scheme = null;
$rating_value = null;
if (isset($rating['attribs']['']['scheme'])) {
$rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
} else {
$rating_scheme = 'urn:simple';
}
if (isset($rating['data'])) {
$rating_value = $this->sanitize($rating['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
$ratings_parent[] = $this->registry->create(Rating::class, [$rating_scheme, $rating_value]);
}
} elseif ($ratings = $parent->get_channel_tags(\SimplePie\SimplePie::NAMESPACE_ITUNES, 'explicit')) {
foreach ($ratings as $rating) {
$rating_scheme = 'urn:itunes';
$rating_value = null;
if (isset($rating['data'])) {
$rating_value = $this->sanitize($rating['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
$ratings_parent[] = $this->registry->create(Rating::class, [$rating_scheme, $rating_value]);
}
}
if (is_array($ratings_parent)) {
$ratings_parent = array_values(array_unique($ratings_parent));
}
// RESTRICTIONS
if ($restrictions = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_MEDIARSS, 'restriction')) {
foreach ($restrictions as $restriction) {
$restriction_relationship = null;
$restriction_type = null;
$restriction_value = null;
if (isset($restriction['attribs']['']['relationship'])) {
$restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($restriction['attribs']['']['type'])) {
$restriction_type = $this->sanitize($restriction['attribs']['']['type'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($restriction['data'])) {
$restriction_value = $this->sanitize($restriction['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
$restrictions_parent[] = $this->registry->create(Restriction::class, [$restriction_relationship, $restriction_type, $restriction_value]);
}
} elseif ($restrictions = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_ITUNES, 'block')) {
foreach ($restrictions as $restriction) {
$restriction_relationship = 'allow';
$restriction_type = null;
$restriction_value = 'itunes';
if (isset($restriction['data']) && strtolower($restriction['data']) === 'yes') {
$restriction_relationship = 'deny';
}
$restrictions_parent[] = $this->registry->create(Restriction::class, [$restriction_relationship, $restriction_type, $restriction_value]);
}
} elseif ($restrictions = $parent->get_channel_tags(\SimplePie\SimplePie::NAMESPACE_MEDIARSS, 'restriction')) {
foreach ($restrictions as $restriction) {
$restriction_relationship = null;
$restriction_type = null;
$restriction_value = null;
if (isset($restriction['attribs']['']['relationship'])) {
$restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($restriction['attribs']['']['type'])) {
$restriction_type = $this->sanitize($restriction['attribs']['']['type'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($restriction['data'])) {
$restriction_value = $this->sanitize($restriction['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
$restrictions_parent[] = $this->registry->create(Restriction::class, [$restriction_relationship, $restriction_type, $restriction_value]);
}
} elseif ($restrictions = $parent->get_channel_tags(\SimplePie\SimplePie::NAMESPACE_ITUNES, 'block')) {
foreach ($restrictions as $restriction) {
$restriction_relationship = 'allow';
$restriction_type = null;
$restriction_value = 'itunes';
if (isset($restriction['data']) && strtolower($restriction['data']) === 'yes') {
$restriction_relationship = 'deny';
}
$restrictions_parent[] = $this->registry->create(Restriction::class, [$restriction_relationship, $restriction_type, $restriction_value]);
}
}
if (is_array($restrictions_parent)) {
$restrictions_parent = array_values(array_unique($restrictions_parent));
} else {
$restrictions_parent = [new \SimplePie\Restriction('allow', null, 'default')];
}
// THUMBNAILS
if ($thumbnails = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_MEDIARSS, 'thumbnail')) {
foreach ($thumbnails as $thumbnail) {
if (isset($thumbnail['attribs']['']['url'])) {
$thumbnails_parent[] = $this->sanitize($thumbnail['attribs']['']['url'], \SimplePie\SimplePie::CONSTRUCT_IRI);
}
}
} elseif ($thumbnails = $parent->get_channel_tags(\SimplePie\SimplePie::NAMESPACE_MEDIARSS, 'thumbnail')) {
foreach ($thumbnails as $thumbnail) {
if (isset($thumbnail['attribs']['']['url'])) {
$thumbnails_parent[] = $this->sanitize($thumbnail['attribs']['']['url'], \SimplePie\SimplePie::CONSTRUCT_IRI);
}
}
}
// TITLES
if ($title_parent = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_MEDIARSS, 'title')) {
if (isset($title_parent[0]['data'])) {
$title_parent = $this->sanitize($title_parent[0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
} elseif ($title_parent = $parent->get_channel_tags(\SimplePie\SimplePie::NAMESPACE_MEDIARSS, 'title')) {
if (isset($title_parent[0]['data'])) {
$title_parent = $this->sanitize($title_parent[0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
}
// Clear the memory
unset($parent);
// Attributes
$bitrate = null;
$channels = null;
$duration = null;
$expression = null;
$framerate = null;
$height = null;
$javascript = null;
$lang = null;
$length = null;
$medium = null;
$samplingrate = null;
$type = null;
$url = null;
$width = null;
// Elements
$captions = null;
$categories = null;
$copyrights = null;
$credits = null;
$description = null;
$hashes = null;
$keywords = null;
$player = null;
$ratings = null;
$restrictions = null;
$thumbnails = null;
$title = null;
// If we have media:group tags, loop through them.
foreach ((array) $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_MEDIARSS, 'group') as $group) {
if (isset($group['child']) && isset($group['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['content'])) {
// If we have media:content tags, loop through them.
foreach ((array) $group['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['content'] as $content) {
if (isset($content['attribs']['']['url'])) {
// Attributes
$bitrate = null;
$channels = null;
$duration = null;
$expression = null;
$framerate = null;
$height = null;
$javascript = null;
$lang = null;
$length = null;
$medium = null;
$samplingrate = null;
$type = null;
$url = null;
$width = null;
// Elements
$captions = null;
$categories = null;
$copyrights = null;
$credits = null;
$description = null;
$hashes = null;
$keywords = null;
$player = null;
$ratings = null;
$restrictions = null;
$thumbnails = null;
$title = null;
// Start checking the attributes of media:content
if (isset($content['attribs']['']['bitrate'])) {
$bitrate = $this->sanitize($content['attribs']['']['bitrate'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($content['attribs']['']['channels'])) {
$channels = $this->sanitize($content['attribs']['']['channels'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($content['attribs']['']['duration'])) {
$duration = $this->sanitize($content['attribs']['']['duration'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
} else {
$duration = $duration_parent;
}
if (isset($content['attribs']['']['expression'])) {
$expression = $this->sanitize($content['attribs']['']['expression'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($content['attribs']['']['framerate'])) {
$framerate = $this->sanitize($content['attribs']['']['framerate'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($content['attribs']['']['height'])) {
$height = $this->sanitize($content['attribs']['']['height'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($content['attribs']['']['lang'])) {
$lang = $this->sanitize($content['attribs']['']['lang'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($content['attribs']['']['fileSize'])) {
$length = intval($content['attribs']['']['fileSize']);
}
if (isset($content['attribs']['']['medium'])) {
$medium = $this->sanitize($content['attribs']['']['medium'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($content['attribs']['']['samplingrate'])) {
$samplingrate = $this->sanitize($content['attribs']['']['samplingrate'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($content['attribs']['']['type'])) {
$type = $this->sanitize($content['attribs']['']['type'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($content['attribs']['']['width'])) {
$width = $this->sanitize($content['attribs']['']['width'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
$url = $this->sanitize($content['attribs']['']['url'], \SimplePie\SimplePie::CONSTRUCT_IRI);
// Checking the other optional media: elements. Priority: media:content, media:group, item, channel
// CAPTIONS
if (isset($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['text'])) {
foreach ($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['text'] as $caption) {
$caption_type = null;
$caption_lang = null;
$caption_startTime = null;
$caption_endTime = null;
$caption_text = null;
if (isset($caption['attribs']['']['type'])) {
$caption_type = $this->sanitize($caption['attribs']['']['type'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($caption['attribs']['']['lang'])) {
$caption_lang = $this->sanitize($caption['attribs']['']['lang'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($caption['attribs']['']['start'])) {
$caption_startTime = $this->sanitize($caption['attribs']['']['start'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($caption['attribs']['']['end'])) {
$caption_endTime = $this->sanitize($caption['attribs']['']['end'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($caption['data'])) {
$caption_text = $this->sanitize($caption['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
$captions[] = $this->registry->create(Caption::class, [$caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text]);
}
if (is_array($captions)) {
$captions = array_values(array_unique($captions));
}
} elseif (isset($group['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['text'])) {
foreach ($group['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['text'] as $caption) {
$caption_type = null;
$caption_lang = null;
$caption_startTime = null;
$caption_endTime = null;
$caption_text = null;
if (isset($caption['attribs']['']['type'])) {
$caption_type = $this->sanitize($caption['attribs']['']['type'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($caption['attribs']['']['lang'])) {
$caption_lang = $this->sanitize($caption['attribs']['']['lang'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($caption['attribs']['']['start'])) {
$caption_startTime = $this->sanitize($caption['attribs']['']['start'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($caption['attribs']['']['end'])) {
$caption_endTime = $this->sanitize($caption['attribs']['']['end'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($caption['data'])) {
$caption_text = $this->sanitize($caption['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
$captions[] = $this->registry->create(Caption::class, [$caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text]);
}
if (is_array($captions)) {
$captions = array_values(array_unique($captions));
}
} else {
$captions = $captions_parent;
}
// CATEGORIES
if (isset($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['category'])) {
foreach ((array) $content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['category'] as $category) {
$term = null;
$scheme = null;
$label = null;
if (isset($category['data'])) {
$term = $this->sanitize($category['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($category['attribs']['']['scheme'])) {
$scheme = $this->sanitize($category['attribs']['']['scheme'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
} else {
$scheme = 'http://search.yahoo.com/mrss/category_schema';
}
if (isset($category['attribs']['']['label'])) {
$label = $this->sanitize($category['attribs']['']['label'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
$categories[] = $this->registry->create(Category::class, [$term, $scheme, $label]);
}
}
if (isset($group['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['category'])) {
foreach ((array) $group['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['category'] as $category) {
$term = null;
$scheme = null;
$label = null;
if (isset($category['data'])) {
$term = $this->sanitize($category['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($category['attribs']['']['scheme'])) {
$scheme = $this->sanitize($category['attribs']['']['scheme'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
} else {
$scheme = 'http://search.yahoo.com/mrss/category_schema';
}
if (isset($category['attribs']['']['label'])) {
$label = $this->sanitize($category['attribs']['']['label'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
$categories[] = $this->registry->create(Category::class, [$term, $scheme, $label]);
}
}
if (is_array($categories) && is_array($categories_parent)) {
$categories = array_values(array_unique(array_merge($categories, $categories_parent)));
} elseif (is_array($categories)) {
$categories = array_values(array_unique($categories));
} elseif (is_array($categories_parent)) {
$categories = array_values(array_unique($categories_parent));
}
// COPYRIGHTS
if (isset($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['copyright'])) {
$copyright_url = null;
$copyright_label = null;
if (isset($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'])) {
$copyright_url = $this->sanitize($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['copyright'][0]['data'])) {
$copyright_label = $this->sanitize($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['copyright'][0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
$copyrights = $this->registry->create(Copyright::class, [$copyright_url, $copyright_label]);
} elseif (isset($group['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['copyright'])) {
$copyright_url = null;
$copyright_label = null;
if (isset($group['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'])) {
$copyright_url = $this->sanitize($group['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($group['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['copyright'][0]['data'])) {
$copyright_label = $this->sanitize($group['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['copyright'][0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
$copyrights = $this->registry->create(Copyright::class, [$copyright_url, $copyright_label]);
} else {
$copyrights = $copyrights_parent;
}
// CREDITS
if (isset($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['credit'])) {
foreach ($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['credit'] as $credit) {
$credit_role = null;
$credit_scheme = null;
$credit_name = null;
if (isset($credit['attribs']['']['role'])) {
$credit_role = $this->sanitize($credit['attribs']['']['role'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($credit['attribs']['']['scheme'])) {
$credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
} else {
$credit_scheme = 'urn:ebu';
}
if (isset($credit['data'])) {
$credit_name = $this->sanitize($credit['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
$credits[] = $this->registry->create(Credit::class, [$credit_role, $credit_scheme, $credit_name]);
}
if (is_array($credits)) {
$credits = array_values(array_unique($credits));
}
} elseif (isset($group['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['credit'])) {
foreach ($group['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['credit'] as $credit) {
$credit_role = null;
$credit_scheme = null;
$credit_name = null;
if (isset($credit['attribs']['']['role'])) {
$credit_role = $this->sanitize($credit['attribs']['']['role'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($credit['attribs']['']['scheme'])) {
$credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
} else {
$credit_scheme = 'urn:ebu';
}
if (isset($credit['data'])) {
$credit_name = $this->sanitize($credit['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
$credits[] = $this->registry->create(Credit::class, [$credit_role, $credit_scheme, $credit_name]);
}
if (is_array($credits)) {
$credits = array_values(array_unique($credits));
}
} else {
$credits = $credits_parent;
}
// DESCRIPTION
if (isset($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['description'])) {
$description = $this->sanitize($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['description'][0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
} elseif (isset($group['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['description'])) {
$description = $this->sanitize($group['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['description'][0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
} else {
$description = $description_parent;
}
// HASHES
if (isset($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['hash'])) {
foreach ($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['hash'] as $hash) {
$value = null;
$algo = null;
if (isset($hash['data'])) {
$value = $this->sanitize($hash['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($hash['attribs']['']['algo'])) {
$algo = $this->sanitize($hash['attribs']['']['algo'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
} else {
$algo = 'md5';
}
$hashes[] = $algo.':'.$value;
}
if (is_array($hashes)) {
$hashes = array_values(array_unique($hashes));
}
} elseif (isset($group['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['hash'])) {
foreach ($group['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['hash'] as $hash) {
$value = null;
$algo = null;
if (isset($hash['data'])) {
$value = $this->sanitize($hash['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($hash['attribs']['']['algo'])) {
$algo = $this->sanitize($hash['attribs']['']['algo'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
} else {
$algo = 'md5';
}
$hashes[] = $algo.':'.$value;
}
if (is_array($hashes)) {
$hashes = array_values(array_unique($hashes));
}
} else {
$hashes = $hashes_parent;
}
// KEYWORDS
if (isset($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['keywords'])) {
if (isset($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['keywords'][0]['data'])) {
$temp = explode(',', $this->sanitize($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['keywords'][0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT));
foreach ($temp as $word) {
$keywords[] = trim($word);
}
unset($temp);
}
if (is_array($keywords)) {
$keywords = array_values(array_unique($keywords));
}
} elseif (isset($group['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['keywords'])) {
if (isset($group['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['keywords'][0]['data'])) {
$temp = explode(',', $this->sanitize($group['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['keywords'][0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT));
foreach ($temp as $word) {
$keywords[] = trim($word);
}
unset($temp);
}
if (is_array($keywords)) {
$keywords = array_values(array_unique($keywords));
}
} else {
$keywords = $keywords_parent;
}
// PLAYER
if (isset($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['player'])) {
$player = $this->sanitize($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['player'][0]['attribs']['']['url'], \SimplePie\SimplePie::CONSTRUCT_IRI);
} elseif (isset($group['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['player'])) {
$player = $this->sanitize($group['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['player'][0]['attribs']['']['url'], \SimplePie\SimplePie::CONSTRUCT_IRI);
} else {
$player = $player_parent;
}
// RATINGS
if (isset($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['rating'])) {
foreach ($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['rating'] as $rating) {
$rating_scheme = null;
$rating_value = null;
if (isset($rating['attribs']['']['scheme'])) {
$rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
} else {
$rating_scheme = 'urn:simple';
}
if (isset($rating['data'])) {
$rating_value = $this->sanitize($rating['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
$ratings[] = $this->registry->create(Rating::class, [$rating_scheme, $rating_value]);
}
if (is_array($ratings)) {
$ratings = array_values(array_unique($ratings));
}
} elseif (isset($group['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['rating'])) {
foreach ($group['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['rating'] as $rating) {
$rating_scheme = null;
$rating_value = null;
if (isset($rating['attribs']['']['scheme'])) {
$rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
} else {
$rating_scheme = 'urn:simple';
}
if (isset($rating['data'])) {
$rating_value = $this->sanitize($rating['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
$ratings[] = $this->registry->create(Rating::class, [$rating_scheme, $rating_value]);
}
if (is_array($ratings)) {
$ratings = array_values(array_unique($ratings));
}
} else {
$ratings = $ratings_parent;
}
// RESTRICTIONS
if (isset($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['restriction'])) {
foreach ($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['restriction'] as $restriction) {
$restriction_relationship = null;
$restriction_type = null;
$restriction_value = null;
if (isset($restriction['attribs']['']['relationship'])) {
$restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($restriction['attribs']['']['type'])) {
$restriction_type = $this->sanitize($restriction['attribs']['']['type'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($restriction['data'])) {
$restriction_value = $this->sanitize($restriction['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
$restrictions[] = $this->registry->create(Restriction::class, [$restriction_relationship, $restriction_type, $restriction_value]);
}
if (is_array($restrictions)) {
$restrictions = array_values(array_unique($restrictions));
}
} elseif (isset($group['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['restriction'])) {
foreach ($group['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['restriction'] as $restriction) {
$restriction_relationship = null;
$restriction_type = null;
$restriction_value = null;
if (isset($restriction['attribs']['']['relationship'])) {
$restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($restriction['attribs']['']['type'])) {
$restriction_type = $this->sanitize($restriction['attribs']['']['type'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($restriction['data'])) {
$restriction_value = $this->sanitize($restriction['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
$restrictions[] = $this->registry->create(Restriction::class, [$restriction_relationship, $restriction_type, $restriction_value]);
}
if (is_array($restrictions)) {
$restrictions = array_values(array_unique($restrictions));
}
} else {
$restrictions = $restrictions_parent;
}
// THUMBNAILS
if (isset($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['thumbnail'])) {
foreach ($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['thumbnail'] as $thumbnail) {
$thumbnails[] = $this->sanitize($thumbnail['attribs']['']['url'], \SimplePie\SimplePie::CONSTRUCT_IRI);
}
if (is_array($thumbnails)) {
$thumbnails = array_values(array_unique($thumbnails));
}
} elseif (isset($group['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['thumbnail'])) {
foreach ($group['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['thumbnail'] as $thumbnail) {
$thumbnails[] = $this->sanitize($thumbnail['attribs']['']['url'], \SimplePie\SimplePie::CONSTRUCT_IRI);
}
if (is_array($thumbnails)) {
$thumbnails = array_values(array_unique($thumbnails));
}
} else {
$thumbnails = $thumbnails_parent;
}
// TITLES
if (isset($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['title'])) {
$title = $this->sanitize($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['title'][0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
} elseif (isset($group['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['title'])) {
$title = $this->sanitize($group['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['title'][0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
} else {
$title = $title_parent;
}
$this->data['enclosures'][] = $this->registry->create(Enclosure::class, [$url, $type, $length, null, $bitrate, $captions, $categories, $channels, $copyrights, $credits, $description, $duration, $expression, $framerate, $hashes, $height, $keywords, $lang, $medium, $player, $ratings, $restrictions, $samplingrate, $thumbnails, $title, $width]);
}
}
}
}
// If we have standalone media:content tags, loop through them.
if (isset($this->data['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['content'])) {
foreach ((array) $this->data['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['content'] as $content) {
if (isset($content['attribs']['']['url']) || isset($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['player'])) {
// Attributes
$bitrate = null;
$channels = null;
$duration = null;
$expression = null;
$framerate = null;
$height = null;
$javascript = null;
$lang = null;
$length = null;
$medium = null;
$samplingrate = null;
$type = null;
$url = null;
$width = null;
// Elements
$captions = null;
$categories = null;
$copyrights = null;
$credits = null;
$description = null;
$hashes = null;
$keywords = null;
$player = null;
$ratings = null;
$restrictions = null;
$thumbnails = null;
$title = null;
// Start checking the attributes of media:content
if (isset($content['attribs']['']['bitrate'])) {
$bitrate = $this->sanitize($content['attribs']['']['bitrate'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($content['attribs']['']['channels'])) {
$channels = $this->sanitize($content['attribs']['']['channels'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($content['attribs']['']['duration'])) {
$duration = $this->sanitize($content['attribs']['']['duration'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
} else {
$duration = $duration_parent;
}
if (isset($content['attribs']['']['expression'])) {
$expression = $this->sanitize($content['attribs']['']['expression'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($content['attribs']['']['framerate'])) {
$framerate = $this->sanitize($content['attribs']['']['framerate'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($content['attribs']['']['height'])) {
$height = $this->sanitize($content['attribs']['']['height'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($content['attribs']['']['lang'])) {
$lang = $this->sanitize($content['attribs']['']['lang'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($content['attribs']['']['fileSize'])) {
$length = intval($content['attribs']['']['fileSize']);
}
if (isset($content['attribs']['']['medium'])) {
$medium = $this->sanitize($content['attribs']['']['medium'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($content['attribs']['']['samplingrate'])) {
$samplingrate = $this->sanitize($content['attribs']['']['samplingrate'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($content['attribs']['']['type'])) {
$type = $this->sanitize($content['attribs']['']['type'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($content['attribs']['']['width'])) {
$width = $this->sanitize($content['attribs']['']['width'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($content['attribs']['']['url'])) {
$url = $this->sanitize($content['attribs']['']['url'], \SimplePie\SimplePie::CONSTRUCT_IRI);
}
// Checking the other optional media: elements. Priority: media:content, media:group, item, channel
// CAPTIONS
if (isset($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['text'])) {
foreach ($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['text'] as $caption) {
$caption_type = null;
$caption_lang = null;
$caption_startTime = null;
$caption_endTime = null;
$caption_text = null;
if (isset($caption['attribs']['']['type'])) {
$caption_type = $this->sanitize($caption['attribs']['']['type'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($caption['attribs']['']['lang'])) {
$caption_lang = $this->sanitize($caption['attribs']['']['lang'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($caption['attribs']['']['start'])) {
$caption_startTime = $this->sanitize($caption['attribs']['']['start'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($caption['attribs']['']['end'])) {
$caption_endTime = $this->sanitize($caption['attribs']['']['end'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($caption['data'])) {
$caption_text = $this->sanitize($caption['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
$captions[] = $this->registry->create(Caption::class, [$caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text]);
}
if (is_array($captions)) {
$captions = array_values(array_unique($captions));
}
} else {
$captions = $captions_parent;
}
// CATEGORIES
if (isset($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['category'])) {
foreach ((array) $content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['category'] as $category) {
$term = null;
$scheme = null;
$label = null;
if (isset($category['data'])) {
$term = $this->sanitize($category['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($category['attribs']['']['scheme'])) {
$scheme = $this->sanitize($category['attribs']['']['scheme'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
} else {
$scheme = 'http://search.yahoo.com/mrss/category_schema';
}
if (isset($category['attribs']['']['label'])) {
$label = $this->sanitize($category['attribs']['']['label'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
$categories[] = $this->registry->create(Category::class, [$term, $scheme, $label]);
}
}
if (is_array($categories) && is_array($categories_parent)) {
$categories = array_values(array_unique(array_merge($categories, $categories_parent)));
} elseif (is_array($categories)) {
$categories = array_values(array_unique($categories));
} elseif (is_array($categories_parent)) {
$categories = array_values(array_unique($categories_parent));
} else {
$categories = null;
}
// COPYRIGHTS
if (isset($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['copyright'])) {
$copyright_url = null;
$copyright_label = null;
if (isset($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'])) {
$copyright_url = $this->sanitize($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['copyright'][0]['data'])) {
$copyright_label = $this->sanitize($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['copyright'][0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
$copyrights = $this->registry->create(Copyright::class, [$copyright_url, $copyright_label]);
} else {
$copyrights = $copyrights_parent;
}
// CREDITS
if (isset($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['credit'])) {
foreach ($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['credit'] as $credit) {
$credit_role = null;
$credit_scheme = null;
$credit_name = null;
if (isset($credit['attribs']['']['role'])) {
$credit_role = $this->sanitize($credit['attribs']['']['role'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($credit['attribs']['']['scheme'])) {
$credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
} else {
$credit_scheme = 'urn:ebu';
}
if (isset($credit['data'])) {
$credit_name = $this->sanitize($credit['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
$credits[] = $this->registry->create(Credit::class, [$credit_role, $credit_scheme, $credit_name]);
}
if (is_array($credits)) {
$credits = array_values(array_unique($credits));
}
} else {
$credits = $credits_parent;
}
// DESCRIPTION
if (isset($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['description'])) {
$description = $this->sanitize($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['description'][0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
} else {
$description = $description_parent;
}
// HASHES
if (isset($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['hash'])) {
foreach ($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['hash'] as $hash) {
$value = null;
$algo = null;
if (isset($hash['data'])) {
$value = $this->sanitize($hash['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($hash['attribs']['']['algo'])) {
$algo = $this->sanitize($hash['attribs']['']['algo'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
} else {
$algo = 'md5';
}
$hashes[] = $algo.':'.$value;
}
if (is_array($hashes)) {
$hashes = array_values(array_unique($hashes));
}
} else {
$hashes = $hashes_parent;
}
// KEYWORDS
if (isset($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['keywords'])) {
if (isset($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['keywords'][0]['data'])) {
$temp = explode(',', $this->sanitize($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['keywords'][0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT));
foreach ($temp as $word) {
$keywords[] = trim($word);
}
unset($temp);
}
if (is_array($keywords)) {
$keywords = array_values(array_unique($keywords));
}
} else {
$keywords = $keywords_parent;
}
// PLAYER
if (isset($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['player'])) {
if (isset($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['player'][0]['attribs']['']['url'])) {
$player = $this->sanitize($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['player'][0]['attribs']['']['url'], \SimplePie\SimplePie::CONSTRUCT_IRI);
}
} else {
$player = $player_parent;
}
// RATINGS
if (isset($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['rating'])) {
foreach ($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['rating'] as $rating) {
$rating_scheme = null;
$rating_value = null;
if (isset($rating['attribs']['']['scheme'])) {
$rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
} else {
$rating_scheme = 'urn:simple';
}
if (isset($rating['data'])) {
$rating_value = $this->sanitize($rating['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
$ratings[] = $this->registry->create(Rating::class, [$rating_scheme, $rating_value]);
}
if (is_array($ratings)) {
$ratings = array_values(array_unique($ratings));
}
} else {
$ratings = $ratings_parent;
}
// RESTRICTIONS
if (isset($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['restriction'])) {
foreach ($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['restriction'] as $restriction) {
$restriction_relationship = null;
$restriction_type = null;
$restriction_value = null;
if (isset($restriction['attribs']['']['relationship'])) {
$restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($restriction['attribs']['']['type'])) {
$restriction_type = $this->sanitize($restriction['attribs']['']['type'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($restriction['data'])) {
$restriction_value = $this->sanitize($restriction['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
$restrictions[] = $this->registry->create(Restriction::class, [$restriction_relationship, $restriction_type, $restriction_value]);
}
if (is_array($restrictions)) {
$restrictions = array_values(array_unique($restrictions));
}
} else {
$restrictions = $restrictions_parent;
}
// THUMBNAILS
if (isset($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['thumbnail'])) {
foreach ($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['thumbnail'] as $thumbnail) {
if (isset($thumbnail['attribs']['']['url'])) {
$thumbnails[] = $this->sanitize($thumbnail['attribs']['']['url'], \SimplePie\SimplePie::CONSTRUCT_IRI);
}
}
if (is_array($thumbnails)) {
$thumbnails = array_values(array_unique($thumbnails));
}
} else {
$thumbnails = $thumbnails_parent;
}
// TITLES
if (isset($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['title'])) {
$title = $this->sanitize($content['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['title'][0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
} else {
$title = $title_parent;
}
$this->data['enclosures'][] = $this->registry->create(Enclosure::class, [$url, $type, $length, null, $bitrate, $captions, $categories, $channels, $copyrights, $credits, $description, $duration, $expression, $framerate, $hashes, $height, $keywords, $lang, $medium, $player, $ratings, $restrictions, $samplingrate, $thumbnails, $title, $width]);
}
}
}
foreach ((array) $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_ATOM_10, 'link') as $link) {
if (isset($link['attribs']['']['href']) && !empty($link['attribs']['']['rel']) && $link['attribs']['']['rel'] === 'enclosure') {
// Attributes
$bitrate = null;
$channels = null;
$duration = null;
$expression = null;
$framerate = null;
$height = null;
$javascript = null;
$lang = null;
$length = null;
$medium = null;
$samplingrate = null;
$type = null;
$url = null;
$width = null;
$url = $this->sanitize($link['attribs']['']['href'], \SimplePie\SimplePie::CONSTRUCT_IRI, $this->get_base($link));
if (isset($link['attribs']['']['type'])) {
$type = $this->sanitize($link['attribs']['']['type'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($link['attribs']['']['length'])) {
$length = intval($link['attribs']['']['length']);
}
if (isset($link['attribs']['']['title'])) {
$title = $this->sanitize($link['attribs']['']['title'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
} else {
$title = $title_parent;
}
// Since we don't have group or content for these, we'll just pass the '*_parent' variables directly to the constructor
$this->data['enclosures'][] = $this->registry->create(Enclosure::class, [$url, $type, $length, null, $bitrate, $captions_parent, $categories_parent, $channels, $copyrights_parent, $credits_parent, $description_parent, $duration_parent, $expression, $framerate, $hashes_parent, $height, $keywords_parent, $lang, $medium, $player_parent, $ratings_parent, $restrictions_parent, $samplingrate, $thumbnails_parent, $title, $width]);
}
}
foreach ((array) $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_ATOM_03, 'link') as $link) {
if (isset($link['attribs']['']['href']) && !empty($link['attribs']['']['rel']) && $link['attribs']['']['rel'] === 'enclosure') {
// Attributes
$bitrate = null;
$channels = null;
$duration = null;
$expression = null;
$framerate = null;
$height = null;
$javascript = null;
$lang = null;
$length = null;
$medium = null;
$samplingrate = null;
$type = null;
$url = null;
$width = null;
$url = $this->sanitize($link['attribs']['']['href'], \SimplePie\SimplePie::CONSTRUCT_IRI, $this->get_base($link));
if (isset($link['attribs']['']['type'])) {
$type = $this->sanitize($link['attribs']['']['type'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($link['attribs']['']['length'])) {
$length = intval($link['attribs']['']['length']);
}
// Since we don't have group or content for these, we'll just pass the '*_parent' variables directly to the constructor
$this->data['enclosures'][] = $this->registry->create(Enclosure::class, [$url, $type, $length, null, $bitrate, $captions_parent, $categories_parent, $channels, $copyrights_parent, $credits_parent, $description_parent, $duration_parent, $expression, $framerate, $hashes_parent, $height, $keywords_parent, $lang, $medium, $player_parent, $ratings_parent, $restrictions_parent, $samplingrate, $thumbnails_parent, $title_parent, $width]);
}
}
foreach ($this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_RSS_20, 'enclosure') ?? [] as $enclosure) {
if (isset($enclosure['attribs']['']['url'])) {
// Attributes
$bitrate = null;
$channels = null;
$duration = null;
$expression = null;
$framerate = null;
$height = null;
$javascript = null;
$lang = null;
$length = null;
$medium = null;
$samplingrate = null;
$type = null;
$url = null;
$width = null;
$url = $this->sanitize($enclosure['attribs']['']['url'], \SimplePie\SimplePie::CONSTRUCT_IRI, $this->get_base($enclosure));
$url = $this->feed->sanitize->https_url($url);
if (isset($enclosure['attribs']['']['type'])) {
$type = $this->sanitize($enclosure['attribs']['']['type'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($enclosure['attribs']['']['length'])) {
$length = intval($enclosure['attribs']['']['length']);
}
// Since we don't have group or content for these, we'll just pass the '*_parent' variables directly to the constructor
$this->data['enclosures'][] = $this->registry->create(Enclosure::class, [$url, $type, $length, null, $bitrate, $captions_parent, $categories_parent, $channels, $copyrights_parent, $credits_parent, $description_parent, $duration_parent, $expression, $framerate, $hashes_parent, $height, $keywords_parent, $lang, $medium, $player_parent, $ratings_parent, $restrictions_parent, $samplingrate, $thumbnails_parent, $title_parent, $width]);
}
}
if (sizeof($this->data['enclosures']) === 0 && ($url || $type || $length || $bitrate || $captions_parent || $categories_parent || $channels || $copyrights_parent || $credits_parent || $description_parent || $duration_parent || $expression || $framerate || $hashes_parent || $height || $keywords_parent || $lang || $medium || $player_parent || $ratings_parent || $restrictions_parent || $samplingrate || $thumbnails_parent || $title_parent || $width)) {
// Since we don't have group or content for these, we'll just pass the '*_parent' variables directly to the constructor
$this->data['enclosures'][] = $this->registry->create(Enclosure::class, [$url, $type, $length, null, $bitrate, $captions_parent, $categories_parent, $channels, $copyrights_parent, $credits_parent, $description_parent, $duration_parent, $expression, $framerate, $hashes_parent, $height, $keywords_parent, $lang, $medium, $player_parent, $ratings_parent, $restrictions_parent, $samplingrate, $thumbnails_parent, $title_parent, $width]);
}
$this->data['enclosures'] = array_values(array_unique($this->data['enclosures']));
}
if (!empty($this->data['enclosures'])) {
return $this->data['enclosures'];
}
return null;
}
/**
* Get the latitude coordinates for the item
*
* Compatible with the W3C WGS84 Basic Geo and GeoRSS specifications
*
* Uses `<geo:lat>` or `<georss:point>`
*
* @since 1.0
* @link http://www.w3.org/2003/01/geo/ W3C WGS84 Basic Geo
* @link http://www.georss.org/ GeoRSS
* @return string|null
*/
public function get_latitude()
{
if ($return = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_W3C_BASIC_GEO, 'lat')) {
return (float) $return[0]['data'];
} elseif (($return = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', trim($return[0]['data']), $match)) {
return (float) $match[1];
}
return null;
}
/**
* Get the longitude coordinates for the item
*
* Compatible with the W3C WGS84 Basic Geo and GeoRSS specifications
*
* Uses `<geo:long>`, `<geo:lon>` or `<georss:point>`
*
* @since 1.0
* @link http://www.w3.org/2003/01/geo/ W3C WGS84 Basic Geo
* @link http://www.georss.org/ GeoRSS
* @return string|null
*/
public function get_longitude()
{
if ($return = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_W3C_BASIC_GEO, 'long')) {
return (float) $return[0]['data'];
} elseif ($return = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_W3C_BASIC_GEO, 'lon')) {
return (float) $return[0]['data'];
} elseif (($return = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', trim($return[0]['data']), $match)) {
return (float) $match[2];
}
return null;
}
/**
* Get the `<atom:source>` for the item
*
* @since 1.1
* @return \SimplePie\Source|null
*/
public function get_source()
{
if ($return = $this->get_item_tags(\SimplePie\SimplePie::NAMESPACE_ATOM_10, 'source')) {
return $this->registry->create(Source::class, [$this, $return[0]]);
}
return null;
}
}
class_alias('SimplePie\Item', 'SimplePie_Item');
Source.php 0000644 00000057305 15133170420 0006523 0 ustar 00 <?php
/**
* SimplePie
*
* A PHP-Based RSS and Atom Feed Framework.
* Takes the hard work out of managing a complete RSS/Atom solution.
*
* Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* * Neither the name of the SimplePie Team nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
* AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package SimplePie
* @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
* @author Ryan Parman
* @author Sam Sneddon
* @author Ryan McCue
* @link http://simplepie.org/ SimplePie
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
namespace SimplePie;
/**
* Handles `<atom:source>`
*
* Used by {@see \SimplePie\Item::get_source()}
*
* This class can be overloaded with {@see \SimplePie::set_source_class()}
*
* @package SimplePie
* @subpackage API
*/
class Source implements RegistryAware
{
public $item;
public $data = [];
protected $registry;
public function __construct($item, $data)
{
$this->item = $item;
$this->data = $data;
}
public function set_registry(\SimplePie\Registry $registry)/* : void */
{
$this->registry = $registry;
}
public function __toString()
{
return md5(serialize($this->data));
}
public function get_source_tags($namespace, $tag)
{
if (isset($this->data['child'][$namespace][$tag])) {
return $this->data['child'][$namespace][$tag];
}
return null;
}
public function get_base($element = [])
{
return $this->item->get_base($element);
}
public function sanitize($data, $type, $base = '')
{
return $this->item->sanitize($data, $type, $base);
}
public function get_item()
{
return $this->item;
}
public function get_title()
{
if ($return = $this->get_source_tags(\SimplePie\SimplePie::NAMESPACE_ATOM_10, 'title')) {
return $this->sanitize($return[0]['data'], $this->registry->call(Misc::class, 'atom_10_construct_type', [$return[0]['attribs']]), $this->get_base($return[0]));
} elseif ($return = $this->get_source_tags(\SimplePie\SimplePie::NAMESPACE_ATOM_03, 'title')) {
return $this->sanitize($return[0]['data'], $this->registry->call(Misc::class, 'atom_03_construct_type', [$return[0]['attribs']]), $this->get_base($return[0]));
} elseif ($return = $this->get_source_tags(\SimplePie\SimplePie::NAMESPACE_RSS_10, 'title')) {
return $this->sanitize($return[0]['data'], \SimplePie\SimplePie::CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
} elseif ($return = $this->get_source_tags(\SimplePie\SimplePie::NAMESPACE_RSS_090, 'title')) {
return $this->sanitize($return[0]['data'], \SimplePie\SimplePie::CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
} elseif ($return = $this->get_source_tags(\SimplePie\SimplePie::NAMESPACE_RSS_20, 'title')) {
return $this->sanitize($return[0]['data'], \SimplePie\SimplePie::CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
} elseif ($return = $this->get_source_tags(\SimplePie\SimplePie::NAMESPACE_DC_11, 'title')) {
return $this->sanitize($return[0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
} elseif ($return = $this->get_source_tags(\SimplePie\SimplePie::NAMESPACE_DC_10, 'title')) {
return $this->sanitize($return[0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
return null;
}
public function get_category($key = 0)
{
$categories = $this->get_categories();
if (isset($categories[$key])) {
return $categories[$key];
}
return null;
}
public function get_categories()
{
$categories = [];
foreach ((array) $this->get_source_tags(\SimplePie\SimplePie::NAMESPACE_ATOM_10, 'category') as $category) {
$term = null;
$scheme = null;
$label = null;
if (isset($category['attribs']['']['term'])) {
$term = $this->sanitize($category['attribs']['']['term'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($category['attribs']['']['scheme'])) {
$scheme = $this->sanitize($category['attribs']['']['scheme'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($category['attribs']['']['label'])) {
$label = $this->sanitize($category['attribs']['']['label'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
$categories[] = $this->registry->create(Category::class, [$term, $scheme, $label]);
}
foreach ((array) $this->get_source_tags(\SimplePie\SimplePie::NAMESPACE_RSS_20, 'category') as $category) {
// This is really the label, but keep this as the term also for BC.
// Label will also work on retrieving because that falls back to term.
$term = $this->sanitize($category['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
if (isset($category['attribs']['']['domain'])) {
$scheme = $this->sanitize($category['attribs']['']['domain'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
} else {
$scheme = null;
}
$categories[] = $this->registry->create(Category::class, [$term, $scheme, null]);
}
foreach ((array) $this->get_source_tags(\SimplePie\SimplePie::NAMESPACE_DC_11, 'subject') as $category) {
$categories[] = $this->registry->create(Category::class, [$this->sanitize($category['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT), null, null]);
}
foreach ((array) $this->get_source_tags(\SimplePie\SimplePie::NAMESPACE_DC_10, 'subject') as $category) {
$categories[] = $this->registry->create(Category::class, [$this->sanitize($category['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT), null, null]);
}
if (!empty($categories)) {
return array_unique($categories);
}
return null;
}
public function get_author($key = 0)
{
$authors = $this->get_authors();
if (isset($authors[$key])) {
return $authors[$key];
}
return null;
}
public function get_authors()
{
$authors = [];
foreach ((array) $this->get_source_tags(\SimplePie\SimplePie::NAMESPACE_ATOM_10, 'author') as $author) {
$name = null;
$uri = null;
$email = null;
if (isset($author['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_10]['name'][0]['data'])) {
$name = $this->sanitize($author['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_10]['name'][0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($author['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_10]['uri'][0]['data'])) {
$uri = $this->sanitize($author['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_10]['uri'][0]['data'], \SimplePie\SimplePie::CONSTRUCT_IRI, $this->get_base($author['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_10]['uri'][0]));
}
if (isset($author['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_10]['email'][0]['data'])) {
$email = $this->sanitize($author['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_10]['email'][0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if ($name !== null || $email !== null || $uri !== null) {
$authors[] = $this->registry->create(Author::class, [$name, $uri, $email]);
}
}
if ($author = $this->get_source_tags(\SimplePie\SimplePie::NAMESPACE_ATOM_03, 'author')) {
$name = null;
$url = null;
$email = null;
if (isset($author[0]['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_03]['name'][0]['data'])) {
$name = $this->sanitize($author[0]['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_03]['name'][0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($author[0]['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_03]['url'][0]['data'])) {
$url = $this->sanitize($author[0]['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_03]['url'][0]['data'], \SimplePie\SimplePie::CONSTRUCT_IRI, $this->get_base($author[0]['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_03]['url'][0]));
}
if (isset($author[0]['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_03]['email'][0]['data'])) {
$email = $this->sanitize($author[0]['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_03]['email'][0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if ($name !== null || $email !== null || $url !== null) {
$authors[] = $this->registry->create(Author::class, [$name, $url, $email]);
}
}
foreach ((array) $this->get_source_tags(\SimplePie\SimplePie::NAMESPACE_DC_11, 'creator') as $author) {
$authors[] = $this->registry->create(Author::class, [$this->sanitize($author['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT), null, null]);
}
foreach ((array) $this->get_source_tags(\SimplePie\SimplePie::NAMESPACE_DC_10, 'creator') as $author) {
$authors[] = $this->registry->create(Author::class, [$this->sanitize($author['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT), null, null]);
}
foreach ((array) $this->get_source_tags(\SimplePie\SimplePie::NAMESPACE_ITUNES, 'author') as $author) {
$authors[] = $this->registry->create(Author::class, [$this->sanitize($author['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT), null, null]);
}
if (!empty($authors)) {
return array_unique($authors);
}
return null;
}
public function get_contributor($key = 0)
{
$contributors = $this->get_contributors();
if (isset($contributors[$key])) {
return $contributors[$key];
}
return null;
}
public function get_contributors()
{
$contributors = [];
foreach ((array) $this->get_source_tags(\SimplePie\SimplePie::NAMESPACE_ATOM_10, 'contributor') as $contributor) {
$name = null;
$uri = null;
$email = null;
if (isset($contributor['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_10]['name'][0]['data'])) {
$name = $this->sanitize($contributor['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_10]['name'][0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($contributor['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_10]['uri'][0]['data'])) {
$uri = $this->sanitize($contributor['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_10]['uri'][0]['data'], \SimplePie\SimplePie::CONSTRUCT_IRI, $this->get_base($contributor['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_10]['uri'][0]));
}
if (isset($contributor['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_10]['email'][0]['data'])) {
$email = $this->sanitize($contributor['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_10]['email'][0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if ($name !== null || $email !== null || $uri !== null) {
$contributors[] = $this->registry->create(Author::class, [$name, $uri, $email]);
}
}
foreach ((array) $this->get_source_tags(\SimplePie\SimplePie::NAMESPACE_ATOM_03, 'contributor') as $contributor) {
$name = null;
$url = null;
$email = null;
if (isset($contributor['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_03]['name'][0]['data'])) {
$name = $this->sanitize($contributor['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_03]['name'][0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if (isset($contributor['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_03]['url'][0]['data'])) {
$url = $this->sanitize($contributor['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_03]['url'][0]['data'], \SimplePie\SimplePie::CONSTRUCT_IRI, $this->get_base($contributor['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_03]['url'][0]));
}
if (isset($contributor['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_03]['email'][0]['data'])) {
$email = $this->sanitize($contributor['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_03]['email'][0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
if ($name !== null || $email !== null || $url !== null) {
$contributors[] = $this->registry->create(Author::class, [$name, $url, $email]);
}
}
if (!empty($contributors)) {
return array_unique($contributors);
}
return null;
}
public function get_link($key = 0, $rel = 'alternate')
{
$links = $this->get_links($rel);
if (isset($links[$key])) {
return $links[$key];
}
return null;
}
/**
* Added for parity between the parent-level and the item/entry-level.
*/
public function get_permalink()
{
return $this->get_link(0);
}
public function get_links($rel = 'alternate')
{
if (!isset($this->data['links'])) {
$this->data['links'] = [];
if ($links = $this->get_source_tags(\SimplePie\SimplePie::NAMESPACE_ATOM_10, 'link')) {
foreach ($links as $link) {
if (isset($link['attribs']['']['href'])) {
$link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate';
$this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], \SimplePie\SimplePie::CONSTRUCT_IRI, $this->get_base($link));
}
}
}
if ($links = $this->get_source_tags(\SimplePie\SimplePie::NAMESPACE_ATOM_03, 'link')) {
foreach ($links as $link) {
if (isset($link['attribs']['']['href'])) {
$link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate';
$this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], \SimplePie\SimplePie::CONSTRUCT_IRI, $this->get_base($link));
}
}
}
if ($links = $this->get_source_tags(\SimplePie\SimplePie::NAMESPACE_RSS_10, 'link')) {
$this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], \SimplePie\SimplePie::CONSTRUCT_IRI, $this->get_base($links[0]));
}
if ($links = $this->get_source_tags(\SimplePie\SimplePie::NAMESPACE_RSS_090, 'link')) {
$this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], \SimplePie\SimplePie::CONSTRUCT_IRI, $this->get_base($links[0]));
}
if ($links = $this->get_source_tags(\SimplePie\SimplePie::NAMESPACE_RSS_20, 'link')) {
$this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], \SimplePie\SimplePie::CONSTRUCT_IRI, $this->get_base($links[0]));
}
$keys = array_keys($this->data['links']);
foreach ($keys as $key) {
if ($this->registry->call(Misc::class, 'is_isegment_nz_nc', [$key])) {
if (isset($this->data['links'][\SimplePie\SimplePie::IANA_LINK_RELATIONS_REGISTRY . $key])) {
$this->data['links'][\SimplePie\SimplePie::IANA_LINK_RELATIONS_REGISTRY . $key] = array_merge($this->data['links'][$key], $this->data['links'][\SimplePie\SimplePie::IANA_LINK_RELATIONS_REGISTRY . $key]);
$this->data['links'][$key] = &$this->data['links'][\SimplePie\SimplePie::IANA_LINK_RELATIONS_REGISTRY . $key];
} else {
$this->data['links'][\SimplePie\SimplePie::IANA_LINK_RELATIONS_REGISTRY . $key] = &$this->data['links'][$key];
}
} elseif (substr($key, 0, 41) === \SimplePie\SimplePie::IANA_LINK_RELATIONS_REGISTRY) {
$this->data['links'][substr($key, 41)] = &$this->data['links'][$key];
}
$this->data['links'][$key] = array_unique($this->data['links'][$key]);
}
}
if (isset($this->data['links'][$rel])) {
return $this->data['links'][$rel];
}
return null;
}
public function get_description()
{
if ($return = $this->get_source_tags(\SimplePie\SimplePie::NAMESPACE_ATOM_10, 'subtitle')) {
return $this->sanitize($return[0]['data'], $this->registry->call(Misc::class, 'atom_10_construct_type', [$return[0]['attribs']]), $this->get_base($return[0]));
} elseif ($return = $this->get_source_tags(\SimplePie\SimplePie::NAMESPACE_ATOM_03, 'tagline')) {
return $this->sanitize($return[0]['data'], $this->registry->call(Misc::class, 'atom_03_construct_type', [$return[0]['attribs']]), $this->get_base($return[0]));
} elseif ($return = $this->get_source_tags(\SimplePie\SimplePie::NAMESPACE_RSS_10, 'description')) {
return $this->sanitize($return[0]['data'], \SimplePie\SimplePie::CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
} elseif ($return = $this->get_source_tags(\SimplePie\SimplePie::NAMESPACE_RSS_090, 'description')) {
return $this->sanitize($return[0]['data'], \SimplePie\SimplePie::CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
} elseif ($return = $this->get_source_tags(\SimplePie\SimplePie::NAMESPACE_RSS_20, 'description')) {
return $this->sanitize($return[0]['data'], \SimplePie\SimplePie::CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
} elseif ($return = $this->get_source_tags(\SimplePie\SimplePie::NAMESPACE_DC_11, 'description')) {
return $this->sanitize($return[0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
} elseif ($return = $this->get_source_tags(\SimplePie\SimplePie::NAMESPACE_DC_10, 'description')) {
return $this->sanitize($return[0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
} elseif ($return = $this->get_source_tags(\SimplePie\SimplePie::NAMESPACE_ITUNES, 'summary')) {
return $this->sanitize($return[0]['data'], \SimplePie\SimplePie::CONSTRUCT_HTML, $this->get_base($return[0]));
} elseif ($return = $this->get_source_tags(\SimplePie\SimplePie::NAMESPACE_ITUNES, 'subtitle')) {
return $this->sanitize($return[0]['data'], \SimplePie\SimplePie::CONSTRUCT_HTML, $this->get_base($return[0]));
}
return null;
}
public function get_copyright()
{
if ($return = $this->get_source_tags(\SimplePie\SimplePie::NAMESPACE_ATOM_10, 'rights')) {
return $this->sanitize($return[0]['data'], $this->registry->call(Misc::class, 'atom_10_construct_type', [$return[0]['attribs']]), $this->get_base($return[0]));
} elseif ($return = $this->get_source_tags(\SimplePie\SimplePie::NAMESPACE_ATOM_03, 'copyright')) {
return $this->sanitize($return[0]['data'], $this->registry->call(Misc::class, 'atom_03_construct_type', [$return[0]['attribs']]), $this->get_base($return[0]));
} elseif ($return = $this->get_source_tags(\SimplePie\SimplePie::NAMESPACE_RSS_20, 'copyright')) {
return $this->sanitize($return[0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
} elseif ($return = $this->get_source_tags(\SimplePie\SimplePie::NAMESPACE_DC_11, 'rights')) {
return $this->sanitize($return[0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
} elseif ($return = $this->get_source_tags(\SimplePie\SimplePie::NAMESPACE_DC_10, 'rights')) {
return $this->sanitize($return[0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
return null;
}
public function get_language()
{
if ($return = $this->get_source_tags(\SimplePie\SimplePie::NAMESPACE_RSS_20, 'language')) {
return $this->sanitize($return[0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
} elseif ($return = $this->get_source_tags(\SimplePie\SimplePie::NAMESPACE_DC_11, 'language')) {
return $this->sanitize($return[0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
} elseif ($return = $this->get_source_tags(\SimplePie\SimplePie::NAMESPACE_DC_10, 'language')) {
return $this->sanitize($return[0]['data'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
} elseif (isset($this->data['xml_lang'])) {
return $this->sanitize($this->data['xml_lang'], \SimplePie\SimplePie::CONSTRUCT_TEXT);
}
return null;
}
public function get_latitude()
{
if ($return = $this->get_source_tags(\SimplePie\SimplePie::NAMESPACE_W3C_BASIC_GEO, 'lat')) {
return (float) $return[0]['data'];
} elseif (($return = $this->get_source_tags(\SimplePie\SimplePie::NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', trim($return[0]['data']), $match)) {
return (float) $match[1];
}
return null;
}
public function get_longitude()
{
if ($return = $this->get_source_tags(\SimplePie\SimplePie::NAMESPACE_W3C_BASIC_GEO, 'long')) {
return (float) $return[0]['data'];
} elseif ($return = $this->get_source_tags(\SimplePie\SimplePie::NAMESPACE_W3C_BASIC_GEO, 'lon')) {
return (float) $return[0]['data'];
} elseif (($return = $this->get_source_tags(\SimplePie\SimplePie::NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', trim($return[0]['data']), $match)) {
return (float) $match[2];
}
return null;
}
public function get_image_url()
{
if ($return = $this->get_source_tags(\SimplePie\SimplePie::NAMESPACE_ITUNES, 'image')) {
return $this->sanitize($return[0]['attribs']['']['href'], \SimplePie\SimplePie::CONSTRUCT_IRI);
} elseif ($return = $this->get_source_tags(\SimplePie\SimplePie::NAMESPACE_ATOM_10, 'logo')) {
return $this->sanitize($return[0]['data'], \SimplePie\SimplePie::CONSTRUCT_IRI, $this->get_base($return[0]));
} elseif ($return = $this->get_source_tags(\SimplePie\SimplePie::NAMESPACE_ATOM_10, 'icon')) {
return $this->sanitize($return[0]['data'], \SimplePie\SimplePie::CONSTRUCT_IRI, $this->get_base($return[0]));
}
return null;
}
}
class_alias('SimplePie\Source', 'SimplePie_Source');
RegistryAware.php 0000644 00000004553 15133170420 0010050 0 ustar 00 <?php
/**
* SimplePie
*
* A PHP-Based RSS and Atom Feed Framework.
* Takes the hard work out of managing a complete RSS/Atom solution.
*
* Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* * Neither the name of the SimplePie Team nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
* AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package SimplePie
* @copyright 2004-2022 Ryan Parman, Sam Sneddon, Ryan McCue
* @author Ryan Parman
* @author Sam Sneddon
* @author Ryan McCue
* @link http://simplepie.org/ SimplePie
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
namespace SimplePie;
/**
* Handles the injection of Registry into other class
*
* {@see \SimplePie\SimplePie::get_registry()}
*
* @package SimplePie
*/
interface RegistryAware
{
/**
* Set the Registry into the class
*
* @param Registry $registry
*
* @return void
*/
public function set_registry(Registry $registry)/* : void */;
}
Restriction.php 0000644 00000010020 15133170420 0007547 0 ustar 00 <?php
/**
* SimplePie
*
* A PHP-Based RSS and Atom Feed Framework.
* Takes the hard work out of managing a complete RSS/Atom solution.
*
* Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* * Neither the name of the SimplePie Team nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
* AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package SimplePie
* @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
* @author Ryan Parman
* @author Sam Sneddon
* @author Ryan McCue
* @link http://simplepie.org/ SimplePie
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
namespace SimplePie;
/**
* Handles `<media:restriction>` as defined in Media RSS
*
* Used by {@see \SimplePie\Enclosure::get_restriction()} and {@see \SimplePie\Enclosure::get_restrictions()}
*
* This class can be overloaded with {@see \SimplePie\SimplePie::set_restriction_class()}
*
* @package SimplePie
* @subpackage API
*/
class Restriction
{
/**
* Relationship ('allow'/'deny')
*
* @var string
* @see get_relationship()
*/
public $relationship;
/**
* Type of restriction
*
* @var string
* @see get_type()
*/
public $type;
/**
* Restricted values
*
* @var string
* @see get_value()
*/
public $value;
/**
* Constructor, used to input the data
*
* For documentation on all the parameters, see the corresponding
* properties and their accessors
*/
public function __construct($relationship = null, $type = null, $value = null)
{
$this->relationship = $relationship;
$this->type = $type;
$this->value = $value;
}
/**
* String-ified version
*
* @return string
*/
public function __toString()
{
// There is no $this->data here
return md5(serialize($this));
}
/**
* Get the relationship
*
* @return string|null Either 'allow' or 'deny'
*/
public function get_relationship()
{
if ($this->relationship !== null) {
return $this->relationship;
}
return null;
}
/**
* Get the type
*
* @return string|null
*/
public function get_type()
{
if ($this->type !== null) {
return $this->type;
}
return null;
}
/**
* Get the list of restricted things
*
* @return string|null
*/
public function get_value()
{
if ($this->value !== null) {
return $this->value;
}
return null;
}
}
class_alias('SimplePie\Restriction', 'SimplePie_Restriction');
Misc.php 0000644 00000206032 15133170420 0006147 0 ustar 00 <?php
/**
* SimplePie
*
* A PHP-Based RSS and Atom Feed Framework.
* Takes the hard work out of managing a complete RSS/Atom solution.
*
* Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* * Neither the name of the SimplePie Team nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
* AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package SimplePie
* @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
* @author Ryan Parman
* @author Sam Sneddon
* @author Ryan McCue
* @link http://simplepie.org/ SimplePie
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
namespace SimplePie;
use SimplePie\XML\Declaration\Parser;
/**
* Miscellaneous utilities
*
* @package SimplePie
*/
class Misc
{
private static $SIMPLEPIE_BUILD = null;
public static function time_hms($seconds)
{
$time = '';
$hours = floor($seconds / 3600);
$remainder = $seconds % 3600;
if ($hours > 0) {
$time .= $hours.':';
}
$minutes = floor($remainder / 60);
$seconds = $remainder % 60;
if ($minutes < 10 && $hours > 0) {
$minutes = '0' . $minutes;
}
if ($seconds < 10) {
$seconds = '0' . $seconds;
}
$time .= $minutes.':';
$time .= $seconds;
return $time;
}
public static function absolutize_url($relative, $base)
{
$iri = \SimplePie\IRI::absolutize(new \SimplePie\IRI($base), $relative);
if ($iri === false) {
return false;
}
return $iri->get_uri();
}
/**
* Get a HTML/XML element from a HTML string
*
* @deprecated since SimplePie 1.3, use DOMDocument instead (parsing HTML with regex is bad!)
* @param string $realname Element name (including namespace prefix if applicable)
* @param string $string HTML document
* @return array
*/
public static function get_element($realname, $string)
{
// trigger_error(sprintf('Using method "' . __METHOD__ . '" is deprecated since SimplePie 1.3, use "DOMDocument" instead.'), \E_USER_DEPRECATED);
$return = [];
$name = preg_quote($realname, '/');
if (preg_match_all("/<($name)" . \SimplePie\SimplePie::PCRE_HTML_ATTRIBUTE . "(>(.*)<\/$name>|(\/)?>)/siU", $string, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE)) {
for ($i = 0, $total_matches = count($matches); $i < $total_matches; $i++) {
$return[$i]['tag'] = $realname;
$return[$i]['full'] = $matches[$i][0][0];
$return[$i]['offset'] = $matches[$i][0][1];
if (strlen($matches[$i][3][0]) <= 2) {
$return[$i]['self_closing'] = true;
} else {
$return[$i]['self_closing'] = false;
$return[$i]['content'] = $matches[$i][4][0];
}
$return[$i]['attribs'] = [];
if (isset($matches[$i][2][0]) && preg_match_all('/[\x09\x0A\x0B\x0C\x0D\x20]+([^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3E][^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3D\x3E]*)(?:[\x09\x0A\x0B\x0C\x0D\x20]*=[\x09\x0A\x0B\x0C\x0D\x20]*(?:"([^"]*)"|\'([^\']*)\'|([^\x09\x0A\x0B\x0C\x0D\x20\x22\x27\x3E][^\x09\x0A\x0B\x0C\x0D\x20\x3E]*)?))?/', ' ' . $matches[$i][2][0] . ' ', $attribs, PREG_SET_ORDER)) {
for ($j = 0, $total_attribs = count($attribs); $j < $total_attribs; $j++) {
if (count($attribs[$j]) === 2) {
$attribs[$j][2] = $attribs[$j][1];
}
$return[$i]['attribs'][strtolower($attribs[$j][1])]['data'] = Misc::entities_decode(end($attribs[$j]));
}
}
}
}
return $return;
}
public static function element_implode($element)
{
$full = "<$element[tag]";
foreach ($element['attribs'] as $key => $value) {
$key = strtolower($key);
$full .= " $key=\"" . htmlspecialchars($value['data'], ENT_COMPAT, 'UTF-8') . '"';
}
if ($element['self_closing']) {
$full .= ' />';
} else {
$full .= ">$element[content]</$element[tag]>";
}
return $full;
}
public static function error($message, $level, $file, $line)
{
if ((error_reporting() & $level) > 0) {
switch ($level) {
case E_USER_ERROR:
$note = 'PHP Error';
break;
case E_USER_WARNING:
$note = 'PHP Warning';
break;
case E_USER_NOTICE:
$note = 'PHP Notice';
break;
default:
$note = 'Unknown Error';
break;
}
$log_error = true;
if (!function_exists('error_log')) {
$log_error = false;
}
$log_file = @ini_get('error_log');
if (!empty($log_file) && ('syslog' !== $log_file) && !@is_writable($log_file)) {
$log_error = false;
}
if ($log_error) {
@error_log("$note: $message in $file on line $line", 0);
}
}
return $message;
}
public static function fix_protocol($url, $http = 1)
{
$url = Misc::normalize_url($url);
$parsed = Misc::parse_url($url);
if ($parsed['scheme'] !== '' && $parsed['scheme'] !== 'http' && $parsed['scheme'] !== 'https') {
return Misc::fix_protocol(Misc::compress_parse_url('http', $parsed['authority'], $parsed['path'], $parsed['query'], $parsed['fragment']), $http);
}
if ($parsed['scheme'] === '' && $parsed['authority'] === '' && !file_exists($url)) {
return Misc::fix_protocol(Misc::compress_parse_url('http', $parsed['path'], '', $parsed['query'], $parsed['fragment']), $http);
}
if ($http === 2 && $parsed['scheme'] !== '') {
return "feed:$url";
} elseif ($http === 3 && strtolower($parsed['scheme']) === 'http') {
return substr_replace($url, 'podcast', 0, 4);
} elseif ($http === 4 && strtolower($parsed['scheme']) === 'http') {
return substr_replace($url, 'itpc', 0, 4);
}
return $url;
}
/**
* @deprecated since SimplePie 1.8.0, use PHP native array_replace_recursive() instead.
*/
public static function array_merge_recursive($array1, $array2)
{
foreach ($array2 as $key => $value) {
if (is_array($value)) {
$array1[$key] = Misc::array_merge_recursive($array1[$key], $value);
} else {
$array1[$key] = $value;
}
}
return $array1;
}
public static function parse_url($url)
{
$iri = new \SimplePie\IRI($url);
return [
'scheme' => (string) $iri->scheme,
'authority' => (string) $iri->authority,
'path' => (string) $iri->path,
'query' => (string) $iri->query,
'fragment' => (string) $iri->fragment
];
}
public static function compress_parse_url($scheme = '', $authority = '', $path = '', $query = '', $fragment = '')
{
$iri = new \SimplePie\IRI('');
$iri->scheme = $scheme;
$iri->authority = $authority;
$iri->path = $path;
$iri->query = $query;
$iri->fragment = $fragment;
return $iri->get_uri();
}
public static function normalize_url($url)
{
$iri = new \SimplePie\IRI($url);
return $iri->get_uri();
}
public static function percent_encoding_normalization($match)
{
$integer = hexdec($match[1]);
if ($integer >= 0x41 && $integer <= 0x5A || $integer >= 0x61 && $integer <= 0x7A || $integer >= 0x30 && $integer <= 0x39 || $integer === 0x2D || $integer === 0x2E || $integer === 0x5F || $integer === 0x7E) {
return chr($integer);
}
return strtoupper($match[0]);
}
/**
* Converts a Windows-1252 encoded string to a UTF-8 encoded string
*
* @static
* @param string $string Windows-1252 encoded string
* @return string UTF-8 encoded string
*/
public static function windows_1252_to_utf8($string)
{
static $convert_table = ["\x80" => "\xE2\x82\xAC", "\x81" => "\xEF\xBF\xBD", "\x82" => "\xE2\x80\x9A", "\x83" => "\xC6\x92", "\x84" => "\xE2\x80\x9E", "\x85" => "\xE2\x80\xA6", "\x86" => "\xE2\x80\xA0", "\x87" => "\xE2\x80\xA1", "\x88" => "\xCB\x86", "\x89" => "\xE2\x80\xB0", "\x8A" => "\xC5\xA0", "\x8B" => "\xE2\x80\xB9", "\x8C" => "\xC5\x92", "\x8D" => "\xEF\xBF\xBD", "\x8E" => "\xC5\xBD", "\x8F" => "\xEF\xBF\xBD", "\x90" => "\xEF\xBF\xBD", "\x91" => "\xE2\x80\x98", "\x92" => "\xE2\x80\x99", "\x93" => "\xE2\x80\x9C", "\x94" => "\xE2\x80\x9D", "\x95" => "\xE2\x80\xA2", "\x96" => "\xE2\x80\x93", "\x97" => "\xE2\x80\x94", "\x98" => "\xCB\x9C", "\x99" => "\xE2\x84\xA2", "\x9A" => "\xC5\xA1", "\x9B" => "\xE2\x80\xBA", "\x9C" => "\xC5\x93", "\x9D" => "\xEF\xBF\xBD", "\x9E" => "\xC5\xBE", "\x9F" => "\xC5\xB8", "\xA0" => "\xC2\xA0", "\xA1" => "\xC2\xA1", "\xA2" => "\xC2\xA2", "\xA3" => "\xC2\xA3", "\xA4" => "\xC2\xA4", "\xA5" => "\xC2\xA5", "\xA6" => "\xC2\xA6", "\xA7" => "\xC2\xA7", "\xA8" => "\xC2\xA8", "\xA9" => "\xC2\xA9", "\xAA" => "\xC2\xAA", "\xAB" => "\xC2\xAB", "\xAC" => "\xC2\xAC", "\xAD" => "\xC2\xAD", "\xAE" => "\xC2\xAE", "\xAF" => "\xC2\xAF", "\xB0" => "\xC2\xB0", "\xB1" => "\xC2\xB1", "\xB2" => "\xC2\xB2", "\xB3" => "\xC2\xB3", "\xB4" => "\xC2\xB4", "\xB5" => "\xC2\xB5", "\xB6" => "\xC2\xB6", "\xB7" => "\xC2\xB7", "\xB8" => "\xC2\xB8", "\xB9" => "\xC2\xB9", "\xBA" => "\xC2\xBA", "\xBB" => "\xC2\xBB", "\xBC" => "\xC2\xBC", "\xBD" => "\xC2\xBD", "\xBE" => "\xC2\xBE", "\xBF" => "\xC2\xBF", "\xC0" => "\xC3\x80", "\xC1" => "\xC3\x81", "\xC2" => "\xC3\x82", "\xC3" => "\xC3\x83", "\xC4" => "\xC3\x84", "\xC5" => "\xC3\x85", "\xC6" => "\xC3\x86", "\xC7" => "\xC3\x87", "\xC8" => "\xC3\x88", "\xC9" => "\xC3\x89", "\xCA" => "\xC3\x8A", "\xCB" => "\xC3\x8B", "\xCC" => "\xC3\x8C", "\xCD" => "\xC3\x8D", "\xCE" => "\xC3\x8E", "\xCF" => "\xC3\x8F", "\xD0" => "\xC3\x90", "\xD1" => "\xC3\x91", "\xD2" => "\xC3\x92", "\xD3" => "\xC3\x93", "\xD4" => "\xC3\x94", "\xD5" => "\xC3\x95", "\xD6" => "\xC3\x96", "\xD7" => "\xC3\x97", "\xD8" => "\xC3\x98", "\xD9" => "\xC3\x99", "\xDA" => "\xC3\x9A", "\xDB" => "\xC3\x9B", "\xDC" => "\xC3\x9C", "\xDD" => "\xC3\x9D", "\xDE" => "\xC3\x9E", "\xDF" => "\xC3\x9F", "\xE0" => "\xC3\xA0", "\xE1" => "\xC3\xA1", "\xE2" => "\xC3\xA2", "\xE3" => "\xC3\xA3", "\xE4" => "\xC3\xA4", "\xE5" => "\xC3\xA5", "\xE6" => "\xC3\xA6", "\xE7" => "\xC3\xA7", "\xE8" => "\xC3\xA8", "\xE9" => "\xC3\xA9", "\xEA" => "\xC3\xAA", "\xEB" => "\xC3\xAB", "\xEC" => "\xC3\xAC", "\xED" => "\xC3\xAD", "\xEE" => "\xC3\xAE", "\xEF" => "\xC3\xAF", "\xF0" => "\xC3\xB0", "\xF1" => "\xC3\xB1", "\xF2" => "\xC3\xB2", "\xF3" => "\xC3\xB3", "\xF4" => "\xC3\xB4", "\xF5" => "\xC3\xB5", "\xF6" => "\xC3\xB6", "\xF7" => "\xC3\xB7", "\xF8" => "\xC3\xB8", "\xF9" => "\xC3\xB9", "\xFA" => "\xC3\xBA", "\xFB" => "\xC3\xBB", "\xFC" => "\xC3\xBC", "\xFD" => "\xC3\xBD", "\xFE" => "\xC3\xBE", "\xFF" => "\xC3\xBF"];
return strtr($string, $convert_table);
}
/**
* Change a string from one encoding to another
*
* @param string $data Raw data in $input encoding
* @param string $input Encoding of $data
* @param string $output Encoding you want
* @return string|boolean False if we can't convert it
*/
public static function change_encoding($data, $input, $output)
{
$input = Misc::encoding($input);
$output = Misc::encoding($output);
// We fail to fail on non US-ASCII bytes
if ($input === 'US-ASCII') {
static $non_ascii_octects = '';
if (!$non_ascii_octects) {
for ($i = 0x80; $i <= 0xFF; $i++) {
$non_ascii_octects .= chr($i);
}
}
$data = substr($data, 0, strcspn($data, $non_ascii_octects));
}
// This is first, as behaviour of this is completely predictable
if ($input === 'windows-1252' && $output === 'UTF-8') {
return Misc::windows_1252_to_utf8($data);
}
// This is second, as behaviour of this varies only with PHP version (the middle part of this expression checks the encoding is supported).
elseif (function_exists('mb_convert_encoding') && ($return = Misc::change_encoding_mbstring($data, $input, $output))) {
return $return;
}
// This is third, as behaviour of this varies with OS userland and PHP version
elseif (function_exists('iconv') && ($return = Misc::change_encoding_iconv($data, $input, $output))) {
return $return;
}
// This is last, as behaviour of this varies with OS userland and PHP version
elseif (class_exists('\UConverter') && ($return = Misc::change_encoding_uconverter($data, $input, $output))) {
return $return;
}
// If we can't do anything, just fail
return false;
}
protected static function change_encoding_mbstring($data, $input, $output)
{
if ($input === 'windows-949') {
$input = 'EUC-KR';
}
if ($output === 'windows-949') {
$output = 'EUC-KR';
}
if ($input === 'Windows-31J') {
$input = 'SJIS';
}
if ($output === 'Windows-31J') {
$output = 'SJIS';
}
// Check that the encoding is supported
if (!in_array($input, mb_list_encodings())) {
return false;
}
if (@mb_convert_encoding("\x80", 'UTF-16BE', $input) === "\x00\x80") {
return false;
}
// Let's do some conversion
if ($return = @mb_convert_encoding($data, $output, $input)) {
return $return;
}
return false;
}
protected static function change_encoding_iconv($data, $input, $output)
{
return @iconv($input, $output, $data);
}
/**
* @param string $data
* @param string $input
* @param string $output
* @return string|false
*/
protected static function change_encoding_uconverter($data, $input, $output)
{
return @\UConverter::transcode($data, $output, $input);
}
/**
* Normalize an encoding name
*
* This is automatically generated by create.php
*
* To generate it, run `php create.php` on the command line, and copy the
* output to replace this function.
*
* @param string $charset Character set to standardise
* @return string Standardised name
*/
public static function encoding($charset)
{
// Normalization from UTS #22
switch (strtolower(preg_replace('/(?:[^a-zA-Z0-9]+|([^0-9])0+)/', '\1', $charset))) {
case 'adobestandardencoding':
case 'csadobestandardencoding':
return 'Adobe-Standard-Encoding';
case 'adobesymbolencoding':
case 'cshppsmath':
return 'Adobe-Symbol-Encoding';
case 'ami1251':
case 'amiga1251':
return 'Amiga-1251';
case 'ansix31101983':
case 'csat5001983':
case 'csiso99naplps':
case 'isoir99':
case 'naplps':
return 'ANSI_X3.110-1983';
case 'arabic7':
case 'asmo449':
case 'csiso89asmo449':
case 'iso9036':
case 'isoir89':
return 'ASMO_449';
case 'big5':
case 'csbig5':
return 'Big5';
case 'big5hkscs':
return 'Big5-HKSCS';
case 'bocu1':
case 'csbocu1':
return 'BOCU-1';
case 'brf':
case 'csbrf':
return 'BRF';
case 'bs4730':
case 'csiso4unitedkingdom':
case 'gb':
case 'iso646gb':
case 'isoir4':
case 'uk':
return 'BS_4730';
case 'bsviewdata':
case 'csiso47bsviewdata':
case 'isoir47':
return 'BS_viewdata';
case 'cesu8':
case 'cscesu8':
return 'CESU-8';
case 'ca':
case 'csa71':
case 'csaz243419851':
case 'csiso121canadian1':
case 'iso646ca':
case 'isoir121':
return 'CSA_Z243.4-1985-1';
case 'csa72':
case 'csaz243419852':
case 'csiso122canadian2':
case 'iso646ca2':
case 'isoir122':
return 'CSA_Z243.4-1985-2';
case 'csaz24341985gr':
case 'csiso123csaz24341985gr':
case 'isoir123':
return 'CSA_Z243.4-1985-gr';
case 'csiso139csn369103':
case 'csn369103':
case 'isoir139':
return 'CSN_369103';
case 'csdecmcs':
case 'dec':
case 'decmcs':
return 'DEC-MCS';
case 'csiso21german':
case 'de':
case 'din66003':
case 'iso646de':
case 'isoir21':
return 'DIN_66003';
case 'csdkus':
case 'dkus':
return 'dk-us';
case 'csiso646danish':
case 'dk':
case 'ds2089':
case 'iso646dk':
return 'DS_2089';
case 'csibmebcdicatde':
case 'ebcdicatde':
return 'EBCDIC-AT-DE';
case 'csebcdicatdea':
case 'ebcdicatdea':
return 'EBCDIC-AT-DE-A';
case 'csebcdiccafr':
case 'ebcdiccafr':
return 'EBCDIC-CA-FR';
case 'csebcdicdkno':
case 'ebcdicdkno':
return 'EBCDIC-DK-NO';
case 'csebcdicdknoa':
case 'ebcdicdknoa':
return 'EBCDIC-DK-NO-A';
case 'csebcdices':
case 'ebcdices':
return 'EBCDIC-ES';
case 'csebcdicesa':
case 'ebcdicesa':
return 'EBCDIC-ES-A';
case 'csebcdicess':
case 'ebcdicess':
return 'EBCDIC-ES-S';
case 'csebcdicfise':
case 'ebcdicfise':
return 'EBCDIC-FI-SE';
case 'csebcdicfisea':
case 'ebcdicfisea':
return 'EBCDIC-FI-SE-A';
case 'csebcdicfr':
case 'ebcdicfr':
return 'EBCDIC-FR';
case 'csebcdicit':
case 'ebcdicit':
return 'EBCDIC-IT';
case 'csebcdicpt':
case 'ebcdicpt':
return 'EBCDIC-PT';
case 'csebcdicuk':
case 'ebcdicuk':
return 'EBCDIC-UK';
case 'csebcdicus':
case 'ebcdicus':
return 'EBCDIC-US';
case 'csiso111ecmacyrillic':
case 'ecmacyrillic':
case 'isoir111':
case 'koi8e':
return 'ECMA-cyrillic';
case 'csiso17spanish':
case 'es':
case 'iso646es':
case 'isoir17':
return 'ES';
case 'csiso85spanish2':
case 'es2':
case 'iso646es2':
case 'isoir85':
return 'ES2';
case 'cseucpkdfmtjapanese':
case 'eucjp':
case 'extendedunixcodepackedformatforjapanese':
return 'EUC-JP';
case 'cseucfixwidjapanese':
case 'extendedunixcodefixedwidthforjapanese':
return 'Extended_UNIX_Code_Fixed_Width_for_Japanese';
case 'gb18030':
return 'GB18030';
case 'chinese':
case 'cp936':
case 'csgb2312':
case 'csiso58gb231280':
case 'gb2312':
case 'gb231280':
case 'gbk':
case 'isoir58':
case 'ms936':
case 'windows936':
return 'GBK';
case 'cn':
case 'csiso57gb1988':
case 'gb198880':
case 'iso646cn':
case 'isoir57':
return 'GB_1988-80';
case 'csiso153gost1976874':
case 'gost1976874':
case 'isoir153':
case 'stsev35888':
return 'GOST_19768-74';
case 'csiso150':
case 'csiso150greekccitt':
case 'greekccitt':
case 'isoir150':
return 'greek-ccitt';
case 'csiso88greek7':
case 'greek7':
case 'isoir88':
return 'greek7';
case 'csiso18greek7old':
case 'greek7old':
case 'isoir18':
return 'greek7-old';
case 'cshpdesktop':
case 'hpdesktop':
return 'HP-DeskTop';
case 'cshplegal':
case 'hplegal':
return 'HP-Legal';
case 'cshpmath8':
case 'hpmath8':
return 'HP-Math8';
case 'cshppifont':
case 'hppifont':
return 'HP-Pi-font';
case 'cshproman8':
case 'hproman8':
case 'r8':
case 'roman8':
return 'hp-roman8';
case 'hzgb2312':
return 'HZ-GB-2312';
case 'csibmsymbols':
case 'ibmsymbols':
return 'IBM-Symbols';
case 'csibmthai':
case 'ibmthai':
return 'IBM-Thai';
case 'cp37':
case 'csibm37':
case 'ebcdiccpca':
case 'ebcdiccpnl':
case 'ebcdiccpus':
case 'ebcdiccpwt':
case 'ibm37':
return 'IBM037';
case 'cp38':
case 'csibm38':
case 'ebcdicint':
case 'ibm38':
return 'IBM038';
case 'cp273':
case 'csibm273':
case 'ibm273':
return 'IBM273';
case 'cp274':
case 'csibm274':
case 'ebcdicbe':
case 'ibm274':
return 'IBM274';
case 'cp275':
case 'csibm275':
case 'ebcdicbr':
case 'ibm275':
return 'IBM275';
case 'csibm277':
case 'ebcdiccpdk':
case 'ebcdiccpno':
case 'ibm277':
return 'IBM277';
case 'cp278':
case 'csibm278':
case 'ebcdiccpfi':
case 'ebcdiccpse':
case 'ibm278':
return 'IBM278';
case 'cp280':
case 'csibm280':
case 'ebcdiccpit':
case 'ibm280':
return 'IBM280';
case 'cp281':
case 'csibm281':
case 'ebcdicjpe':
case 'ibm281':
return 'IBM281';
case 'cp284':
case 'csibm284':
case 'ebcdiccpes':
case 'ibm284':
return 'IBM284';
case 'cp285':
case 'csibm285':
case 'ebcdiccpgb':
case 'ibm285':
return 'IBM285';
case 'cp290':
case 'csibm290':
case 'ebcdicjpkana':
case 'ibm290':
return 'IBM290';
case 'cp297':
case 'csibm297':
case 'ebcdiccpfr':
case 'ibm297':
return 'IBM297';
case 'cp420':
case 'csibm420':
case 'ebcdiccpar1':
case 'ibm420':
return 'IBM420';
case 'cp423':
case 'csibm423':
case 'ebcdiccpgr':
case 'ibm423':
return 'IBM423';
case 'cp424':
case 'csibm424':
case 'ebcdiccphe':
case 'ibm424':
return 'IBM424';
case '437':
case 'cp437':
case 'cspc8codepage437':
case 'ibm437':
return 'IBM437';
case 'cp500':
case 'csibm500':
case 'ebcdiccpbe':
case 'ebcdiccpch':
case 'ibm500':
return 'IBM500';
case 'cp775':
case 'cspc775baltic':
case 'ibm775':
return 'IBM775';
case '850':
case 'cp850':
case 'cspc850multilingual':
case 'ibm850':
return 'IBM850';
case '851':
case 'cp851':
case 'csibm851':
case 'ibm851':
return 'IBM851';
case '852':
case 'cp852':
case 'cspcp852':
case 'ibm852':
return 'IBM852';
case '855':
case 'cp855':
case 'csibm855':
case 'ibm855':
return 'IBM855';
case '857':
case 'cp857':
case 'csibm857':
case 'ibm857':
return 'IBM857';
case 'ccsid858':
case 'cp858':
case 'ibm858':
case 'pcmultilingual850euro':
return 'IBM00858';
case '860':
case 'cp860':
case 'csibm860':
case 'ibm860':
return 'IBM860';
case '861':
case 'cp861':
case 'cpis':
case 'csibm861':
case 'ibm861':
return 'IBM861';
case '862':
case 'cp862':
case 'cspc862latinhebrew':
case 'ibm862':
return 'IBM862';
case '863':
case 'cp863':
case 'csibm863':
case 'ibm863':
return 'IBM863';
case 'cp864':
case 'csibm864':
case 'ibm864':
return 'IBM864';
case '865':
case 'cp865':
case 'csibm865':
case 'ibm865':
return 'IBM865';
case '866':
case 'cp866':
case 'csibm866':
case 'ibm866':
return 'IBM866';
case 'cp868':
case 'cpar':
case 'csibm868':
case 'ibm868':
return 'IBM868';
case '869':
case 'cp869':
case 'cpgr':
case 'csibm869':
case 'ibm869':
return 'IBM869';
case 'cp870':
case 'csibm870':
case 'ebcdiccproece':
case 'ebcdiccpyu':
case 'ibm870':
return 'IBM870';
case 'cp871':
case 'csibm871':
case 'ebcdiccpis':
case 'ibm871':
return 'IBM871';
case 'cp880':
case 'csibm880':
case 'ebcdiccyrillic':
case 'ibm880':
return 'IBM880';
case 'cp891':
case 'csibm891':
case 'ibm891':
return 'IBM891';
case 'cp903':
case 'csibm903':
case 'ibm903':
return 'IBM903';
case '904':
case 'cp904':
case 'csibbm904':
case 'ibm904':
return 'IBM904';
case 'cp905':
case 'csibm905':
case 'ebcdiccptr':
case 'ibm905':
return 'IBM905';
case 'cp918':
case 'csibm918':
case 'ebcdiccpar2':
case 'ibm918':
return 'IBM918';
case 'ccsid924':
case 'cp924':
case 'ebcdiclatin9euro':
case 'ibm924':
return 'IBM00924';
case 'cp1026':
case 'csibm1026':
case 'ibm1026':
return 'IBM1026';
case 'ibm1047':
return 'IBM1047';
case 'ccsid1140':
case 'cp1140':
case 'ebcdicus37euro':
case 'ibm1140':
return 'IBM01140';
case 'ccsid1141':
case 'cp1141':
case 'ebcdicde273euro':
case 'ibm1141':
return 'IBM01141';
case 'ccsid1142':
case 'cp1142':
case 'ebcdicdk277euro':
case 'ebcdicno277euro':
case 'ibm1142':
return 'IBM01142';
case 'ccsid1143':
case 'cp1143':
case 'ebcdicfi278euro':
case 'ebcdicse278euro':
case 'ibm1143':
return 'IBM01143';
case 'ccsid1144':
case 'cp1144':
case 'ebcdicit280euro':
case 'ibm1144':
return 'IBM01144';
case 'ccsid1145':
case 'cp1145':
case 'ebcdices284euro':
case 'ibm1145':
return 'IBM01145';
case 'ccsid1146':
case 'cp1146':
case 'ebcdicgb285euro':
case 'ibm1146':
return 'IBM01146';
case 'ccsid1147':
case 'cp1147':
case 'ebcdicfr297euro':
case 'ibm1147':
return 'IBM01147';
case 'ccsid1148':
case 'cp1148':
case 'ebcdicinternational500euro':
case 'ibm1148':
return 'IBM01148';
case 'ccsid1149':
case 'cp1149':
case 'ebcdicis871euro':
case 'ibm1149':
return 'IBM01149';
case 'csiso143iecp271':
case 'iecp271':
case 'isoir143':
return 'IEC_P27-1';
case 'csiso49inis':
case 'inis':
case 'isoir49':
return 'INIS';
case 'csiso50inis8':
case 'inis8':
case 'isoir50':
return 'INIS-8';
case 'csiso51iniscyrillic':
case 'iniscyrillic':
case 'isoir51':
return 'INIS-cyrillic';
case 'csinvariant':
case 'invariant':
return 'INVARIANT';
case 'iso2022cn':
return 'ISO-2022-CN';
case 'iso2022cnext':
return 'ISO-2022-CN-EXT';
case 'csiso2022jp':
case 'iso2022jp':
return 'ISO-2022-JP';
case 'csiso2022jp2':
case 'iso2022jp2':
return 'ISO-2022-JP-2';
case 'csiso2022kr':
case 'iso2022kr':
return 'ISO-2022-KR';
case 'cswindows30latin1':
case 'iso88591windows30latin1':
return 'ISO-8859-1-Windows-3.0-Latin-1';
case 'cswindows31latin1':
case 'iso88591windows31latin1':
return 'ISO-8859-1-Windows-3.1-Latin-1';
case 'csisolatin2':
case 'iso88592':
case 'iso885921987':
case 'isoir101':
case 'l2':
case 'latin2':
return 'ISO-8859-2';
case 'cswindows31latin2':
case 'iso88592windowslatin2':
return 'ISO-8859-2-Windows-Latin-2';
case 'csisolatin3':
case 'iso88593':
case 'iso885931988':
case 'isoir109':
case 'l3':
case 'latin3':
return 'ISO-8859-3';
case 'csisolatin4':
case 'iso88594':
case 'iso885941988':
case 'isoir110':
case 'l4':
case 'latin4':
return 'ISO-8859-4';
case 'csisolatincyrillic':
case 'cyrillic':
case 'iso88595':
case 'iso885951988':
case 'isoir144':
return 'ISO-8859-5';
case 'arabic':
case 'asmo708':
case 'csisolatinarabic':
case 'ecma114':
case 'iso88596':
case 'iso885961987':
case 'isoir127':
return 'ISO-8859-6';
case 'csiso88596e':
case 'iso88596e':
return 'ISO-8859-6-E';
case 'csiso88596i':
case 'iso88596i':
return 'ISO-8859-6-I';
case 'csisolatingreek':
case 'ecma118':
case 'elot928':
case 'greek':
case 'greek8':
case 'iso88597':
case 'iso885971987':
case 'isoir126':
return 'ISO-8859-7';
case 'csisolatinhebrew':
case 'hebrew':
case 'iso88598':
case 'iso885981988':
case 'isoir138':
return 'ISO-8859-8';
case 'csiso88598e':
case 'iso88598e':
return 'ISO-8859-8-E';
case 'csiso88598i':
case 'iso88598i':
return 'ISO-8859-8-I';
case 'cswindows31latin5':
case 'iso88599windowslatin5':
return 'ISO-8859-9-Windows-Latin-5';
case 'csisolatin6':
case 'iso885910':
case 'iso8859101992':
case 'isoir157':
case 'l6':
case 'latin6':
return 'ISO-8859-10';
case 'iso885913':
return 'ISO-8859-13';
case 'iso885914':
case 'iso8859141998':
case 'isoceltic':
case 'isoir199':
case 'l8':
case 'latin8':
return 'ISO-8859-14';
case 'iso885915':
case 'latin9':
return 'ISO-8859-15';
case 'iso885916':
case 'iso8859162001':
case 'isoir226':
case 'l10':
case 'latin10':
return 'ISO-8859-16';
case 'iso10646j1':
return 'ISO-10646-J-1';
case 'csunicode':
case 'iso10646ucs2':
return 'ISO-10646-UCS-2';
case 'csucs4':
case 'iso10646ucs4':
return 'ISO-10646-UCS-4';
case 'csunicodeascii':
case 'iso10646ucsbasic':
return 'ISO-10646-UCS-Basic';
case 'csunicodelatin1':
case 'iso10646':
case 'iso10646unicodelatin1':
return 'ISO-10646-Unicode-Latin1';
case 'csiso10646utf1':
case 'iso10646utf1':
return 'ISO-10646-UTF-1';
case 'csiso115481':
case 'iso115481':
case 'isotr115481':
return 'ISO-11548-1';
case 'csiso90':
case 'isoir90':
return 'iso-ir-90';
case 'csunicodeibm1261':
case 'isounicodeibm1261':
return 'ISO-Unicode-IBM-1261';
case 'csunicodeibm1264':
case 'isounicodeibm1264':
return 'ISO-Unicode-IBM-1264';
case 'csunicodeibm1265':
case 'isounicodeibm1265':
return 'ISO-Unicode-IBM-1265';
case 'csunicodeibm1268':
case 'isounicodeibm1268':
return 'ISO-Unicode-IBM-1268';
case 'csunicodeibm1276':
case 'isounicodeibm1276':
return 'ISO-Unicode-IBM-1276';
case 'csiso646basic1983':
case 'iso646basic1983':
case 'ref':
return 'ISO_646.basic:1983';
case 'csiso2intlrefversion':
case 'irv':
case 'iso646irv1983':
case 'isoir2':
return 'ISO_646.irv:1983';
case 'csiso2033':
case 'e13b':
case 'iso20331983':
case 'isoir98':
return 'ISO_2033-1983';
case 'csiso5427cyrillic':
case 'iso5427':
case 'isoir37':
return 'ISO_5427';
case 'iso5427cyrillic1981':
case 'iso54271981':
case 'isoir54':
return 'ISO_5427:1981';
case 'csiso5428greek':
case 'iso54281980':
case 'isoir55':
return 'ISO_5428:1980';
case 'csiso6937add':
case 'iso6937225':
case 'isoir152':
return 'ISO_6937-2-25';
case 'csisotextcomm':
case 'iso69372add':
case 'isoir142':
return 'ISO_6937-2-add';
case 'csiso8859supp':
case 'iso8859supp':
case 'isoir154':
case 'latin125':
return 'ISO_8859-supp';
case 'csiso10367box':
case 'iso10367box':
case 'isoir155':
return 'ISO_10367-box';
case 'csiso15italian':
case 'iso646it':
case 'isoir15':
case 'it':
return 'IT';
case 'csiso13jisc6220jp':
case 'isoir13':
case 'jisc62201969':
case 'jisc62201969jp':
case 'katakana':
case 'x2017':
return 'JIS_C6220-1969-jp';
case 'csiso14jisc6220ro':
case 'iso646jp':
case 'isoir14':
case 'jisc62201969ro':
case 'jp':
return 'JIS_C6220-1969-ro';
case 'csiso42jisc62261978':
case 'isoir42':
case 'jisc62261978':
return 'JIS_C6226-1978';
case 'csiso87jisx208':
case 'isoir87':
case 'jisc62261983':
case 'jisx2081983':
case 'x208':
return 'JIS_C6226-1983';
case 'csiso91jisc62291984a':
case 'isoir91':
case 'jisc62291984a':
case 'jpocra':
return 'JIS_C6229-1984-a';
case 'csiso92jisc62991984b':
case 'iso646jpocrb':
case 'isoir92':
case 'jisc62291984b':
case 'jpocrb':
return 'JIS_C6229-1984-b';
case 'csiso93jis62291984badd':
case 'isoir93':
case 'jisc62291984badd':
case 'jpocrbadd':
return 'JIS_C6229-1984-b-add';
case 'csiso94jis62291984hand':
case 'isoir94':
case 'jisc62291984hand':
case 'jpocrhand':
return 'JIS_C6229-1984-hand';
case 'csiso95jis62291984handadd':
case 'isoir95':
case 'jisc62291984handadd':
case 'jpocrhandadd':
return 'JIS_C6229-1984-hand-add';
case 'csiso96jisc62291984kana':
case 'isoir96':
case 'jisc62291984kana':
return 'JIS_C6229-1984-kana';
case 'csjisencoding':
case 'jisencoding':
return 'JIS_Encoding';
case 'cshalfwidthkatakana':
case 'jisx201':
case 'x201':
return 'JIS_X0201';
case 'csiso159jisx2121990':
case 'isoir159':
case 'jisx2121990':
case 'x212':
return 'JIS_X0212-1990';
case 'csiso141jusib1002':
case 'iso646yu':
case 'isoir141':
case 'js':
case 'jusib1002':
case 'yu':
return 'JUS_I.B1.002';
case 'csiso147macedonian':
case 'isoir147':
case 'jusib1003mac':
case 'macedonian':
return 'JUS_I.B1.003-mac';
case 'csiso146serbian':
case 'isoir146':
case 'jusib1003serb':
case 'serbian':
return 'JUS_I.B1.003-serb';
case 'koi7switched':
return 'KOI7-switched';
case 'cskoi8r':
case 'koi8r':
return 'KOI8-R';
case 'koi8u':
return 'KOI8-U';
case 'csksc5636':
case 'iso646kr':
case 'ksc5636':
return 'KSC5636';
case 'cskz1048':
case 'kz1048':
case 'rk1048':
case 'strk10482002':
return 'KZ-1048';
case 'csiso19latingreek':
case 'isoir19':
case 'latingreek':
return 'latin-greek';
case 'csiso27latingreek1':
case 'isoir27':
case 'latingreek1':
return 'Latin-greek-1';
case 'csiso158lap':
case 'isoir158':
case 'lap':
case 'latinlap':
return 'latin-lap';
case 'csmacintosh':
case 'mac':
case 'macintosh':
return 'macintosh';
case 'csmicrosoftpublishing':
case 'microsoftpublishing':
return 'Microsoft-Publishing';
case 'csmnem':
case 'mnem':
return 'MNEM';
case 'csmnemonic':
case 'mnemonic':
return 'MNEMONIC';
case 'csiso86hungarian':
case 'hu':
case 'iso646hu':
case 'isoir86':
case 'msz77953':
return 'MSZ_7795.3';
case 'csnatsdano':
case 'isoir91':
case 'natsdano':
return 'NATS-DANO';
case 'csnatsdanoadd':
case 'isoir92':
case 'natsdanoadd':
return 'NATS-DANO-ADD';
case 'csnatssefi':
case 'isoir81':
case 'natssefi':
return 'NATS-SEFI';
case 'csnatssefiadd':
case 'isoir82':
case 'natssefiadd':
return 'NATS-SEFI-ADD';
case 'csiso151cuba':
case 'cuba':
case 'iso646cu':
case 'isoir151':
case 'ncnc1081':
return 'NC_NC00-10:81';
case 'csiso69french':
case 'fr':
case 'iso646fr':
case 'isoir69':
case 'nfz62010':
return 'NF_Z_62-010';
case 'csiso25french':
case 'iso646fr1':
case 'isoir25':
case 'nfz620101973':
return 'NF_Z_62-010_(1973)';
case 'csiso60danishnorwegian':
case 'csiso60norwegian1':
case 'iso646no':
case 'isoir60':
case 'no':
case 'ns45511':
return 'NS_4551-1';
case 'csiso61norwegian2':
case 'iso646no2':
case 'isoir61':
case 'no2':
case 'ns45512':
return 'NS_4551-2';
case 'osdebcdicdf3irv':
return 'OSD_EBCDIC_DF03_IRV';
case 'osdebcdicdf41':
return 'OSD_EBCDIC_DF04_1';
case 'osdebcdicdf415':
return 'OSD_EBCDIC_DF04_15';
case 'cspc8danishnorwegian':
case 'pc8danishnorwegian':
return 'PC8-Danish-Norwegian';
case 'cspc8turkish':
case 'pc8turkish':
return 'PC8-Turkish';
case 'csiso16portuguese':
case 'iso646pt':
case 'isoir16':
case 'pt':
return 'PT';
case 'csiso84portuguese2':
case 'iso646pt2':
case 'isoir84':
case 'pt2':
return 'PT2';
case 'cp154':
case 'csptcp154':
case 'cyrillicasian':
case 'pt154':
case 'ptcp154':
return 'PTCP154';
case 'scsu':
return 'SCSU';
case 'csiso10swedish':
case 'fi':
case 'iso646fi':
case 'iso646se':
case 'isoir10':
case 'se':
case 'sen850200b':
return 'SEN_850200_B';
case 'csiso11swedishfornames':
case 'iso646se2':
case 'isoir11':
case 'se2':
case 'sen850200c':
return 'SEN_850200_C';
case 'csiso102t617bit':
case 'isoir102':
case 't617bit':
return 'T.61-7bit';
case 'csiso103t618bit':
case 'isoir103':
case 't61':
case 't618bit':
return 'T.61-8bit';
case 'csiso128t101g2':
case 'isoir128':
case 't101g2':
return 'T.101-G2';
case 'cstscii':
case 'tscii':
return 'TSCII';
case 'csunicode11':
case 'unicode11':
return 'UNICODE-1-1';
case 'csunicode11utf7':
case 'unicode11utf7':
return 'UNICODE-1-1-UTF-7';
case 'csunknown8bit':
case 'unknown8bit':
return 'UNKNOWN-8BIT';
case 'ansix341968':
case 'ansix341986':
case 'ascii':
case 'cp367':
case 'csascii':
case 'ibm367':
case 'iso646irv1991':
case 'iso646us':
case 'isoir6':
case 'us':
case 'usascii':
return 'US-ASCII';
case 'csusdk':
case 'usdk':
return 'us-dk';
case 'utf7':
return 'UTF-7';
case 'utf8':
return 'UTF-8';
case 'utf16':
return 'UTF-16';
case 'utf16be':
return 'UTF-16BE';
case 'utf16le':
return 'UTF-16LE';
case 'utf32':
return 'UTF-32';
case 'utf32be':
return 'UTF-32BE';
case 'utf32le':
return 'UTF-32LE';
case 'csventurainternational':
case 'venturainternational':
return 'Ventura-International';
case 'csventuramath':
case 'venturamath':
return 'Ventura-Math';
case 'csventuraus':
case 'venturaus':
return 'Ventura-US';
case 'csiso70videotexsupp1':
case 'isoir70':
case 'videotexsuppl':
return 'videotex-suppl';
case 'csviqr':
case 'viqr':
return 'VIQR';
case 'csviscii':
case 'viscii':
return 'VISCII';
case 'csshiftjis':
case 'cswindows31j':
case 'mskanji':
case 'shiftjis':
case 'windows31j':
return 'Windows-31J';
case 'iso885911':
case 'tis620':
return 'windows-874';
case 'cseuckr':
case 'csksc56011987':
case 'euckr':
case 'isoir149':
case 'korean':
case 'ksc5601':
case 'ksc56011987':
case 'ksc56011989':
case 'windows949':
return 'windows-949';
case 'windows1250':
return 'windows-1250';
case 'windows1251':
return 'windows-1251';
case 'cp819':
case 'csisolatin1':
case 'ibm819':
case 'iso88591':
case 'iso885911987':
case 'isoir100':
case 'l1':
case 'latin1':
case 'windows1252':
return 'windows-1252';
case 'windows1253':
return 'windows-1253';
case 'csisolatin5':
case 'iso88599':
case 'iso885991989':
case 'isoir148':
case 'l5':
case 'latin5':
case 'windows1254':
return 'windows-1254';
case 'windows1255':
return 'windows-1255';
case 'windows1256':
return 'windows-1256';
case 'windows1257':
return 'windows-1257';
case 'windows1258':
return 'windows-1258';
default:
return $charset;
}
}
public static function get_curl_version()
{
if (is_array($curl = curl_version())) {
$curl = $curl['version'];
} elseif (substr($curl, 0, 5) === 'curl/') {
$curl = substr($curl, 5, strcspn($curl, "\x09\x0A\x0B\x0C\x0D", 5));
} elseif (substr($curl, 0, 8) === 'libcurl/') {
$curl = substr($curl, 8, strcspn($curl, "\x09\x0A\x0B\x0C\x0D", 8));
} else {
$curl = 0;
}
return $curl;
}
/**
* Strip HTML comments
*
* @param string $data Data to strip comments from
* @return string Comment stripped string
*/
public static function strip_comments($data)
{
$output = '';
while (($start = strpos($data, '<!--')) !== false) {
$output .= substr($data, 0, $start);
if (($end = strpos($data, '-->', $start)) !== false) {
$data = substr_replace($data, '', 0, $end + 3);
} else {
$data = '';
}
}
return $output . $data;
}
public static function parse_date($dt)
{
$parser = \SimplePie\Parse\Date::get();
return $parser->parse($dt);
}
/**
* Decode HTML entities
*
* @deprecated since SimplePie 1.3, use DOMDocument instead
* @param string $data Input data
* @return string Output data
*/
public static function entities_decode($data)
{
// trigger_error(sprintf('Using method "' . __METHOD__ . '" is deprecated since SimplePie 1.3, use "DOMDocument" instead.'), \E_USER_DEPRECATED);
$decoder = new \SimplePie_Decode_HTML_Entities($data);
return $decoder->parse();
}
/**
* Remove RFC822 comments
*
* @param string $data Data to strip comments from
* @return string Comment stripped string
*/
public static function uncomment_rfc822($string)
{
$string = (string) $string;
$position = 0;
$length = strlen($string);
$depth = 0;
$output = '';
while ($position < $length && ($pos = strpos($string, '(', $position)) !== false) {
$output .= substr($string, $position, $pos - $position);
$position = $pos + 1;
if ($string[$pos - 1] !== '\\') {
$depth++;
while ($depth && $position < $length) {
$position += strcspn($string, '()', $position);
if ($string[$position - 1] === '\\') {
$position++;
continue;
} elseif (isset($string[$position])) {
switch ($string[$position]) {
case '(':
$depth++;
break;
case ')':
$depth--;
break;
}
$position++;
} else {
break;
}
}
} else {
$output .= '(';
}
}
$output .= substr($string, $position);
return $output;
}
public static function parse_mime($mime)
{
if (($pos = strpos($mime, ';')) === false) {
return trim($mime);
}
return trim(substr($mime, 0, $pos));
}
public static function atom_03_construct_type($attribs)
{
if (isset($attribs['']['mode']) && strtolower(trim($attribs['']['mode'])) === 'base64') {
$mode = \SimplePie\SimplePie::CONSTRUCT_BASE64;
} else {
$mode = \SimplePie\SimplePie::CONSTRUCT_NONE;
}
if (isset($attribs['']['type'])) {
switch (strtolower(trim($attribs['']['type']))) {
case 'text':
case 'text/plain':
return \SimplePie\SimplePie::CONSTRUCT_TEXT | $mode;
case 'html':
case 'text/html':
return \SimplePie\SimplePie::CONSTRUCT_HTML | $mode;
case 'xhtml':
case 'application/xhtml+xml':
return \SimplePie\SimplePie::CONSTRUCT_XHTML | $mode;
default:
return \SimplePie\SimplePie::CONSTRUCT_NONE | $mode;
}
}
return \SimplePie\SimplePie::CONSTRUCT_TEXT | $mode;
}
public static function atom_10_construct_type($attribs)
{
if (isset($attribs['']['type'])) {
switch (strtolower(trim($attribs['']['type']))) {
case 'text':
return \SimplePie\SimplePie::CONSTRUCT_TEXT;
case 'html':
return \SimplePie\SimplePie::CONSTRUCT_HTML;
case 'xhtml':
return \SimplePie\SimplePie::CONSTRUCT_XHTML;
default:
return \SimplePie\SimplePie::CONSTRUCT_NONE;
}
}
return \SimplePie\SimplePie::CONSTRUCT_TEXT;
}
public static function atom_10_content_construct_type($attribs)
{
if (isset($attribs['']['type'])) {
$type = strtolower(trim($attribs['']['type']));
switch ($type) {
case 'text':
return \SimplePie\SimplePie::CONSTRUCT_TEXT;
case 'html':
return \SimplePie\SimplePie::CONSTRUCT_HTML;
case 'xhtml':
return \SimplePie\SimplePie::CONSTRUCT_XHTML;
}
if (in_array(substr($type, -4), ['+xml', '/xml']) || substr($type, 0, 5) === 'text/') {
return \SimplePie\SimplePie::CONSTRUCT_NONE;
} else {
return \SimplePie\SimplePie::CONSTRUCT_BASE64;
}
}
return \SimplePie\SimplePie::CONSTRUCT_TEXT;
}
public static function is_isegment_nz_nc($string)
{
return (bool) preg_match('/^([A-Za-z0-9\-._~\x{A0}-\x{D7FF}\x{F900}-\x{FDCF}\x{FDF0}-\x{FFEF}\x{10000}-\x{1FFFD}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}\x{40000}-\x{4FFFD}\x{50000}-\x{5FFFD}\x{60000}-\x{6FFFD}\x{70000}-\x{7FFFD}\x{80000}-\x{8FFFD}\x{90000}-\x{9FFFD}\x{A0000}-\x{AFFFD}\x{B0000}-\x{BFFFD}\x{C0000}-\x{CFFFD}\x{D0000}-\x{DFFFD}\x{E1000}-\x{EFFFD}!$&\'()*+,;=@]|(%[0-9ABCDEF]{2}))+$/u', $string);
}
public static function space_separated_tokens($string)
{
$space_characters = "\x20\x09\x0A\x0B\x0C\x0D";
$string_length = strlen($string);
$position = strspn($string, $space_characters);
$tokens = [];
while ($position < $string_length) {
$len = strcspn($string, $space_characters, $position);
$tokens[] = substr($string, $position, $len);
$position += $len;
$position += strspn($string, $space_characters, $position);
}
return $tokens;
}
/**
* Converts a unicode codepoint to a UTF-8 character
*
* @static
* @param int $codepoint Unicode codepoint
* @return string UTF-8 character
*/
public static function codepoint_to_utf8($codepoint)
{
$codepoint = (int) $codepoint;
if ($codepoint < 0) {
return false;
} elseif ($codepoint <= 0x7f) {
return chr($codepoint);
} elseif ($codepoint <= 0x7ff) {
return chr(0xc0 | ($codepoint >> 6)) . chr(0x80 | ($codepoint & 0x3f));
} elseif ($codepoint <= 0xffff) {
return chr(0xe0 | ($codepoint >> 12)) . chr(0x80 | (($codepoint >> 6) & 0x3f)) . chr(0x80 | ($codepoint & 0x3f));
} elseif ($codepoint <= 0x10ffff) {
return chr(0xf0 | ($codepoint >> 18)) . chr(0x80 | (($codepoint >> 12) & 0x3f)) . chr(0x80 | (($codepoint >> 6) & 0x3f)) . chr(0x80 | ($codepoint & 0x3f));
}
// U+FFFD REPLACEMENT CHARACTER
return "\xEF\xBF\xBD";
}
/**
* Similar to parse_str()
*
* Returns an associative array of name/value pairs, where the value is an
* array of values that have used the same name
*
* @static
* @param string $str The input string.
* @return array
*/
public static function parse_str($str)
{
$return = [];
$str = explode('&', $str);
foreach ($str as $section) {
if (strpos($section, '=') !== false) {
[$name, $value] = explode('=', $section, 2);
$return[urldecode($name)][] = urldecode($value);
} else {
$return[urldecode($section)][] = null;
}
}
return $return;
}
/**
* Detect XML encoding, as per XML 1.0 Appendix F.1
*
* @todo Add support for EBCDIC
* @param string $data XML data
* @param \SimplePie\Registry $registry Class registry
* @return array Possible encodings
*/
public static function xml_encoding($data, $registry)
{
// UTF-32 Big Endian BOM
if (substr($data, 0, 4) === "\x00\x00\xFE\xFF") {
$encoding[] = 'UTF-32BE';
}
// UTF-32 Little Endian BOM
elseif (substr($data, 0, 4) === "\xFF\xFE\x00\x00") {
$encoding[] = 'UTF-32LE';
}
// UTF-16 Big Endian BOM
elseif (substr($data, 0, 2) === "\xFE\xFF") {
$encoding[] = 'UTF-16BE';
}
// UTF-16 Little Endian BOM
elseif (substr($data, 0, 2) === "\xFF\xFE") {
$encoding[] = 'UTF-16LE';
}
// UTF-8 BOM
elseif (substr($data, 0, 3) === "\xEF\xBB\xBF") {
$encoding[] = 'UTF-8';
}
// UTF-32 Big Endian Without BOM
elseif (substr($data, 0, 20) === "\x00\x00\x00\x3C\x00\x00\x00\x3F\x00\x00\x00\x78\x00\x00\x00\x6D\x00\x00\x00\x6C") {
if ($pos = strpos($data, "\x00\x00\x00\x3F\x00\x00\x00\x3E")) {
$parser = $registry->create(Parser::class, [Misc::change_encoding(substr($data, 20, $pos - 20), 'UTF-32BE', 'UTF-8')]);
if ($parser->parse()) {
$encoding[] = $parser->encoding;
}
}
$encoding[] = 'UTF-32BE';
}
// UTF-32 Little Endian Without BOM
elseif (substr($data, 0, 20) === "\x3C\x00\x00\x00\x3F\x00\x00\x00\x78\x00\x00\x00\x6D\x00\x00\x00\x6C\x00\x00\x00") {
if ($pos = strpos($data, "\x3F\x00\x00\x00\x3E\x00\x00\x00")) {
$parser = $registry->create(Parser::class, [Misc::change_encoding(substr($data, 20, $pos - 20), 'UTF-32LE', 'UTF-8')]);
if ($parser->parse()) {
$encoding[] = $parser->encoding;
}
}
$encoding[] = 'UTF-32LE';
}
// UTF-16 Big Endian Without BOM
elseif (substr($data, 0, 10) === "\x00\x3C\x00\x3F\x00\x78\x00\x6D\x00\x6C") {
if ($pos = strpos($data, "\x00\x3F\x00\x3E")) {
$parser = $registry->create(Parser::class, [Misc::change_encoding(substr($data, 20, $pos - 10), 'UTF-16BE', 'UTF-8')]);
if ($parser->parse()) {
$encoding[] = $parser->encoding;
}
}
$encoding[] = 'UTF-16BE';
}
// UTF-16 Little Endian Without BOM
elseif (substr($data, 0, 10) === "\x3C\x00\x3F\x00\x78\x00\x6D\x00\x6C\x00") {
if ($pos = strpos($data, "\x3F\x00\x3E\x00")) {
$parser = $registry->create(Parser::class, [Misc::change_encoding(substr($data, 20, $pos - 10), 'UTF-16LE', 'UTF-8')]);
if ($parser->parse()) {
$encoding[] = $parser->encoding;
}
}
$encoding[] = 'UTF-16LE';
}
// US-ASCII (or superset)
elseif (substr($data, 0, 5) === "\x3C\x3F\x78\x6D\x6C") {
if ($pos = strpos($data, "\x3F\x3E")) {
$parser = $registry->create(Parser::class, [substr($data, 5, $pos - 5)]);
if ($parser->parse()) {
$encoding[] = $parser->encoding;
}
}
$encoding[] = 'UTF-8';
}
// Fallback to UTF-8
else {
$encoding[] = 'UTF-8';
}
return $encoding;
}
public static function output_javascript()
{
if (function_exists('ob_gzhandler')) {
ob_start('ob_gzhandler');
}
header('Content-type: text/javascript; charset: UTF-8');
header('Cache-Control: must-revalidate');
header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 604800) . ' GMT'); // 7 days
$body = <<<END
function embed_quicktime(type, bgcolor, width, height, link, placeholder, loop) {
if (placeholder != '') {
document.writeln('<embed type="'+type+'" style="cursor:hand; cursor:pointer;" href="'+link+'" src="'+placeholder+'" width="'+width+'" height="'+height+'" autoplay="false" target="myself" controller="false" loop="'+loop+'" scale="aspect" bgcolor="'+bgcolor+'" pluginspage="http://www.apple.com/quicktime/download/"></embed>');
}
else {
document.writeln('<embed type="'+type+'" style="cursor:hand; cursor:pointer;" src="'+link+'" width="'+width+'" height="'+height+'" autoplay="false" target="myself" controller="true" loop="'+loop+'" scale="aspect" bgcolor="'+bgcolor+'" pluginspage="http://www.apple.com/quicktime/download/"></embed>');
}
}
function embed_flash(bgcolor, width, height, link, loop, type) {
document.writeln('<embed src="'+link+'" pluginspage="http://www.macromedia.com/go/getflashplayer" type="'+type+'" quality="high" width="'+width+'" height="'+height+'" bgcolor="'+bgcolor+'" loop="'+loop+'"></embed>');
}
function embed_flv(width, height, link, placeholder, loop, player) {
document.writeln('<embed src="'+player+'" pluginspage="http://www.macromedia.com/go/getflashplayer" type="application/x-shockwave-flash" quality="high" width="'+width+'" height="'+height+'" wmode="transparent" flashvars="file='+link+'&autostart=false&repeat='+loop+'&showdigits=true&showfsbutton=false"></embed>');
}
function embed_wmedia(width, height, link) {
document.writeln('<embed type="application/x-mplayer2" src="'+link+'" autosize="1" width="'+width+'" height="'+height+'" showcontrols="1" showstatusbar="0" showdisplay="0" autostart="0"></embed>');
}
END;
echo $body;
}
/**
* Get the SimplePie build timestamp
*
* Uses the git index if it exists, otherwise uses the modification time
* of the newest file.
*/
public static function get_build()
{
if (static::$SIMPLEPIE_BUILD !== null) {
return static::$SIMPLEPIE_BUILD;
}
$root = dirname(__FILE__, 2);
if (file_exists($root . '/.git/index')) {
static::$SIMPLEPIE_BUILD = filemtime($root . '/.git/index');
return static::$SIMPLEPIE_BUILD;
} elseif (file_exists($root . '/SimplePie')) {
$time = 0;
foreach (glob($root . '/SimplePie/*.php') as $file) {
if (($mtime = filemtime($file)) > $time) {
$time = $mtime;
}
}
static::$SIMPLEPIE_BUILD = $time;
return static::$SIMPLEPIE_BUILD;
} elseif (file_exists(dirname(__FILE__) . '/Core.php')) {
static::$SIMPLEPIE_BUILD = filemtime(dirname(__FILE__) . '/Core.php');
return static::$SIMPLEPIE_BUILD;
}
static::$SIMPLEPIE_BUILD = filemtime(__FILE__);
return static::$SIMPLEPIE_BUILD;
}
/**
* Get the default user agent string
*
* @return string
*/
public static function get_default_useragent()
{
return \SimplePie\SimplePie::NAME . '/' . \SimplePie\SimplePie::VERSION . ' (Feed Parser; ' . \SimplePie\SimplePie::URL . '; Allow like Gecko) Build/' . static::get_build();
}
/**
* Format debugging information
*/
public static function debug(&$sp)
{
$info = 'SimplePie ' . \SimplePie\SimplePie::VERSION . ' Build ' . static::get_build() . "\n";
$info .= 'PHP ' . PHP_VERSION . "\n";
if ($sp->error() !== null) {
$info .= 'Error occurred: ' . $sp->error() . "\n";
} else {
$info .= "No error found.\n";
}
$info .= "Extensions:\n";
$extensions = ['pcre', 'curl', 'zlib', 'mbstring', 'iconv', 'xmlreader', 'xml'];
foreach ($extensions as $ext) {
if (extension_loaded($ext)) {
$info .= " $ext loaded\n";
switch ($ext) {
case 'pcre':
$info .= ' Version ' . PCRE_VERSION . "\n";
break;
case 'curl':
$version = curl_version();
$info .= ' Version ' . $version['version'] . "\n";
break;
case 'mbstring':
$info .= ' Overloading: ' . mb_get_info('func_overload') . "\n";
break;
case 'iconv':
$info .= ' Version ' . ICONV_VERSION . "\n";
break;
case 'xml':
$info .= ' Version ' . LIBXML_DOTTED_VERSION . "\n";
break;
}
} else {
$info .= " $ext not loaded\n";
}
}
return $info;
}
public static function silence_errors($num, $str)
{
// No-op
}
/**
* Sanitize a URL by removing HTTP credentials.
* @param string $url the URL to sanitize.
* @return string the same URL without HTTP credentials.
*/
public static function url_remove_credentials($url)
{
return preg_replace('#^(https?://)[^/:@]+:[^/:@]+@#i', '$1', $url);
}
}
class_alias('SimplePie\Misc', 'SimplePie_Misc', false);
Core.php 0000644 00000004273 15133170420 0006147 0 ustar 00 <?php
/**
* SimplePie
*
* A PHP-Based RSS and Atom Feed Framework.
* Takes the hard work out of managing a complete RSS/Atom solution.
*
* Copyright (c) 2004-2016, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* * Neither the name of the SimplePie Team nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
* AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package SimplePie
* @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
* @author Ryan Parman
* @author Sam Sneddon
* @author Ryan McCue
* @link http://simplepie.org/ SimplePie
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
/**
* SimplePie class.
*
* Class for backward compatibility.
*
* @deprecated Use {@see SimplePie} directly
* @package SimplePie
* @subpackage API
*/
class SimplePie_Core extends SimplePie
{
} Copyright.php 0000644 00000007031 15133170420 0007222 0 ustar 00 <?php
/**
* SimplePie
*
* A PHP-Based RSS and Atom Feed Framework.
* Takes the hard work out of managing a complete RSS/Atom solution.
*
* Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* * Neither the name of the SimplePie Team nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
* AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package SimplePie
* @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
* @author Ryan Parman
* @author Sam Sneddon
* @author Ryan McCue
* @link http://simplepie.org/ SimplePie
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
namespace SimplePie;
/**
* Manages `<media:copyright>` copyright tags as defined in Media RSS
*
* Used by {@see \SimplePie\Enclosure::get_copyright()}
*
* This class can be overloaded with {@see \SimplePie\SimplePie::set_copyright_class()}
*
* @package SimplePie
* @subpackage API
*/
class Copyright
{
/**
* Copyright URL
*
* @var string
* @see get_url()
*/
public $url;
/**
* Attribution
*
* @var string
* @see get_attribution()
*/
public $label;
/**
* Constructor, used to input the data
*
* For documentation on all the parameters, see the corresponding
* properties and their accessors
*/
public function __construct($url = null, $label = null)
{
$this->url = $url;
$this->label = $label;
}
/**
* String-ified version
*
* @return string
*/
public function __toString()
{
// There is no $this->data here
return md5(serialize($this));
}
/**
* Get the copyright URL
*
* @return string|null URL to copyright information
*/
public function get_url()
{
if ($this->url !== null) {
return $this->url;
}
return null;
}
/**
* Get the attribution text
*
* @return string|null
*/
public function get_attribution()
{
if ($this->label !== null) {
return $this->label;
}
return null;
}
}
class_alias('SimplePie\Copyright', 'SimplePie_Copyright');
Gzdecode.php 0000644 00000023754 15133170420 0007010 0 ustar 00 <?php
/**
* SimplePie
*
* A PHP-Based RSS and Atom Feed Framework.
* Takes the hard work out of managing a complete RSS/Atom solution.
*
* Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* * Neither the name of the SimplePie Team nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
* AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package SimplePie
* @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
* @author Ryan Parman
* @author Sam Sneddon
* @author Ryan McCue
* @link http://simplepie.org/ SimplePie
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
namespace SimplePie;
/**
* Decode 'gzip' encoded HTTP data
*
* @package SimplePie
* @subpackage HTTP
* @link http://www.gzip.org/format.txt
*/
class Gzdecode
{
/**
* Compressed data
*
* @access private
* @var string
* @see gzdecode::$data
*/
public $compressed_data;
/**
* Size of compressed data
*
* @access private
* @var int
*/
public $compressed_size;
/**
* Minimum size of a valid gzip string
*
* @access private
* @var int
*/
public $min_compressed_size = 18;
/**
* Current position of pointer
*
* @access private
* @var int
*/
public $position = 0;
/**
* Flags (FLG)
*
* @access private
* @var int
*/
public $flags;
/**
* Uncompressed data
*
* @access public
* @see gzdecode::$compressed_data
* @var string
*/
public $data;
/**
* Modified time
*
* @access public
* @var int
*/
public $MTIME;
/**
* Extra Flags
*
* @access public
* @var int
*/
public $XFL;
/**
* Operating System
*
* @access public
* @var int
*/
public $OS;
/**
* Subfield ID 1
*
* @access public
* @see gzdecode::$extra_field
* @see gzdecode::$SI2
* @var string
*/
public $SI1;
/**
* Subfield ID 2
*
* @access public
* @see gzdecode::$extra_field
* @see gzdecode::$SI1
* @var string
*/
public $SI2;
/**
* Extra field content
*
* @access public
* @see gzdecode::$SI1
* @see gzdecode::$SI2
* @var string
*/
public $extra_field;
/**
* Original filename
*
* @access public
* @var string
*/
public $filename;
/**
* Human readable comment
*
* @access public
* @var string
*/
public $comment;
/**
* Don't allow anything to be set
*
* @param string $name
* @param mixed $value
*/
public function __set($name, $value)
{
throw new Exception("Cannot write property $name");
}
/**
* Set the compressed string and related properties
*
* @param string $data
*/
public function __construct($data)
{
$this->compressed_data = $data;
$this->compressed_size = strlen($data);
}
/**
* Decode the GZIP stream
*
* @return bool Successfulness
*/
public function parse()
{
if ($this->compressed_size >= $this->min_compressed_size) {
$len = 0;
// Check ID1, ID2, and CM
if (substr($this->compressed_data, 0, 3) !== "\x1F\x8B\x08") {
return false;
}
// Get the FLG (FLaGs)
$this->flags = ord($this->compressed_data[3]);
// FLG bits above (1 << 4) are reserved
if ($this->flags > 0x1F) {
return false;
}
// Advance the pointer after the above
$this->position += 4;
// MTIME
$mtime = substr($this->compressed_data, $this->position, 4);
// Reverse the string if we're on a big-endian arch because l is the only signed long and is machine endianness
if (current(unpack('S', "\x00\x01")) === 1) {
$mtime = strrev($mtime);
}
$this->MTIME = current(unpack('l', $mtime));
$this->position += 4;
// Get the XFL (eXtra FLags)
$this->XFL = ord($this->compressed_data[$this->position++]);
// Get the OS (Operating System)
$this->OS = ord($this->compressed_data[$this->position++]);
// Parse the FEXTRA
if ($this->flags & 4) {
// Read subfield IDs
$this->SI1 = $this->compressed_data[$this->position++];
$this->SI2 = $this->compressed_data[$this->position++];
// SI2 set to zero is reserved for future use
if ($this->SI2 === "\x00") {
return false;
}
// Get the length of the extra field
$len = current(unpack('v', substr($this->compressed_data, $this->position, 2)));
$this->position += 2;
// Check the length of the string is still valid
$this->min_compressed_size += $len + 4;
if ($this->compressed_size >= $this->min_compressed_size) {
// Set the extra field to the given data
$this->extra_field = substr($this->compressed_data, $this->position, $len);
$this->position += $len;
} else {
return false;
}
}
// Parse the FNAME
if ($this->flags & 8) {
// Get the length of the filename
$len = strcspn($this->compressed_data, "\x00", $this->position);
// Check the length of the string is still valid
$this->min_compressed_size += $len + 1;
if ($this->compressed_size >= $this->min_compressed_size) {
// Set the original filename to the given string
$this->filename = substr($this->compressed_data, $this->position, $len);
$this->position += $len + 1;
} else {
return false;
}
}
// Parse the FCOMMENT
if ($this->flags & 16) {
// Get the length of the comment
$len = strcspn($this->compressed_data, "\x00", $this->position);
// Check the length of the string is still valid
$this->min_compressed_size += $len + 1;
if ($this->compressed_size >= $this->min_compressed_size) {
// Set the original comment to the given string
$this->comment = substr($this->compressed_data, $this->position, $len);
$this->position += $len + 1;
} else {
return false;
}
}
// Parse the FHCRC
if ($this->flags & 2) {
// Check the length of the string is still valid
$this->min_compressed_size += $len + 2;
if ($this->compressed_size >= $this->min_compressed_size) {
// Read the CRC
$crc = current(unpack('v', substr($this->compressed_data, $this->position, 2)));
// Check the CRC matches
if ((crc32(substr($this->compressed_data, 0, $this->position)) & 0xFFFF) === $crc) {
$this->position += 2;
} else {
return false;
}
} else {
return false;
}
}
// Decompress the actual data
if (($this->data = gzinflate(substr($this->compressed_data, $this->position, -8))) === false) {
return false;
}
$this->position = $this->compressed_size - 8;
// Check CRC of data
$crc = current(unpack('V', substr($this->compressed_data, $this->position, 4)));
$this->position += 4;
/*if (extension_loaded('hash') && sprintf('%u', current(unpack('V', hash('crc32b', $this->data)))) !== sprintf('%u', $crc))
{
return false;
}*/
// Check ISIZE of data
$isize = current(unpack('V', substr($this->compressed_data, $this->position, 4)));
$this->position += 4;
if (sprintf('%u', strlen($this->data) & 0xFFFFFFFF) !== sprintf('%u', $isize)) {
return false;
}
// Wow, against all odds, we've actually got a valid gzip string
return true;
}
return false;
}
}
class_alias('SimplePie\Gzdecode', 'SimplePie_gzdecode');
HTTP/Parser.php 0000644 00000035073 15133170420 0007274 0 ustar 00 <?php
/**
* SimplePie
*
* A PHP-Based RSS and Atom Feed Framework.
* Takes the hard work out of managing a complete RSS/Atom solution.
*
* Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* * Neither the name of the SimplePie Team nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
* AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package SimplePie
* @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
* @author Ryan Parman
* @author Sam Sneddon
* @author Ryan McCue
* @link http://simplepie.org/ SimplePie
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
namespace SimplePie\HTTP;
/**
* HTTP Response Parser
*
* @package SimplePie
* @subpackage HTTP
*/
class Parser
{
/**
* HTTP Version
*
* @var float
*/
public $http_version = 0.0;
/**
* Status code
*
* @var int
*/
public $status_code = 0;
/**
* Reason phrase
*
* @var string
*/
public $reason = '';
/**
* Key/value pairs of the headers
*
* @var array
*/
public $headers = [];
/**
* Body of the response
*
* @var string
*/
public $body = '';
private const STATE_HTTP_VERSION = 'http_version';
private const STATE_STATUS = 'status';
private const STATE_REASON = 'reason';
private const STATE_NEW_LINE = 'new_line';
private const STATE_BODY = 'body';
private const STATE_NAME = 'name';
private const STATE_VALUE = 'value';
private const STATE_VALUE_CHAR = 'value_char';
private const STATE_QUOTE = 'quote';
private const STATE_QUOTE_ESCAPED = 'quote_escaped';
private const STATE_QUOTE_CHAR = 'quote_char';
private const STATE_CHUNKED = 'chunked';
private const STATE_EMIT = 'emit';
private const STATE_ERROR = false;
/**
* Current state of the state machine
*
* @var self::STATE_*
*/
protected $state = self::STATE_HTTP_VERSION;
/**
* Input data
*
* @var string
*/
protected $data = '';
/**
* Input data length (to avoid calling strlen() everytime this is needed)
*
* @var int
*/
protected $data_length = 0;
/**
* Current position of the pointer
*
* @var int
*/
protected $position = 0;
/**
* Name of the hedaer currently being parsed
*
* @var string
*/
protected $name = '';
/**
* Value of the hedaer currently being parsed
*
* @var string
*/
protected $value = '';
/**
* Create an instance of the class with the input data
*
* @param string $data Input data
*/
public function __construct($data)
{
$this->data = $data;
$this->data_length = strlen($this->data);
}
/**
* Parse the input data
*
* @return bool true on success, false on failure
*/
public function parse()
{
while ($this->state && $this->state !== self::STATE_EMIT && $this->has_data()) {
$state = $this->state;
$this->$state();
}
$this->data = '';
if ($this->state === self::STATE_EMIT || $this->state === self::STATE_BODY) {
return true;
}
$this->http_version = '';
$this->status_code = 0;
$this->reason = '';
$this->headers = [];
$this->body = '';
return false;
}
/**
* Check whether there is data beyond the pointer
*
* @return bool true if there is further data, false if not
*/
protected function has_data()
{
return (bool) ($this->position < $this->data_length);
}
/**
* See if the next character is LWS
*
* @return bool true if the next character is LWS, false if not
*/
protected function is_linear_whitespace()
{
return (bool) ($this->data[$this->position] === "\x09"
|| $this->data[$this->position] === "\x20"
|| ($this->data[$this->position] === "\x0A"
&& isset($this->data[$this->position + 1])
&& ($this->data[$this->position + 1] === "\x09" || $this->data[$this->position + 1] === "\x20")));
}
/**
* Parse the HTTP version
*/
protected function http_version()
{
if (strpos($this->data, "\x0A") !== false && strtoupper(substr($this->data, 0, 5)) === 'HTTP/') {
$len = strspn($this->data, '0123456789.', 5);
$this->http_version = substr($this->data, 5, $len);
$this->position += 5 + $len;
if (substr_count($this->http_version, '.') <= 1) {
$this->http_version = (float) $this->http_version;
$this->position += strspn($this->data, "\x09\x20", $this->position);
$this->state = self::STATE_STATUS;
} else {
$this->state = self::STATE_ERROR;
}
} else {
$this->state = self::STATE_ERROR;
}
}
/**
* Parse the status code
*/
protected function status()
{
if ($len = strspn($this->data, '0123456789', $this->position)) {
$this->status_code = (int) substr($this->data, $this->position, $len);
$this->position += $len;
$this->state = self::STATE_REASON;
} else {
$this->state = self::STATE_ERROR;
}
}
/**
* Parse the reason phrase
*/
protected function reason()
{
$len = strcspn($this->data, "\x0A", $this->position);
$this->reason = trim(substr($this->data, $this->position, $len), "\x09\x0D\x20");
$this->position += $len + 1;
$this->state = self::STATE_NEW_LINE;
}
/**
* Deal with a new line, shifting data around as needed
*/
protected function new_line()
{
$this->value = trim($this->value, "\x0D\x20");
if ($this->name !== '' && $this->value !== '') {
$this->name = strtolower($this->name);
// We should only use the last Content-Type header. c.f. issue #1
if (isset($this->headers[$this->name]) && $this->name !== 'content-type') {
$this->headers[$this->name] .= ', ' . $this->value;
} else {
$this->headers[$this->name] = $this->value;
}
}
$this->name = '';
$this->value = '';
if (substr($this->data[$this->position], 0, 2) === "\x0D\x0A") {
$this->position += 2;
$this->state = self::STATE_BODY;
} elseif ($this->data[$this->position] === "\x0A") {
$this->position++;
$this->state = self::STATE_BODY;
} else {
$this->state = self::STATE_NAME;
}
}
/**
* Parse a header name
*/
protected function name()
{
$len = strcspn($this->data, "\x0A:", $this->position);
if (isset($this->data[$this->position + $len])) {
if ($this->data[$this->position + $len] === "\x0A") {
$this->position += $len;
$this->state = self::STATE_NEW_LINE;
} else {
$this->name = substr($this->data, $this->position, $len);
$this->position += $len + 1;
$this->state = self::STATE_VALUE;
}
} else {
$this->state = self::STATE_ERROR;
}
}
/**
* Parse LWS, replacing consecutive LWS characters with a single space
*/
protected function linear_whitespace()
{
do {
if (substr($this->data, $this->position, 2) === "\x0D\x0A") {
$this->position += 2;
} elseif ($this->data[$this->position] === "\x0A") {
$this->position++;
}
$this->position += strspn($this->data, "\x09\x20", $this->position);
} while ($this->has_data() && $this->is_linear_whitespace());
$this->value .= "\x20";
}
/**
* See what state to move to while within non-quoted header values
*/
protected function value()
{
if ($this->is_linear_whitespace()) {
$this->linear_whitespace();
} else {
switch ($this->data[$this->position]) {
case '"':
// Workaround for ETags: we have to include the quotes as
// part of the tag.
if (strtolower($this->name) === 'etag') {
$this->value .= '"';
$this->position++;
$this->state = self::STATE_VALUE_CHAR;
break;
}
$this->position++;
$this->state = self::STATE_QUOTE;
break;
case "\x0A":
$this->position++;
$this->state = self::STATE_NEW_LINE;
break;
default:
$this->state = self::STATE_VALUE_CHAR;
break;
}
}
}
/**
* Parse a header value while outside quotes
*/
protected function value_char()
{
$len = strcspn($this->data, "\x09\x20\x0A\"", $this->position);
$this->value .= substr($this->data, $this->position, $len);
$this->position += $len;
$this->state = self::STATE_VALUE;
}
/**
* See what state to move to while within quoted header values
*/
protected function quote()
{
if ($this->is_linear_whitespace()) {
$this->linear_whitespace();
} else {
switch ($this->data[$this->position]) {
case '"':
$this->position++;
$this->state = self::STATE_VALUE;
break;
case "\x0A":
$this->position++;
$this->state = self::STATE_NEW_LINE;
break;
case '\\':
$this->position++;
$this->state = self::STATE_QUOTE_ESCAPED;
break;
default:
$this->state = self::STATE_QUOTE_CHAR;
break;
}
}
}
/**
* Parse a header value while within quotes
*/
protected function quote_char()
{
$len = strcspn($this->data, "\x09\x20\x0A\"\\", $this->position);
$this->value .= substr($this->data, $this->position, $len);
$this->position += $len;
$this->state = self::STATE_VALUE;
}
/**
* Parse an escaped character within quotes
*/
protected function quote_escaped()
{
$this->value .= $this->data[$this->position];
$this->position++;
$this->state = self::STATE_QUOTE;
}
/**
* Parse the body
*/
protected function body()
{
$this->body = substr($this->data, $this->position);
if (!empty($this->headers['transfer-encoding'])) {
unset($this->headers['transfer-encoding']);
$this->state = self::STATE_CHUNKED;
} else {
$this->state = self::STATE_EMIT;
}
}
/**
* Parsed a "Transfer-Encoding: chunked" body
*/
protected function chunked()
{
if (!preg_match('/^([0-9a-f]+)[^\r\n]*\r\n/i', trim($this->body))) {
$this->state = self::STATE_EMIT;
return;
}
$decoded = '';
$encoded = $this->body;
while (true) {
$is_chunked = (bool) preg_match('/^([0-9a-f]+)[^\r\n]*\r\n/i', $encoded, $matches);
if (!$is_chunked) {
// Looks like it's not chunked after all
$this->state = self::STATE_EMIT;
return;
}
$length = hexdec(trim($matches[1]));
if ($length === 0) {
// Ignore trailer headers
$this->state = self::STATE_EMIT;
$this->body = $decoded;
return;
}
$chunk_length = strlen($matches[0]);
$decoded .= substr($encoded, $chunk_length, $length);
$encoded = substr($encoded, $chunk_length + $length + 2);
// BC for PHP < 8.0: substr() can return bool instead of string
$encoded = ($encoded === false) ? '' : $encoded;
if (trim($encoded) === '0' || empty($encoded)) {
$this->state = self::STATE_EMIT;
$this->body = $decoded;
return;
}
}
}
/**
* Prepare headers (take care of proxies headers)
*
* @param string $headers Raw headers
* @param integer $count Redirection count. Default to 1.
*
* @return string
*/
public static function prepareHeaders($headers, $count = 1)
{
$data = explode("\r\n\r\n", $headers, $count);
$data = array_pop($data);
if (false !== stripos($data, "HTTP/1.0 200 Connection established\r\n")) {
$exploded = explode("\r\n\r\n", $data, 2);
$data = end($exploded);
}
if (false !== stripos($data, "HTTP/1.1 200 Connection established\r\n")) {
$exploded = explode("\r\n\r\n", $data, 2);
$data = end($exploded);
}
return $data;
}
}
class_alias('SimplePie\HTTP\Parser', 'SimplePie_HTTP_Parser');
Enclosure.php 0000644 00000100400 15133170420 0007203 0 ustar 00 <?php
/**
* SimplePie
*
* A PHP-Based RSS and Atom Feed Framework.
* Takes the hard work out of managing a complete RSS/Atom solution.
*
* Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* * Neither the name of the SimplePie Team nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
* AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package SimplePie
* @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
* @author Ryan Parman
* @author Sam Sneddon
* @author Ryan McCue
* @link http://simplepie.org/ SimplePie
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
namespace SimplePie;
/**
* Handles everything related to enclosures (including Media RSS and iTunes RSS)
*
* Used by {@see \SimplePie\Item::get_enclosure()} and {@see \SimplePie\Item::get_enclosures()}
*
* This class can be overloaded with {@see \SimplePie\SimplePie::set_enclosure_class()}
*
* @package SimplePie
* @subpackage API
*/
class Enclosure
{
/**
* @var string
* @see get_bitrate()
*/
public $bitrate;
/**
* @var array
* @see get_captions()
*/
public $captions;
/**
* @var array
* @see get_categories()
*/
public $categories;
/**
* @var int
* @see get_channels()
*/
public $channels;
/**
* @var \SimplePie\Copyright
* @see get_copyright()
*/
public $copyright;
/**
* @var array
* @see get_credits()
*/
public $credits;
/**
* @var string
* @see get_description()
*/
public $description;
/**
* @var int
* @see get_duration()
*/
public $duration;
/**
* @var string
* @see get_expression()
*/
public $expression;
/**
* @var string
* @see get_framerate()
*/
public $framerate;
/**
* @var string
* @see get_handler()
*/
public $handler;
/**
* @var array
* @see get_hashes()
*/
public $hashes;
/**
* @var string
* @see get_height()
*/
public $height;
/**
* @deprecated
* @var null
*/
public $javascript;
/**
* @var array
* @see get_keywords()
*/
public $keywords;
/**
* @var string
* @see get_language()
*/
public $lang;
/**
* @var string
* @see get_length()
*/
public $length;
/**
* @var string
* @see get_link()
*/
public $link;
/**
* @var string
* @see get_medium()
*/
public $medium;
/**
* @var string
* @see get_player()
*/
public $player;
/**
* @var array
* @see get_ratings()
*/
public $ratings;
/**
* @var array
* @see get_restrictions()
*/
public $restrictions;
/**
* @var string
* @see get_sampling_rate()
*/
public $samplingrate;
/**
* @var array
* @see get_thumbnails()
*/
public $thumbnails;
/**
* @var string
* @see get_title()
*/
public $title;
/**
* @var string
* @see get_type()
*/
public $type;
/**
* @var string
* @see get_width()
*/
public $width;
/**
* Constructor, used to input the data
*
* For documentation on all the parameters, see the corresponding
* properties and their accessors
*
* @uses idna_convert If available, this will convert an IDN
*/
public function __construct($link = null, $type = null, $length = null, $javascript = null, $bitrate = null, $captions = null, $categories = null, $channels = null, $copyright = null, $credits = null, $description = null, $duration = null, $expression = null, $framerate = null, $hashes = null, $height = null, $keywords = null, $lang = null, $medium = null, $player = null, $ratings = null, $restrictions = null, $samplingrate = null, $thumbnails = null, $title = null, $width = null)
{
$this->bitrate = $bitrate;
$this->captions = $captions;
$this->categories = $categories;
$this->channels = $channels;
$this->copyright = $copyright;
$this->credits = $credits;
$this->description = $description;
$this->duration = $duration;
$this->expression = $expression;
$this->framerate = $framerate;
$this->hashes = $hashes;
$this->height = $height;
$this->keywords = $keywords;
$this->lang = $lang;
$this->length = $length;
$this->link = $link;
$this->medium = $medium;
$this->player = $player;
$this->ratings = $ratings;
$this->restrictions = $restrictions;
$this->samplingrate = $samplingrate;
$this->thumbnails = $thumbnails;
$this->title = $title;
$this->type = $type;
$this->width = $width;
if (class_exists('idna_convert')) {
$idn = new \idna_convert();
$parsed = \SimplePie\Misc::parse_url($link);
$this->link = \SimplePie\Misc::compress_parse_url($parsed['scheme'], $idn->encode($parsed['authority']), $parsed['path'], $parsed['query'], $parsed['fragment']);
}
$this->handler = $this->get_handler(); // Needs to load last
}
/**
* String-ified version
*
* @return string
*/
public function __toString()
{
// There is no $this->data here
return md5(serialize($this));
}
/**
* Get the bitrate
*
* @return string|null
*/
public function get_bitrate()
{
if ($this->bitrate !== null) {
return $this->bitrate;
}
return null;
}
/**
* Get a single caption
*
* @param int $key
* @return \SimplePie\Caption|null
*/
public function get_caption($key = 0)
{
$captions = $this->get_captions();
if (isset($captions[$key])) {
return $captions[$key];
}
return null;
}
/**
* Get all captions
*
* @return array|null Array of {@see \SimplePie\Caption} objects
*/
public function get_captions()
{
if ($this->captions !== null) {
return $this->captions;
}
return null;
}
/**
* Get a single category
*
* @param int $key
* @return \SimplePie\Category|null
*/
public function get_category($key = 0)
{
$categories = $this->get_categories();
if (isset($categories[$key])) {
return $categories[$key];
}
return null;
}
/**
* Get all categories
*
* @return array|null Array of {@see \SimplePie\Category} objects
*/
public function get_categories()
{
if ($this->categories !== null) {
return $this->categories;
}
return null;
}
/**
* Get the number of audio channels
*
* @return int|null
*/
public function get_channels()
{
if ($this->channels !== null) {
return $this->channels;
}
return null;
}
/**
* Get the copyright information
*
* @return \SimplePie\Copyright|null
*/
public function get_copyright()
{
if ($this->copyright !== null) {
return $this->copyright;
}
return null;
}
/**
* Get a single credit
*
* @param int $key
* @return \SimplePie\Credit|null
*/
public function get_credit($key = 0)
{
$credits = $this->get_credits();
if (isset($credits[$key])) {
return $credits[$key];
}
return null;
}
/**
* Get all credits
*
* @return array|null Array of {@see \SimplePie\Credit} objects
*/
public function get_credits()
{
if ($this->credits !== null) {
return $this->credits;
}
return null;
}
/**
* Get the description of the enclosure
*
* @return string|null
*/
public function get_description()
{
if ($this->description !== null) {
return $this->description;
}
return null;
}
/**
* Get the duration of the enclosure
*
* @param bool $convert Convert seconds into hh:mm:ss
* @return string|int|null 'hh:mm:ss' string if `$convert` was specified, otherwise integer (or null if none found)
*/
public function get_duration($convert = false)
{
if ($this->duration !== null) {
if ($convert) {
$time = \SimplePie\Misc::time_hms($this->duration);
return $time;
}
return $this->duration;
}
return null;
}
/**
* Get the expression
*
* @return string Probably one of 'sample', 'full', 'nonstop', 'clip'. Defaults to 'full'
*/
public function get_expression()
{
if ($this->expression !== null) {
return $this->expression;
}
return 'full';
}
/**
* Get the file extension
*
* @return string|null
*/
public function get_extension()
{
if ($this->link !== null) {
$url = \SimplePie\Misc::parse_url($this->link);
if ($url['path'] !== '') {
return pathinfo($url['path'], PATHINFO_EXTENSION);
}
}
return null;
}
/**
* Get the framerate (in frames-per-second)
*
* @return string|null
*/
public function get_framerate()
{
if ($this->framerate !== null) {
return $this->framerate;
}
return null;
}
/**
* Get the preferred handler
*
* @return string|null One of 'flash', 'fmedia', 'quicktime', 'wmedia', 'mp3'
*/
public function get_handler()
{
return $this->get_real_type(true);
}
/**
* Get a single hash
*
* @link http://www.rssboard.org/media-rss#media-hash
* @param int $key
* @return string|null Hash as per `media:hash`, prefixed with "$algo:"
*/
public function get_hash($key = 0)
{
$hashes = $this->get_hashes();
if (isset($hashes[$key])) {
return $hashes[$key];
}
return null;
}
/**
* Get all credits
*
* @return array|null Array of strings, see {@see get_hash()}
*/
public function get_hashes()
{
if ($this->hashes !== null) {
return $this->hashes;
}
return null;
}
/**
* Get the height
*
* @return string|null
*/
public function get_height()
{
if ($this->height !== null) {
return $this->height;
}
return null;
}
/**
* Get the language
*
* @link http://tools.ietf.org/html/rfc3066
* @return string|null Language code as per RFC 3066
*/
public function get_language()
{
if ($this->lang !== null) {
return $this->lang;
}
return null;
}
/**
* Get a single keyword
*
* @param int $key
* @return string|null
*/
public function get_keyword($key = 0)
{
$keywords = $this->get_keywords();
if (isset($keywords[$key])) {
return $keywords[$key];
}
return null;
}
/**
* Get all keywords
*
* @return array|null Array of strings
*/
public function get_keywords()
{
if ($this->keywords !== null) {
return $this->keywords;
}
return null;
}
/**
* Get length
*
* @return float Length in bytes
*/
public function get_length()
{
if ($this->length !== null) {
return $this->length;
}
return null;
}
/**
* Get the URL
*
* @return string|null
*/
public function get_link()
{
if ($this->link !== null) {
return $this->link;
}
return null;
}
/**
* Get the medium
*
* @link http://www.rssboard.org/media-rss#media-content
* @return string|null Should be one of 'image', 'audio', 'video', 'document', 'executable'
*/
public function get_medium()
{
if ($this->medium !== null) {
return $this->medium;
}
return null;
}
/**
* Get the player URL
*
* Typically the same as {@see get_permalink()}
* @return string|null Player URL
*/
public function get_player()
{
if ($this->player !== null) {
return $this->player;
}
return null;
}
/**
* Get a single rating
*
* @param int $key
* @return \SimplePie\Rating|null
*/
public function get_rating($key = 0)
{
$ratings = $this->get_ratings();
if (isset($ratings[$key])) {
return $ratings[$key];
}
return null;
}
/**
* Get all ratings
*
* @return array|null Array of {@see \SimplePie\Rating} objects
*/
public function get_ratings()
{
if ($this->ratings !== null) {
return $this->ratings;
}
return null;
}
/**
* Get a single restriction
*
* @param int $key
* @return \SimplePie\Restriction|null
*/
public function get_restriction($key = 0)
{
$restrictions = $this->get_restrictions();
if (isset($restrictions[$key])) {
return $restrictions[$key];
}
return null;
}
/**
* Get all restrictions
*
* @return array|null Array of {@see \SimplePie\Restriction} objects
*/
public function get_restrictions()
{
if ($this->restrictions !== null) {
return $this->restrictions;
}
return null;
}
/**
* Get the sampling rate (in kHz)
*
* @return string|null
*/
public function get_sampling_rate()
{
if ($this->samplingrate !== null) {
return $this->samplingrate;
}
return null;
}
/**
* Get the file size (in MiB)
*
* @return float|null File size in mebibytes (1048 bytes)
*/
public function get_size()
{
$length = $this->get_length();
if ($length !== null) {
return round($length / 1048576, 2);
}
return null;
}
/**
* Get a single thumbnail
*
* @param int $key
* @return string|null Thumbnail URL
*/
public function get_thumbnail($key = 0)
{
$thumbnails = $this->get_thumbnails();
if (isset($thumbnails[$key])) {
return $thumbnails[$key];
}
return null;
}
/**
* Get all thumbnails
*
* @return array|null Array of thumbnail URLs
*/
public function get_thumbnails()
{
if ($this->thumbnails !== null) {
return $this->thumbnails;
}
return null;
}
/**
* Get the title
*
* @return string|null
*/
public function get_title()
{
if ($this->title !== null) {
return $this->title;
}
return null;
}
/**
* Get mimetype of the enclosure
*
* @see get_real_type()
* @return string|null MIME type
*/
public function get_type()
{
if ($this->type !== null) {
return $this->type;
}
return null;
}
/**
* Get the width
*
* @return string|null
*/
public function get_width()
{
if ($this->width !== null) {
return $this->width;
}
return null;
}
/**
* Embed the enclosure using `<embed>`
*
* @deprecated Use the second parameter to {@see embed} instead
*
* @param array|string $options See first parameter to {@see embed}
* @return string HTML string to output
*/
public function native_embed($options = '')
{
return $this->embed($options, true);
}
/**
* Embed the enclosure using Javascript
*
* `$options` is an array or comma-separated key:value string, with the
* following properties:
*
* - `alt` (string): Alternate content for when an end-user does not have
* the appropriate handler installed or when a file type is
* unsupported. Can be any text or HTML. Defaults to blank.
* - `altclass` (string): If a file type is unsupported, the end-user will
* see the alt text (above) linked directly to the content. That link
* will have this value as its class name. Defaults to blank.
* - `audio` (string): This is an image that should be used as a
* placeholder for audio files before they're loaded (QuickTime-only).
* Can be any relative or absolute URL. Defaults to blank.
* - `bgcolor` (string): The background color for the media, if not
* already transparent. Defaults to `#ffffff`.
* - `height` (integer): The height of the embedded media. Accepts any
* numeric pixel value (such as `360`) or `auto`. Defaults to `auto`,
* and it is recommended that you use this default.
* - `loop` (boolean): Do you want the media to loop when it's done?
* Defaults to `false`.
* - `mediaplayer` (string): The location of the included
* `mediaplayer.swf` file. This allows for the playback of Flash Video
* (`.flv`) files, and is the default handler for non-Odeo MP3's.
* Defaults to blank.
* - `video` (string): This is an image that should be used as a
* placeholder for video files before they're loaded (QuickTime-only).
* Can be any relative or absolute URL. Defaults to blank.
* - `width` (integer): The width of the embedded media. Accepts any
* numeric pixel value (such as `480`) or `auto`. Defaults to `auto`,
* and it is recommended that you use this default.
* - `widescreen` (boolean): Is the enclosure widescreen or standard?
* This applies only to video enclosures, and will automatically resize
* the content appropriately. Defaults to `false`, implying 4:3 mode.
*
* Note: Non-widescreen (4:3) mode with `width` and `height` set to `auto`
* will default to 480x360 video resolution. Widescreen (16:9) mode with
* `width` and `height` set to `auto` will default to 480x270 video resolution.
*
* @todo If the dimensions for media:content are defined, use them when width/height are set to 'auto'.
* @param array|string $options Comma-separated key:value list, or array
* @param bool $native Use `<embed>`
* @return string HTML string to output
*/
public function embed($options = '', $native = false)
{
// Set up defaults
$audio = '';
$video = '';
$alt = '';
$altclass = '';
$loop = 'false';
$width = 'auto';
$height = 'auto';
$bgcolor = '#ffffff';
$mediaplayer = '';
$widescreen = false;
$handler = $this->get_handler();
$type = $this->get_real_type();
$placeholder = '';
// Process options and reassign values as necessary
if (is_array($options)) {
extract($options);
} else {
$options = explode(',', $options);
foreach ($options as $option) {
$opt = explode(':', $option, 2);
if (isset($opt[0], $opt[1])) {
$opt[0] = trim($opt[0]);
$opt[1] = trim($opt[1]);
switch ($opt[0]) {
case 'audio':
$audio = $opt[1];
break;
case 'video':
$video = $opt[1];
break;
case 'alt':
$alt = $opt[1];
break;
case 'altclass':
$altclass = $opt[1];
break;
case 'loop':
$loop = $opt[1];
break;
case 'width':
$width = $opt[1];
break;
case 'height':
$height = $opt[1];
break;
case 'bgcolor':
$bgcolor = $opt[1];
break;
case 'mediaplayer':
$mediaplayer = $opt[1];
break;
case 'widescreen':
$widescreen = $opt[1];
break;
}
}
}
}
$mime = explode('/', $type, 2);
$mime = $mime[0];
// Process values for 'auto'
if ($width === 'auto') {
if ($mime === 'video') {
if ($height === 'auto') {
$width = 480;
} elseif ($widescreen) {
$width = round((intval($height) / 9) * 16);
} else {
$width = round((intval($height) / 3) * 4);
}
} else {
$width = '100%';
}
}
if ($height === 'auto') {
if ($mime === 'audio') {
$height = 0;
} elseif ($mime === 'video') {
if ($width === 'auto') {
if ($widescreen) {
$height = 270;
} else {
$height = 360;
}
} elseif ($widescreen) {
$height = round((intval($width) / 16) * 9);
} else {
$height = round((intval($width) / 4) * 3);
}
} else {
$height = 376;
}
} elseif ($mime === 'audio') {
$height = 0;
}
// Set proper placeholder value
if ($mime === 'audio') {
$placeholder = $audio;
} elseif ($mime === 'video') {
$placeholder = $video;
}
$embed = '';
// Flash
if ($handler === 'flash') {
if ($native) {
$embed .= "<embed src=\"" . $this->get_link() . "\" pluginspage=\"http://adobe.com/go/getflashplayer\" type=\"$type\" quality=\"high\" width=\"$width\" height=\"$height\" bgcolor=\"$bgcolor\" loop=\"$loop\"></embed>";
} else {
$embed .= "<script type='text/javascript'>embed_flash('$bgcolor', '$width', '$height', '" . $this->get_link() . "', '$loop', '$type');</script>";
}
}
// Flash Media Player file types.
// Preferred handler for MP3 file types.
elseif ($handler === 'fmedia' || ($handler === 'mp3' && $mediaplayer !== '')) {
$height += 20;
if ($native) {
$embed .= "<embed src=\"$mediaplayer\" pluginspage=\"http://adobe.com/go/getflashplayer\" type=\"application/x-shockwave-flash\" quality=\"high\" width=\"$width\" height=\"$height\" wmode=\"transparent\" flashvars=\"file=" . rawurlencode($this->get_link().'?file_extension=.'.$this->get_extension()) . "&autostart=false&repeat=$loop&showdigits=true&showfsbutton=false\"></embed>";
} else {
$embed .= "<script type='text/javascript'>embed_flv('$width', '$height', '" . rawurlencode($this->get_link().'?file_extension=.'.$this->get_extension()) . "', '$placeholder', '$loop', '$mediaplayer');</script>";
}
}
// QuickTime 7 file types. Need to test with QuickTime 6.
// Only handle MP3's if the Flash Media Player is not present.
elseif ($handler === 'quicktime' || ($handler === 'mp3' && $mediaplayer === '')) {
$height += 16;
if ($native) {
if ($placeholder !== '') {
$embed .= "<embed type=\"$type\" style=\"cursor:hand; cursor:pointer;\" href=\"" . $this->get_link() . "\" src=\"$placeholder\" width=\"$width\" height=\"$height\" autoplay=\"false\" target=\"myself\" controller=\"false\" loop=\"$loop\" scale=\"aspect\" bgcolor=\"$bgcolor\" pluginspage=\"http://apple.com/quicktime/download/\"></embed>";
} else {
$embed .= "<embed type=\"$type\" style=\"cursor:hand; cursor:pointer;\" src=\"" . $this->get_link() . "\" width=\"$width\" height=\"$height\" autoplay=\"false\" target=\"myself\" controller=\"true\" loop=\"$loop\" scale=\"aspect\" bgcolor=\"$bgcolor\" pluginspage=\"http://apple.com/quicktime/download/\"></embed>";
}
} else {
$embed .= "<script type='text/javascript'>embed_quicktime('$type', '$bgcolor', '$width', '$height', '" . $this->get_link() . "', '$placeholder', '$loop');</script>";
}
}
// Windows Media
elseif ($handler === 'wmedia') {
$height += 45;
if ($native) {
$embed .= "<embed type=\"application/x-mplayer2\" src=\"" . $this->get_link() . "\" autosize=\"1\" width=\"$width\" height=\"$height\" showcontrols=\"1\" showstatusbar=\"0\" showdisplay=\"0\" autostart=\"0\"></embed>";
} else {
$embed .= "<script type='text/javascript'>embed_wmedia('$width', '$height', '" . $this->get_link() . "');</script>";
}
}
// Everything else
else {
$embed .= '<a href="' . $this->get_link() . '" class="' . $altclass . '">' . $alt . '</a>';
}
return $embed;
}
/**
* Get the real media type
*
* Often, feeds lie to us, necessitating a bit of deeper inspection. This
* converts types to their canonical representations based on the file
* extension
*
* @see get_type()
* @param bool $find_handler Internal use only, use {@see get_handler()} instead
* @return string MIME type
*/
public function get_real_type($find_handler = false)
{
// Mime-types by handler.
$types_flash = ['application/x-shockwave-flash', 'application/futuresplash']; // Flash
$types_fmedia = ['video/flv', 'video/x-flv','flv-application/octet-stream']; // Flash Media Player
$types_quicktime = ['audio/3gpp', 'audio/3gpp2', 'audio/aac', 'audio/x-aac', 'audio/aiff', 'audio/x-aiff', 'audio/mid', 'audio/midi', 'audio/x-midi', 'audio/mp4', 'audio/m4a', 'audio/x-m4a', 'audio/wav', 'audio/x-wav', 'video/3gpp', 'video/3gpp2', 'video/m4v', 'video/x-m4v', 'video/mp4', 'video/mpeg', 'video/x-mpeg', 'video/quicktime', 'video/sd-video']; // QuickTime
$types_wmedia = ['application/asx', 'application/x-mplayer2', 'audio/x-ms-wma', 'audio/x-ms-wax', 'video/x-ms-asf-plugin', 'video/x-ms-asf', 'video/x-ms-wm', 'video/x-ms-wmv', 'video/x-ms-wvx']; // Windows Media
$types_mp3 = ['audio/mp3', 'audio/x-mp3', 'audio/mpeg', 'audio/x-mpeg']; // MP3
if ($this->get_type() !== null) {
$type = strtolower($this->type);
} else {
$type = null;
}
// If we encounter an unsupported mime-type, check the file extension and guess intelligently.
if (!in_array($type, array_merge($types_flash, $types_fmedia, $types_quicktime, $types_wmedia, $types_mp3))) {
$extension = $this->get_extension();
if ($extension === null) {
return null;
}
switch (strtolower($extension)) {
// Audio mime-types
case 'aac':
case 'adts':
$type = 'audio/acc';
break;
case 'aif':
case 'aifc':
case 'aiff':
case 'cdda':
$type = 'audio/aiff';
break;
case 'bwf':
$type = 'audio/wav';
break;
case 'kar':
case 'mid':
case 'midi':
case 'smf':
$type = 'audio/midi';
break;
case 'm4a':
$type = 'audio/x-m4a';
break;
case 'mp3':
case 'swa':
$type = 'audio/mp3';
break;
case 'wav':
$type = 'audio/wav';
break;
case 'wax':
$type = 'audio/x-ms-wax';
break;
case 'wma':
$type = 'audio/x-ms-wma';
break;
// Video mime-types
case '3gp':
case '3gpp':
$type = 'video/3gpp';
break;
case '3g2':
case '3gp2':
$type = 'video/3gpp2';
break;
case 'asf':
$type = 'video/x-ms-asf';
break;
case 'flv':
$type = 'video/x-flv';
break;
case 'm1a':
case 'm1s':
case 'm1v':
case 'm15':
case 'm75':
case 'mp2':
case 'mpa':
case 'mpeg':
case 'mpg':
case 'mpm':
case 'mpv':
$type = 'video/mpeg';
break;
case 'm4v':
$type = 'video/x-m4v';
break;
case 'mov':
case 'qt':
$type = 'video/quicktime';
break;
case 'mp4':
case 'mpg4':
$type = 'video/mp4';
break;
case 'sdv':
$type = 'video/sd-video';
break;
case 'wm':
$type = 'video/x-ms-wm';
break;
case 'wmv':
$type = 'video/x-ms-wmv';
break;
case 'wvx':
$type = 'video/x-ms-wvx';
break;
// Flash mime-types
case 'spl':
$type = 'application/futuresplash';
break;
case 'swf':
$type = 'application/x-shockwave-flash';
break;
}
}
if ($find_handler) {
if (in_array($type, $types_flash)) {
return 'flash';
} elseif (in_array($type, $types_fmedia)) {
return 'fmedia';
} elseif (in_array($type, $types_quicktime)) {
return 'quicktime';
} elseif (in_array($type, $types_wmedia)) {
return 'wmedia';
} elseif (in_array($type, $types_mp3)) {
return 'mp3';
}
return null;
}
return $type;
}
}
class_alias('SimplePie\Enclosure', 'SimplePie_Enclosure');
IRI.php 0000644 00000105473 15133170420 0005706 0 ustar 00 <?php
/**
* SimplePie
*
* A PHP-Based RSS and Atom Feed Framework.
* Takes the hard work out of managing a complete RSS/Atom solution.
*
* Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* * Neither the name of the SimplePie Team nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
* AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package SimplePie
* @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
* @author Ryan Parman
* @author Sam Sneddon
* @author Ryan McCue
* @link http://simplepie.org/ SimplePie
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
namespace SimplePie;
/**
* IRI parser/serialiser/normaliser
*
* @package SimplePie
* @subpackage HTTP
* @author Sam Sneddon
* @author Steve Minutillo
* @author Ryan McCue
* @copyright 2007-2012 Sam Sneddon, Steve Minutillo, Ryan McCue
* @license http://www.opensource.org/licenses/bsd-license.php
*/
class IRI
{
/**
* Scheme
*
* @var string
*/
protected $scheme = null;
/**
* User Information
*
* @var string
*/
protected $iuserinfo = null;
/**
* ihost
*
* @var string
*/
protected $ihost = null;
/**
* Port
*
* @var string
*/
protected $port = null;
/**
* ipath
*
* @var string
*/
protected $ipath = '';
/**
* iquery
*
* @var string
*/
protected $iquery = null;
/**
* ifragment
*
* @var string
*/
protected $ifragment = null;
/**
* Normalization database
*
* Each key is the scheme, each value is an array with each key as the IRI
* part and value as the default value for that part.
*/
protected $normalization = [
'acap' => [
'port' => 674
],
'dict' => [
'port' => 2628
],
'file' => [
'ihost' => 'localhost'
],
'http' => [
'port' => 80,
'ipath' => '/'
],
'https' => [
'port' => 443,
'ipath' => '/'
],
];
/**
* Return the entire IRI when you try and read the object as a string
*
* @return string
*/
public function __toString()
{
return $this->get_iri();
}
/**
* Overload __set() to provide access via properties
*
* @param string $name Property name
* @param mixed $value Property value
*/
public function __set($name, $value)
{
if (method_exists($this, 'set_' . $name)) {
call_user_func([$this, 'set_' . $name], $value);
} elseif (
$name === 'iauthority'
|| $name === 'iuserinfo'
|| $name === 'ihost'
|| $name === 'ipath'
|| $name === 'iquery'
|| $name === 'ifragment'
) {
call_user_func([$this, 'set_' . substr($name, 1)], $value);
}
}
/**
* Overload __get() to provide access via properties
*
* @param string $name Property name
* @return mixed
*/
public function __get($name)
{
// isset() returns false for null, we don't want to do that
// Also why we use array_key_exists below instead of isset()
$props = get_object_vars($this);
if (
$name === 'iri' ||
$name === 'uri' ||
$name === 'iauthority' ||
$name === 'authority'
) {
$return = $this->{"get_$name"}();
} elseif (array_key_exists($name, $props)) {
$return = $this->$name;
}
// host -> ihost
elseif (($prop = 'i' . $name) && array_key_exists($prop, $props)) {
$name = $prop;
$return = $this->$prop;
}
// ischeme -> scheme
elseif (($prop = substr($name, 1)) && array_key_exists($prop, $props)) {
$name = $prop;
$return = $this->$prop;
} else {
trigger_error('Undefined property: ' . get_class($this) . '::' . $name, E_USER_NOTICE);
$return = null;
}
if ($return === null && isset($this->normalization[$this->scheme][$name])) {
return $this->normalization[$this->scheme][$name];
}
return $return;
}
/**
* Overload __isset() to provide access via properties
*
* @param string $name Property name
* @return bool
*/
public function __isset($name)
{
return method_exists($this, 'get_' . $name) || isset($this->$name);
}
/**
* Overload __unset() to provide access via properties
*
* @param string $name Property name
*/
public function __unset($name)
{
if (method_exists($this, 'set_' . $name)) {
call_user_func([$this, 'set_' . $name], '');
}
}
/**
* Create a new IRI object, from a specified string
*
* @param string $iri
*/
public function __construct($iri = null)
{
$this->set_iri($iri);
}
/**
* Clean up
*/
public function __destruct()
{
$this->set_iri(null, true);
$this->set_path(null, true);
$this->set_authority(null, true);
}
/**
* Create a new IRI object by resolving a relative IRI
*
* Returns false if $base is not absolute, otherwise an IRI.
*
* @param IRI|string $base (Absolute) Base IRI
* @param IRI|string $relative Relative IRI
* @return IRI|false
*/
public static function absolutize($base, $relative)
{
if (!($relative instanceof IRI)) {
$relative = new IRI($relative);
}
if (!$relative->is_valid()) {
return false;
} elseif ($relative->scheme !== null) {
return clone $relative;
} else {
if (!($base instanceof IRI)) {
$base = new IRI($base);
}
if ($base->scheme !== null && $base->is_valid()) {
if ($relative->get_iri() !== '') {
if ($relative->iuserinfo !== null || $relative->ihost !== null || $relative->port !== null) {
$target = clone $relative;
$target->scheme = $base->scheme;
} else {
$target = new IRI();
$target->scheme = $base->scheme;
$target->iuserinfo = $base->iuserinfo;
$target->ihost = $base->ihost;
$target->port = $base->port;
if ($relative->ipath !== '') {
if ($relative->ipath[0] === '/') {
$target->ipath = $relative->ipath;
} elseif (($base->iuserinfo !== null || $base->ihost !== null || $base->port !== null) && $base->ipath === '') {
$target->ipath = '/' . $relative->ipath;
} elseif (($last_segment = strrpos($base->ipath, '/')) !== false) {
$target->ipath = substr($base->ipath, 0, $last_segment + 1) . $relative->ipath;
} else {
$target->ipath = $relative->ipath;
}
$target->ipath = $target->remove_dot_segments($target->ipath);
$target->iquery = $relative->iquery;
} else {
$target->ipath = $base->ipath;
if ($relative->iquery !== null) {
$target->iquery = $relative->iquery;
} elseif ($base->iquery !== null) {
$target->iquery = $base->iquery;
}
}
$target->ifragment = $relative->ifragment;
}
} else {
$target = clone $base;
$target->ifragment = null;
}
$target->scheme_normalization();
return $target;
}
return false;
}
}
/**
* Parse an IRI into scheme/authority/path/query/fragment segments
*
* @param string $iri
* @return array
*/
protected function parse_iri($iri)
{
$iri = trim($iri, "\x20\x09\x0A\x0C\x0D");
if (preg_match('/^((?P<scheme>[^:\/?#]+):)?(\/\/(?P<authority>[^\/?#]*))?(?P<path>[^?#]*)(\?(?P<query>[^#]*))?(#(?P<fragment>.*))?$/', $iri, $match)) {
if ($match[1] === '') {
$match['scheme'] = null;
}
if (!isset($match[3]) || $match[3] === '') {
$match['authority'] = null;
}
if (!isset($match[5])) {
$match['path'] = '';
}
if (!isset($match[6]) || $match[6] === '') {
$match['query'] = null;
}
if (!isset($match[8]) || $match[8] === '') {
$match['fragment'] = null;
}
return $match;
}
// This can occur when a paragraph is accidentally parsed as a URI
return false;
}
/**
* Remove dot segments from a path
*
* @param string $input
* @return string
*/
protected function remove_dot_segments($input)
{
$output = '';
while (strpos($input, './') !== false || strpos($input, '/.') !== false || $input === '.' || $input === '..') {
// A: If the input buffer begins with a prefix of "../" or "./", then remove that prefix from the input buffer; otherwise,
if (strpos($input, '../') === 0) {
$input = substr($input, 3);
} elseif (strpos($input, './') === 0) {
$input = substr($input, 2);
}
// B: if the input buffer begins with a prefix of "/./" or "/.", where "." is a complete path segment, then replace that prefix with "/" in the input buffer; otherwise,
elseif (strpos($input, '/./') === 0) {
$input = substr($input, 2);
} elseif ($input === '/.') {
$input = '/';
}
// C: if the input buffer begins with a prefix of "/../" or "/..", where ".." is a complete path segment, then replace that prefix with "/" in the input buffer and remove the last segment and its preceding "/" (if any) from the output buffer; otherwise,
elseif (strpos($input, '/../') === 0) {
$input = substr($input, 3);
$output = substr_replace($output, '', intval(strrpos($output, '/')));
} elseif ($input === '/..') {
$input = '/';
$output = substr_replace($output, '', intval(strrpos($output, '/')));
}
// D: if the input buffer consists only of "." or "..", then remove that from the input buffer; otherwise,
elseif ($input === '.' || $input === '..') {
$input = '';
}
// E: move the first path segment in the input buffer to the end of the output buffer, including the initial "/" character (if any) and any subsequent characters up to, but not including, the next "/" character or the end of the input buffer
elseif (($pos = strpos($input, '/', 1)) !== false) {
$output .= substr($input, 0, $pos);
$input = substr_replace($input, '', 0, $pos);
} else {
$output .= $input;
$input = '';
}
}
return $output . $input;
}
/**
* Replace invalid character with percent encoding
*
* @param string $string Input string
* @param string $extra_chars Valid characters not in iunreserved or
* iprivate (this is ASCII-only)
* @param bool $iprivate Allow iprivate
* @return string
*/
protected function replace_invalid_with_pct_encoding($string, $extra_chars, $iprivate = false)
{
// Normalize as many pct-encoded sections as possible
$string = preg_replace_callback('/(?:%[A-Fa-f0-9]{2})+/', [$this, 'remove_iunreserved_percent_encoded'], $string);
// Replace invalid percent characters
$string = preg_replace('/%(?![A-Fa-f0-9]{2})/', '%25', $string);
// Add unreserved and % to $extra_chars (the latter is safe because all
// pct-encoded sections are now valid).
$extra_chars .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~%';
// Now replace any bytes that aren't allowed with their pct-encoded versions
$position = 0;
$strlen = strlen($string);
while (($position += strspn($string, $extra_chars, $position)) < $strlen) {
$value = ord($string[$position]);
$character = 0;
// Start position
$start = $position;
// By default we are valid
$valid = true;
// No one byte sequences are valid due to the while.
// Two byte sequence:
if (($value & 0xE0) === 0xC0) {
$character = ($value & 0x1F) << 6;
$length = 2;
$remaining = 1;
}
// Three byte sequence:
elseif (($value & 0xF0) === 0xE0) {
$character = ($value & 0x0F) << 12;
$length = 3;
$remaining = 2;
}
// Four byte sequence:
elseif (($value & 0xF8) === 0xF0) {
$character = ($value & 0x07) << 18;
$length = 4;
$remaining = 3;
}
// Invalid byte:
else {
$valid = false;
$length = 1;
$remaining = 0;
}
if ($remaining) {
if ($position + $length <= $strlen) {
for ($position++; $remaining; $position++) {
$value = ord($string[$position]);
// Check that the byte is valid, then add it to the character:
if (($value & 0xC0) === 0x80) {
$character |= ($value & 0x3F) << (--$remaining * 6);
}
// If it is invalid, count the sequence as invalid and reprocess the current byte:
else {
$valid = false;
$position--;
break;
}
}
} else {
$position = $strlen - 1;
$valid = false;
}
}
// Percent encode anything invalid or not in ucschar
if (
// Invalid sequences
!$valid
// Non-shortest form sequences are invalid
|| $length > 1 && $character <= 0x7F
|| $length > 2 && $character <= 0x7FF
|| $length > 3 && $character <= 0xFFFF
// Outside of range of ucschar codepoints
// Noncharacters
|| ($character & 0xFFFE) === 0xFFFE
|| $character >= 0xFDD0 && $character <= 0xFDEF
|| (
// Everything else not in ucschar
$character > 0xD7FF && $character < 0xF900
|| $character < 0xA0
|| $character > 0xEFFFD
)
&& (
// Everything not in iprivate, if it applies
!$iprivate
|| $character < 0xE000
|| $character > 0x10FFFD
)
) {
// If we were a character, pretend we weren't, but rather an error.
if ($valid) {
$position--;
}
for ($j = $start; $j <= $position; $j++) {
$string = substr_replace($string, sprintf('%%%02X', ord($string[$j])), $j, 1);
$j += 2;
$position += 2;
$strlen += 2;
}
}
}
return $string;
}
/**
* Callback function for preg_replace_callback.
*
* Removes sequences of percent encoded bytes that represent UTF-8
* encoded characters in iunreserved
*
* @param array $match PCRE match
* @return string Replacement
*/
protected function remove_iunreserved_percent_encoded($match)
{
// As we just have valid percent encoded sequences we can just explode
// and ignore the first member of the returned array (an empty string).
$bytes = explode('%', $match[0]);
// Initialize the new string (this is what will be returned) and that
// there are no bytes remaining in the current sequence (unsurprising
// at the first byte!).
$string = '';
$remaining = 0;
// these variables will be initialized in the loop but PHPStan is not able to detect it currently
$start = 0;
$character = 0;
$length = 0;
$valid = true;
// Loop over each and every byte, and set $value to its value
for ($i = 1, $len = count($bytes); $i < $len; $i++) {
$value = hexdec($bytes[$i]);
// If we're the first byte of sequence:
if (!$remaining) {
// Start position
$start = $i;
// By default we are valid
$valid = true;
// One byte sequence:
if ($value <= 0x7F) {
$character = $value;
$length = 1;
}
// Two byte sequence:
elseif (($value & 0xE0) === 0xC0) {
$character = ($value & 0x1F) << 6;
$length = 2;
$remaining = 1;
}
// Three byte sequence:
elseif (($value & 0xF0) === 0xE0) {
$character = ($value & 0x0F) << 12;
$length = 3;
$remaining = 2;
}
// Four byte sequence:
elseif (($value & 0xF8) === 0xF0) {
$character = ($value & 0x07) << 18;
$length = 4;
$remaining = 3;
}
// Invalid byte:
else {
$valid = false;
$remaining = 0;
}
}
// Continuation byte:
else {
// Check that the byte is valid, then add it to the character:
if (($value & 0xC0) === 0x80) {
$remaining--;
$character |= ($value & 0x3F) << ($remaining * 6);
}
// If it is invalid, count the sequence as invalid and reprocess the current byte as the start of a sequence:
else {
$valid = false;
$remaining = 0;
$i--;
}
}
// If we've reached the end of the current byte sequence, append it to Unicode::$data
if (!$remaining) {
// Percent encode anything invalid or not in iunreserved
if (
// Invalid sequences
!$valid
// Non-shortest form sequences are invalid
|| $length > 1 && $character <= 0x7F
|| $length > 2 && $character <= 0x7FF
|| $length > 3 && $character <= 0xFFFF
// Outside of range of iunreserved codepoints
|| $character < 0x2D
|| $character > 0xEFFFD
// Noncharacters
|| ($character & 0xFFFE) === 0xFFFE
|| $character >= 0xFDD0 && $character <= 0xFDEF
// Everything else not in iunreserved (this is all BMP)
|| $character === 0x2F
|| $character > 0x39 && $character < 0x41
|| $character > 0x5A && $character < 0x61
|| $character > 0x7A && $character < 0x7E
|| $character > 0x7E && $character < 0xA0
|| $character > 0xD7FF && $character < 0xF900
) {
for ($j = $start; $j <= $i; $j++) {
$string .= '%' . strtoupper($bytes[$j]);
}
} else {
for ($j = $start; $j <= $i; $j++) {
$string .= chr(hexdec($bytes[$j]));
}
}
}
}
// If we have any bytes left over they are invalid (i.e., we are
// mid-way through a multi-byte sequence)
if ($remaining) {
for ($j = $start; $j < $len; $j++) {
$string .= '%' . strtoupper($bytes[$j]);
}
}
return $string;
}
protected function scheme_normalization()
{
if (isset($this->normalization[$this->scheme]['iuserinfo']) && $this->iuserinfo === $this->normalization[$this->scheme]['iuserinfo']) {
$this->iuserinfo = null;
}
if (isset($this->normalization[$this->scheme]['ihost']) && $this->ihost === $this->normalization[$this->scheme]['ihost']) {
$this->ihost = null;
}
if (isset($this->normalization[$this->scheme]['port']) && $this->port === $this->normalization[$this->scheme]['port']) {
$this->port = null;
}
if (isset($this->normalization[$this->scheme]['ipath']) && $this->ipath === $this->normalization[$this->scheme]['ipath']) {
$this->ipath = '';
}
if (isset($this->normalization[$this->scheme]['iquery']) && $this->iquery === $this->normalization[$this->scheme]['iquery']) {
$this->iquery = null;
}
if (isset($this->normalization[$this->scheme]['ifragment']) && $this->ifragment === $this->normalization[$this->scheme]['ifragment']) {
$this->ifragment = null;
}
}
/**
* Check if the object represents a valid IRI. This needs to be done on each
* call as some things change depending on another part of the IRI.
*
* @return bool
*/
public function is_valid()
{
if ($this->ipath === '') {
return true;
}
$isauthority = $this->iuserinfo !== null || $this->ihost !== null ||
$this->port !== null;
if ($isauthority && $this->ipath[0] === '/') {
return true;
}
if (!$isauthority && (substr($this->ipath, 0, 2) === '//')) {
return false;
}
// Relative urls cannot have a colon in the first path segment (and the
// slashes themselves are not included so skip the first character).
if (!$this->scheme && !$isauthority &&
strpos($this->ipath, ':') !== false &&
strpos($this->ipath, '/', 1) !== false &&
strpos($this->ipath, ':') < strpos($this->ipath, '/', 1)) {
return false;
}
return true;
}
/**
* Set the entire IRI. Returns true on success, false on failure (if there
* are any invalid characters).
*
* @param string $iri
* @return bool
*/
public function set_iri($iri, $clear_cache = false)
{
static $cache;
if ($clear_cache) {
$cache = null;
return;
}
if (!$cache) {
$cache = [];
}
if ($iri === null) {
return true;
} elseif (isset($cache[$iri])) {
[
$this->scheme,
$this->iuserinfo,
$this->ihost,
$this->port,
$this->ipath,
$this->iquery,
$this->ifragment,
$return
] = $cache[$iri];
return $return;
}
$parsed = $this->parse_iri((string) $iri);
if (!$parsed) {
return false;
}
$return = $this->set_scheme($parsed['scheme'])
&& $this->set_authority($parsed['authority'])
&& $this->set_path($parsed['path'])
&& $this->set_query($parsed['query'])
&& $this->set_fragment($parsed['fragment']);
$cache[$iri] = [
$this->scheme,
$this->iuserinfo,
$this->ihost,
$this->port,
$this->ipath,
$this->iquery,
$this->ifragment,
$return
];
return $return;
}
/**
* Set the scheme. Returns true on success, false on failure (if there are
* any invalid characters).
*
* @param string $scheme
* @return bool
*/
public function set_scheme($scheme)
{
if ($scheme === null) {
$this->scheme = null;
} elseif (!preg_match('/^[A-Za-z][0-9A-Za-z+\-.]*$/', $scheme)) {
$this->scheme = null;
return false;
} else {
$this->scheme = strtolower($scheme);
}
return true;
}
/**
* Set the authority. Returns true on success, false on failure (if there are
* any invalid characters).
*
* @param string $authority
* @return bool
*/
public function set_authority($authority, $clear_cache = false)
{
static $cache;
if ($clear_cache) {
$cache = null;
return;
}
if (!$cache) {
$cache = [];
}
if ($authority === null) {
$this->iuserinfo = null;
$this->ihost = null;
$this->port = null;
return true;
} elseif (isset($cache[$authority])) {
[
$this->iuserinfo,
$this->ihost,
$this->port,
$return
] = $cache[$authority];
return $return;
}
$remaining = $authority;
if (($iuserinfo_end = strrpos($remaining, '@')) !== false) {
$iuserinfo = substr($remaining, 0, $iuserinfo_end);
$remaining = substr($remaining, $iuserinfo_end + 1);
} else {
$iuserinfo = null;
}
if (($port_start = strpos($remaining, ':', intval(strpos($remaining, ']')))) !== false) {
if (($port = substr($remaining, $port_start + 1)) === false) {
$port = null;
}
$remaining = substr($remaining, 0, $port_start);
} else {
$port = null;
}
$return = $this->set_userinfo($iuserinfo) &&
$this->set_host($remaining) &&
$this->set_port($port);
$cache[$authority] = [
$this->iuserinfo,
$this->ihost,
$this->port,
$return
];
return $return;
}
/**
* Set the iuserinfo.
*
* @param string $iuserinfo
* @return bool
*/
public function set_userinfo($iuserinfo)
{
if ($iuserinfo === null) {
$this->iuserinfo = null;
} else {
$this->iuserinfo = $this->replace_invalid_with_pct_encoding($iuserinfo, '!$&\'()*+,;=:');
$this->scheme_normalization();
}
return true;
}
/**
* Set the ihost. Returns true on success, false on failure (if there are
* any invalid characters).
*
* @param string $ihost
* @return bool
*/
public function set_host($ihost)
{
if ($ihost === null) {
$this->ihost = null;
return true;
} elseif (substr($ihost, 0, 1) === '[' && substr($ihost, -1) === ']') {
if (\SimplePie\Net\IPv6::check_ipv6(substr($ihost, 1, -1))) {
$this->ihost = '[' . \SimplePie\Net\IPv6::compress(substr($ihost, 1, -1)) . ']';
} else {
$this->ihost = null;
return false;
}
} else {
$ihost = $this->replace_invalid_with_pct_encoding($ihost, '!$&\'()*+,;=');
// Lowercase, but ignore pct-encoded sections (as they should
// remain uppercase). This must be done after the previous step
// as that can add unescaped characters.
$position = 0;
$strlen = strlen($ihost);
while (($position += strcspn($ihost, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ%', $position)) < $strlen) {
if ($ihost[$position] === '%') {
$position += 3;
} else {
$ihost[$position] = strtolower($ihost[$position]);
$position++;
}
}
$this->ihost = $ihost;
}
$this->scheme_normalization();
return true;
}
/**
* Set the port. Returns true on success, false on failure (if there are
* any invalid characters).
*
* @param string $port
* @return bool
*/
public function set_port($port)
{
if ($port === null) {
$this->port = null;
return true;
} elseif (strspn($port, '0123456789') === strlen($port)) {
$this->port = (int) $port;
$this->scheme_normalization();
return true;
}
$this->port = null;
return false;
}
/**
* Set the ipath.
*
* @param string $ipath
* @return bool
*/
public function set_path($ipath, $clear_cache = false)
{
static $cache;
if ($clear_cache) {
$cache = null;
return;
}
if (!$cache) {
$cache = [];
}
$ipath = (string) $ipath;
if (isset($cache[$ipath])) {
$this->ipath = $cache[$ipath][(int) ($this->scheme !== null)];
} else {
$valid = $this->replace_invalid_with_pct_encoding($ipath, '!$&\'()*+,;=@:/');
$removed = $this->remove_dot_segments($valid);
$cache[$ipath] = [$valid, $removed];
$this->ipath = ($this->scheme !== null) ? $removed : $valid;
}
$this->scheme_normalization();
return true;
}
/**
* Set the iquery.
*
* @param string $iquery
* @return bool
*/
public function set_query($iquery)
{
if ($iquery === null) {
$this->iquery = null;
} else {
$this->iquery = $this->replace_invalid_with_pct_encoding($iquery, '!$&\'()*+,;=:@/?', true);
$this->scheme_normalization();
}
return true;
}
/**
* Set the ifragment.
*
* @param string $ifragment
* @return bool
*/
public function set_fragment($ifragment)
{
if ($ifragment === null) {
$this->ifragment = null;
} else {
$this->ifragment = $this->replace_invalid_with_pct_encoding($ifragment, '!$&\'()*+,;=:@/?');
$this->scheme_normalization();
}
return true;
}
/**
* Convert an IRI to a URI (or parts thereof)
*
* @return string
*/
public function to_uri($string)
{
static $non_ascii;
if (!$non_ascii) {
$non_ascii = implode('', range("\x80", "\xFF"));
}
$position = 0;
$strlen = strlen($string);
while (($position += strcspn($string, $non_ascii, $position)) < $strlen) {
$string = substr_replace($string, sprintf('%%%02X', ord($string[$position])), $position, 1);
$position += 3;
$strlen += 2;
}
return $string;
}
/**
* Get the complete IRI
*
* @return string
*/
public function get_iri()
{
if (!$this->is_valid()) {
return false;
}
$iri = '';
if ($this->scheme !== null) {
$iri .= $this->scheme . ':';
}
if (($iauthority = $this->get_iauthority()) !== null) {
$iri .= '//' . $iauthority;
}
if ($this->ipath !== '') {
$iri .= $this->ipath;
} elseif (!empty($this->normalization[$this->scheme]['ipath']) && $iauthority !== null && $iauthority !== '') {
$iri .= $this->normalization[$this->scheme]['ipath'];
}
if ($this->iquery !== null) {
$iri .= '?' . $this->iquery;
}
if ($this->ifragment !== null) {
$iri .= '#' . $this->ifragment;
}
return $iri;
}
/**
* Get the complete URI
*
* @return string
*/
public function get_uri()
{
return $this->to_uri($this->get_iri());
}
/**
* Get the complete iauthority
*
* @return string
*/
protected function get_iauthority()
{
if ($this->iuserinfo !== null || $this->ihost !== null || $this->port !== null) {
$iauthority = '';
if ($this->iuserinfo !== null) {
$iauthority .= $this->iuserinfo . '@';
}
if ($this->ihost !== null) {
$iauthority .= $this->ihost;
}
if ($this->port !== null && $this->port !== 0) {
$iauthority .= ':' . $this->port;
}
return $iauthority;
}
return null;
}
/**
* Get the complete authority
*
* @return string
*/
protected function get_authority()
{
$iauthority = $this->get_iauthority();
if (is_string($iauthority)) {
return $this->to_uri($iauthority);
}
return $iauthority;
}
}
class_alias('SimplePie\IRI', 'SimplePie_IRI');
SimplePie.php 0000644 00000352313 15133170420 0007147 0 ustar 00 <?php
/**
* SimplePie
*
* A PHP-Based RSS and Atom Feed Framework.
* Takes the hard work out of managing a complete RSS/Atom solution.
*
* Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* * Neither the name of the SimplePie Team nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
* AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package SimplePie
* @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
* @author Ryan Parman
* @author Sam Sneddon
* @author Ryan McCue
* @link http://simplepie.org/ SimplePie
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
namespace SimplePie;
use InvalidArgumentException;
use Psr\SimpleCache\CacheInterface;
use SimplePie\Cache\Base;
use SimplePie\Cache\BaseDataCache;
use SimplePie\Cache\CallableNameFilter;
use SimplePie\Cache\DataCache;
use SimplePie\Cache\NameFilter;
use SimplePie\Cache\Psr16;
use SimplePie\Content\Type\Sniffer;
/**
* SimplePie
*
* @package SimplePie
* @subpackage API
*/
class SimplePie
{
/**
* SimplePie Name
*/
public const NAME = 'SimplePie';
/**
* SimplePie Version
*/
public const VERSION = '1.8.0';
/**
* SimplePie Website URL
*/
public const URL = 'http://simplepie.org';
/**
* SimplePie Linkback
*/
public const LINKBACK = '<a href="' . self::URL . '" title="' . self::NAME . ' ' . self::VERSION . '">' . self::NAME . '</a>';
/**
* No Autodiscovery
* @see SimplePie::set_autodiscovery_level()
*/
public const LOCATOR_NONE = 0;
/**
* Feed Link Element Autodiscovery
* @see SimplePie::set_autodiscovery_level()
*/
public const LOCATOR_AUTODISCOVERY = 1;
/**
* Local Feed Extension Autodiscovery
* @see SimplePie::set_autodiscovery_level()
*/
public const LOCATOR_LOCAL_EXTENSION = 2;
/**
* Local Feed Body Autodiscovery
* @see SimplePie::set_autodiscovery_level()
*/
public const LOCATOR_LOCAL_BODY = 4;
/**
* Remote Feed Extension Autodiscovery
* @see SimplePie::set_autodiscovery_level()
*/
public const LOCATOR_REMOTE_EXTENSION = 8;
/**
* Remote Feed Body Autodiscovery
* @see SimplePie::set_autodiscovery_level()
*/
public const LOCATOR_REMOTE_BODY = 16;
/**
* All Feed Autodiscovery
* @see SimplePie::set_autodiscovery_level()
*/
public const LOCATOR_ALL = 31;
/**
* No known feed type
*/
public const TYPE_NONE = 0;
/**
* RSS 0.90
*/
public const TYPE_RSS_090 = 1;
/**
* RSS 0.91 (Netscape)
*/
public const TYPE_RSS_091_NETSCAPE = 2;
/**
* RSS 0.91 (Userland)
*/
public const TYPE_RSS_091_USERLAND = 4;
/**
* RSS 0.91 (both Netscape and Userland)
*/
public const TYPE_RSS_091 = 6;
/**
* RSS 0.92
*/
public const TYPE_RSS_092 = 8;
/**
* RSS 0.93
*/
public const TYPE_RSS_093 = 16;
/**
* RSS 0.94
*/
public const TYPE_RSS_094 = 32;
/**
* RSS 1.0
*/
public const TYPE_RSS_10 = 64;
/**
* RSS 2.0
*/
public const TYPE_RSS_20 = 128;
/**
* RDF-based RSS
*/
public const TYPE_RSS_RDF = 65;
/**
* Non-RDF-based RSS (truly intended as syndication format)
*/
public const TYPE_RSS_SYNDICATION = 190;
/**
* All RSS
*/
public const TYPE_RSS_ALL = 255;
/**
* Atom 0.3
*/
public const TYPE_ATOM_03 = 256;
/**
* Atom 1.0
*/
public const TYPE_ATOM_10 = 512;
/**
* All Atom
*/
public const TYPE_ATOM_ALL = 768;
/**
* All feed types
*/
public const TYPE_ALL = 1023;
/**
* No construct
*/
public const CONSTRUCT_NONE = 0;
/**
* Text construct
*/
public const CONSTRUCT_TEXT = 1;
/**
* HTML construct
*/
public const CONSTRUCT_HTML = 2;
/**
* XHTML construct
*/
public const CONSTRUCT_XHTML = 4;
/**
* base64-encoded construct
*/
public const CONSTRUCT_BASE64 = 8;
/**
* IRI construct
*/
public const CONSTRUCT_IRI = 16;
/**
* A construct that might be HTML
*/
public const CONSTRUCT_MAYBE_HTML = 32;
/**
* All constructs
*/
public const CONSTRUCT_ALL = 63;
/**
* Don't change case
*/
public const SAME_CASE = 1;
/**
* Change to lowercase
*/
public const LOWERCASE = 2;
/**
* Change to uppercase
*/
public const UPPERCASE = 4;
/**
* PCRE for HTML attributes
*/
public const PCRE_HTML_ATTRIBUTE = '((?:[\x09\x0A\x0B\x0C\x0D\x20]+[^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3E][^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3D\x3E]*(?:[\x09\x0A\x0B\x0C\x0D\x20]*=[\x09\x0A\x0B\x0C\x0D\x20]*(?:"(?:[^"]*)"|\'(?:[^\']*)\'|(?:[^\x09\x0A\x0B\x0C\x0D\x20\x22\x27\x3E][^\x09\x0A\x0B\x0C\x0D\x20\x3E]*)?))?)*)[\x09\x0A\x0B\x0C\x0D\x20]*';
/**
* PCRE for XML attributes
*/
public const PCRE_XML_ATTRIBUTE = '((?:\s+(?:(?:[^\s:]+:)?[^\s:]+)\s*=\s*(?:"(?:[^"]*)"|\'(?:[^\']*)\'))*)\s*';
/**
* XML Namespace
*/
public const NAMESPACE_XML = 'http://www.w3.org/XML/1998/namespace';
/**
* Atom 1.0 Namespace
*/
public const NAMESPACE_ATOM_10 = 'http://www.w3.org/2005/Atom';
/**
* Atom 0.3 Namespace
*/
public const NAMESPACE_ATOM_03 = 'http://purl.org/atom/ns#';
/**
* RDF Namespace
*/
public const NAMESPACE_RDF = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#';
/**
* RSS 0.90 Namespace
*/
public const NAMESPACE_RSS_090 = 'http://my.netscape.com/rdf/simple/0.9/';
/**
* RSS 1.0 Namespace
*/
public const NAMESPACE_RSS_10 = 'http://purl.org/rss/1.0/';
/**
* RSS 1.0 Content Module Namespace
*/
public const NAMESPACE_RSS_10_MODULES_CONTENT = 'http://purl.org/rss/1.0/modules/content/';
/**
* RSS 2.0 Namespace
* (Stupid, I know, but I'm certain it will confuse people less with support.)
*/
public const NAMESPACE_RSS_20 = '';
/**
* DC 1.0 Namespace
*/
public const NAMESPACE_DC_10 = 'http://purl.org/dc/elements/1.0/';
/**
* DC 1.1 Namespace
*/
public const NAMESPACE_DC_11 = 'http://purl.org/dc/elements/1.1/';
/**
* W3C Basic Geo (WGS84 lat/long) Vocabulary Namespace
*/
public const NAMESPACE_W3C_BASIC_GEO = 'http://www.w3.org/2003/01/geo/wgs84_pos#';
/**
* GeoRSS Namespace
*/
public const NAMESPACE_GEORSS = 'http://www.georss.org/georss';
/**
* Media RSS Namespace
*/
public const NAMESPACE_MEDIARSS = 'http://search.yahoo.com/mrss/';
/**
* Wrong Media RSS Namespace. Caused by a long-standing typo in the spec.
*/
public const NAMESPACE_MEDIARSS_WRONG = 'http://search.yahoo.com/mrss';
/**
* Wrong Media RSS Namespace #2. New namespace introduced in Media RSS 1.5.
*/
public const NAMESPACE_MEDIARSS_WRONG2 = 'http://video.search.yahoo.com/mrss';
/**
* Wrong Media RSS Namespace #3. A possible typo of the Media RSS 1.5 namespace.
*/
public const NAMESPACE_MEDIARSS_WRONG3 = 'http://video.search.yahoo.com/mrss/';
/**
* Wrong Media RSS Namespace #4. New spec location after the RSS Advisory Board takes it over, but not a valid namespace.
*/
public const NAMESPACE_MEDIARSS_WRONG4 = 'http://www.rssboard.org/media-rss';
/**
* Wrong Media RSS Namespace #5. A possible typo of the RSS Advisory Board URL.
*/
public const NAMESPACE_MEDIARSS_WRONG5 = 'http://www.rssboard.org/media-rss/';
/**
* iTunes RSS Namespace
*/
public const NAMESPACE_ITUNES = 'http://www.itunes.com/dtds/podcast-1.0.dtd';
/**
* XHTML Namespace
*/
public const NAMESPACE_XHTML = 'http://www.w3.org/1999/xhtml';
/**
* IANA Link Relations Registry
*/
public const IANA_LINK_RELATIONS_REGISTRY = 'http://www.iana.org/assignments/relation/';
/**
* No file source
*/
public const FILE_SOURCE_NONE = 0;
/**
* Remote file source
*/
public const FILE_SOURCE_REMOTE = 1;
/**
* Local file source
*/
public const FILE_SOURCE_LOCAL = 2;
/**
* fsockopen() file source
*/
public const FILE_SOURCE_FSOCKOPEN = 4;
/**
* cURL file source
*/
public const FILE_SOURCE_CURL = 8;
/**
* file_get_contents() file source
*/
public const FILE_SOURCE_FILE_GET_CONTENTS = 16;
/**
* @var array Raw data
* @access private
*/
public $data = [];
/**
* @var mixed Error string
* @access private
*/
public $error;
/**
* @var int HTTP status code
* @see SimplePie::status_code()
* @access private
*/
public $status_code = 0;
/**
* @var object Instance of \SimplePie\Sanitize (or other class)
* @see SimplePie::set_sanitize_class()
* @access private
*/
public $sanitize;
/**
* @var string SimplePie Useragent
* @see SimplePie::set_useragent()
* @access private
*/
public $useragent = '';
/**
* @var string Feed URL
* @see SimplePie::set_feed_url()
* @access private
*/
public $feed_url;
/**
* @var string Original feed URL, or new feed URL iff HTTP 301 Moved Permanently
* @see SimplePie::subscribe_url()
* @access private
*/
public $permanent_url = null;
/**
* @var object Instance of \SimplePie\File to use as a feed
* @see SimplePie::set_file()
* @access private
*/
public $file;
/**
* @var string Raw feed data
* @see SimplePie::set_raw_data()
* @access private
*/
public $raw_data;
/**
* @var int Timeout for fetching remote files
* @see SimplePie::set_timeout()
* @access private
*/
public $timeout = 10;
/**
* @var array Custom curl options
* @see SimplePie::set_curl_options()
* @access private
*/
public $curl_options = [];
/**
* @var bool Forces fsockopen() to be used for remote files instead
* of cURL, even if a new enough version is installed
* @see SimplePie::force_fsockopen()
* @access private
*/
public $force_fsockopen = false;
/**
* @var bool Force the given data/URL to be treated as a feed no matter what
* it appears like
* @see SimplePie::force_feed()
* @access private
*/
public $force_feed = false;
/**
* @var bool Enable/Disable Caching
* @see SimplePie::enable_cache()
* @access private
*/
private $enable_cache = true;
/**
* @var DataCache|null
* @see SimplePie::set_cache()
*/
private $cache = null;
/**
* @var NameFilter
* @see SimplePie::set_cache_namefilter()
*/
private $cache_namefilter;
/**
* @var bool Force SimplePie to fallback to expired cache, if enabled,
* when feed is unavailable.
* @see SimplePie::force_cache_fallback()
* @access private
*/
public $force_cache_fallback = false;
/**
* @var int Cache duration (in seconds)
* @see SimplePie::set_cache_duration()
* @access private
*/
public $cache_duration = 3600;
/**
* @var int Auto-discovery cache duration (in seconds)
* @see SimplePie::set_autodiscovery_cache_duration()
* @access private
*/
public $autodiscovery_cache_duration = 604800; // 7 Days.
/**
* @var string Cache location (relative to executing script)
* @see SimplePie::set_cache_location()
* @access private
*/
public $cache_location = './cache';
/**
* @var string Function that creates the cache filename
* @see SimplePie::set_cache_name_function()
* @access private
*/
public $cache_name_function = 'md5';
/**
* @var bool Reorder feed by date descending
* @see SimplePie::enable_order_by_date()
* @access private
*/
public $order_by_date = true;
/**
* @var mixed Force input encoding to be set to the follow value
* (false, or anything type-cast to false, disables this feature)
* @see SimplePie::set_input_encoding()
* @access private
*/
public $input_encoding = false;
/**
* @var int Feed Autodiscovery Level
* @see SimplePie::set_autodiscovery_level()
* @access private
*/
public $autodiscovery = self::LOCATOR_ALL;
/**
* Class registry object
*
* @var \SimplePie\Registry
*/
public $registry;
/**
* @var int Maximum number of feeds to check with autodiscovery
* @see SimplePie::set_max_checked_feeds()
* @access private
*/
public $max_checked_feeds = 10;
/**
* @var array All the feeds found during the autodiscovery process
* @see SimplePie::get_all_discovered_feeds()
* @access private
*/
public $all_discovered_feeds = [];
/**
* @var string Web-accessible path to the handler_image.php file.
* @see SimplePie::set_image_handler()
* @access private
*/
public $image_handler = '';
/**
* @var array Stores the URLs when multiple feeds are being initialized.
* @see SimplePie::set_feed_url()
* @access private
*/
public $multifeed_url = [];
/**
* @var array Stores SimplePie objects when multiple feeds initialized.
* @access private
*/
public $multifeed_objects = [];
/**
* @var array Stores the get_object_vars() array for use with multifeeds.
* @see SimplePie::set_feed_url()
* @access private
*/
public $config_settings = null;
/**
* @var integer Stores the number of items to return per-feed with multifeeds.
* @see SimplePie::set_item_limit()
* @access private
*/
public $item_limit = 0;
/**
* @var bool Stores if last-modified and/or etag headers were sent with the
* request when checking a feed.
*/
public $check_modified = false;
/**
* @var array Stores the default attributes to be stripped by strip_attributes().
* @see SimplePie::strip_attributes()
* @access private
*/
public $strip_attributes = ['bgsound', 'class', 'expr', 'id', 'style', 'onclick', 'onerror', 'onfinish', 'onmouseover', 'onmouseout', 'onfocus', 'onblur', 'lowsrc', 'dynsrc'];
/**
* @var array Stores the default attributes to add to different tags by add_attributes().
* @see SimplePie::add_attributes()
* @access private
*/
public $add_attributes = ['audio' => ['preload' => 'none'], 'iframe' => ['sandbox' => 'allow-scripts allow-same-origin'], 'video' => ['preload' => 'none']];
/**
* @var array Stores the default tags to be stripped by strip_htmltags().
* @see SimplePie::strip_htmltags()
* @access private
*/
public $strip_htmltags = ['base', 'blink', 'body', 'doctype', 'embed', 'font', 'form', 'frame', 'frameset', 'html', 'iframe', 'input', 'marquee', 'meta', 'noscript', 'object', 'param', 'script', 'style'];
/**
* @var array Stores the default attributes to be renamed by rename_attributes().
* @see SimplePie::rename_attributes()
* @access private
*/
public $rename_attributes = [];
/**
* @var bool Should we throw exceptions, or use the old-style error property?
* @access private
*/
public $enable_exceptions = false;
/**
* The SimplePie class contains feed level data and options
*
* To use SimplePie, create the SimplePie object with no parameters. You can
* then set configuration options using the provided methods. After setting
* them, you must initialise the feed using $feed->init(). At that point the
* object's methods and properties will be available to you.
*
* Previously, it was possible to pass in the feed URL along with cache
* options directly into the constructor. This has been removed as of 1.3 as
* it caused a lot of confusion.
*
* @since 1.0 Preview Release
*/
public function __construct()
{
if (version_compare(PHP_VERSION, '7.2', '<')) {
exit('Please upgrade to PHP 7.2 or newer.');
}
$this->set_useragent();
$this->set_cache_namefilter(new CallableNameFilter($this->cache_name_function));
// Other objects, instances created here so we can set options on them
$this->sanitize = new \SimplePie\Sanitize();
$this->registry = new \SimplePie\Registry();
if (func_num_args() > 0) {
trigger_error('Passing parameters to the constructor is no longer supported. Please use set_feed_url(), set_cache_location(), and set_cache_duration() directly.', \E_USER_DEPRECATED);
$args = func_get_args();
switch (count($args)) {
case 3:
$this->set_cache_duration($args[2]);
// no break
case 2:
$this->set_cache_location($args[1]);
// no break
case 1:
$this->set_feed_url($args[0]);
$this->init();
}
}
}
/**
* Used for converting object to a string
*/
public function __toString()
{
return md5(serialize($this->data));
}
/**
* Remove items that link back to this before destroying this object
*/
public function __destruct()
{
if (!gc_enabled()) {
if (!empty($this->data['items'])) {
foreach ($this->data['items'] as $item) {
$item->__destruct();
}
unset($item, $this->data['items']);
}
if (!empty($this->data['ordered_items'])) {
foreach ($this->data['ordered_items'] as $item) {
$item->__destruct();
}
unset($item, $this->data['ordered_items']);
}
}
}
/**
* Force the given data/URL to be treated as a feed
*
* This tells SimplePie to ignore the content-type provided by the server.
* Be careful when using this option, as it will also disable autodiscovery.
*
* @since 1.1
* @param bool $enable Force the given data/URL to be treated as a feed
*/
public function force_feed($enable = false)
{
$this->force_feed = (bool) $enable;
}
/**
* Set the URL of the feed you want to parse
*
* This allows you to enter the URL of the feed you want to parse, or the
* website you want to try to use auto-discovery on. This takes priority
* over any set raw data.
*
* You can set multiple feeds to mash together by passing an array instead
* of a string for the $url. Remember that with each additional feed comes
* additional processing and resources.
*
* @since 1.0 Preview Release
* @see set_raw_data()
* @param string|array $url This is the URL (or array of URLs) that you want to parse.
*/
public function set_feed_url($url)
{
$this->multifeed_url = [];
if (is_array($url)) {
foreach ($url as $value) {
$this->multifeed_url[] = $this->registry->call(Misc::class, 'fix_protocol', [$value, 1]);
}
} else {
$this->feed_url = $this->registry->call(Misc::class, 'fix_protocol', [$url, 1]);
$this->permanent_url = $this->feed_url;
}
}
/**
* Set an instance of {@see \SimplePie\File} to use as a feed
*
* @param \SimplePie\File &$file
* @return bool True on success, false on failure
*/
public function set_file(&$file)
{
if ($file instanceof \SimplePie\File) {
$this->feed_url = $file->url;
$this->permanent_url = $this->feed_url;
$this->file = &$file;
return true;
}
return false;
}
/**
* Set the raw XML data to parse
*
* Allows you to use a string of RSS/Atom data instead of a remote feed.
*
* If you have a feed available as a string in PHP, you can tell SimplePie
* to parse that data string instead of a remote feed. Any set feed URL
* takes precedence.
*
* @since 1.0 Beta 3
* @param string $data RSS or Atom data as a string.
* @see set_feed_url()
*/
public function set_raw_data($data)
{
$this->raw_data = $data;
}
/**
* Set the default timeout for fetching remote feeds
*
* This allows you to change the maximum time the feed's server to respond
* and send the feed back.
*
* @since 1.0 Beta 3
* @param int $timeout The maximum number of seconds to spend waiting to retrieve a feed.
*/
public function set_timeout($timeout = 10)
{
$this->timeout = (int) $timeout;
}
/**
* Set custom curl options
*
* This allows you to change default curl options
*
* @since 1.0 Beta 3
* @param array $curl_options Curl options to add to default settings
*/
public function set_curl_options(array $curl_options = [])
{
$this->curl_options = $curl_options;
}
/**
* Force SimplePie to use fsockopen() instead of cURL
*
* @since 1.0 Beta 3
* @param bool $enable Force fsockopen() to be used
*/
public function force_fsockopen($enable = false)
{
$this->force_fsockopen = (bool) $enable;
}
/**
* Enable/disable caching in SimplePie.
*
* This option allows you to disable caching all-together in SimplePie.
* However, disabling the cache can lead to longer load times.
*
* @since 1.0 Preview Release
* @param bool $enable Enable caching
*/
public function enable_cache($enable = true)
{
$this->enable_cache = (bool) $enable;
}
/**
* Set a PSR-16 implementation as cache
*
* @param CacheInterface $psr16cache The PSR-16 cache implementation
*
* @return void
*/
public function set_cache(CacheInterface $cache)
{
$this->cache = new Psr16($cache);
}
/**
* SimplePie to continue to fall back to expired cache, if enabled, when
* feed is unavailable.
*
* This tells SimplePie to ignore any file errors and fall back to cache
* instead. This only works if caching is enabled and cached content
* still exists.
*
* @deprecated since SimplePie 1.8.0, expired cache will not be used anymore.
*
* @param bool $enable Force use of cache on fail.
*/
public function force_cache_fallback($enable = false)
{
// @trigger_error(sprintf('SimplePie\SimplePie::force_cache_fallback() is deprecated since SimplePie 1.8.0, expired cache will not be used anymore.'), \E_USER_DEPRECATED);
$this->force_cache_fallback = (bool) $enable;
}
/**
* Set the length of time (in seconds) that the contents of a feed will be
* cached
*
* @param int $seconds The feed content cache duration
*/
public function set_cache_duration($seconds = 3600)
{
$this->cache_duration = (int) $seconds;
}
/**
* Set the length of time (in seconds) that the autodiscovered feed URL will
* be cached
*
* @param int $seconds The autodiscovered feed URL cache duration.
*/
public function set_autodiscovery_cache_duration($seconds = 604800)
{
$this->autodiscovery_cache_duration = (int) $seconds;
}
/**
* Set the file system location where the cached files should be stored
*
* @deprecated since SimplePie 1.8.0, use \SimplePie\SimplePie::set_cache() instead.
*
* @param string $location The file system location.
*/
public function set_cache_location($location = './cache')
{
// @trigger_error(sprintf('SimplePie\SimplePie::set_cache_location() is deprecated since SimplePie 1.8.0, please use "SimplePie\SimplePie::set_cache()" instead.'), \E_USER_DEPRECATED);
$this->cache_location = (string) $location;
}
/**
* Return the filename (i.e. hash, without path and without extension) of the file to cache a given URL.
*
* @param string $url The URL of the feed to be cached.
* @return string A filename (i.e. hash, without path and without extension).
*/
public function get_cache_filename($url)
{
// Append custom parameters to the URL to avoid cache pollution in case of multiple calls with different parameters.
$url .= $this->force_feed ? '#force_feed' : '';
$options = [];
if ($this->timeout != 10) {
$options[CURLOPT_TIMEOUT] = $this->timeout;
}
if ($this->useragent !== \SimplePie\Misc::get_default_useragent()) {
$options[CURLOPT_USERAGENT] = $this->useragent;
}
if (!empty($this->curl_options)) {
foreach ($this->curl_options as $k => $v) {
$options[$k] = $v;
}
}
if (!empty($options)) {
ksort($options);
$url .= '#' . urlencode(var_export($options, true));
}
return $this->cache_namefilter->filter($url);
}
/**
* Set whether feed items should be sorted into reverse chronological order
*
* @param bool $enable Sort as reverse chronological order.
*/
public function enable_order_by_date($enable = true)
{
$this->order_by_date = (bool) $enable;
}
/**
* Set the character encoding used to parse the feed
*
* This overrides the encoding reported by the feed, however it will fall
* back to the normal encoding detection if the override fails
*
* @param string $encoding Character encoding
*/
public function set_input_encoding($encoding = false)
{
if ($encoding) {
$this->input_encoding = (string) $encoding;
} else {
$this->input_encoding = false;
}
}
/**
* Set how much feed autodiscovery to do
*
* @see \SimplePie\SimplePie::LOCATOR_NONE
* @see \SimplePie\SimplePie::LOCATOR_AUTODISCOVERY
* @see \SimplePie\SimplePie::LOCATOR_LOCAL_EXTENSION
* @see \SimplePie\SimplePie::LOCATOR_LOCAL_BODY
* @see \SimplePie\SimplePie::LOCATOR_REMOTE_EXTENSION
* @see \SimplePie\SimplePie::LOCATOR_REMOTE_BODY
* @see \SimplePie\SimplePie::LOCATOR_ALL
* @param int $level Feed Autodiscovery Level (level can be a combination of the above constants, see bitwise OR operator)
*/
public function set_autodiscovery_level($level = self::LOCATOR_ALL)
{
$this->autodiscovery = (int) $level;
}
/**
* Get the class registry
*
* Use this to override SimplePie's default classes
* @see \SimplePie\Registry
*
* @return Registry
*/
public function &get_registry()
{
return $this->registry;
}
/**
* Set which class SimplePie uses for caching
*
* @deprecated since SimplePie 1.3, use {@see set_cache()} instead
*
* @param string $class Name of custom class
*
* @return boolean True on success, false otherwise
*/
public function set_cache_class($class = Cache::class)
{
// trigger_error(sprintf('"%s()" is deprecated since SimplePie 1.3, please use "SimplePie\SimplePie::set_cache()" instead.', __METHOD__), \E_USER_DEPRECATED);
return $this->registry->register(Cache::class, $class, true);
}
/**
* Set which class SimplePie uses for auto-discovery
*
* @deprecated since SimplePie 1.3, use {@see get_registry()} instead
*
* @param string $class Name of custom class
*
* @return boolean True on success, false otherwise
*/
public function set_locator_class($class = Locator::class)
{
// trigger_error(sprintf('"%s()" is deprecated since SimplePie 1.3, please use "SimplePie\SimplePie::get_registry()" instead.', __METHOD__), \E_USER_DEPRECATED);
return $this->registry->register(Locator::class, $class, true);
}
/**
* Set which class SimplePie uses for XML parsing
*
* @deprecated since SimplePie 1.3, use {@see get_registry()} instead
*
* @param string $class Name of custom class
*
* @return boolean True on success, false otherwise
*/
public function set_parser_class($class = Parser::class)
{
// trigger_error(sprintf('"%s()" is deprecated since SimplePie 1.3, please use "SimplePie\SimplePie::get_registry()" instead.', __METHOD__), \E_USER_DEPRECATED);
return $this->registry->register(Parser::class, $class, true);
}
/**
* Set which class SimplePie uses for remote file fetching
*
* @deprecated since SimplePie 1.3, use {@see get_registry()} instead
*
* @param string $class Name of custom class
*
* @return boolean True on success, false otherwise
*/
public function set_file_class($class = File::class)
{
// trigger_error(sprintf('"%s()" is deprecated since SimplePie 1.3, please use "SimplePie\SimplePie::get_registry()" instead.', __METHOD__), \E_USER_DEPRECATED);
return $this->registry->register(File::class, $class, true);
}
/**
* Set which class SimplePie uses for data sanitization
*
* @deprecated since SimplePie 1.3, use {@see get_registry()} instead
*
* @param string $class Name of custom class
*
* @return boolean True on success, false otherwise
*/
public function set_sanitize_class($class = Sanitize::class)
{
// trigger_error(sprintf('"%s()" is deprecated since SimplePie 1.3, please use "SimplePie\SimplePie::get_registry()" instead.', __METHOD__), \E_USER_DEPRECATED);
return $this->registry->register(Sanitize::class, $class, true);
}
/**
* Set which class SimplePie uses for handling feed items
*
* @deprecated since SimplePie 1.3, use {@see get_registry()} instead
*
* @param string $class Name of custom class
*
* @return boolean True on success, false otherwise
*/
public function set_item_class($class = Item::class)
{
// trigger_error(sprintf('"%s()" is deprecated since SimplePie 1.3, please use "SimplePie\SimplePie::get_registry()" instead.', __METHOD__), \E_USER_DEPRECATED);
return $this->registry->register(Item::class, $class, true);
}
/**
* Set which class SimplePie uses for handling author data
*
* @deprecated since SimplePie 1.3, use {@see get_registry()} instead
*
* @param string $class Name of custom class
*
* @return boolean True on success, false otherwise
*/
public function set_author_class($class = Author::class)
{
// trigger_error(sprintf('"%s()" is deprecated since SimplePie 1.3, please use "SimplePie\SimplePie::get_registry()" instead.', __METHOD__), \E_USER_DEPRECATED);
return $this->registry->register(Author::class, $class, true);
}
/**
* Set which class SimplePie uses for handling category data
*
* @deprecated since SimplePie 1.3, use {@see get_registry()} instead
*
* @param string $class Name of custom class
*
* @return boolean True on success, false otherwise
*/
public function set_category_class($class = Category::class)
{
// trigger_error(sprintf('"%s()" is deprecated since SimplePie 1.3, please use "SimplePie\SimplePie::get_registry()" instead.', __METHOD__), \E_USER_DEPRECATED);
return $this->registry->register(Category::class, $class, true);
}
/**
* Set which class SimplePie uses for feed enclosures
*
* @deprecated since SimplePie 1.3, use {@see get_registry()} instead
*
* @param string $class Name of custom class
*
* @return boolean True on success, false otherwise
*/
public function set_enclosure_class($class = Enclosure::class)
{
// trigger_error(sprintf('"%s()" is deprecated since SimplePie 1.3, please use "SimplePie\SimplePie::get_registry()" instead.', __METHOD__), \E_USER_DEPRECATED);
return $this->registry->register(Enclosure::class, $class, true);
}
/**
* Set which class SimplePie uses for `<media:text>` captions
*
* @deprecated since SimplePie 1.3, use {@see get_registry()} instead
*
* @param string $class Name of custom class
*
* @return boolean True on success, false otherwise
*/
public function set_caption_class($class = Caption::class)
{
// trigger_error(sprintf('"%s()" is deprecated since SimplePie 1.3, please use "SimplePie\SimplePie::get_registry()" instead.', __METHOD__), \E_USER_DEPRECATED);
return $this->registry->register(Caption::class, $class, true);
}
/**
* Set which class SimplePie uses for `<media:copyright>`
*
* @deprecated since SimplePie 1.3, use {@see get_registry()} instead
*
* @param string $class Name of custom class
*
* @return boolean True on success, false otherwise
*/
public function set_copyright_class($class = Copyright::class)
{
// trigger_error(sprintf('"%s()" is deprecated since SimplePie 1.3, please use "SimplePie\SimplePie::get_registry()" instead.', __METHOD__), \E_USER_DEPRECATED);
return $this->registry->register(Copyright::class, $class, true);
}
/**
* Set which class SimplePie uses for `<media:credit>`
*
* @deprecated since SimplePie 1.3, use {@see get_registry()} instead
*
* @param string $class Name of custom class
*
* @return boolean True on success, false otherwise
*/
public function set_credit_class($class = Credit::class)
{
// trigger_error(sprintf('"%s()" is deprecated since SimplePie 1.3, please use "SimplePie\SimplePie::get_registry()" instead.', __METHOD__), \E_USER_DEPRECATED);
return $this->registry->register(Credit::class, $class, true);
}
/**
* Set which class SimplePie uses for `<media:rating>`
*
* @deprecated since SimplePie 1.3, use {@see get_registry()} instead
*
* @param string $class Name of custom class
*
* @return boolean True on success, false otherwise
*/
public function set_rating_class($class = Rating::class)
{
// trigger_error(sprintf('"%s()" is deprecated since SimplePie 1.3, please use "SimplePie\SimplePie::get_registry()" instead.', __METHOD__), \E_USER_DEPRECATED);
return $this->registry->register(Rating::class, $class, true);
}
/**
* Set which class SimplePie uses for `<media:restriction>`
*
* @deprecated since SimplePie 1.3, use {@see get_registry()} instead
*
* @param string $class Name of custom class
*
* @return boolean True on success, false otherwise
*/
public function set_restriction_class($class = Restriction::class)
{
// trigger_error(sprintf('"%s()" is deprecated since SimplePie 1.3, please use "SimplePie\SimplePie::get_registry()" instead.', __METHOD__), \E_USER_DEPRECATED);
return $this->registry->register(Restriction::class, $class, true);
}
/**
* Set which class SimplePie uses for content-type sniffing
*
* @deprecated since SimplePie 1.3, use {@see get_registry()} instead
*
* @param string $class Name of custom class
*
* @return boolean True on success, false otherwise
*/
public function set_content_type_sniffer_class($class = Sniffer::class)
{
// trigger_error(sprintf('"%s()" is deprecated since SimplePie 1.3, please use "SimplePie\SimplePie::get_registry()" instead.', __METHOD__), \E_USER_DEPRECATED);
return $this->registry->register(Sniffer::class, $class, true);
}
/**
* Set which class SimplePie uses item sources
*
* @deprecated since SimplePie 1.3, use {@see get_registry()} instead
*
* @param string $class Name of custom class
*
* @return boolean True on success, false otherwise
*/
public function set_source_class($class = Source::class)
{
// trigger_error(sprintf('"%s()" is deprecated since SimplePie 1.3, please use "SimplePie\SimplePie::get_registry()" instead.', __METHOD__), \E_USER_DEPRECATED);
return $this->registry->register(Source::class, $class, true);
}
/**
* Set the user agent string
*
* @param string $ua New user agent string.
*/
public function set_useragent($ua = null)
{
if ($ua === null) {
$ua = \SimplePie\Misc::get_default_useragent();
}
$this->useragent = (string) $ua;
}
/**
* Set a namefilter to modify the cache filename with
*
* @param NameFilter $filter
*
* @return void
*/
public function set_cache_namefilter(NameFilter $filter): void
{
$this->cache_namefilter = $filter;
}
/**
* Set callback function to create cache filename with
*
* @deprecated since SimplePie 1.8.0, use {@see set_cache_namefilter()} instead
*
* @param mixed $function Callback function
*/
public function set_cache_name_function($function = 'md5')
{
// trigger_error(sprintf('"%s()" is deprecated since SimplePie 1.8.0, please use "SimplePie\SimplePie::set_cache_namefilter()" instead.', __METHOD__), \E_USER_DEPRECATED);
if (is_callable($function)) {
$this->cache_name_function = $function;
$this->set_cache_namefilter(new CallableNameFilter($this->cache_name_function));
}
}
/**
* Set options to make SP as fast as possible
*
* Forgoes a substantial amount of data sanitization in favor of speed. This
* turns SimplePie into a dumb parser of feeds.
*
* @param bool $set Whether to set them or not
*/
public function set_stupidly_fast($set = false)
{
if ($set) {
$this->enable_order_by_date(false);
$this->remove_div(false);
$this->strip_comments(false);
$this->strip_htmltags(false);
$this->strip_attributes(false);
$this->add_attributes(false);
$this->set_image_handler(false);
$this->set_https_domains([]);
}
}
/**
* Set maximum number of feeds to check with autodiscovery
*
* @param int $max Maximum number of feeds to check
*/
public function set_max_checked_feeds($max = 10)
{
$this->max_checked_feeds = (int) $max;
}
public function remove_div($enable = true)
{
$this->sanitize->remove_div($enable);
}
public function strip_htmltags($tags = '', $encode = null)
{
if ($tags === '') {
$tags = $this->strip_htmltags;
}
$this->sanitize->strip_htmltags($tags);
if ($encode !== null) {
$this->sanitize->encode_instead_of_strip($tags);
}
}
public function encode_instead_of_strip($enable = true)
{
$this->sanitize->encode_instead_of_strip($enable);
}
public function rename_attributes($attribs = '')
{
if ($attribs === '') {
$attribs = $this->rename_attributes;
}
$this->sanitize->rename_attributes($attribs);
}
public function strip_attributes($attribs = '')
{
if ($attribs === '') {
$attribs = $this->strip_attributes;
}
$this->sanitize->strip_attributes($attribs);
}
public function add_attributes($attribs = '')
{
if ($attribs === '') {
$attribs = $this->add_attributes;
}
$this->sanitize->add_attributes($attribs);
}
/**
* Set the output encoding
*
* Allows you to override SimplePie's output to match that of your webpage.
* This is useful for times when your webpages are not being served as
* UTF-8. This setting will be obeyed by {@see handle_content_type()}, and
* is similar to {@see set_input_encoding()}.
*
* It should be noted, however, that not all character encodings can support
* all characters. If your page is being served as ISO-8859-1 and you try
* to display a Japanese feed, you'll likely see garbled characters.
* Because of this, it is highly recommended to ensure that your webpages
* are served as UTF-8.
*
* The number of supported character encodings depends on whether your web
* host supports {@link http://php.net/mbstring mbstring},
* {@link http://php.net/iconv iconv}, or both. See
* {@link http://simplepie.org/wiki/faq/Supported_Character_Encodings} for
* more information.
*
* @param string $encoding
*/
public function set_output_encoding($encoding = 'UTF-8')
{
$this->sanitize->set_output_encoding($encoding);
}
public function strip_comments($strip = false)
{
$this->sanitize->strip_comments($strip);
}
/**
* Set element/attribute key/value pairs of HTML attributes
* containing URLs that need to be resolved relative to the feed
*
* Defaults to |a|@href, |area|@href, |blockquote|@cite, |del|@cite,
* |form|@action, |img|@longdesc, |img|@src, |input|@src, |ins|@cite,
* |q|@cite
*
* @since 1.0
* @param array|null $element_attribute Element/attribute key/value pairs, null for default
*/
public function set_url_replacements($element_attribute = null)
{
$this->sanitize->set_url_replacements($element_attribute);
}
/**
* Set the list of domains for which to force HTTPS.
* @see \SimplePie\Sanitize::set_https_domains()
* @param array List of HTTPS domains. Example array('biz', 'example.com', 'example.org', 'www.example.net').
*/
public function set_https_domains($domains = [])
{
if (is_array($domains)) {
$this->sanitize->set_https_domains($domains);
}
}
/**
* Set the handler to enable the display of cached images.
*
* @param string $page Web-accessible path to the handler_image.php file.
* @param string $qs The query string that the value should be passed to.
*/
public function set_image_handler($page = false, $qs = 'i')
{
if ($page !== false) {
$this->sanitize->set_image_handler($page . '?' . $qs . '=');
} else {
$this->image_handler = '';
}
}
/**
* Set the limit for items returned per-feed with multifeeds
*
* @param integer $limit The maximum number of items to return.
*/
public function set_item_limit($limit = 0)
{
$this->item_limit = (int) $limit;
}
/**
* Enable throwing exceptions
*
* @param boolean $enable Should we throw exceptions, or use the old-style error property?
*/
public function enable_exceptions($enable = true)
{
$this->enable_exceptions = $enable;
}
/**
* Initialize the feed object
*
* This is what makes everything happen. Period. This is where all of the
* configuration options get processed, feeds are fetched, cached, and
* parsed, and all of that other good stuff.
*
* @return boolean True if successful, false otherwise
*/
public function init()
{
// Check absolute bare minimum requirements.
if (!extension_loaded('xml') || !extension_loaded('pcre')) {
$this->error = 'XML or PCRE extensions not loaded!';
return false;
}
// Then check the xml extension is sane (i.e., libxml 2.7.x issue on PHP < 5.2.9 and libxml 2.7.0 to 2.7.2 on any version) if we don't have xmlreader.
elseif (!extension_loaded('xmlreader')) {
static $xml_is_sane = null;
if ($xml_is_sane === null) {
$parser_check = xml_parser_create();
xml_parse_into_struct($parser_check, '<foo>&</foo>', $values);
xml_parser_free($parser_check);
$xml_is_sane = isset($values[0]['value']);
}
if (!$xml_is_sane) {
return false;
}
}
// The default sanitize class gets set in the constructor, check if it has
// changed.
if ($this->registry->get_class(Sanitize::class) !== 'SimplePie\Sanitize') {
$this->sanitize = $this->registry->create(Sanitize::class);
}
if (method_exists($this->sanitize, 'set_registry')) {
$this->sanitize->set_registry($this->registry);
}
// Pass whatever was set with config options over to the sanitizer.
// Pass the classes in for legacy support; new classes should use the registry instead
$this->sanitize->pass_cache_data(
$this->enable_cache,
$this->cache_location,
$this->cache_namefilter,
$this->registry->get_class(Cache::class),
$this->cache
);
$this->sanitize->pass_file_data($this->registry->get_class(File::class), $this->timeout, $this->useragent, $this->force_fsockopen, $this->curl_options);
if (!empty($this->multifeed_url)) {
$i = 0;
$success = 0;
$this->multifeed_objects = [];
$this->error = [];
foreach ($this->multifeed_url as $url) {
$this->multifeed_objects[$i] = clone $this;
$this->multifeed_objects[$i]->set_feed_url($url);
$single_success = $this->multifeed_objects[$i]->init();
$success |= $single_success;
if (!$single_success) {
$this->error[$i] = $this->multifeed_objects[$i]->error();
}
$i++;
}
return (bool) $success;
} elseif ($this->feed_url === null && $this->raw_data === null) {
return false;
}
$this->error = null;
$this->data = [];
$this->check_modified = false;
$this->multifeed_objects = [];
$cache = false;
if ($this->feed_url !== null) {
$parsed_feed_url = $this->registry->call(Misc::class, 'parse_url', [$this->feed_url]);
// Decide whether to enable caching
if ($this->enable_cache && $parsed_feed_url['scheme'] !== '') {
$cache = $this->get_cache($this->feed_url);
}
// Fetch the data via \SimplePie\File into $this->raw_data
if (($fetched = $this->fetch_data($cache)) === true) {
return true;
} elseif ($fetched === false) {
return false;
}
[$headers, $sniffed] = $fetched;
}
// Empty response check
if (empty($this->raw_data)) {
$this->error = "A feed could not be found at `$this->feed_url`. Empty body.";
$this->registry->call(Misc::class, 'error', [$this->error, E_USER_NOTICE, __FILE__, __LINE__]);
return false;
}
// Set up array of possible encodings
$encodings = [];
// First check to see if input has been overridden.
if ($this->input_encoding !== false) {
$encodings[] = strtoupper($this->input_encoding);
}
$application_types = ['application/xml', 'application/xml-dtd', 'application/xml-external-parsed-entity'];
$text_types = ['text/xml', 'text/xml-external-parsed-entity'];
// RFC 3023 (only applies to sniffed content)
if (isset($sniffed)) {
if (in_array($sniffed, $application_types) || substr($sniffed, 0, 12) === 'application/' && substr($sniffed, -4) === '+xml') {
if (isset($headers['content-type']) && preg_match('/;\x20?charset=([^;]*)/i', $headers['content-type'], $charset)) {
$encodings[] = strtoupper($charset[1]);
}
$encodings = array_merge($encodings, $this->registry->call(Misc::class, 'xml_encoding', [$this->raw_data, &$this->registry]));
$encodings[] = 'UTF-8';
} elseif (in_array($sniffed, $text_types) || substr($sniffed, 0, 5) === 'text/' && substr($sniffed, -4) === '+xml') {
if (isset($headers['content-type']) && preg_match('/;\x20?charset=([^;]*)/i', $headers['content-type'], $charset)) {
$encodings[] = strtoupper($charset[1]);
}
$encodings[] = 'US-ASCII';
}
// Text MIME-type default
elseif (substr($sniffed, 0, 5) === 'text/') {
$encodings[] = 'UTF-8';
}
}
// Fallback to XML 1.0 Appendix F.1/UTF-8/ISO-8859-1
$encodings = array_merge($encodings, $this->registry->call(Misc::class, 'xml_encoding', [$this->raw_data, &$this->registry]));
$encodings[] = 'UTF-8';
$encodings[] = 'ISO-8859-1';
// There's no point in trying an encoding twice
$encodings = array_unique($encodings);
// Loop through each possible encoding, till we return something, or run out of possibilities
foreach ($encodings as $encoding) {
// Change the encoding to UTF-8 (as we always use UTF-8 internally)
if ($utf8_data = $this->registry->call(Misc::class, 'change_encoding', [$this->raw_data, $encoding, 'UTF-8'])) {
// Create new parser
$parser = $this->registry->create(Parser::class);
// If it's parsed fine
if ($parser->parse($utf8_data, 'UTF-8', $this->permanent_url)) {
$this->data = $parser->get_data();
if (!($this->get_type() & ~self::TYPE_NONE)) {
$this->error = "A feed could not be found at `$this->feed_url`. This does not appear to be a valid RSS or Atom feed.";
$this->registry->call(Misc::class, 'error', [$this->error, E_USER_NOTICE, __FILE__, __LINE__]);
return false;
}
if (isset($headers)) {
$this->data['headers'] = $headers;
}
$this->data['build'] = \SimplePie\Misc::get_build();
// Cache the file if caching is enabled
$this->data['cache_expiration_time'] = $this->cache_duration + time();
if ($cache && !$cache->set_data($this->get_cache_filename($this->feed_url), $this->data, $this->cache_duration)) {
trigger_error("$this->cache_location is not writable. Make sure you've set the correct relative or absolute path, and that the location is server-writable.", E_USER_WARNING);
}
return true;
}
}
}
if (isset($parser)) {
// We have an error, just set \SimplePie\Misc::error to it and quit
$this->error = $this->feed_url;
$this->error .= sprintf(' is invalid XML, likely due to invalid characters. XML error: %s at line %d, column %d', $parser->get_error_string(), $parser->get_current_line(), $parser->get_current_column());
} else {
$this->error = 'The data could not be converted to UTF-8.';
if (!extension_loaded('mbstring') && !extension_loaded('iconv') && !class_exists('\UConverter')) {
$this->error .= ' You MUST have either the iconv, mbstring or intl (PHP 5.5+) extension installed and enabled.';
} else {
$missingExtensions = [];
if (!extension_loaded('iconv')) {
$missingExtensions[] = 'iconv';
}
if (!extension_loaded('mbstring')) {
$missingExtensions[] = 'mbstring';
}
if (!class_exists('\UConverter')) {
$missingExtensions[] = 'intl (PHP 5.5+)';
}
$this->error .= ' Try installing/enabling the ' . implode(' or ', $missingExtensions) . ' extension.';
}
}
$this->registry->call(Misc::class, 'error', [$this->error, E_USER_NOTICE, __FILE__, __LINE__]);
return false;
}
/**
* Fetch the data via \SimplePie\File
*
* If the data is already cached, attempt to fetch it from there instead
* @param Base|DataCache|false $cache Cache handler, or false to not load from the cache
* @return array|true Returns true if the data was loaded from the cache, or an array of HTTP headers and sniffed type
*/
protected function fetch_data(&$cache)
{
if (is_object($cache) && $cache instanceof Base) {
// @trigger_error(sprintf('Providing $cache as "\SimplePie\Cache\Base" in %s() is deprecated since SimplePie 1.8.0, please provide "\SimplePie\Cache\DataCache" implementation instead.', __METHOD__), \E_USER_DEPRECATED);
$cache = new BaseDataCache($cache);
}
if ($cache !== false && !$cache instanceof DataCache) {
throw new InvalidArgumentException(sprintf(
'%s(): Argument #1 ($cache) must be of type %s|false',
__METHOD__,
DataCache::class
), 1);
}
$cacheKey = $this->get_cache_filename($this->feed_url);
// If it's enabled, use the cache
if ($cache) {
// Load the Cache
$this->data = $cache->get_data($cacheKey, []);
if (!empty($this->data)) {
// If the cache is for an outdated build of SimplePie
if (!isset($this->data['build']) || $this->data['build'] !== \SimplePie\Misc::get_build()) {
$cache->delete_data($cacheKey);
$this->data = [];
}
// If we've hit a collision just rerun it with caching disabled
elseif (isset($this->data['url']) && $this->data['url'] !== $this->feed_url) {
$cache = false;
$this->data = [];
}
// If we've got a non feed_url stored (if the page isn't actually a feed, or is a redirect) use that URL.
elseif (isset($this->data['feed_url'])) {
// Do not need to do feed autodiscovery yet.
if ($this->data['feed_url'] !== $this->data['url']) {
$this->set_feed_url($this->data['feed_url']);
$this->data['url'] = $this->data['feed_url'];
$cache->set_data($this->get_cache_filename($this->feed_url), $this->data, $this->autodiscovery_cache_duration);
return $this->init();
}
$cache->delete_data($this->get_cache_filename($this->feed_url));
$this->data = [];
}
// Check if the cache has been updated
elseif (isset($this->data['cache_expiration_time']) && $this->data['cache_expiration_time'] > time()) {
// Want to know if we tried to send last-modified and/or etag headers
// when requesting this file. (Note that it's up to the file to
// support this, but we don't always send the headers either.)
$this->check_modified = true;
if (isset($this->data['headers']['last-modified']) || isset($this->data['headers']['etag'])) {
$headers = [
'Accept' => 'application/atom+xml, application/rss+xml, application/rdf+xml;q=0.9, application/xml;q=0.8, text/xml;q=0.8, text/html;q=0.7, unknown/unknown;q=0.1, application/unknown;q=0.1, */*;q=0.1',
];
if (isset($this->data['headers']['last-modified'])) {
$headers['if-modified-since'] = $this->data['headers']['last-modified'];
}
if (isset($this->data['headers']['etag'])) {
$headers['if-none-match'] = $this->data['headers']['etag'];
}
$file = $this->registry->create(File::class, [$this->feed_url, $this->timeout / 10, 5, $headers, $this->useragent, $this->force_fsockopen, $this->curl_options]);
$this->status_code = $file->status_code;
if ($file->success) {
if ($file->status_code === 304) {
// Set raw_data to false here too, to signify that the cache
// is still valid.
$this->raw_data = false;
$cache->set_data($cacheKey, $this->data, $this->cache_duration);
return true;
}
} else {
$this->check_modified = false;
if ($this->force_cache_fallback) {
$cache->set_data($cacheKey, $this->data, $this->cache_duration);
return true;
}
unset($file);
}
}
}
// If the cache is still valid, just return true
else {
$this->raw_data = false;
return true;
}
}
// If the cache is empty
else {
$this->data = [];
}
}
// If we don't already have the file (it'll only exist if we've opened it to check if the cache has been modified), open it.
if (!isset($file)) {
if ($this->file instanceof \SimplePie\File && $this->file->url === $this->feed_url) {
$file = &$this->file;
} else {
$headers = [
'Accept' => 'application/atom+xml, application/rss+xml, application/rdf+xml;q=0.9, application/xml;q=0.8, text/xml;q=0.8, text/html;q=0.7, unknown/unknown;q=0.1, application/unknown;q=0.1, */*;q=0.1',
];
$file = $this->registry->create(File::class, [$this->feed_url, $this->timeout, 5, $headers, $this->useragent, $this->force_fsockopen, $this->curl_options]);
}
}
$this->status_code = $file->status_code;
// If the file connection has an error, set SimplePie::error to that and quit
if (!$file->success && !($file->method & self::FILE_SOURCE_REMOTE === 0 || ($file->status_code === 200 || $file->status_code > 206 && $file->status_code < 300))) {
$this->error = $file->error;
return !empty($this->data);
}
if (!$this->force_feed) {
// Check if the supplied URL is a feed, if it isn't, look for it.
$locate = $this->registry->create(Locator::class, [&$file, $this->timeout, $this->useragent, $this->max_checked_feeds, $this->force_fsockopen, $this->curl_options]);
if (!$locate->is_feed($file)) {
$copyStatusCode = $file->status_code;
$copyContentType = $file->headers['content-type'];
try {
$microformats = false;
if (class_exists('DOMXpath') && function_exists('Mf2\parse')) {
$doc = new \DOMDocument();
@$doc->loadHTML($file->body);
$xpath = new \DOMXpath($doc);
// Check for both h-feed and h-entry, as both a feed with no entries
// and a list of entries without an h-feed wrapper are both valid.
$query = '//*[contains(concat(" ", @class, " "), " h-feed ") or '.
'contains(concat(" ", @class, " "), " h-entry ")]';
$result = $xpath->query($query);
$microformats = $result->length !== 0;
}
// Now also do feed discovery, but if microformats were found don't
// overwrite the current value of file.
$discovered = $locate->find(
$this->autodiscovery,
$this->all_discovered_feeds
);
if ($microformats) {
if ($hub = $locate->get_rel_link('hub')) {
$self = $locate->get_rel_link('self');
$this->store_links($file, $hub, $self);
}
// Push the current file onto all_discovered feeds so the user can
// be shown this as one of the options.
if (isset($this->all_discovered_feeds)) {
$this->all_discovered_feeds[] = $file;
}
} else {
if ($discovered) {
$file = $discovered;
} else {
// We need to unset this so that if SimplePie::set_file() has
// been called that object is untouched
unset($file);
$this->error = "A feed could not be found at `$this->feed_url`; the status code is `$copyStatusCode` and content-type is `$copyContentType`";
$this->registry->call(Misc::class, 'error', [$this->error, E_USER_NOTICE, __FILE__, __LINE__]);
return false;
}
}
} catch (\SimplePie\Exception $e) {
// We need to unset this so that if SimplePie::set_file() has been called that object is untouched
unset($file);
// This is usually because DOMDocument doesn't exist
$this->error = $e->getMessage();
$this->registry->call(Misc::class, 'error', [$this->error, E_USER_NOTICE, $e->getFile(), $e->getLine()]);
return false;
}
if ($cache) {
$this->data = [
'url' => $this->feed_url,
'feed_url' => $file->url,
'build' => \SimplePie\Misc::get_build(),
'cache_expiration_time' => $this->cache_duration + time(),
];
if (!$cache->set_data($cacheKey, $this->data, $this->cache_duration)) {
trigger_error("$this->cache_location is not writable. Make sure you've set the correct relative or absolute path, and that the location is server-writable.", E_USER_WARNING);
}
}
}
$this->feed_url = $file->url;
$locate = null;
}
$this->raw_data = $file->body;
$this->permanent_url = $file->permanent_url;
$headers = $file->headers;
$sniffer = $this->registry->create(Sniffer::class, [&$file]);
$sniffed = $sniffer->get_type();
return [$headers, $sniffed];
}
/**
* Get the error message for the occurred error
*
* @return string|array Error message, or array of messages for multifeeds
*/
public function error()
{
return $this->error;
}
/**
* Get the last HTTP status code
*
* @return int Status code
*/
public function status_code()
{
return $this->status_code;
}
/**
* Get the raw XML
*
* This is the same as the old `$feed->enable_xml_dump(true)`, but returns
* the data instead of printing it.
*
* @return string|boolean Raw XML data, false if the cache is used
*/
public function get_raw_data()
{
return $this->raw_data;
}
/**
* Get the character encoding used for output
*
* @since Preview Release
* @return string
*/
public function get_encoding()
{
return $this->sanitize->output_encoding;
}
/**
* Send the content-type header with correct encoding
*
* This method ensures that the SimplePie-enabled page is being served with
* the correct {@link http://www.iana.org/assignments/media-types/ mime-type}
* and character encoding HTTP headers (character encoding determined by the
* {@see set_output_encoding} config option).
*
* This won't work properly if any content or whitespace has already been
* sent to the browser, because it relies on PHP's
* {@link http://php.net/header header()} function, and these are the
* circumstances under which the function works.
*
* Because it's setting these settings for the entire page (as is the nature
* of HTTP headers), this should only be used once per page (again, at the
* top).
*
* @param string $mime MIME type to serve the page as
*/
public function handle_content_type($mime = 'text/html')
{
if (!headers_sent()) {
$header = "Content-type: $mime;";
if ($this->get_encoding()) {
$header .= ' charset=' . $this->get_encoding();
} else {
$header .= ' charset=UTF-8';
}
header($header);
}
}
/**
* Get the type of the feed
*
* This returns a \SimplePie\SimplePie::TYPE_* constant, which can be tested against
* using {@link http://php.net/language.operators.bitwise bitwise operators}
*
* @since 0.8 (usage changed to using constants in 1.0)
* @see \SimplePie\SimplePie::TYPE_NONE Unknown.
* @see \SimplePie\SimplePie::TYPE_RSS_090 RSS 0.90.
* @see \SimplePie\SimplePie::TYPE_RSS_091_NETSCAPE RSS 0.91 (Netscape).
* @see \SimplePie\SimplePie::TYPE_RSS_091_USERLAND RSS 0.91 (Userland).
* @see \SimplePie\SimplePie::TYPE_RSS_091 RSS 0.91.
* @see \SimplePie\SimplePie::TYPE_RSS_092 RSS 0.92.
* @see \SimplePie\SimplePie::TYPE_RSS_093 RSS 0.93.
* @see \SimplePie\SimplePie::TYPE_RSS_094 RSS 0.94.
* @see \SimplePie\SimplePie::TYPE_RSS_10 RSS 1.0.
* @see \SimplePie\SimplePie::TYPE_RSS_20 RSS 2.0.x.
* @see \SimplePie\SimplePie::TYPE_RSS_RDF RDF-based RSS.
* @see \SimplePie\SimplePie::TYPE_RSS_SYNDICATION Non-RDF-based RSS (truly intended as syndication format).
* @see \SimplePie\SimplePie::TYPE_RSS_ALL Any version of RSS.
* @see \SimplePie\SimplePie::TYPE_ATOM_03 Atom 0.3.
* @see \SimplePie\SimplePie::TYPE_ATOM_10 Atom 1.0.
* @see \SimplePie\SimplePie::TYPE_ATOM_ALL Any version of Atom.
* @see \SimplePie\SimplePie::TYPE_ALL Any known/supported feed type.
* @return int \SimplePie\SimplePie::TYPE_* constant
*/
public function get_type()
{
if (!isset($this->data['type'])) {
$this->data['type'] = self::TYPE_ALL;
if (isset($this->data['child'][self::NAMESPACE_ATOM_10]['feed'])) {
$this->data['type'] &= self::TYPE_ATOM_10;
} elseif (isset($this->data['child'][self::NAMESPACE_ATOM_03]['feed'])) {
$this->data['type'] &= self::TYPE_ATOM_03;
} elseif (isset($this->data['child'][self::NAMESPACE_RDF]['RDF'])) {
if (isset($this->data['child'][self::NAMESPACE_RDF]['RDF'][0]['child'][self::NAMESPACE_RSS_10]['channel'])
|| isset($this->data['child'][self::NAMESPACE_RDF]['RDF'][0]['child'][self::NAMESPACE_RSS_10]['image'])
|| isset($this->data['child'][self::NAMESPACE_RDF]['RDF'][0]['child'][self::NAMESPACE_RSS_10]['item'])
|| isset($this->data['child'][self::NAMESPACE_RDF]['RDF'][0]['child'][self::NAMESPACE_RSS_10]['textinput'])) {
$this->data['type'] &= self::TYPE_RSS_10;
}
if (isset($this->data['child'][self::NAMESPACE_RDF]['RDF'][0]['child'][self::NAMESPACE_RSS_090]['channel'])
|| isset($this->data['child'][self::NAMESPACE_RDF]['RDF'][0]['child'][self::NAMESPACE_RSS_090]['image'])
|| isset($this->data['child'][self::NAMESPACE_RDF]['RDF'][0]['child'][self::NAMESPACE_RSS_090]['item'])
|| isset($this->data['child'][self::NAMESPACE_RDF]['RDF'][0]['child'][self::NAMESPACE_RSS_090]['textinput'])) {
$this->data['type'] &= self::TYPE_RSS_090;
}
} elseif (isset($this->data['child'][self::NAMESPACE_RSS_20]['rss'])) {
$this->data['type'] &= self::TYPE_RSS_ALL;
if (isset($this->data['child'][self::NAMESPACE_RSS_20]['rss'][0]['attribs']['']['version'])) {
switch (trim($this->data['child'][self::NAMESPACE_RSS_20]['rss'][0]['attribs']['']['version'])) {
case '0.91':
$this->data['type'] &= self::TYPE_RSS_091;
if (isset($this->data['child'][self::NAMESPACE_RSS_20]['rss'][0]['child'][self::NAMESPACE_RSS_20]['skiphours']['hour'][0]['data'])) {
switch (trim($this->data['child'][self::NAMESPACE_RSS_20]['rss'][0]['child'][self::NAMESPACE_RSS_20]['skiphours']['hour'][0]['data'])) {
case '0':
$this->data['type'] &= self::TYPE_RSS_091_NETSCAPE;
break;
case '24':
$this->data['type'] &= self::TYPE_RSS_091_USERLAND;
break;
}
}
break;
case '0.92':
$this->data['type'] &= self::TYPE_RSS_092;
break;
case '0.93':
$this->data['type'] &= self::TYPE_RSS_093;
break;
case '0.94':
$this->data['type'] &= self::TYPE_RSS_094;
break;
case '2.0':
$this->data['type'] &= self::TYPE_RSS_20;
break;
}
}
} else {
$this->data['type'] = self::TYPE_NONE;
}
}
return $this->data['type'];
}
/**
* Get the URL for the feed
*
* When the 'permanent' mode is enabled, returns the original feed URL,
* except in the case of an `HTTP 301 Moved Permanently` status response,
* in which case the location of the first redirection is returned.
*
* When the 'permanent' mode is disabled (default),
* may or may not be different from the URL passed to {@see set_feed_url()},
* depending on whether auto-discovery was used, and whether there were
* any redirects along the way.
*
* @since Preview Release (previously called `get_feed_url()` since SimplePie 0.8.)
* @todo Support <itunes:new-feed-url>
* @todo Also, |atom:link|@rel=self
* @param bool $permanent Permanent mode to return only the original URL or the first redirection
* iff it is a 301 redirection
* @return string|null
*/
public function subscribe_url($permanent = false)
{
if ($permanent) {
if ($this->permanent_url !== null) {
// sanitize encodes ampersands which are required when used in a url.
return str_replace(
'&',
'&',
$this->sanitize(
$this->permanent_url,
self::CONSTRUCT_IRI
)
);
}
} else {
if ($this->feed_url !== null) {
return str_replace(
'&',
'&',
$this->sanitize(
$this->feed_url,
self::CONSTRUCT_IRI
)
);
}
}
return null;
}
/**
* Get data for an feed-level element
*
* This method allows you to get access to ANY element/attribute that is a
* sub-element of the opening feed tag.
*
* The return value is an indexed array of elements matching the given
* namespace and tag name. Each element has `attribs`, `data` and `child`
* subkeys. For `attribs` and `child`, these contain namespace subkeys.
* `attribs` then has one level of associative name => value data (where
* `value` is a string) after the namespace. `child` has tag-indexed keys
* after the namespace, each member of which is an indexed array matching
* this same format.
*
* For example:
* <pre>
* // This is probably a bad example because we already support
* // <media:content> natively, but it shows you how to parse through
* // the nodes.
* $group = $item->get_item_tags(\SimplePie\SimplePie::NAMESPACE_MEDIARSS, 'group');
* $content = $group[0]['child'][\SimplePie\SimplePie::NAMESPACE_MEDIARSS]['content'];
* $file = $content[0]['attribs']['']['url'];
* echo $file;
* </pre>
*
* @since 1.0
* @see http://simplepie.org/wiki/faq/supported_xml_namespaces
* @param string $namespace The URL of the XML namespace of the elements you're trying to access
* @param string $tag Tag name
* @return array
*/
public function get_feed_tags($namespace, $tag)
{
$type = $this->get_type();
if ($type & self::TYPE_ATOM_10) {
if (isset($this->data['child'][self::NAMESPACE_ATOM_10]['feed'][0]['child'][$namespace][$tag])) {
return $this->data['child'][self::NAMESPACE_ATOM_10]['feed'][0]['child'][$namespace][$tag];
}
}
if ($type & self::TYPE_ATOM_03) {
if (isset($this->data['child'][self::NAMESPACE_ATOM_03]['feed'][0]['child'][$namespace][$tag])) {
return $this->data['child'][self::NAMESPACE_ATOM_03]['feed'][0]['child'][$namespace][$tag];
}
}
if ($type & self::TYPE_RSS_RDF) {
if (isset($this->data['child'][self::NAMESPACE_RDF]['RDF'][0]['child'][$namespace][$tag])) {
return $this->data['child'][self::NAMESPACE_RDF]['RDF'][0]['child'][$namespace][$tag];
}
}
if ($type & self::TYPE_RSS_SYNDICATION) {
if (isset($this->data['child'][self::NAMESPACE_RSS_20]['rss'][0]['child'][$namespace][$tag])) {
return $this->data['child'][self::NAMESPACE_RSS_20]['rss'][0]['child'][$namespace][$tag];
}
}
return null;
}
/**
* Get data for an channel-level element
*
* This method allows you to get access to ANY element/attribute in the
* channel/header section of the feed.
*
* See {@see SimplePie::get_feed_tags()} for a description of the return value
*
* @since 1.0
* @see http://simplepie.org/wiki/faq/supported_xml_namespaces
* @param string $namespace The URL of the XML namespace of the elements you're trying to access
* @param string $tag Tag name
* @return array
*/
public function get_channel_tags($namespace, $tag)
{
$type = $this->get_type();
if ($type & self::TYPE_ATOM_ALL) {
if ($return = $this->get_feed_tags($namespace, $tag)) {
return $return;
}
}
if ($type & self::TYPE_RSS_10) {
if ($channel = $this->get_feed_tags(self::NAMESPACE_RSS_10, 'channel')) {
if (isset($channel[0]['child'][$namespace][$tag])) {
return $channel[0]['child'][$namespace][$tag];
}
}
}
if ($type & self::TYPE_RSS_090) {
if ($channel = $this->get_feed_tags(self::NAMESPACE_RSS_090, 'channel')) {
if (isset($channel[0]['child'][$namespace][$tag])) {
return $channel[0]['child'][$namespace][$tag];
}
}
}
if ($type & self::TYPE_RSS_SYNDICATION) {
if ($channel = $this->get_feed_tags(self::NAMESPACE_RSS_20, 'channel')) {
if (isset($channel[0]['child'][$namespace][$tag])) {
return $channel[0]['child'][$namespace][$tag];
}
}
}
return null;
}
/**
* Get data for an channel-level element
*
* This method allows you to get access to ANY element/attribute in the
* image/logo section of the feed.
*
* See {@see SimplePie::get_feed_tags()} for a description of the return value
*
* @since 1.0
* @see http://simplepie.org/wiki/faq/supported_xml_namespaces
* @param string $namespace The URL of the XML namespace of the elements you're trying to access
* @param string $tag Tag name
* @return array
*/
public function get_image_tags($namespace, $tag)
{
$type = $this->get_type();
if ($type & self::TYPE_RSS_10) {
if ($image = $this->get_feed_tags(self::NAMESPACE_RSS_10, 'image')) {
if (isset($image[0]['child'][$namespace][$tag])) {
return $image[0]['child'][$namespace][$tag];
}
}
}
if ($type & self::TYPE_RSS_090) {
if ($image = $this->get_feed_tags(self::NAMESPACE_RSS_090, 'image')) {
if (isset($image[0]['child'][$namespace][$tag])) {
return $image[0]['child'][$namespace][$tag];
}
}
}
if ($type & self::TYPE_RSS_SYNDICATION) {
if ($image = $this->get_channel_tags(self::NAMESPACE_RSS_20, 'image')) {
if (isset($image[0]['child'][$namespace][$tag])) {
return $image[0]['child'][$namespace][$tag];
}
}
}
return null;
}
/**
* Get the base URL value from the feed
*
* Uses `<xml:base>` if available, otherwise uses the first link in the
* feed, or failing that, the URL of the feed itself.
*
* @see get_link
* @see subscribe_url
*
* @param array $element
* @return string
*/
public function get_base($element = [])
{
if (!empty($element['xml_base_explicit']) && isset($element['xml_base'])) {
return $element['xml_base'];
} elseif ($this->get_link() !== null) {
return $this->get_link();
}
return $this->subscribe_url();
}
/**
* Sanitize feed data
*
* @access private
* @see \SimplePie\Sanitize::sanitize()
* @param string $data Data to sanitize
* @param int $type One of the \SimplePie\SimplePie::CONSTRUCT_* constants
* @param string $base Base URL to resolve URLs against
* @return string Sanitized data
*/
public function sanitize($data, $type, $base = '')
{
try {
return $this->sanitize->sanitize($data, $type, $base);
} catch (\SimplePie\Exception $e) {
if (!$this->enable_exceptions) {
$this->error = $e->getMessage();
$this->registry->call(Misc::class, 'error', [$this->error, E_USER_WARNING, $e->getFile(), $e->getLine()]);
return '';
}
throw $e;
}
}
/**
* Get the title of the feed
*
* Uses `<atom:title>`, `<title>` or `<dc:title>`
*
* @since 1.0 (previously called `get_feed_title` since 0.8)
* @return string|null
*/
public function get_title()
{
if ($return = $this->get_channel_tags(self::NAMESPACE_ATOM_10, 'title')) {
return $this->sanitize($return[0]['data'], $this->registry->call(Misc::class, 'atom_10_construct_type', [$return[0]['attribs']]), $this->get_base($return[0]));
} elseif ($return = $this->get_channel_tags(self::NAMESPACE_ATOM_03, 'title')) {
return $this->sanitize($return[0]['data'], $this->registry->call(Misc::class, 'atom_03_construct_type', [$return[0]['attribs']]), $this->get_base($return[0]));
} elseif ($return = $this->get_channel_tags(self::NAMESPACE_RSS_10, 'title')) {
return $this->sanitize($return[0]['data'], self::CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
} elseif ($return = $this->get_channel_tags(self::NAMESPACE_RSS_090, 'title')) {
return $this->sanitize($return[0]['data'], self::CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
} elseif ($return = $this->get_channel_tags(self::NAMESPACE_RSS_20, 'title')) {
return $this->sanitize($return[0]['data'], self::CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
} elseif ($return = $this->get_channel_tags(self::NAMESPACE_DC_11, 'title')) {
return $this->sanitize($return[0]['data'], self::CONSTRUCT_TEXT);
} elseif ($return = $this->get_channel_tags(self::NAMESPACE_DC_10, 'title')) {
return $this->sanitize($return[0]['data'], self::CONSTRUCT_TEXT);
}
return null;
}
/**
* Get a category for the feed
*
* @since Unknown
* @param int $key The category that you want to return. Remember that arrays begin with 0, not 1
* @return \SimplePie\Category|null
*/
public function get_category($key = 0)
{
$categories = $this->get_categories();
if (isset($categories[$key])) {
return $categories[$key];
}
return null;
}
/**
* Get all categories for the feed
*
* Uses `<atom:category>`, `<category>` or `<dc:subject>`
*
* @since Unknown
* @return array|null List of {@see \SimplePie\Category} objects
*/
public function get_categories()
{
$categories = [];
foreach ((array) $this->get_channel_tags(self::NAMESPACE_ATOM_10, 'category') as $category) {
$term = null;
$scheme = null;
$label = null;
if (isset($category['attribs']['']['term'])) {
$term = $this->sanitize($category['attribs']['']['term'], self::CONSTRUCT_TEXT);
}
if (isset($category['attribs']['']['scheme'])) {
$scheme = $this->sanitize($category['attribs']['']['scheme'], self::CONSTRUCT_TEXT);
}
if (isset($category['attribs']['']['label'])) {
$label = $this->sanitize($category['attribs']['']['label'], self::CONSTRUCT_TEXT);
}
$categories[] = $this->registry->create(Category::class, [$term, $scheme, $label]);
}
foreach ((array) $this->get_channel_tags(self::NAMESPACE_RSS_20, 'category') as $category) {
// This is really the label, but keep this as the term also for BC.
// Label will also work on retrieving because that falls back to term.
$term = $this->sanitize($category['data'], self::CONSTRUCT_TEXT);
if (isset($category['attribs']['']['domain'])) {
$scheme = $this->sanitize($category['attribs']['']['domain'], self::CONSTRUCT_TEXT);
} else {
$scheme = null;
}
$categories[] = $this->registry->create(Category::class, [$term, $scheme, null]);
}
foreach ((array) $this->get_channel_tags(self::NAMESPACE_DC_11, 'subject') as $category) {
$categories[] = $this->registry->create(Category::class, [$this->sanitize($category['data'], self::CONSTRUCT_TEXT), null, null]);
}
foreach ((array) $this->get_channel_tags(self::NAMESPACE_DC_10, 'subject') as $category) {
$categories[] = $this->registry->create(Category::class, [$this->sanitize($category['data'], self::CONSTRUCT_TEXT), null, null]);
}
if (!empty($categories)) {
return array_unique($categories);
}
return null;
}
/**
* Get an author for the feed
*
* @since 1.1
* @param int $key The author that you want to return. Remember that arrays begin with 0, not 1
* @return \SimplePie\Author|null
*/
public function get_author($key = 0)
{
$authors = $this->get_authors();
if (isset($authors[$key])) {
return $authors[$key];
}
return null;
}
/**
* Get all authors for the feed
*
* Uses `<atom:author>`, `<author>`, `<dc:creator>` or `<itunes:author>`
*
* @since 1.1
* @return array|null List of {@see \SimplePie\Author} objects
*/
public function get_authors()
{
$authors = [];
foreach ((array) $this->get_channel_tags(self::NAMESPACE_ATOM_10, 'author') as $author) {
$name = null;
$uri = null;
$email = null;
if (isset($author['child'][self::NAMESPACE_ATOM_10]['name'][0]['data'])) {
$name = $this->sanitize($author['child'][self::NAMESPACE_ATOM_10]['name'][0]['data'], self::CONSTRUCT_TEXT);
}
if (isset($author['child'][self::NAMESPACE_ATOM_10]['uri'][0]['data'])) {
$uri = $this->sanitize($author['child'][self::NAMESPACE_ATOM_10]['uri'][0]['data'], self::CONSTRUCT_IRI, $this->get_base($author['child'][self::NAMESPACE_ATOM_10]['uri'][0]));
}
if (isset($author['child'][self::NAMESPACE_ATOM_10]['email'][0]['data'])) {
$email = $this->sanitize($author['child'][self::NAMESPACE_ATOM_10]['email'][0]['data'], self::CONSTRUCT_TEXT);
}
if ($name !== null || $email !== null || $uri !== null) {
$authors[] = $this->registry->create(Author::class, [$name, $uri, $email]);
}
}
if ($author = $this->get_channel_tags(self::NAMESPACE_ATOM_03, 'author')) {
$name = null;
$url = null;
$email = null;
if (isset($author[0]['child'][self::NAMESPACE_ATOM_03]['name'][0]['data'])) {
$name = $this->sanitize($author[0]['child'][self::NAMESPACE_ATOM_03]['name'][0]['data'], self::CONSTRUCT_TEXT);
}
if (isset($author[0]['child'][self::NAMESPACE_ATOM_03]['url'][0]['data'])) {
$url = $this->sanitize($author[0]['child'][self::NAMESPACE_ATOM_03]['url'][0]['data'], self::CONSTRUCT_IRI, $this->get_base($author[0]['child'][self::NAMESPACE_ATOM_03]['url'][0]));
}
if (isset($author[0]['child'][self::NAMESPACE_ATOM_03]['email'][0]['data'])) {
$email = $this->sanitize($author[0]['child'][self::NAMESPACE_ATOM_03]['email'][0]['data'], self::CONSTRUCT_TEXT);
}
if ($name !== null || $email !== null || $url !== null) {
$authors[] = $this->registry->create(Author::class, [$name, $url, $email]);
}
}
foreach ((array) $this->get_channel_tags(self::NAMESPACE_DC_11, 'creator') as $author) {
$authors[] = $this->registry->create(Author::class, [$this->sanitize($author['data'], self::CONSTRUCT_TEXT), null, null]);
}
foreach ((array) $this->get_channel_tags(self::NAMESPACE_DC_10, 'creator') as $author) {
$authors[] = $this->registry->create(Author::class, [$this->sanitize($author['data'], self::CONSTRUCT_TEXT), null, null]);
}
foreach ((array) $this->get_channel_tags(self::NAMESPACE_ITUNES, 'author') as $author) {
$authors[] = $this->registry->create(Author::class, [$this->sanitize($author['data'], self::CONSTRUCT_TEXT), null, null]);
}
if (!empty($authors)) {
return array_unique($authors);
}
return null;
}
/**
* Get a contributor for the feed
*
* @since 1.1
* @param int $key The contrbutor that you want to return. Remember that arrays begin with 0, not 1
* @return \SimplePie\Author|null
*/
public function get_contributor($key = 0)
{
$contributors = $this->get_contributors();
if (isset($contributors[$key])) {
return $contributors[$key];
}
return null;
}
/**
* Get all contributors for the feed
*
* Uses `<atom:contributor>`
*
* @since 1.1
* @return array|null List of {@see \SimplePie\Author} objects
*/
public function get_contributors()
{
$contributors = [];
foreach ((array) $this->get_channel_tags(self::NAMESPACE_ATOM_10, 'contributor') as $contributor) {
$name = null;
$uri = null;
$email = null;
if (isset($contributor['child'][self::NAMESPACE_ATOM_10]['name'][0]['data'])) {
$name = $this->sanitize($contributor['child'][self::NAMESPACE_ATOM_10]['name'][0]['data'], self::CONSTRUCT_TEXT);
}
if (isset($contributor['child'][self::NAMESPACE_ATOM_10]['uri'][0]['data'])) {
$uri = $this->sanitize($contributor['child'][self::NAMESPACE_ATOM_10]['uri'][0]['data'], self::CONSTRUCT_IRI, $this->get_base($contributor['child'][self::NAMESPACE_ATOM_10]['uri'][0]));
}
if (isset($contributor['child'][self::NAMESPACE_ATOM_10]['email'][0]['data'])) {
$email = $this->sanitize($contributor['child'][self::NAMESPACE_ATOM_10]['email'][0]['data'], self::CONSTRUCT_TEXT);
}
if ($name !== null || $email !== null || $uri !== null) {
$contributors[] = $this->registry->create(Author::class, [$name, $uri, $email]);
}
}
foreach ((array) $this->get_channel_tags(self::NAMESPACE_ATOM_03, 'contributor') as $contributor) {
$name = null;
$url = null;
$email = null;
if (isset($contributor['child'][self::NAMESPACE_ATOM_03]['name'][0]['data'])) {
$name = $this->sanitize($contributor['child'][self::NAMESPACE_ATOM_03]['name'][0]['data'], self::CONSTRUCT_TEXT);
}
if (isset($contributor['child'][self::NAMESPACE_ATOM_03]['url'][0]['data'])) {
$url = $this->sanitize($contributor['child'][self::NAMESPACE_ATOM_03]['url'][0]['data'], self::CONSTRUCT_IRI, $this->get_base($contributor['child'][self::NAMESPACE_ATOM_03]['url'][0]));
}
if (isset($contributor['child'][self::NAMESPACE_ATOM_03]['email'][0]['data'])) {
$email = $this->sanitize($contributor['child'][self::NAMESPACE_ATOM_03]['email'][0]['data'], self::CONSTRUCT_TEXT);
}
if ($name !== null || $email !== null || $url !== null) {
$contributors[] = $this->registry->create(Author::class, [$name, $url, $email]);
}
}
if (!empty($contributors)) {
return array_unique($contributors);
}
return null;
}
/**
* Get a single link for the feed
*
* @since 1.0 (previously called `get_feed_link` since Preview Release, `get_feed_permalink()` since 0.8)
* @param int $key The link that you want to return. Remember that arrays begin with 0, not 1
* @param string $rel The relationship of the link to return
* @return string|null Link URL
*/
public function get_link($key = 0, $rel = 'alternate')
{
$links = $this->get_links($rel);
if (isset($links[$key])) {
return $links[$key];
}
return null;
}
/**
* Get the permalink for the item
*
* Returns the first link available with a relationship of "alternate".
* Identical to {@see get_link()} with key 0
*
* @see get_link
* @since 1.0 (previously called `get_feed_link` since Preview Release, `get_feed_permalink()` since 0.8)
* @internal Added for parity between the parent-level and the item/entry-level.
* @return string|null Link URL
*/
public function get_permalink()
{
return $this->get_link(0);
}
/**
* Get all links for the feed
*
* Uses `<atom:link>` or `<link>`
*
* @since Beta 2
* @param string $rel The relationship of links to return
* @return array|null Links found for the feed (strings)
*/
public function get_links($rel = 'alternate')
{
if (!isset($this->data['links'])) {
$this->data['links'] = [];
if ($links = $this->get_channel_tags(self::NAMESPACE_ATOM_10, 'link')) {
foreach ($links as $link) {
if (isset($link['attribs']['']['href'])) {
$link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate';
$this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], self::CONSTRUCT_IRI, $this->get_base($link));
}
}
}
if ($links = $this->get_channel_tags(self::NAMESPACE_ATOM_03, 'link')) {
foreach ($links as $link) {
if (isset($link['attribs']['']['href'])) {
$link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate';
$this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], self::CONSTRUCT_IRI, $this->get_base($link));
}
}
}
if ($links = $this->get_channel_tags(self::NAMESPACE_RSS_10, 'link')) {
$this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], self::CONSTRUCT_IRI, $this->get_base($links[0]));
}
if ($links = $this->get_channel_tags(self::NAMESPACE_RSS_090, 'link')) {
$this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], self::CONSTRUCT_IRI, $this->get_base($links[0]));
}
if ($links = $this->get_channel_tags(self::NAMESPACE_RSS_20, 'link')) {
$this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], self::CONSTRUCT_IRI, $this->get_base($links[0]));
}
$keys = array_keys($this->data['links']);
foreach ($keys as $key) {
if ($this->registry->call(Misc::class, 'is_isegment_nz_nc', [$key])) {
if (isset($this->data['links'][self::IANA_LINK_RELATIONS_REGISTRY . $key])) {
$this->data['links'][self::IANA_LINK_RELATIONS_REGISTRY . $key] = array_merge($this->data['links'][$key], $this->data['links'][self::IANA_LINK_RELATIONS_REGISTRY . $key]);
$this->data['links'][$key] = &$this->data['links'][self::IANA_LINK_RELATIONS_REGISTRY . $key];
} else {
$this->data['links'][self::IANA_LINK_RELATIONS_REGISTRY . $key] = &$this->data['links'][$key];
}
} elseif (substr($key, 0, 41) === self::IANA_LINK_RELATIONS_REGISTRY) {
$this->data['links'][substr($key, 41)] = &$this->data['links'][$key];
}
$this->data['links'][$key] = array_unique($this->data['links'][$key]);
}
}
if (isset($this->data['headers']['link'])) {
$link_headers = $this->data['headers']['link'];
if (is_array($link_headers)) {
$link_headers = implode(',', $link_headers);
}
// https://datatracker.ietf.org/doc/html/rfc8288
if (is_string($link_headers) &&
preg_match_all('/<(?P<uri>[^>]+)>\s*;\s*rel\s*=\s*(?P<quote>"?)' . preg_quote($rel) . '(?P=quote)\s*(?=,|$)/i', $link_headers, $matches)) {
return $matches['uri'];
}
}
if (isset($this->data['links'][$rel])) {
return $this->data['links'][$rel];
}
return null;
}
public function get_all_discovered_feeds()
{
return $this->all_discovered_feeds;
}
/**
* Get the content for the item
*
* Uses `<atom:subtitle>`, `<atom:tagline>`, `<description>`,
* `<dc:description>`, `<itunes:summary>` or `<itunes:subtitle>`
*
* @since 1.0 (previously called `get_feed_description()` since 0.8)
* @return string|null
*/
public function get_description()
{
if ($return = $this->get_channel_tags(self::NAMESPACE_ATOM_10, 'subtitle')) {
return $this->sanitize($return[0]['data'], $this->registry->call(Misc::class, 'atom_10_construct_type', [$return[0]['attribs']]), $this->get_base($return[0]));
} elseif ($return = $this->get_channel_tags(self::NAMESPACE_ATOM_03, 'tagline')) {
return $this->sanitize($return[0]['data'], $this->registry->call(Misc::class, 'atom_03_construct_type', [$return[0]['attribs']]), $this->get_base($return[0]));
} elseif ($return = $this->get_channel_tags(self::NAMESPACE_RSS_10, 'description')) {
return $this->sanitize($return[0]['data'], self::CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
} elseif ($return = $this->get_channel_tags(self::NAMESPACE_RSS_090, 'description')) {
return $this->sanitize($return[0]['data'], self::CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
} elseif ($return = $this->get_channel_tags(self::NAMESPACE_RSS_20, 'description')) {
return $this->sanitize($return[0]['data'], self::CONSTRUCT_HTML, $this->get_base($return[0]));
} elseif ($return = $this->get_channel_tags(self::NAMESPACE_DC_11, 'description')) {
return $this->sanitize($return[0]['data'], self::CONSTRUCT_TEXT);
} elseif ($return = $this->get_channel_tags(self::NAMESPACE_DC_10, 'description')) {
return $this->sanitize($return[0]['data'], self::CONSTRUCT_TEXT);
} elseif ($return = $this->get_channel_tags(self::NAMESPACE_ITUNES, 'summary')) {
return $this->sanitize($return[0]['data'], self::CONSTRUCT_HTML, $this->get_base($return[0]));
} elseif ($return = $this->get_channel_tags(self::NAMESPACE_ITUNES, 'subtitle')) {
return $this->sanitize($return[0]['data'], self::CONSTRUCT_HTML, $this->get_base($return[0]));
}
return null;
}
/**
* Get the copyright info for the feed
*
* Uses `<atom:rights>`, `<atom:copyright>` or `<dc:rights>`
*
* @since 1.0 (previously called `get_feed_copyright()` since 0.8)
* @return string|null
*/
public function get_copyright()
{
if ($return = $this->get_channel_tags(self::NAMESPACE_ATOM_10, 'rights')) {
return $this->sanitize($return[0]['data'], $this->registry->call(Misc::class, 'atom_10_construct_type', [$return[0]['attribs']]), $this->get_base($return[0]));
} elseif ($return = $this->get_channel_tags(self::NAMESPACE_ATOM_03, 'copyright')) {
return $this->sanitize($return[0]['data'], $this->registry->call(Misc::class, 'atom_03_construct_type', [$return[0]['attribs']]), $this->get_base($return[0]));
} elseif ($return = $this->get_channel_tags(self::NAMESPACE_RSS_20, 'copyright')) {
return $this->sanitize($return[0]['data'], self::CONSTRUCT_TEXT);
} elseif ($return = $this->get_channel_tags(self::NAMESPACE_DC_11, 'rights')) {
return $this->sanitize($return[0]['data'], self::CONSTRUCT_TEXT);
} elseif ($return = $this->get_channel_tags(self::NAMESPACE_DC_10, 'rights')) {
return $this->sanitize($return[0]['data'], self::CONSTRUCT_TEXT);
}
return null;
}
/**
* Get the language for the feed
*
* Uses `<language>`, `<dc:language>`, or @xml_lang
*
* @since 1.0 (previously called `get_feed_language()` since 0.8)
* @return string|null
*/
public function get_language()
{
if ($return = $this->get_channel_tags(self::NAMESPACE_RSS_20, 'language')) {
return $this->sanitize($return[0]['data'], self::CONSTRUCT_TEXT);
} elseif ($return = $this->get_channel_tags(self::NAMESPACE_DC_11, 'language')) {
return $this->sanitize($return[0]['data'], self::CONSTRUCT_TEXT);
} elseif ($return = $this->get_channel_tags(self::NAMESPACE_DC_10, 'language')) {
return $this->sanitize($return[0]['data'], self::CONSTRUCT_TEXT);
} elseif (isset($this->data['child'][self::NAMESPACE_ATOM_10]['feed'][0]['xml_lang'])) {
return $this->sanitize($this->data['child'][self::NAMESPACE_ATOM_10]['feed'][0]['xml_lang'], self::CONSTRUCT_TEXT);
} elseif (isset($this->data['child'][self::NAMESPACE_ATOM_03]['feed'][0]['xml_lang'])) {
return $this->sanitize($this->data['child'][self::NAMESPACE_ATOM_03]['feed'][0]['xml_lang'], self::CONSTRUCT_TEXT);
} elseif (isset($this->data['child'][self::NAMESPACE_RDF]['RDF'][0]['xml_lang'])) {
return $this->sanitize($this->data['child'][self::NAMESPACE_RDF]['RDF'][0]['xml_lang'], self::CONSTRUCT_TEXT);
} elseif (isset($this->data['headers']['content-language'])) {
return $this->sanitize($this->data['headers']['content-language'], self::CONSTRUCT_TEXT);
}
return null;
}
/**
* Get the latitude coordinates for the item
*
* Compatible with the W3C WGS84 Basic Geo and GeoRSS specifications
*
* Uses `<geo:lat>` or `<georss:point>`
*
* @since 1.0
* @link http://www.w3.org/2003/01/geo/ W3C WGS84 Basic Geo
* @link http://www.georss.org/ GeoRSS
* @return string|null
*/
public function get_latitude()
{
if ($return = $this->get_channel_tags(self::NAMESPACE_W3C_BASIC_GEO, 'lat')) {
return (float) $return[0]['data'];
} elseif (($return = $this->get_channel_tags(self::NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', trim($return[0]['data']), $match)) {
return (float) $match[1];
}
return null;
}
/**
* Get the longitude coordinates for the feed
*
* Compatible with the W3C WGS84 Basic Geo and GeoRSS specifications
*
* Uses `<geo:long>`, `<geo:lon>` or `<georss:point>`
*
* @since 1.0
* @link http://www.w3.org/2003/01/geo/ W3C WGS84 Basic Geo
* @link http://www.georss.org/ GeoRSS
* @return string|null
*/
public function get_longitude()
{
if ($return = $this->get_channel_tags(self::NAMESPACE_W3C_BASIC_GEO, 'long')) {
return (float) $return[0]['data'];
} elseif ($return = $this->get_channel_tags(self::NAMESPACE_W3C_BASIC_GEO, 'lon')) {
return (float) $return[0]['data'];
} elseif (($return = $this->get_channel_tags(self::NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', trim($return[0]['data']), $match)) {
return (float) $match[2];
}
return null;
}
/**
* Get the feed logo's title
*
* RSS 0.9.0, 1.0 and 2.0 feeds are allowed to have a "feed logo" title.
*
* Uses `<image><title>` or `<image><dc:title>`
*
* @return string|null
*/
public function get_image_title()
{
if ($return = $this->get_image_tags(self::NAMESPACE_RSS_10, 'title')) {
return $this->sanitize($return[0]['data'], self::CONSTRUCT_TEXT);
} elseif ($return = $this->get_image_tags(self::NAMESPACE_RSS_090, 'title')) {
return $this->sanitize($return[0]['data'], self::CONSTRUCT_TEXT);
} elseif ($return = $this->get_image_tags(self::NAMESPACE_RSS_20, 'title')) {
return $this->sanitize($return[0]['data'], self::CONSTRUCT_TEXT);
} elseif ($return = $this->get_image_tags(self::NAMESPACE_DC_11, 'title')) {
return $this->sanitize($return[0]['data'], self::CONSTRUCT_TEXT);
} elseif ($return = $this->get_image_tags(self::NAMESPACE_DC_10, 'title')) {
return $this->sanitize($return[0]['data'], self::CONSTRUCT_TEXT);
}
return null;
}
/**
* Get the feed logo's URL
*
* RSS 0.9.0, 2.0, Atom 1.0, and feeds with iTunes RSS tags are allowed to
* have a "feed logo" URL. This points directly to the image itself.
*
* Uses `<itunes:image>`, `<atom:logo>`, `<atom:icon>`,
* `<image><title>` or `<image><dc:title>`
*
* @return string|null
*/
public function get_image_url()
{
if ($return = $this->get_channel_tags(self::NAMESPACE_ITUNES, 'image')) {
return $this->sanitize($return[0]['attribs']['']['href'], self::CONSTRUCT_IRI);
} elseif ($return = $this->get_channel_tags(self::NAMESPACE_ATOM_10, 'logo')) {
return $this->sanitize($return[0]['data'], self::CONSTRUCT_IRI, $this->get_base($return[0]));
} elseif ($return = $this->get_channel_tags(self::NAMESPACE_ATOM_10, 'icon')) {
return $this->sanitize($return[0]['data'], self::CONSTRUCT_IRI, $this->get_base($return[0]));
} elseif ($return = $this->get_image_tags(self::NAMESPACE_RSS_10, 'url')) {
return $this->sanitize($return[0]['data'], self::CONSTRUCT_IRI, $this->get_base($return[0]));
} elseif ($return = $this->get_image_tags(self::NAMESPACE_RSS_090, 'url')) {
return $this->sanitize($return[0]['data'], self::CONSTRUCT_IRI, $this->get_base($return[0]));
} elseif ($return = $this->get_image_tags(self::NAMESPACE_RSS_20, 'url')) {
return $this->sanitize($return[0]['data'], self::CONSTRUCT_IRI, $this->get_base($return[0]));
}
return null;
}
/**
* Get the feed logo's link
*
* RSS 0.9.0, 1.0 and 2.0 feeds are allowed to have a "feed logo" link. This
* points to a human-readable page that the image should link to.
*
* Uses `<itunes:image>`, `<atom:logo>`, `<atom:icon>`,
* `<image><title>` or `<image><dc:title>`
*
* @return string|null
*/
public function get_image_link()
{
if ($return = $this->get_image_tags(self::NAMESPACE_RSS_10, 'link')) {
return $this->sanitize($return[0]['data'], self::CONSTRUCT_IRI, $this->get_base($return[0]));
} elseif ($return = $this->get_image_tags(self::NAMESPACE_RSS_090, 'link')) {
return $this->sanitize($return[0]['data'], self::CONSTRUCT_IRI, $this->get_base($return[0]));
} elseif ($return = $this->get_image_tags(self::NAMESPACE_RSS_20, 'link')) {
return $this->sanitize($return[0]['data'], self::CONSTRUCT_IRI, $this->get_base($return[0]));
}
return null;
}
/**
* Get the feed logo's link
*
* RSS 2.0 feeds are allowed to have a "feed logo" width.
*
* Uses `<image><width>` or defaults to 88 if no width is specified and
* the feed is an RSS 2.0 feed.
*
* @return int|null
*/
public function get_image_width()
{
if ($return = $this->get_image_tags(self::NAMESPACE_RSS_20, 'width')) {
return intval($return[0]['data']);
} elseif ($this->get_type() & self::TYPE_RSS_SYNDICATION && $this->get_image_tags(self::NAMESPACE_RSS_20, 'url')) {
return 88;
}
return null;
}
/**
* Get the feed logo's height
*
* RSS 2.0 feeds are allowed to have a "feed logo" height.
*
* Uses `<image><height>` or defaults to 31 if no height is specified and
* the feed is an RSS 2.0 feed.
*
* @return int|null
*/
public function get_image_height()
{
if ($return = $this->get_image_tags(self::NAMESPACE_RSS_20, 'height')) {
return intval($return[0]['data']);
} elseif ($this->get_type() & self::TYPE_RSS_SYNDICATION && $this->get_image_tags(self::NAMESPACE_RSS_20, 'url')) {
return 31;
}
return null;
}
/**
* Get the number of items in the feed
*
* This is well-suited for {@link http://php.net/for for()} loops with
* {@see get_item()}
*
* @param int $max Maximum value to return. 0 for no limit
* @return int Number of items in the feed
*/
public function get_item_quantity($max = 0)
{
$max = (int) $max;
$qty = count($this->get_items());
if ($max === 0) {
return $qty;
}
return ($qty > $max) ? $max : $qty;
}
/**
* Get a single item from the feed
*
* This is better suited for {@link http://php.net/for for()} loops, whereas
* {@see get_items()} is better suited for
* {@link http://php.net/foreach foreach()} loops.
*
* @see get_item_quantity()
* @since Beta 2
* @param int $key The item that you want to return. Remember that arrays begin with 0, not 1
* @return \SimplePie\Item|null
*/
public function get_item($key = 0)
{
$items = $this->get_items();
if (isset($items[$key])) {
return $items[$key];
}
return null;
}
/**
* Get all items from the feed
*
* This is better suited for {@link http://php.net/for for()} loops, whereas
* {@see get_items()} is better suited for
* {@link http://php.net/foreach foreach()} loops.
*
* @see get_item_quantity
* @since Beta 2
* @param int $start Index to start at
* @param int $end Number of items to return. 0 for all items after `$start`
* @return \SimplePie\Item[]|null List of {@see \SimplePie\Item} objects
*/
public function get_items($start = 0, $end = 0)
{
if (!isset($this->data['items'])) {
if (!empty($this->multifeed_objects)) {
$this->data['items'] = SimplePie::merge_items($this->multifeed_objects, $start, $end, $this->item_limit);
if (empty($this->data['items'])) {
return [];
}
return $this->data['items'];
}
$this->data['items'] = [];
if ($items = $this->get_feed_tags(self::NAMESPACE_ATOM_10, 'entry')) {
$keys = array_keys($items);
foreach ($keys as $key) {
$this->data['items'][] = $this->registry->create(Item::class, [$this, $items[$key]]);
}
}
if ($items = $this->get_feed_tags(self::NAMESPACE_ATOM_03, 'entry')) {
$keys = array_keys($items);
foreach ($keys as $key) {
$this->data['items'][] = $this->registry->create(Item::class, [$this, $items[$key]]);
}
}
if ($items = $this->get_feed_tags(self::NAMESPACE_RSS_10, 'item')) {
$keys = array_keys($items);
foreach ($keys as $key) {
$this->data['items'][] = $this->registry->create(Item::class, [$this, $items[$key]]);
}
}
if ($items = $this->get_feed_tags(self::NAMESPACE_RSS_090, 'item')) {
$keys = array_keys($items);
foreach ($keys as $key) {
$this->data['items'][] = $this->registry->create(Item::class, [$this, $items[$key]]);
}
}
if ($items = $this->get_channel_tags(self::NAMESPACE_RSS_20, 'item')) {
$keys = array_keys($items);
foreach ($keys as $key) {
$this->data['items'][] = $this->registry->create(Item::class, [$this, $items[$key]]);
}
}
}
if (empty($this->data['items'])) {
return [];
}
if ($this->order_by_date) {
if (!isset($this->data['ordered_items'])) {
$this->data['ordered_items'] = $this->data['items'];
usort($this->data['ordered_items'], [get_class($this), 'sort_items']);
}
$items = $this->data['ordered_items'];
} else {
$items = $this->data['items'];
}
// Slice the data as desired
if ($end === 0) {
return array_slice($items, $start);
}
return array_slice($items, $start, $end);
}
/**
* Set the favicon handler
*
* @deprecated Use your own favicon handling instead
*/
public function set_favicon_handler($page = false, $qs = 'i')
{
trigger_error('Favicon handling has been removed, please use your own handling', \E_USER_DEPRECATED);
return false;
}
/**
* Get the favicon for the current feed
*
* @deprecated Use your own favicon handling instead
*/
public function get_favicon()
{
trigger_error('Favicon handling has been removed, please use your own handling', \E_USER_DEPRECATED);
if (($url = $this->get_link()) !== null) {
return 'https://www.google.com/s2/favicons?domain=' . urlencode($url);
}
return false;
}
/**
* Magic method handler
*
* @param string $method Method name
* @param array $args Arguments to the method
* @return mixed
*/
public function __call($method, $args)
{
if (strpos($method, 'subscribe_') === 0) {
trigger_error('subscribe_*() has been deprecated, implement the callback yourself', \E_USER_DEPRECATED);
return '';
}
if ($method === 'enable_xml_dump') {
trigger_error('enable_xml_dump() has been deprecated, use get_raw_data() instead', \E_USER_DEPRECATED);
return false;
}
$class = get_class($this);
$trace = debug_backtrace(); // phpcs:ignore PHPCompatibility.FunctionUse.ArgumentFunctionsReportCurrentValue.NeedsInspection
$file = $trace[0]['file'];
$line = $trace[0]['line'];
throw new SimplePieException("Call to undefined method $class::$method() in $file on line $line");
}
/**
* Sorting callback for items
*
* @access private
* @param SimplePie $a
* @param SimplePie $b
* @return boolean
*/
public static function sort_items($a, $b)
{
$a_date = $a->get_date('U');
$b_date = $b->get_date('U');
if ($a_date && $b_date) {
return $a_date > $b_date ? -1 : 1;
}
// Sort items without dates to the top.
if ($a_date) {
return 1;
}
if ($b_date) {
return -1;
}
return 0;
}
/**
* Merge items from several feeds into one
*
* If you're merging multiple feeds together, they need to all have dates
* for the items or else SimplePie will refuse to sort them.
*
* @link http://simplepie.org/wiki/tutorial/sort_multiple_feeds_by_time_and_date#if_feeds_require_separate_per-feed_settings
* @param array $urls List of SimplePie feed objects to merge
* @param int $start Starting item
* @param int $end Number of items to return
* @param int $limit Maximum number of items per feed
* @return array
*/
public static function merge_items($urls, $start = 0, $end = 0, $limit = 0)
{
if (is_array($urls) && sizeof($urls) > 0) {
$items = [];
foreach ($urls as $arg) {
if ($arg instanceof SimplePie) {
$items = array_merge($items, $arg->get_items(0, $limit));
} else {
trigger_error('Arguments must be SimplePie objects', E_USER_WARNING);
}
}
usort($items, [get_class($urls[0]), 'sort_items']);
if ($end === 0) {
return array_slice($items, $start);
}
return array_slice($items, $start, $end);
}
trigger_error('Cannot merge zero SimplePie objects', E_USER_WARNING);
return [];
}
/**
* Store PubSubHubbub links as headers
*
* There is no way to find PuSH links in the body of a microformats feed,
* so they are added to the headers when found, to be used later by get_links.
* @param \SimplePie\File $file
* @param string $hub
* @param string $self
*/
private function store_links(&$file, $hub, $self)
{
if (isset($file->headers['link']['hub']) ||
(isset($file->headers['link']) &&
preg_match('/rel=hub/', $file->headers['link']))) {
return;
}
if ($hub) {
if (isset($file->headers['link'])) {
if ($file->headers['link'] !== '') {
$file->headers['link'] = ', ';
}
} else {
$file->headers['link'] = '';
}
$file->headers['link'] .= '<'.$hub.'>; rel=hub';
if ($self) {
$file->headers['link'] .= ', <'.$self.'>; rel=self';
}
}
}
/**
* Get a DataCache
*
* @param string $feed_url Only needed for BC, can be removed in SimplePie 2.0.0
*
* @return DataCache
*/
private function get_cache($feed_url = '')
{
if ($this->cache === null) {
// @trigger_error(sprintf('Not providing as PSR-16 cache implementation is deprecated since SimplePie 1.8.0, please use "SimplePie\SimplePie::set_cache()".'), \E_USER_DEPRECATED);
$cache = $this->registry->call(Cache::class, 'get_handler', [
$this->cache_location,
$this->get_cache_filename($feed_url),
Base::TYPE_FEED
]);
return new BaseDataCache($cache);
}
return $this->cache;
}
}
class_alias('SimplePie\SimplePie', 'SimplePie');
Category.php 0000644 00000010416 15133170420 0007030 0 ustar 00 <?php
/**
* SimplePie
*
* A PHP-Based RSS and Atom Feed Framework.
* Takes the hard work out of managing a complete RSS/Atom solution.
*
* Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* * Neither the name of the SimplePie Team nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
* AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package SimplePie
* @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
* @author Ryan Parman
* @author Sam Sneddon
* @author Ryan McCue
* @link http://simplepie.org/ SimplePie
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
namespace SimplePie;
/**
* Manages all category-related data
*
* Used by {@see \SimplePie\Item::get_category()} and {@see \SimplePie\Item::get_categories()}
*
* This class can be overloaded with {@see \SimplePie\SimplePie::set_category_class()}
*
* @package SimplePie
* @subpackage API
*/
class Category
{
/**
* Category identifier
*
* @var string|null
* @see get_term
*/
public $term;
/**
* Categorization scheme identifier
*
* @var string|null
* @see get_scheme()
*/
public $scheme;
/**
* Human readable label
*
* @var string|null
* @see get_label()
*/
public $label;
/**
* Category type
*
* category for <category>
* subject for <dc:subject>
*
* @var string|null
* @see get_type()
*/
public $type;
/**
* Constructor, used to input the data
*
* @param string|null $term
* @param string|null $scheme
* @param string|null $label
* @param string|null $type
*/
public function __construct($term = null, $scheme = null, $label = null, $type = null)
{
$this->term = $term;
$this->scheme = $scheme;
$this->label = $label;
$this->type = $type;
}
/**
* String-ified version
*
* @return string
*/
public function __toString()
{
// There is no $this->data here
return md5(serialize($this));
}
/**
* Get the category identifier
*
* @return string|null
*/
public function get_term()
{
return $this->term;
}
/**
* Get the categorization scheme identifier
*
* @return string|null
*/
public function get_scheme()
{
return $this->scheme;
}
/**
* Get the human readable label
*
* @param bool $strict
* @return string|null
*/
public function get_label($strict = false)
{
if ($this->label === null && $strict !== true) {
return $this->get_term();
}
return $this->label;
}
/**
* Get the category type
*
* @return string|null
*/
public function get_type()
{
return $this->type;
}
}
class_alias('SimplePie\Category', 'SimplePie_Category');
Registry.php 0000644 00000020766 15133170420 0007074 0 ustar 00 <?php
/**
* SimplePie
*
* A PHP-Based RSS and Atom Feed Framework.
* Takes the hard work out of managing a complete RSS/Atom solution.
*
* Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* * Neither the name of the SimplePie Team nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
* AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package SimplePie
* @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
* @author Ryan Parman
* @author Sam Sneddon
* @author Ryan McCue
* @link http://simplepie.org/ SimplePie
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
namespace SimplePie;
use SimplePie\Content\Type\Sniffer;
use SimplePie\Parse\Date;
use SimplePie\XML\Declaration\Parser as DeclarationParser;
/**
* Handles creating objects and calling methods
*
* Access this via {@see \SimplePie\SimplePie::get_registry()}
*
* @package SimplePie
*/
class Registry
{
/**
* Default class mapping
*
* Overriding classes *must* subclass these.
*
* @var array<class-string, class-string>
*/
protected $default = [
Cache::class => Cache::class,
Locator::class => Locator::class,
Parser::class => Parser::class,
File::class => File::class,
Sanitize::class => Sanitize::class,
Item::class => Item::class,
Author::class => Author::class,
Category::class => Category::class,
Enclosure::class => Enclosure::class,
Caption::class => Caption::class,
Copyright::class => Copyright::class,
Credit::class => Credit::class,
Rating::class => Rating::class,
Restriction::class => Restriction::class,
Sniffer::class => Sniffer::class,
Source::class => Source::class,
Misc::class => Misc::class,
DeclarationParser::class => DeclarationParser::class,
Date::class => Date::class,
];
/**
* Class mapping
*
* @see register()
* @var array
*/
protected $classes = [];
/**
* Legacy classes
*
* @see register()
* @var array<class-string>
*/
protected $legacy = [];
/**
* Legacy types
*
* @see register()
* @var array<string, class-string>
*/
private $legacyTypes = [
'Cache' => Cache::class,
'Locator' => Locator::class,
'Parser' => Parser::class,
'File' => File::class,
'Sanitize' => Sanitize::class,
'Item' => Item::class,
'Author' => Author::class,
'Category' => Category::class,
'Enclosure' => Enclosure::class,
'Caption' => Caption::class,
'Copyright' => Copyright::class,
'Credit' => Credit::class,
'Rating' => Rating::class,
'Restriction' => Restriction::class,
'Content_Type_Sniffer' => Sniffer::class,
'Source' => Source::class,
'Misc' => Misc::class,
'XML_Declaration_Parser' => DeclarationParser::class,
'Parse_Date' => Date::class,
];
/**
* Constructor
*
* No-op
*/
public function __construct()
{
}
/**
* Register a class
*
* @param string $type See {@see $default} for names
* @param class-string $class Class name, must subclass the corresponding default
* @param bool $legacy Whether to enable legacy support for this class
* @return bool Successfulness
*/
public function register($type, $class, $legacy = false)
{
if (array_key_exists($type, $this->legacyTypes)) {
// trigger_error(sprintf('"%s"(): Using argument #1 ($type) with value "%s" is deprecated since SimplePie 1.8.0, use class-string "%s" instead.', __METHOD__, $type, $this->legacyTypes[$type]), \E_USER_DEPRECATED);
$type = $this->legacyTypes[$type];
}
if (!array_key_exists($type, $this->default)) {
return false;
}
if (!class_exists($class)) {
return false;
}
/** @var string */
$base_class = $this->default[$type];
if (!is_subclass_of($class, $base_class)) {
return false;
}
$this->classes[$type] = $class;
if ($legacy) {
$this->legacy[] = $class;
}
return true;
}
/**
* Get the class registered for a type
*
* Where possible, use {@see create()} or {@see call()} instead
*
* @template T
* @param class-string<T> $type
* @return class-string<T>|null
*/
public function get_class($type)
{
if (array_key_exists($type, $this->legacyTypes)) {
// trigger_error(sprintf('"%s"(): Using argument #1 ($type) with value "%s" is deprecated since SimplePie 1.8.0, use class-string "%s" instead.', __METHOD__, $type, $this->legacyTypes[$type]), \E_USER_DEPRECATED);
$type = $this->legacyTypes[$type];
}
if (!array_key_exists($type, $this->default)) {
return null;
}
$class = $this->default[$type];
if (array_key_exists($type, $this->classes)) {
$class = $this->classes[$type];
}
return $class;
}
/**
* Create a new instance of a given type
*
* @template T class-string $type
* @param class-string<T> $type
* @param array $parameters Parameters to pass to the constructor
* @return T Instance of class
*/
public function &create($type, $parameters = [])
{
$class = $this->get_class($type);
if (!method_exists($class, '__construct')) {
$instance = new $class();
} else {
$reflector = new \ReflectionClass($class);
$instance = $reflector->newInstanceArgs($parameters);
}
if ($instance instanceof RegistryAware) {
$instance->set_registry($this);
} elseif (method_exists($instance, 'set_registry')) {
trigger_error(sprintf('Using the method "set_registry()" without implementing "%s" is deprecated since SimplePie 1.8.0, implement "%s" in "%s".', RegistryAware::class, RegistryAware::class, $class), \E_USER_DEPRECATED);
$instance->set_registry($this);
}
return $instance;
}
/**
* Call a static method for a type
*
* @param class-string $type
* @param string $method
* @param array $parameters
* @return mixed
*/
public function &call($type, $method, $parameters = [])
{
$class = $this->get_class($type);
if (in_array($class, $this->legacy)) {
switch ($type) {
case Cache::class:
// For backwards compatibility with old non-static
// Cache::create() methods in PHP < 8.0.
// No longer supported as of PHP 8.0.
if ($method === 'get_handler') {
$result = @call_user_func_array([$class, 'create'], $parameters);
return $result;
}
break;
}
}
$result = call_user_func_array([$class, $method], $parameters);
return $result;
}
}
class_alias('SimplePie\Registry', 'SimplePie_Registry');
XML/Declaration/Parser.php 0000644 00000022353 15133170420 0011377 0 ustar 00 <?php
/**
* SimplePie
*
* A PHP-Based RSS and Atom Feed Framework.
* Takes the hard work out of managing a complete RSS/Atom solution.
*
* Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* * Neither the name of the SimplePie Team nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
* AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package SimplePie
* @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
* @author Ryan Parman
* @author Sam Sneddon
* @author Ryan McCue
* @link http://simplepie.org/ SimplePie
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
namespace SimplePie\XML\Declaration;
/**
* Parses the XML Declaration
*
* @package SimplePie
* @subpackage Parsing
*/
class Parser
{
/**
* XML Version
*
* @access public
* @var string
*/
public $version = '1.0';
/**
* Encoding
*
* @access public
* @var string
*/
public $encoding = 'UTF-8';
/**
* Standalone
*
* @access public
* @var bool
*/
public $standalone = false;
private const STATE_BEFORE_VERSION_NAME = 'before_version_name';
private const STATE_VERSION_NAME = 'version_name';
private const STATE_VERSION_EQUALS = 'version_equals';
private const STATE_VERSION_VALUE = 'version_value';
private const STATE_ENCODING_NAME = 'encoding_name';
private const STATE_EMIT = 'emit';
private const STATE_ENCODING_EQUALS = 'encoding_equals';
private const STATE_STANDALONE_NAME = 'standalone_name';
private const STATE_ENCODING_VALUE = 'encoding_value';
private const STATE_STANDALONE_EQUALS = 'standalone_equals';
private const STATE_STANDALONE_VALUE = 'standalone_value';
private const STATE_ERROR = false;
/**
* Current state of the state machine
*
* @access private
* @var self::STATE_*
*/
public $state = self::STATE_BEFORE_VERSION_NAME;
/**
* Input data
*
* @access private
* @var string
*/
public $data = '';
/**
* Input data length (to avoid calling strlen() everytime this is needed)
*
* @access private
* @var int
*/
public $data_length = 0;
/**
* Current position of the pointer
*
* @var int
* @access private
*/
public $position = 0;
/**
* Create an instance of the class with the input data
*
* @access public
* @param string $data Input data
*/
public function __construct($data)
{
$this->data = $data;
$this->data_length = strlen($this->data);
}
/**
* Parse the input data
*
* @access public
* @return bool true on success, false on failure
*/
public function parse()
{
while ($this->state && $this->state !== self::STATE_EMIT && $this->has_data()) {
$state = $this->state;
$this->$state();
}
$this->data = '';
if ($this->state === self::STATE_EMIT) {
return true;
}
$this->version = '';
$this->encoding = '';
$this->standalone = '';
return false;
}
/**
* Check whether there is data beyond the pointer
*
* @access private
* @return bool true if there is further data, false if not
*/
public function has_data()
{
return (bool) ($this->position < $this->data_length);
}
/**
* Advance past any whitespace
*
* @return int Number of whitespace characters passed
*/
public function skip_whitespace()
{
$whitespace = strspn($this->data, "\x09\x0A\x0D\x20", $this->position);
$this->position += $whitespace;
return $whitespace;
}
/**
* Read value
*/
public function get_value()
{
$quote = substr($this->data, $this->position, 1);
if ($quote === '"' || $quote === "'") {
$this->position++;
$len = strcspn($this->data, $quote, $this->position);
if ($this->has_data()) {
$value = substr($this->data, $this->position, $len);
$this->position += $len + 1;
return $value;
}
}
return false;
}
public function before_version_name()
{
if ($this->skip_whitespace()) {
$this->state = self::STATE_VERSION_NAME;
} else {
$this->state = self::STATE_ERROR;
}
}
public function version_name()
{
if (substr($this->data, $this->position, 7) === 'version') {
$this->position += 7;
$this->skip_whitespace();
$this->state = self::STATE_VERSION_EQUALS;
} else {
$this->state = self::STATE_ERROR;
}
}
public function version_equals()
{
if (substr($this->data, $this->position, 1) === '=') {
$this->position++;
$this->skip_whitespace();
$this->state = self::STATE_VERSION_VALUE;
} else {
$this->state = self::STATE_ERROR;
}
}
public function version_value()
{
if ($this->version = $this->get_value()) {
$this->skip_whitespace();
if ($this->has_data()) {
$this->state = self::STATE_ENCODING_NAME;
} else {
$this->state = self::STATE_EMIT;
}
} else {
$this->state = self::STATE_ERROR;
}
}
public function encoding_name()
{
if (substr($this->data, $this->position, 8) === 'encoding') {
$this->position += 8;
$this->skip_whitespace();
$this->state = self::STATE_ENCODING_EQUALS;
} else {
$this->state = self::STATE_STANDALONE_NAME;
}
}
public function encoding_equals()
{
if (substr($this->data, $this->position, 1) === '=') {
$this->position++;
$this->skip_whitespace();
$this->state = self::STATE_ENCODING_VALUE;
} else {
$this->state = self::STATE_ERROR;
}
}
public function encoding_value()
{
if ($this->encoding = $this->get_value()) {
$this->skip_whitespace();
if ($this->has_data()) {
$this->state = self::STATE_STANDALONE_NAME;
} else {
$this->state = self::STATE_EMIT;
}
} else {
$this->state = self::STATE_ERROR;
}
}
public function standalone_name()
{
if (substr($this->data, $this->position, 10) === 'standalone') {
$this->position += 10;
$this->skip_whitespace();
$this->state = self::STATE_STANDALONE_EQUALS;
} else {
$this->state = self::STATE_ERROR;
}
}
public function standalone_equals()
{
if (substr($this->data, $this->position, 1) === '=') {
$this->position++;
$this->skip_whitespace();
$this->state = self::STATE_STANDALONE_VALUE;
} else {
$this->state = self::STATE_ERROR;
}
}
public function standalone_value()
{
if ($standalone = $this->get_value()) {
switch ($standalone) {
case 'yes':
$this->standalone = true;
break;
case 'no':
$this->standalone = false;
break;
default:
$this->state = self::STATE_ERROR;
return;
}
$this->skip_whitespace();
if ($this->has_data()) {
$this->state = self::STATE_ERROR;
} else {
$this->state = self::STATE_EMIT;
}
} else {
$this->state = self::STATE_ERROR;
}
}
}
class_alias('SimplePie\XML\Declaration\Parser', 'SimplePie_XML_Declaration_Parser');
Caption.php 0000644 00000011517 15133170420 0006653 0 ustar 00 <?php
/**
* SimplePie
*
* A PHP-Based RSS and Atom Feed Framework.
* Takes the hard work out of managing a complete RSS/Atom solution.
*
* Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* * Neither the name of the SimplePie Team nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
* AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package SimplePie
* @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
* @author Ryan Parman
* @author Sam Sneddon
* @author Ryan McCue
* @link http://simplepie.org/ SimplePie
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
namespace SimplePie;
/**
* Handles `<media:text>` captions as defined in Media RSS.
*
* Used by {@see \SimplePie\Enclosure::get_caption()} and {@see \SimplePie\Enclosure::get_captions()}
*
* This class can be overloaded with {@see \SimplePie\SimplePie::set_caption_class()}
*
* @package SimplePie
* @subpackage API
*/
class Caption
{
/**
* Content type
*
* @var string
* @see get_type()
*/
public $type;
/**
* Language
*
* @var string
* @see get_language()
*/
public $lang;
/**
* Start time
*
* @var string
* @see get_starttime()
*/
public $startTime;
/**
* End time
*
* @var string
* @see get_endtime()
*/
public $endTime;
/**
* Caption text
*
* @var string
* @see get_text()
*/
public $text;
/**
* Constructor, used to input the data
*
* For documentation on all the parameters, see the corresponding
* properties and their accessors
*/
public function __construct($type = null, $lang = null, $startTime = null, $endTime = null, $text = null)
{
$this->type = $type;
$this->lang = $lang;
$this->startTime = $startTime;
$this->endTime = $endTime;
$this->text = $text;
}
/**
* String-ified version
*
* @return string
*/
public function __toString()
{
// There is no $this->data here
return md5(serialize($this));
}
/**
* Get the end time
*
* @return string|null Time in the format 'hh:mm:ss.SSS'
*/
public function get_endtime()
{
if ($this->endTime !== null) {
return $this->endTime;
}
return null;
}
/**
* Get the language
*
* @link http://tools.ietf.org/html/rfc3066
* @return string|null Language code as per RFC 3066
*/
public function get_language()
{
if ($this->lang !== null) {
return $this->lang;
}
return null;
}
/**
* Get the start time
*
* @return string|null Time in the format 'hh:mm:ss.SSS'
*/
public function get_starttime()
{
if ($this->startTime !== null) {
return $this->startTime;
}
return null;
}
/**
* Get the text of the caption
*
* @return string|null
*/
public function get_text()
{
if ($this->text !== null) {
return $this->text;
}
return null;
}
/**
* Get the content type (not MIME type)
*
* @return string|null Either 'text' or 'html'
*/
public function get_type()
{
if ($this->type !== null) {
return $this->type;
}
return null;
}
}
class_alias('SimplePie\Caption', 'SimplePie_Caption');
Locator.php 0000644 00000036455 15133170420 0006671 0 ustar 00 <?php
/**
* SimplePie
*
* A PHP-Based RSS and Atom Feed Framework.
* Takes the hard work out of managing a complete RSS/Atom solution.
*
* Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* * Neither the name of the SimplePie Team nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
* AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package SimplePie
* @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
* @author Ryan Parman
* @author Sam Sneddon
* @author Ryan McCue
* @link http://simplepie.org/ SimplePie
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
namespace SimplePie;
/**
* Used for feed auto-discovery
*
*
* This class can be overloaded with {@see \SimplePie\SimplePie::set_locator_class()}
*
* @package SimplePie
*/
class Locator implements RegistryAware
{
public $useragent;
public $timeout;
public $file;
public $local = [];
public $elsewhere = [];
public $cached_entities = [];
public $http_base;
public $base;
public $base_location = 0;
public $checked_feeds = 0;
public $max_checked_feeds = 10;
public $force_fsockopen = false;
public $curl_options = [];
public $dom;
protected $registry;
public function __construct(\SimplePie\File $file, $timeout = 10, $useragent = null, $max_checked_feeds = 10, $force_fsockopen = false, $curl_options = [])
{
$this->file = $file;
$this->useragent = $useragent;
$this->timeout = $timeout;
$this->max_checked_feeds = $max_checked_feeds;
$this->force_fsockopen = $force_fsockopen;
$this->curl_options = $curl_options;
if (class_exists('DOMDocument') && $this->file->body != '') {
$this->dom = new \DOMDocument();
set_error_handler(['SimplePie\Misc', 'silence_errors']);
try {
$this->dom->loadHTML($this->file->body);
} catch (\Throwable $ex) {
$this->dom = null;
}
restore_error_handler();
} else {
$this->dom = null;
}
}
public function set_registry(\SimplePie\Registry $registry)/* : void */
{
$this->registry = $registry;
}
public function find($type = \SimplePie\SimplePie::LOCATOR_ALL, &$working = null)
{
if ($this->is_feed($this->file)) {
return $this->file;
}
if ($this->file->method & \SimplePie\SimplePie::FILE_SOURCE_REMOTE) {
$sniffer = $this->registry->create(Content\Type\Sniffer::class, [$this->file]);
if ($sniffer->get_type() !== 'text/html') {
return null;
}
}
if ($type & ~\SimplePie\SimplePie::LOCATOR_NONE) {
$this->get_base();
}
if ($type & \SimplePie\SimplePie::LOCATOR_AUTODISCOVERY && $working = $this->autodiscovery()) {
return $working[0];
}
if ($type & (\SimplePie\SimplePie::LOCATOR_LOCAL_EXTENSION | \SimplePie\SimplePie::LOCATOR_LOCAL_BODY | \SimplePie\SimplePie::LOCATOR_REMOTE_EXTENSION | \SimplePie\SimplePie::LOCATOR_REMOTE_BODY) && $this->get_links()) {
if ($type & \SimplePie\SimplePie::LOCATOR_LOCAL_EXTENSION && $working = $this->extension($this->local)) {
return $working[0];
}
if ($type & \SimplePie\SimplePie::LOCATOR_LOCAL_BODY && $working = $this->body($this->local)) {
return $working[0];
}
if ($type & \SimplePie\SimplePie::LOCATOR_REMOTE_EXTENSION && $working = $this->extension($this->elsewhere)) {
return $working[0];
}
if ($type & \SimplePie\SimplePie::LOCATOR_REMOTE_BODY && $working = $this->body($this->elsewhere)) {
return $working[0];
}
}
return null;
}
public function is_feed($file, $check_html = false)
{
if ($file->method & \SimplePie\SimplePie::FILE_SOURCE_REMOTE) {
$sniffer = $this->registry->create(Content\Type\Sniffer::class, [$file]);
$sniffed = $sniffer->get_type();
$mime_types = ['application/rss+xml', 'application/rdf+xml',
'text/rdf', 'application/atom+xml', 'text/xml',
'application/xml', 'application/x-rss+xml'];
if ($check_html) {
$mime_types[] = 'text/html';
}
return in_array($sniffed, $mime_types);
} elseif ($file->method & \SimplePie\SimplePie::FILE_SOURCE_LOCAL) {
return true;
} else {
return false;
}
}
public function get_base()
{
if ($this->dom === null) {
throw new \SimplePie\Exception('DOMDocument not found, unable to use locator');
}
$this->http_base = $this->file->url;
$this->base = $this->http_base;
$elements = $this->dom->getElementsByTagName('base');
foreach ($elements as $element) {
if ($element->hasAttribute('href')) {
$base = $this->registry->call(Misc::class, 'absolutize_url', [trim($element->getAttribute('href')), $this->http_base]);
if ($base === false) {
continue;
}
$this->base = $base;
$this->base_location = method_exists($element, 'getLineNo') ? $element->getLineNo() : 0;
break;
}
}
}
public function autodiscovery()
{
$done = [];
$feeds = [];
$feeds = array_merge($feeds, $this->search_elements_by_tag('link', $done, $feeds));
$feeds = array_merge($feeds, $this->search_elements_by_tag('a', $done, $feeds));
$feeds = array_merge($feeds, $this->search_elements_by_tag('area', $done, $feeds));
if (!empty($feeds)) {
return array_values($feeds);
}
return null;
}
protected function search_elements_by_tag($name, &$done, $feeds)
{
if ($this->dom === null) {
throw new \SimplePie\Exception('DOMDocument not found, unable to use locator');
}
$links = $this->dom->getElementsByTagName($name);
foreach ($links as $link) {
if ($this->checked_feeds === $this->max_checked_feeds) {
break;
}
if ($link->hasAttribute('href') && $link->hasAttribute('rel')) {
$rel = array_unique($this->registry->call(Misc::class, 'space_separated_tokens', [strtolower($link->getAttribute('rel'))]));
$line = method_exists($link, 'getLineNo') ? $link->getLineNo() : 1;
if ($this->base_location < $line) {
$href = $this->registry->call(Misc::class, 'absolutize_url', [trim($link->getAttribute('href')), $this->base]);
} else {
$href = $this->registry->call(Misc::class, 'absolutize_url', [trim($link->getAttribute('href')), $this->http_base]);
}
if ($href === false) {
continue;
}
if (!in_array($href, $done) && in_array('feed', $rel) || (in_array('alternate', $rel) && !in_array('stylesheet', $rel) && $link->hasAttribute('type') && in_array(strtolower($this->registry->call(Misc::class, 'parse_mime', [$link->getAttribute('type')])), ['text/html', 'application/rss+xml', 'application/atom+xml'])) && !isset($feeds[$href])) {
$this->checked_feeds++;
$headers = [
'Accept' => 'application/atom+xml, application/rss+xml, application/rdf+xml;q=0.9, application/xml;q=0.8, text/xml;q=0.8, text/html;q=0.7, unknown/unknown;q=0.1, application/unknown;q=0.1, */*;q=0.1',
];
$feed = $this->registry->create(File::class, [$href, $this->timeout, 5, $headers, $this->useragent, $this->force_fsockopen, $this->curl_options]);
if ($feed->success && ($feed->method & \SimplePie\SimplePie::FILE_SOURCE_REMOTE === 0 || ($feed->status_code === 200 || $feed->status_code > 206 && $feed->status_code < 300)) && $this->is_feed($feed, true)) {
$feeds[$href] = $feed;
}
}
$done[] = $href;
}
}
return $feeds;
}
public function get_links()
{
if ($this->dom === null) {
throw new \SimplePie\Exception('DOMDocument not found, unable to use locator');
}
$links = $this->dom->getElementsByTagName('a');
foreach ($links as $link) {
if ($link->hasAttribute('href')) {
$href = trim($link->getAttribute('href'));
$parsed = $this->registry->call(Misc::class, 'parse_url', [$href]);
if ($parsed['scheme'] === '' || preg_match('/^(https?|feed)?$/i', $parsed['scheme'])) {
if (method_exists($link, 'getLineNo') && $this->base_location < $link->getLineNo()) {
$href = $this->registry->call(Misc::class, 'absolutize_url', [trim($link->getAttribute('href')), $this->base]);
} else {
$href = $this->registry->call(Misc::class, 'absolutize_url', [trim($link->getAttribute('href')), $this->http_base]);
}
if ($href === false) {
continue;
}
$current = $this->registry->call(Misc::class, 'parse_url', [$this->file->url]);
if ($parsed['authority'] === '' || $parsed['authority'] === $current['authority']) {
$this->local[] = $href;
} else {
$this->elsewhere[] = $href;
}
}
}
}
$this->local = array_unique($this->local);
$this->elsewhere = array_unique($this->elsewhere);
if (!empty($this->local) || !empty($this->elsewhere)) {
return true;
}
return null;
}
public function get_rel_link($rel)
{
if ($this->dom === null) {
throw new \SimplePie\Exception('DOMDocument not found, unable to use '.
'locator');
}
if (!class_exists('DOMXpath')) {
throw new \SimplePie\Exception('DOMXpath not found, unable to use '.
'get_rel_link');
}
$xpath = new \DOMXpath($this->dom);
$query = '//a[@rel and @href] | //link[@rel and @href]';
foreach ($xpath->query($query) as $link) {
$href = trim($link->getAttribute('href'));
$parsed = $this->registry->call(Misc::class, 'parse_url', [$href]);
if ($parsed['scheme'] === '' ||
preg_match('/^https?$/i', $parsed['scheme'])) {
if (method_exists($link, 'getLineNo') &&
$this->base_location < $link->getLineNo()) {
$href = $this->registry->call(
Misc::class,
'absolutize_url',
[trim($link->getAttribute('href')), $this->base]
);
} else {
$href = $this->registry->call(
Misc::class,
'absolutize_url',
[trim($link->getAttribute('href')), $this->http_base]
);
}
if ($href === false) {
return null;
}
$rel_values = explode(' ', strtolower($link->getAttribute('rel')));
if (in_array($rel, $rel_values)) {
return $href;
}
}
}
return null;
}
public function extension(&$array)
{
foreach ($array as $key => $value) {
if ($this->checked_feeds === $this->max_checked_feeds) {
break;
}
if (in_array(strtolower(strrchr($value, '.')), ['.rss', '.rdf', '.atom', '.xml'])) {
$this->checked_feeds++;
$headers = [
'Accept' => 'application/atom+xml, application/rss+xml, application/rdf+xml;q=0.9, application/xml;q=0.8, text/xml;q=0.8, text/html;q=0.7, unknown/unknown;q=0.1, application/unknown;q=0.1, */*;q=0.1',
];
$feed = $this->registry->create(File::class, [$value, $this->timeout, 5, $headers, $this->useragent, $this->force_fsockopen, $this->curl_options]);
if ($feed->success && ($feed->method & \SimplePie\SimplePie::FILE_SOURCE_REMOTE === 0 || ($feed->status_code === 200 || $feed->status_code > 206 && $feed->status_code < 300)) && $this->is_feed($feed)) {
return [$feed];
} else {
unset($array[$key]);
}
}
}
return null;
}
public function body(&$array)
{
foreach ($array as $key => $value) {
if ($this->checked_feeds === $this->max_checked_feeds) {
break;
}
if (preg_match('/(feed|rss|rdf|atom|xml)/i', $value)) {
$this->checked_feeds++;
$headers = [
'Accept' => 'application/atom+xml, application/rss+xml, application/rdf+xml;q=0.9, application/xml;q=0.8, text/xml;q=0.8, text/html;q=0.7, unknown/unknown;q=0.1, application/unknown;q=0.1, */*;q=0.1',
];
$feed = $this->registry->create(File::class, [$value, $this->timeout, 5, null, $this->useragent, $this->force_fsockopen, $this->curl_options]);
if ($feed->success && ($feed->method & \SimplePie\SimplePie::FILE_SOURCE_REMOTE === 0 || ($feed->status_code === 200 || $feed->status_code > 206 && $feed->status_code < 300)) && $this->is_feed($feed)) {
return [$feed];
} else {
unset($array[$key]);
}
}
}
return null;
}
}
class_alias('SimplePie\Locator', 'SimplePie_Locator', false);
Cache/Redis.php 0000644 00000013665 15133170420 0007335 0 ustar 00 <?php
/**
* SimplePie
*
* A PHP-Based RSS and Atom Feed Framework.
* Takes the hard work out of managing a complete RSS/Atom solution.
*
* Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* * Neither the name of the SimplePie Team nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
* AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package SimplePie
* @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
* @author Ryan Parman
* @author Sam Sneddon
* @author Ryan McCue
* @link http://simplepie.org/ SimplePie
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
namespace SimplePie\Cache;
use Redis as NativeRedis;
/**
* Caches data to redis
*
* Registered for URLs with the "redis" protocol
*
* For example, `redis://localhost:6379/?timeout=3600&prefix=sp_&dbIndex=0` will
* connect to redis on `localhost` on port 6379. All tables will be
* prefixed with `simple_primary-` and data will expire after 3600 seconds
*
* @package SimplePie
* @subpackage Caching
* @uses Redis
* @deprecated since SimplePie 1.8.0, use implementation of "Psr\SimpleCache\CacheInterface" instead
*/
class Redis implements Base
{
/**
* Redis instance
*
* @var NativeRedis
*/
protected $cache;
/**
* Options
*
* @var array
*/
protected $options;
/**
* Cache name
*
* @var string
*/
protected $name;
/**
* Create a new cache object
*
* @param string $location Location string (from SimplePie::$cache_location)
* @param string $name Unique ID for the cache
* @param Base::TYPE_FEED|Base::TYPE_IMAGE $type Either TYPE_FEED for SimplePie data, or TYPE_IMAGE for image data
*/
public function __construct($location, $name, $options = null)
{
//$this->cache = \flow\simple\cache\Redis::getRedisClientInstance();
$parsed = \SimplePie\Cache::parse_URL($location);
$redis = new NativeRedis();
$redis->connect($parsed['host'], $parsed['port']);
if (isset($parsed['pass'])) {
$redis->auth($parsed['pass']);
}
if (isset($parsed['path'])) {
$redis->select((int)substr($parsed['path'], 1));
}
$this->cache = $redis;
if (!is_null($options) && is_array($options)) {
$this->options = $options;
} else {
$this->options = [
'prefix' => 'rss:simple_primary:',
'expire' => 0,
];
}
$this->name = $this->options['prefix'] . $name;
}
/**
* @param NativeRedis $cache
*/
public function setRedisClient(NativeRedis $cache)
{
$this->cache = $cache;
}
/**
* Save data to the cache
*
* @param array|\SimplePie\SimplePie $data Data to store in the cache. If passed a SimplePie object, only cache the $data property
* @return bool Successfulness
*/
public function save($data)
{
if ($data instanceof \SimplePie\SimplePie) {
$data = $data->data;
}
$response = $this->cache->set($this->name, serialize($data));
if ($this->options['expire']) {
$this->cache->expire($this->name, $this->options['expire']);
}
return $response;
}
/**
* Retrieve the data saved to the cache
*
* @return array Data for SimplePie::$data
*/
public function load()
{
$data = $this->cache->get($this->name);
if ($data !== false) {
return unserialize($data);
}
return false;
}
/**
* Retrieve the last modified time for the cache
*
* @return int Timestamp
*/
public function mtime()
{
$data = $this->cache->get($this->name);
if ($data !== false) {
return time();
}
return false;
}
/**
* Set the last modified time to the current time
*
* @return bool Success status
*/
public function touch()
{
$data = $this->cache->get($this->name);
if ($data !== false) {
$return = $this->cache->set($this->name, $data);
if ($this->options['expire']) {
return $this->cache->expire($this->name, $this->options['expire']);
}
return $return;
}
return false;
}
/**
* Remove the cache
*
* @return bool Success status
*/
public function unlink()
{
return $this->cache->set($this->name, null);
}
}
class_alias('SimplePie\Cache\Redis', 'SimplePie_Cache_Redis');
Cache/Memcache.php 0000644 00000012717 15133170420 0007766 0 ustar 00 <?php
/**
* SimplePie
*
* A PHP-Based RSS and Atom Feed Framework.
* Takes the hard work out of managing a complete RSS/Atom solution.
*
* Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* * Neither the name of the SimplePie Team nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
* AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package SimplePie
* @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
* @author Ryan Parman
* @author Sam Sneddon
* @author Ryan McCue
* @link http://simplepie.org/ SimplePie
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
namespace SimplePie\Cache;
use Memcache as NativeMemcache;
/**
* Caches data to memcache
*
* Registered for URLs with the "memcache" protocol
*
* For example, `memcache://localhost:11211/?timeout=3600&prefix=sp_` will
* connect to memcache on `localhost` on port 11211. All tables will be
* prefixed with `sp_` and data will expire after 3600 seconds
*
* @package SimplePie
* @subpackage Caching
* @uses Memcache
* @deprecated since SimplePie 1.8.0, use implementation of "Psr\SimpleCache\CacheInterface" instead
*/
class Memcache implements Base
{
/**
* Memcache instance
*
* @var Memcache
*/
protected $cache;
/**
* Options
*
* @var array
*/
protected $options;
/**
* Cache name
*
* @var string
*/
protected $name;
/**
* Create a new cache object
*
* @param string $location Location string (from SimplePie::$cache_location)
* @param string $name Unique ID for the cache
* @param Base::TYPE_FEED|Base::TYPE_IMAGE $type Either TYPE_FEED for SimplePie data, or TYPE_IMAGE for image data
*/
public function __construct($location, $name, $type)
{
$this->options = [
'host' => '127.0.0.1',
'port' => 11211,
'extras' => [
'timeout' => 3600, // one hour
'prefix' => 'simplepie_',
],
];
$this->options = array_replace_recursive($this->options, \SimplePie\Cache::parse_URL($location));
$this->name = $this->options['extras']['prefix'] . md5("$name:$type");
$this->cache = new NativeMemcache();
$this->cache->addServer($this->options['host'], (int) $this->options['port']);
}
/**
* Save data to the cache
*
* @param array|\SimplePie\SimplePie $data Data to store in the cache. If passed a SimplePie object, only cache the $data property
* @return bool Successfulness
*/
public function save($data)
{
if ($data instanceof \SimplePie\SimplePie) {
$data = $data->data;
}
return $this->cache->set($this->name, serialize($data), MEMCACHE_COMPRESSED, (int) $this->options['extras']['timeout']);
}
/**
* Retrieve the data saved to the cache
*
* @return array Data for SimplePie::$data
*/
public function load()
{
$data = $this->cache->get($this->name);
if ($data !== false) {
return unserialize($data);
}
return false;
}
/**
* Retrieve the last modified time for the cache
*
* @return int Timestamp
*/
public function mtime()
{
$data = $this->cache->get($this->name);
if ($data !== false) {
// essentially ignore the mtime because Memcache expires on its own
return time();
}
return false;
}
/**
* Set the last modified time to the current time
*
* @return bool Success status
*/
public function touch()
{
$data = $this->cache->get($this->name);
if ($data !== false) {
return $this->cache->set($this->name, $data, MEMCACHE_COMPRESSED, (int) $this->options['extras']['timeout']);
}
return false;
}
/**
* Remove the cache
*
* @return bool Success status
*/
public function unlink()
{
return $this->cache->delete($this->name, 0);
}
}
class_alias('SimplePie\Cache\Memcache', 'SimplePie_Cache_Memcache');
Cache/Psr16.php 0000644 00000012023 15133170420 0007165 0 ustar 00 <?php
/**
* SimplePie
*
* A PHP-Based RSS and Atom Feed Framework.
* Takes the hard work out of managing a complete RSS/Atom solution.
*
* Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* * Neither the name of the SimplePie Team nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
* AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package SimplePie
* @copyright 2004-2022 Ryan Parman, Sam Sneddon, Ryan McCue
* @author Ryan Parman
* @author Sam Sneddon
* @author Ryan McCue
* @link http://simplepie.org/ SimplePie
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
namespace SimplePie\Cache;
use Psr\SimpleCache\CacheInterface;
use Psr\SimpleCache\InvalidArgumentException;
/**
* Caches data into a PSR-16 cache implementation
*
* @package SimplePie
* @subpackage Caching
* @internal
*/
final class Psr16 implements DataCache
{
/**
* PSR-16 cache implementation
*
* @var CacheInterface
*/
private $cache;
/**
* PSR-16 cache implementation
*
* @param CacheInterface $cache
*/
public function __construct(CacheInterface $cache)
{
$this->cache = $cache;
}
/**
* Fetches a value from the cache.
*
* Equivalent to \Psr\SimpleCache\CacheInterface::get()
* <code>
* public function get(string $key, mixed $default = null): mixed;
* </code>
*
* @param string $key The unique key of this item in the cache.
* @param mixed $default Default value to return if the key does not exist.
*
* @return array|mixed The value of the item from the cache, or $default in case of cache miss.
*
* @throws InvalidArgumentException
* MUST be thrown if the $key string is not a legal value.
*/
public function get_data(string $key, $default = null)
{
$data = $this->cache->get($key, $default);
if (!is_array($data) || $data === $default) {
return $default;
}
return $data;
}
/**
* Persists data in the cache, uniquely referenced by a key with an optional expiration TTL time.
*
* Equivalent to \Psr\SimpleCache\CacheInterface::set()
* <code>
* public function set(string $key, mixed $value, null|int|\DateInterval $ttl = null): bool;
* </code>
*
* @param string $key The key of the item to store.
* @param array $value The value of the item to store, must be serializable.
* @param null|int $ttl Optional. The TTL value of this item. If no value is sent and
* the driver supports TTL then the library may set a default value
* for it or let the driver take care of that.
*
* @return bool True on success and false on failure.
*
* @throws InvalidArgumentException
* MUST be thrown if the $key string is not a legal value.
*/
public function set_data(string $key, array $value, ?int $ttl = null): bool
{
return $this->cache->set($key, $value, $ttl);
}
/**
* Delete an item from the cache by its unique key.
*
* Equivalent to \Psr\SimpleCache\CacheInterface::delete()
* <code>
* public function delete(string $key): bool;
* </code>
*
* @param string $key The unique cache key of the item to delete.
*
* @return bool True if the item was successfully removed. False if there was an error.
*
* @throws InvalidArgumentException
* MUST be thrown if the $key string is not a legal value.
*/
public function delete_data(string $key): bool
{
return $this->cache->delete($key);
}
}
Cache/Memcached.php 0000644 00000013033 15133170420 0010122 0 ustar 00 <?php
/**
* SimplePie
*
* A PHP-Based RSS and Atom Feed Framework.
* Takes the hard work out of managing a complete RSS/Atom solution.
*
* Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* * Neither the name of the SimplePie Team nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
* AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package SimplePie
* @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
* @author Ryan Parman
* @author Sam Sneddon
* @author Ryan McCue
* @link http://simplepie.org/ SimplePie
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
namespace SimplePie\Cache;
use Memcached as NativeMemcached;
/**
* Caches data to memcached
*
* Registered for URLs with the "memcached" protocol
*
* For example, `memcached://localhost:11211/?timeout=3600&prefix=sp_` will
* connect to memcached on `localhost` on port 11211. All tables will be
* prefixed with `sp_` and data will expire after 3600 seconds
*
* @package SimplePie
* @subpackage Caching
* @author Paul L. McNeely
* @uses Memcached
* @deprecated since SimplePie 1.8.0, use implementation of "Psr\SimpleCache\CacheInterface" instead
*/
class Memcached implements Base
{
/**
* NativeMemcached instance
* @var NativeMemcached
*/
protected $cache;
/**
* Options
* @var array
*/
protected $options;
/**
* Cache name
* @var string
*/
protected $name;
/**
* Create a new cache object
* @param string $location Location string (from SimplePie::$cache_location)
* @param string $name Unique ID for the cache
* @param Base::TYPE_FEED|Base::TYPE_IMAGE $type Either TYPE_FEED for SimplePie data, or TYPE_IMAGE for image data
*/
public function __construct($location, $name, $type)
{
$this->options = [
'host' => '127.0.0.1',
'port' => 11211,
'extras' => [
'timeout' => 3600, // one hour
'prefix' => 'simplepie_',
],
];
$this->options = array_replace_recursive($this->options, \SimplePie\Cache::parse_URL($location));
$this->name = $this->options['extras']['prefix'] . md5("$name:$type");
$this->cache = new NativeMemcached();
$this->cache->addServer($this->options['host'], (int)$this->options['port']);
}
/**
* Save data to the cache
* @param array|\SimplePie\SimplePie $data Data to store in the cache. If passed a SimplePie object, only cache the $data property
* @return bool Successfulness
*/
public function save($data)
{
if ($data instanceof \SimplePie\SimplePie) {
$data = $data->data;
}
return $this->setData(serialize($data));
}
/**
* Retrieve the data saved to the cache
* @return array Data for SimplePie::$data
*/
public function load()
{
$data = $this->cache->get($this->name);
if ($data !== false) {
return unserialize($data);
}
return false;
}
/**
* Retrieve the last modified time for the cache
* @return int Timestamp
*/
public function mtime()
{
$data = $this->cache->get($this->name . '_mtime');
return (int) $data;
}
/**
* Set the last modified time to the current time
* @return bool Success status
*/
public function touch()
{
$data = $this->cache->get($this->name);
return $this->setData($data);
}
/**
* Remove the cache
* @return bool Success status
*/
public function unlink()
{
return $this->cache->delete($this->name, 0);
}
/**
* Set the last modified time and data to NativeMemcached
* @return bool Success status
*/
private function setData($data)
{
if ($data !== false) {
$this->cache->set($this->name . '_mtime', time(), (int)$this->options['extras']['timeout']);
return $this->cache->set($this->name, $data, (int)$this->options['extras']['timeout']);
}
return false;
}
}
class_alias('SimplePie\Cache\Memcached', 'SimplePie_Cache_Memcached');
Cache/CallableNameFilter.php 0000644 00000006452 15133170420 0011731 0 ustar 00 <?php
/**
* SimplePie
*
* A PHP-Based RSS and Atom Feed Framework.
* Takes the hard work out of managing a complete RSS/Atom solution.
*
* Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* * Neither the name of the SimplePie Team nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
* AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package SimplePie
* @copyright 2004-2022 Ryan Parman, Sam Sneddon, Ryan McCue
* @author Ryan Parman
* @author Sam Sneddon
* @author Ryan McCue
* @link http://simplepie.org/ SimplePie
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
namespace SimplePie\Cache;
/**
* Creating a cache filename with callables
*
* @package SimplePie
* @subpackage Caching
*/
final class CallableNameFilter implements NameFilter
{
/**
* @var callable
*/
private $callable;
public function __construct(callable $callable)
{
$this->callable = $callable;
}
/**
* Method to create cache filename with.
*
* The returning name MUST follow the rules for keys in PSR-16.
*
* @link https://www.php-fig.org/psr/psr-16/
*
* The returning name MUST be a string of at least one character
* that uniquely identifies a cached item, MUST only contain the
* characters A-Z, a-z, 0-9, _, and . in any order in UTF-8 encoding
* and MUST not longer then 64 characters. The following characters
* are reserved for future extensions and MUST NOT be used: {}()/\@:
*
* A provided implementing library MAY support additional characters
* and encodings or longer lengths, but MUST support at least that
* minimum.
*
* @param string $name The name for the cache will be most likly an url with query string
*
* @return string the new cache name
*/
public function filter(string $name): string
{
return call_user_func($this->callable, $name);
}
}
Cache/DB.php 0000644 00000012676 15133170420 0006555 0 ustar 00 <?php
/**
* SimplePie
*
* A PHP-Based RSS and Atom Feed Framework.
* Takes the hard work out of managing a complete RSS/Atom solution.
*
* Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* * Neither the name of the SimplePie Team nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
* AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package SimplePie
* @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
* @author Ryan Parman
* @author Sam Sneddon
* @author Ryan McCue
* @link http://simplepie.org/ SimplePie
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
namespace SimplePie\Cache;
/**
* Base class for database-based caches
*
* @package SimplePie
* @subpackage Caching
* @deprecated since SimplePie 1.8.0, use implementation of "Psr\SimpleCache\CacheInterface" instead
*/
abstract class DB implements Base
{
/**
* Helper for database conversion
*
* Converts a given {@see SimplePie} object into data to be stored
*
* @param \SimplePie\SimplePie $data
* @return array First item is the serialized data for storage, second item is the unique ID for this item
*/
protected static function prepare_simplepie_object_for_cache($data)
{
$items = $data->get_items();
$items_by_id = [];
if (!empty($items)) {
foreach ($items as $item) {
$items_by_id[$item->get_id()] = $item;
}
if (count($items_by_id) !== count($items)) {
$items_by_id = [];
foreach ($items as $item) {
$items_by_id[$item->get_id(true)] = $item;
}
}
if (isset($data->data['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_10]['feed'][0])) {
$channel = &$data->data['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_10]['feed'][0];
} elseif (isset($data->data['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_03]['feed'][0])) {
$channel = &$data->data['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_03]['feed'][0];
} elseif (isset($data->data['child'][\SimplePie\SimplePie::NAMESPACE_RDF]['RDF'][0])) {
$channel = &$data->data['child'][\SimplePie\SimplePie::NAMESPACE_RDF]['RDF'][0];
} elseif (isset($data->data['child'][\SimplePie\SimplePie::NAMESPACE_RSS_20]['rss'][0]['child'][\SimplePie\SimplePie::NAMESPACE_RSS_20]['channel'][0])) {
$channel = &$data->data['child'][\SimplePie\SimplePie::NAMESPACE_RSS_20]['rss'][0]['child'][\SimplePie\SimplePie::NAMESPACE_RSS_20]['channel'][0];
} else {
$channel = null;
}
if ($channel !== null) {
if (isset($channel['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_10]['entry'])) {
unset($channel['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_10]['entry']);
}
if (isset($channel['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_03]['entry'])) {
unset($channel['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_03]['entry']);
}
if (isset($channel['child'][\SimplePie\SimplePie::NAMESPACE_RSS_10]['item'])) {
unset($channel['child'][\SimplePie\SimplePie::NAMESPACE_RSS_10]['item']);
}
if (isset($channel['child'][\SimplePie\SimplePie::NAMESPACE_RSS_090]['item'])) {
unset($channel['child'][\SimplePie\SimplePie::NAMESPACE_RSS_090]['item']);
}
if (isset($channel['child'][\SimplePie\SimplePie::NAMESPACE_RSS_20]['item'])) {
unset($channel['child'][\SimplePie\SimplePie::NAMESPACE_RSS_20]['item']);
}
}
if (isset($data->data['items'])) {
unset($data->data['items']);
}
if (isset($data->data['ordered_items'])) {
unset($data->data['ordered_items']);
}
}
return [serialize($data->data), $items_by_id];
}
}
class_alias('SimplePie\Cache\DB', 'SimplePie_Cache_DB');
Cache/Base.php 0000644 00000007306 15133170420 0007134 0 ustar 00 <?php
/**
* SimplePie
*
* A PHP-Based RSS and Atom Feed Framework.
* Takes the hard work out of managing a complete RSS/Atom solution.
*
* Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* * Neither the name of the SimplePie Team nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
* AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package SimplePie
* @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
* @author Ryan Parman
* @author Sam Sneddon
* @author Ryan McCue
* @link http://simplepie.org/ SimplePie
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
namespace SimplePie\Cache;
/**
* Base for cache objects
*
* Classes to be used with {@see \SimplePie\Cache::register()} are expected
* to implement this interface.
*
* @package SimplePie
* @subpackage Caching
* @deprecated since SimplePie 1.8.0, use "Psr\SimpleCache\CacheInterface" instead
*/
interface Base
{
/**
* Feed cache type
*
* @var string
*/
public const TYPE_FEED = 'spc';
/**
* Image cache type
*
* @var string
*/
public const TYPE_IMAGE = 'spi';
/**
* Create a new cache object
*
* @param string $location Location string (from SimplePie::$cache_location)
* @param string $name Unique ID for the cache
* @param Base::TYPE_FEED|Base::TYPE_IMAGE $type Either TYPE_FEED for SimplePie data, or TYPE_IMAGE for image data
*/
public function __construct($location, $name, $type);
/**
* Save data to the cache
*
* @param array|\SimplePie\SimplePie $data Data to store in the cache. If passed a SimplePie object, only cache the $data property
* @return bool Successfulness
*/
public function save($data);
/**
* Retrieve the data saved to the cache
*
* @return array Data for SimplePie::$data
*/
public function load();
/**
* Retrieve the last modified time for the cache
*
* @return int Timestamp
*/
public function mtime();
/**
* Set the last modified time to the current time
*
* @return bool Success status
*/
public function touch();
/**
* Remove the cache
*
* @return bool Success status
*/
public function unlink();
}
class_alias('SimplePie\Cache\Base', 'SimplePie_Cache_Base');
Cache/File.php 0000644 00000011374 15133170420 0007141 0 ustar 00 <?php
/**
* SimplePie
*
* A PHP-Based RSS and Atom Feed Framework.
* Takes the hard work out of managing a complete RSS/Atom solution.
*
* Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* * Neither the name of the SimplePie Team nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
* AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package SimplePie
* @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
* @author Ryan Parman
* @author Sam Sneddon
* @author Ryan McCue
* @link http://simplepie.org/ SimplePie
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
namespace SimplePie\Cache;
/**
* Caches data to the filesystem
*
* @package SimplePie
* @subpackage Caching
* @deprecated since SimplePie 1.8.0, use implementation of "Psr\SimpleCache\CacheInterface" instead
*/
class File implements Base
{
/**
* Location string
*
* @see SimplePie::$cache_location
* @var string
*/
protected $location;
/**
* Filename
*
* @var string
*/
protected $filename;
/**
* File extension
*
* @var string
*/
protected $extension;
/**
* File path
*
* @var string
*/
protected $name;
/**
* Create a new cache object
*
* @param string $location Location string (from SimplePie::$cache_location)
* @param string $name Unique ID for the cache
* @param Base::TYPE_FEED|Base::TYPE_IMAGE $type Either TYPE_FEED for SimplePie data, or TYPE_IMAGE for image data
*/
public function __construct($location, $name, $type)
{
$this->location = $location;
$this->filename = $name;
$this->extension = $type;
$this->name = "$this->location/$this->filename.$this->extension";
}
/**
* Save data to the cache
*
* @param array|\SimplePie\SimplePie $data Data to store in the cache. If passed a SimplePie object, only cache the $data property
* @return bool Successfulness
*/
public function save($data)
{
if (file_exists($this->name) && is_writable($this->name) || file_exists($this->location) && is_writable($this->location)) {
if ($data instanceof \SimplePie\SimplePie) {
$data = $data->data;
}
$data = serialize($data);
return (bool) file_put_contents($this->name, $data);
}
return false;
}
/**
* Retrieve the data saved to the cache
*
* @return array Data for SimplePie::$data
*/
public function load()
{
if (file_exists($this->name) && is_readable($this->name)) {
return unserialize(file_get_contents($this->name));
}
return false;
}
/**
* Retrieve the last modified time for the cache
*
* @return int Timestamp
*/
public function mtime()
{
return @filemtime($this->name);
}
/**
* Set the last modified time to the current time
*
* @return bool Success status
*/
public function touch()
{
return @touch($this->name);
}
/**
* Remove the cache
*
* @return bool Success status
*/
public function unlink()
{
if (file_exists($this->name)) {
return unlink($this->name);
}
return false;
}
}
class_alias('SimplePie\Cache\File', 'SimplePie_Cache_File');
Cache/DataCache.php 0000644 00000011167 15133170420 0010057 0 ustar 00 <?php
/**
* SimplePie
*
* A PHP-Based RSS and Atom Feed Framework.
* Takes the hard work out of managing a complete RSS/Atom solution.
*
* Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* * Neither the name of the SimplePie Team nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
* AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package SimplePie
* @copyright 2004-2022 Ryan Parman, Sam Sneddon, Ryan McCue
* @author Ryan Parman
* @author Sam Sneddon
* @author Ryan McCue
* @link http://simplepie.org/ SimplePie
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
namespace SimplePie\Cache;
use InvalidArgumentException;
/**
* Subset of PSR-16 Cache client for caching data arrays
*
* Only get(), set() and delete() methods are used,
* but not has(), getMultiple(), setMultiple() or deleteMultiple().
*
* The methods names must be different, but should be compatible to the
* methods of \Psr\SimpleCache\CacheInterface.
*
* @package SimplePie
* @subpackage Caching
* @internal
*/
interface DataCache
{
/**
* Fetches a value from the cache.
*
* Equivalent to \Psr\SimpleCache\CacheInterface::get()
* <code>
* public function get(string $key, mixed $default = null): mixed;
* </code>
*
* @param string $key The unique key of this item in the cache.
* @param mixed $default Default value to return if the key does not exist.
*
* @return array|mixed The value of the item from the cache, or $default in case of cache miss.
*
* @throws InvalidArgumentException
* MUST be thrown if the $key string is not a legal value.
*/
public function get_data(string $key, $default = null);
/**
* Persists data in the cache, uniquely referenced by a key with an optional expiration TTL time.
*
* Equivalent to \Psr\SimpleCache\CacheInterface::set()
* <code>
* public function set(string $key, mixed $value, null|int|\DateInterval $ttl = null): bool;
* </code>
*
* @param string $key The key of the item to store.
* @param array $value The value of the item to store, must be serializable.
* @param null|int $ttl Optional. The TTL value of this item. If no value is sent and
* the driver supports TTL then the library may set a default value
* for it or let the driver take care of that.
*
* @return bool True on success and false on failure.
*
* @throws InvalidArgumentException
* MUST be thrown if the $key string is not a legal value.
*/
public function set_data(string $key, array $value, ?int $ttl = null): bool;
/**
* Delete an item from the cache by its unique key.
*
* Equivalent to \Psr\SimpleCache\CacheInterface::delete()
* <code>
* public function delete(string $key): bool;
* </code>
*
* @param string $key The unique cache key of the item to delete.
*
* @return bool True if the item was successfully removed. False if there was an error.
*
* @throws InvalidArgumentException
* MUST be thrown if the $key string is not a legal value.
*/
public function delete_data(string $key): bool;
}
Cache/MySQL.php 0000644 00000036306 15133170420 0007231 0 ustar 00 <?php
/**
* SimplePie
*
* A PHP-Based RSS and Atom Feed Framework.
* Takes the hard work out of managing a complete RSS/Atom solution.
*
* Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* * Neither the name of the SimplePie Team nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
* AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package SimplePie
* @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
* @author Ryan Parman
* @author Sam Sneddon
* @author Ryan McCue
* @link http://simplepie.org/ SimplePie
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
namespace SimplePie\Cache;
/**
* Caches data to a MySQL database
*
* Registered for URLs with the "mysql" protocol
*
* For example, `mysql://root:password@localhost:3306/mydb?prefix=sp_` will
* connect to the `mydb` database on `localhost` on port 3306, with the user
* `root` and the password `password`. All tables will be prefixed with `sp_`
*
* @package SimplePie
* @subpackage Caching
* @deprecated since SimplePie 1.8.0, use implementation of "Psr\SimpleCache\CacheInterface" instead
*/
class MySQL extends DB
{
/**
* PDO instance
*
* @var \PDO
*/
protected $mysql;
/**
* Options
*
* @var array
*/
protected $options;
/**
* Cache ID
*
* @var string
*/
protected $id;
/**
* Create a new cache object
*
* @param string $location Location string (from SimplePie::$cache_location)
* @param string $name Unique ID for the cache
* @param Base::TYPE_FEED|Base::TYPE_IMAGE $type Either TYPE_FEED for SimplePie data, or TYPE_IMAGE for image data
*/
public function __construct($location, $name, $type)
{
$this->options = [
'user' => null,
'pass' => null,
'host' => '127.0.0.1',
'port' => '3306',
'path' => '',
'extras' => [
'prefix' => '',
'cache_purge_time' => 2592000
],
];
$this->options = array_replace_recursive($this->options, \SimplePie\Cache::parse_URL($location));
// Path is prefixed with a "/"
$this->options['dbname'] = substr($this->options['path'], 1);
try {
$this->mysql = new \PDO("mysql:dbname={$this->options['dbname']};host={$this->options['host']};port={$this->options['port']}", $this->options['user'], $this->options['pass'], [\PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8']);
} catch (\PDOException $e) {
$this->mysql = null;
return;
}
$this->id = $name . $type;
if (!$query = $this->mysql->query('SHOW TABLES')) {
$this->mysql = null;
return;
}
$db = [];
while ($row = $query->fetchColumn()) {
$db[] = $row;
}
if (!in_array($this->options['extras']['prefix'] . 'cache_data', $db)) {
$query = $this->mysql->exec('CREATE TABLE `' . $this->options['extras']['prefix'] . 'cache_data` (`id` TEXT CHARACTER SET utf8 NOT NULL, `items` SMALLINT NOT NULL DEFAULT 0, `data` BLOB NOT NULL, `mtime` INT UNSIGNED NOT NULL, UNIQUE (`id`(125)))');
if ($query === false) {
trigger_error("Can't create " . $this->options['extras']['prefix'] . "cache_data table, check permissions", \E_USER_WARNING);
$this->mysql = null;
return;
}
}
if (!in_array($this->options['extras']['prefix'] . 'items', $db)) {
$query = $this->mysql->exec('CREATE TABLE `' . $this->options['extras']['prefix'] . 'items` (`feed_id` TEXT CHARACTER SET utf8 NOT NULL, `id` TEXT CHARACTER SET utf8 NOT NULL, `data` MEDIUMBLOB NOT NULL, `posted` INT UNSIGNED NOT NULL, INDEX `feed_id` (`feed_id`(125)))');
if ($query === false) {
trigger_error("Can't create " . $this->options['extras']['prefix'] . "items table, check permissions", \E_USER_WARNING);
$this->mysql = null;
return;
}
}
}
/**
* Save data to the cache
*
* @param array|\SimplePie\SimplePie $data Data to store in the cache. If passed a SimplePie object, only cache the $data property
* @return bool Successfulness
*/
public function save($data)
{
if ($this->mysql === null) {
return false;
}
$query = $this->mysql->prepare('DELETE i, cd FROM `' . $this->options['extras']['prefix'] . 'cache_data` cd, ' .
'`' . $this->options['extras']['prefix'] . 'items` i ' .
'WHERE cd.id = i.feed_id ' .
'AND cd.mtime < (unix_timestamp() - :purge_time)');
$query->bindValue(':purge_time', $this->options['extras']['cache_purge_time']);
if (!$query->execute()) {
return false;
}
if ($data instanceof \SimplePie\SimplePie) {
$data = clone $data;
$prepared = self::prepare_simplepie_object_for_cache($data);
$query = $this->mysql->prepare('SELECT COUNT(*) FROM `' . $this->options['extras']['prefix'] . 'cache_data` WHERE `id` = :feed');
$query->bindValue(':feed', $this->id);
if ($query->execute()) {
if ($query->fetchColumn() > 0) {
$items = count($prepared[1]);
if ($items) {
$sql = 'UPDATE `' . $this->options['extras']['prefix'] . 'cache_data` SET `items` = :items, `data` = :data, `mtime` = :time WHERE `id` = :feed';
$query = $this->mysql->prepare($sql);
$query->bindValue(':items', $items);
} else {
$sql = 'UPDATE `' . $this->options['extras']['prefix'] . 'cache_data` SET `data` = :data, `mtime` = :time WHERE `id` = :feed';
$query = $this->mysql->prepare($sql);
}
$query->bindValue(':data', $prepared[0]);
$query->bindValue(':time', time());
$query->bindValue(':feed', $this->id);
if (!$query->execute()) {
return false;
}
} else {
$query = $this->mysql->prepare('INSERT INTO `' . $this->options['extras']['prefix'] . 'cache_data` (`id`, `items`, `data`, `mtime`) VALUES(:feed, :count, :data, :time)');
$query->bindValue(':feed', $this->id);
$query->bindValue(':count', count($prepared[1]));
$query->bindValue(':data', $prepared[0]);
$query->bindValue(':time', time());
if (!$query->execute()) {
return false;
}
}
$ids = array_keys($prepared[1]);
if (!empty($ids)) {
foreach ($ids as $id) {
$database_ids[] = $this->mysql->quote($id);
}
$query = $this->mysql->prepare('SELECT `id` FROM `' . $this->options['extras']['prefix'] . 'items` WHERE `id` = ' . implode(' OR `id` = ', $database_ids) . ' AND `feed_id` = :feed');
$query->bindValue(':feed', $this->id);
if ($query->execute()) {
$existing_ids = [];
while ($row = $query->fetchColumn()) {
$existing_ids[] = $row;
}
$new_ids = array_diff($ids, $existing_ids);
foreach ($new_ids as $new_id) {
if (!($date = $prepared[1][$new_id]->get_date('U'))) {
$date = time();
}
$query = $this->mysql->prepare('INSERT INTO `' . $this->options['extras']['prefix'] . 'items` (`feed_id`, `id`, `data`, `posted`) VALUES(:feed, :id, :data, :date)');
$query->bindValue(':feed', $this->id);
$query->bindValue(':id', $new_id);
$query->bindValue(':data', serialize($prepared[1][$new_id]->data));
$query->bindValue(':date', $date);
if (!$query->execute()) {
return false;
}
}
return true;
}
} else {
return true;
}
}
} else {
$query = $this->mysql->prepare('SELECT `id` FROM `' . $this->options['extras']['prefix'] . 'cache_data` WHERE `id` = :feed');
$query->bindValue(':feed', $this->id);
if ($query->execute()) {
if ($query->rowCount() > 0) {
$query = $this->mysql->prepare('UPDATE `' . $this->options['extras']['prefix'] . 'cache_data` SET `items` = 0, `data` = :data, `mtime` = :time WHERE `id` = :feed');
$query->bindValue(':data', serialize($data));
$query->bindValue(':time', time());
$query->bindValue(':feed', $this->id);
if ($query->execute()) {
return true;
}
} else {
$query = $this->mysql->prepare('INSERT INTO `' . $this->options['extras']['prefix'] . 'cache_data` (`id`, `items`, `data`, `mtime`) VALUES(:id, 0, :data, :time)');
$query->bindValue(':id', $this->id);
$query->bindValue(':data', serialize($data));
$query->bindValue(':time', time());
if ($query->execute()) {
return true;
}
}
}
}
return false;
}
/**
* Retrieve the data saved to the cache
*
* @return array Data for SimplePie::$data
*/
public function load()
{
if ($this->mysql === null) {
return false;
}
$query = $this->mysql->prepare('SELECT `items`, `data` FROM `' . $this->options['extras']['prefix'] . 'cache_data` WHERE `id` = :id');
$query->bindValue(':id', $this->id);
if ($query->execute() && ($row = $query->fetch())) {
$data = unserialize($row[1]);
if (isset($this->options['items'][0])) {
$items = (int) $this->options['items'][0];
} else {
$items = (int) $row[0];
}
if ($items !== 0) {
if (isset($data['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_10]['feed'][0])) {
$feed = &$data['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_10]['feed'][0];
} elseif (isset($data['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_03]['feed'][0])) {
$feed = &$data['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_03]['feed'][0];
} elseif (isset($data['child'][\SimplePie\SimplePie::NAMESPACE_RDF]['RDF'][0])) {
$feed = &$data['child'][\SimplePie\SimplePie::NAMESPACE_RDF]['RDF'][0];
} elseif (isset($data['child'][\SimplePie\SimplePie::NAMESPACE_RSS_20]['rss'][0])) {
$feed = &$data['child'][\SimplePie\SimplePie::NAMESPACE_RSS_20]['rss'][0];
} else {
$feed = null;
}
if ($feed !== null) {
$sql = 'SELECT `data` FROM `' . $this->options['extras']['prefix'] . 'items` WHERE `feed_id` = :feed ORDER BY `posted` DESC';
if ($items > 0) {
$sql .= ' LIMIT ' . $items;
}
$query = $this->mysql->prepare($sql);
$query->bindValue(':feed', $this->id);
if ($query->execute()) {
while ($row = $query->fetchColumn()) {
$feed['child'][\SimplePie\SimplePie::NAMESPACE_ATOM_10]['entry'][] = unserialize($row);
}
} else {
return false;
}
}
}
return $data;
}
return false;
}
/**
* Retrieve the last modified time for the cache
*
* @return int Timestamp
*/
public function mtime()
{
if ($this->mysql === null) {
return false;
}
$query = $this->mysql->prepare('SELECT `mtime` FROM `' . $this->options['extras']['prefix'] . 'cache_data` WHERE `id` = :id');
$query->bindValue(':id', $this->id);
if ($query->execute() && ($time = $query->fetchColumn())) {
return $time;
}
return false;
}
/**
* Set the last modified time to the current time
*
* @return bool Success status
*/
public function touch()
{
if ($this->mysql === null) {
return false;
}
$query = $this->mysql->prepare('UPDATE `' . $this->options['extras']['prefix'] . 'cache_data` SET `mtime` = :time WHERE `id` = :id');
$query->bindValue(':time', time());
$query->bindValue(':id', $this->id);
return $query->execute() && $query->rowCount() > 0;
}
/**
* Remove the cache
*
* @return bool Success status
*/
public function unlink()
{
if ($this->mysql === null) {
return false;
}
$query = $this->mysql->prepare('DELETE FROM `' . $this->options['extras']['prefix'] . 'cache_data` WHERE `id` = :id');
$query->bindValue(':id', $this->id);
$query2 = $this->mysql->prepare('DELETE FROM `' . $this->options['extras']['prefix'] . 'items` WHERE `feed_id` = :id');
$query2->bindValue(':id', $this->id);
return $query->execute() && $query2->execute();
}
}
class_alias('SimplePie\Cache\MySQL', 'SimplePie_Cache_MySQL');
Cache/NameFilter.php 0000644 00000006044 15133170420 0010306 0 ustar 00 <?php
/**
* SimplePie
*
* A PHP-Based RSS and Atom Feed Framework.
* Takes the hard work out of managing a complete RSS/Atom solution.
*
* Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* * Neither the name of the SimplePie Team nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
* AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package SimplePie
* @copyright 2004-2022 Ryan Parman, Sam Sneddon, Ryan McCue
* @author Ryan Parman
* @author Sam Sneddon
* @author Ryan McCue
* @link http://simplepie.org/ SimplePie
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
namespace SimplePie\Cache;
/**
* Interface for creating a cache filename
*
* @package SimplePie
* @subpackage Caching
*/
interface NameFilter
{
/**
* Method to create cache filename with.
*
* The returning name MUST follow the rules for keys in PSR-16.
*
* @link https://www.php-fig.org/psr/psr-16/
*
* The returning name MUST be a string of at least one character
* that uniquely identifies a cached item, MUST only contain the
* characters A-Z, a-z, 0-9, _, and . in any order in UTF-8 encoding
* and MUST not longer then 64 characters. The following characters
* are reserved for future extensions and MUST NOT be used: {}()/\@:
*
* A provided implementing library MAY support additional characters
* and encodings or longer lengths, but MUST support at least that
* minimum.
*
* @param string $name The name for the cache will be most likly an url with query string
*
* @return string the new cache name
*/
public function filter(string $name): string;
}
Cache/BaseDataCache.php 0000644 00000012613 15133170420 0010647 0 ustar 00 <?php
/**
* SimplePie
*
* A PHP-Based RSS and Atom Feed Framework.
* Takes the hard work out of managing a complete RSS/Atom solution.
*
* Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* * Neither the name of the SimplePie Team nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
* AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package SimplePie
* @copyright 2004-2022 Ryan Parman, Sam Sneddon, Ryan McCue
* @author Ryan Parman
* @author Sam Sneddon
* @author Ryan McCue
* @link http://simplepie.org/ SimplePie
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
namespace SimplePie\Cache;
use InvalidArgumentException;
/**
* Adapter for deprecated \SimplePie\Cache\Base implementations
*
* @package SimplePie
* @subpackage Caching
* @internal
*/
final class BaseDataCache implements DataCache
{
/**
* @var Base
*/
private $cache;
public function __construct(Base $cache)
{
$this->cache = $cache;
}
/**
* Fetches a value from the cache.
*
* Equivalent to \Psr\SimpleCache\CacheInterface::get()
* <code>
* public function get(string $key, mixed $default = null): mixed;
* </code>
*
* @param string $key The unique key of this item in the cache.
* @param mixed $default Default value to return if the key does not exist.
*
* @return array|mixed The value of the item from the cache, or $default in case of cache miss.
*
* @throws InvalidArgumentException
* MUST be thrown if the $key string is not a legal value.
*/
public function get_data(string $key, $default = null)
{
$data = $this->cache->load();
if (!is_array($data)) {
return $default;
}
// ignore data if internal cache expiration time is not set
if (!array_key_exists('__cache_expiration_time', $data)) {
return $default;
}
// ignore data if internal cache expiration time is expired
if ($data['__cache_expiration_time'] < time()) {
return $default;
}
// remove internal cache expiration time
unset($data['__cache_expiration_time']);
return $data;
}
/**
* Persists data in the cache, uniquely referenced by a key with an optional expiration TTL time.
*
* Equivalent to \Psr\SimpleCache\CacheInterface::set()
* <code>
* public function set(string $key, mixed $value, null|int|\DateInterval $ttl = null): bool;
* </code>
*
* @param string $key The key of the item to store.
* @param array $value The value of the item to store, must be serializable.
* @param null|int $ttl Optional. The TTL value of this item. If no value is sent and
* the driver supports TTL then the library may set a default value
* for it or let the driver take care of that.
*
* @return bool True on success and false on failure.
*
* @throws InvalidArgumentException
* MUST be thrown if the $key string is not a legal value.
*/
public function set_data(string $key, array $value, ?int $ttl = null): bool
{
if ($ttl === null) {
$ttl = 3600;
}
// place internal cache expiration time
$value['__cache_expiration_time'] = time() + $ttl;
return $this->cache->save($value);
}
/**
* Delete an item from the cache by its unique key.
*
* Equivalent to \Psr\SimpleCache\CacheInterface::delete()
* <code>
* public function delete(string $key): bool;
* </code>
*
* @param string $key The unique cache key of the item to delete.
*
* @return bool True if the item was successfully removed. False if there was an error.
*
* @throws InvalidArgumentException
* MUST be thrown if the $key string is not a legal value.
*/
public function delete_data(string $key): bool
{
return $this->cache->unlink();
}
}
Parse/Date.php 0000644 00000064345 15133170420 0007214 0 ustar 00 <?php
/**
* SimplePie
*
* A PHP-Based RSS and Atom Feed Framework.
* Takes the hard work out of managing a complete RSS/Atom solution.
*
* Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* * Neither the name of the SimplePie Team nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
* AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package SimplePie
* @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
* @author Ryan Parman
* @author Sam Sneddon
* @author Ryan McCue
* @link http://simplepie.org/ SimplePie
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
namespace SimplePie\Parse;
/**
* Date Parser
*
* @package SimplePie
* @subpackage Parsing
*/
class Date
{
/**
* Input data
*
* @access protected
* @var string
*/
public $date;
/**
* List of days, calendar day name => ordinal day number in the week
*
* @access protected
* @var array
*/
public $day = [
// English
'mon' => 1,
'monday' => 1,
'tue' => 2,
'tuesday' => 2,
'wed' => 3,
'wednesday' => 3,
'thu' => 4,
'thursday' => 4,
'fri' => 5,
'friday' => 5,
'sat' => 6,
'saturday' => 6,
'sun' => 7,
'sunday' => 7,
// Dutch
'maandag' => 1,
'dinsdag' => 2,
'woensdag' => 3,
'donderdag' => 4,
'vrijdag' => 5,
'zaterdag' => 6,
'zondag' => 7,
// French
'lundi' => 1,
'mardi' => 2,
'mercredi' => 3,
'jeudi' => 4,
'vendredi' => 5,
'samedi' => 6,
'dimanche' => 7,
// German
'montag' => 1,
'mo' => 1,
'dienstag' => 2,
'di' => 2,
'mittwoch' => 3,
'mi' => 3,
'donnerstag' => 4,
'do' => 4,
'freitag' => 5,
'fr' => 5,
'samstag' => 6,
'sa' => 6,
'sonnabend' => 6,
// AFAIK no short form for sonnabend
'so' => 7,
'sonntag' => 7,
// Italian
'lunedì' => 1,
'martedì' => 2,
'mercoledì' => 3,
'giovedì' => 4,
'venerdì' => 5,
'sabato' => 6,
'domenica' => 7,
// Spanish
'lunes' => 1,
'martes' => 2,
'miércoles' => 3,
'jueves' => 4,
'viernes' => 5,
'sábado' => 6,
'domingo' => 7,
// Finnish
'maanantai' => 1,
'tiistai' => 2,
'keskiviikko' => 3,
'torstai' => 4,
'perjantai' => 5,
'lauantai' => 6,
'sunnuntai' => 7,
// Hungarian
'hétfő' => 1,
'kedd' => 2,
'szerda' => 3,
'csütörtok' => 4,
'péntek' => 5,
'szombat' => 6,
'vasárnap' => 7,
// Greek
'Δευ' => 1,
'Τρι' => 2,
'Τετ' => 3,
'Πεμ' => 4,
'Παρ' => 5,
'Σαβ' => 6,
'Κυρ' => 7,
// Russian
'Пн.' => 1,
'Вт.' => 2,
'Ср.' => 3,
'Чт.' => 4,
'Пт.' => 5,
'Сб.' => 6,
'Вс.' => 7,
];
/**
* List of months, calendar month name => calendar month number
*
* @access protected
* @var array
*/
public $month = [
// English
'jan' => 1,
'january' => 1,
'feb' => 2,
'february' => 2,
'mar' => 3,
'march' => 3,
'apr' => 4,
'april' => 4,
'may' => 5,
// No long form of May
'jun' => 6,
'june' => 6,
'jul' => 7,
'july' => 7,
'aug' => 8,
'august' => 8,
'sep' => 9,
'september' => 9,
'oct' => 10,
'october' => 10,
'nov' => 11,
'november' => 11,
'dec' => 12,
'december' => 12,
// Dutch
'januari' => 1,
'februari' => 2,
'maart' => 3,
'april' => 4,
'mei' => 5,
'juni' => 6,
'juli' => 7,
'augustus' => 8,
'september' => 9,
'oktober' => 10,
'november' => 11,
'december' => 12,
// French
'janvier' => 1,
'février' => 2,
'mars' => 3,
'avril' => 4,
'mai' => 5,
'juin' => 6,
'juillet' => 7,
'août' => 8,
'septembre' => 9,
'octobre' => 10,
'novembre' => 11,
'décembre' => 12,
// German
'januar' => 1,
'jan' => 1,
'februar' => 2,
'feb' => 2,
'märz' => 3,
'mär' => 3,
'april' => 4,
'apr' => 4,
'mai' => 5, // no short form for may
'juni' => 6,
'jun' => 6,
'juli' => 7,
'jul' => 7,
'august' => 8,
'aug' => 8,
'september' => 9,
'sep' => 9,
'oktober' => 10,
'okt' => 10,
'november' => 11,
'nov' => 11,
'dezember' => 12,
'dez' => 12,
// Italian
'gennaio' => 1,
'febbraio' => 2,
'marzo' => 3,
'aprile' => 4,
'maggio' => 5,
'giugno' => 6,
'luglio' => 7,
'agosto' => 8,
'settembre' => 9,
'ottobre' => 10,
'novembre' => 11,
'dicembre' => 12,
// Spanish
'enero' => 1,
'febrero' => 2,
'marzo' => 3,
'abril' => 4,
'mayo' => 5,
'junio' => 6,
'julio' => 7,
'agosto' => 8,
'septiembre' => 9,
'setiembre' => 9,
'octubre' => 10,
'noviembre' => 11,
'diciembre' => 12,
// Finnish
'tammikuu' => 1,
'helmikuu' => 2,
'maaliskuu' => 3,
'huhtikuu' => 4,
'toukokuu' => 5,
'kesäkuu' => 6,
'heinäkuu' => 7,
'elokuu' => 8,
'suuskuu' => 9,
'lokakuu' => 10,
'marras' => 11,
'joulukuu' => 12,
// Hungarian
'január' => 1,
'február' => 2,
'március' => 3,
'április' => 4,
'május' => 5,
'június' => 6,
'július' => 7,
'augusztus' => 8,
'szeptember' => 9,
'október' => 10,
'november' => 11,
'december' => 12,
// Greek
'Ιαν' => 1,
'Φεβ' => 2,
'Μάώ' => 3,
'Μαώ' => 3,
'Απρ' => 4,
'Μάι' => 5,
'Μαϊ' => 5,
'Μαι' => 5,
'Ιούν' => 6,
'Ιον' => 6,
'Ιούλ' => 7,
'Ιολ' => 7,
'Αύγ' => 8,
'Αυγ' => 8,
'Σεπ' => 9,
'Οκτ' => 10,
'Νοέ' => 11,
'Δεκ' => 12,
// Russian
'Янв' => 1,
'января' => 1,
'Фев' => 2,
'февраля' => 2,
'Мар' => 3,
'марта' => 3,
'Апр' => 4,
'апреля' => 4,
'Май' => 5,
'мая' => 5,
'Июн' => 6,
'июня' => 6,
'Июл' => 7,
'июля' => 7,
'Авг' => 8,
'августа' => 8,
'Сен' => 9,
'сентября' => 9,
'Окт' => 10,
'октября' => 10,
'Ноя' => 11,
'ноября' => 11,
'Дек' => 12,
'декабря' => 12,
];
/**
* List of timezones, abbreviation => offset from UTC
*
* @access protected
* @var array
*/
public $timezone = [
'ACDT' => 37800,
'ACIT' => 28800,
'ACST' => 34200,
'ACT' => -18000,
'ACWDT' => 35100,
'ACWST' => 31500,
'AEDT' => 39600,
'AEST' => 36000,
'AFT' => 16200,
'AKDT' => -28800,
'AKST' => -32400,
'AMDT' => 18000,
'AMT' => -14400,
'ANAST' => 46800,
'ANAT' => 43200,
'ART' => -10800,
'AZOST' => -3600,
'AZST' => 18000,
'AZT' => 14400,
'BIOT' => 21600,
'BIT' => -43200,
'BOT' => -14400,
'BRST' => -7200,
'BRT' => -10800,
'BST' => 3600,
'BTT' => 21600,
'CAST' => 18000,
'CAT' => 7200,
'CCT' => 23400,
'CDT' => -18000,
'CEDT' => 7200,
'CEST' => 7200,
'CET' => 3600,
'CGST' => -7200,
'CGT' => -10800,
'CHADT' => 49500,
'CHAST' => 45900,
'CIST' => -28800,
'CKT' => -36000,
'CLDT' => -10800,
'CLST' => -14400,
'COT' => -18000,
'CST' => -21600,
'CVT' => -3600,
'CXT' => 25200,
'DAVT' => 25200,
'DTAT' => 36000,
'EADT' => -18000,
'EAST' => -21600,
'EAT' => 10800,
'ECT' => -18000,
'EDT' => -14400,
'EEST' => 10800,
'EET' => 7200,
'EGT' => -3600,
'EKST' => 21600,
'EST' => -18000,
'FJT' => 43200,
'FKDT' => -10800,
'FKST' => -14400,
'FNT' => -7200,
'GALT' => -21600,
'GEDT' => 14400,
'GEST' => 10800,
'GFT' => -10800,
'GILT' => 43200,
'GIT' => -32400,
'GST' => 14400,
'GST' => -7200,
'GYT' => -14400,
'HAA' => -10800,
'HAC' => -18000,
'HADT' => -32400,
'HAE' => -14400,
'HAP' => -25200,
'HAR' => -21600,
'HAST' => -36000,
'HAT' => -9000,
'HAY' => -28800,
'HKST' => 28800,
'HMT' => 18000,
'HNA' => -14400,
'HNC' => -21600,
'HNE' => -18000,
'HNP' => -28800,
'HNR' => -25200,
'HNT' => -12600,
'HNY' => -32400,
'IRDT' => 16200,
'IRKST' => 32400,
'IRKT' => 28800,
'IRST' => 12600,
'JFDT' => -10800,
'JFST' => -14400,
'JST' => 32400,
'KGST' => 21600,
'KGT' => 18000,
'KOST' => 39600,
'KOVST' => 28800,
'KOVT' => 25200,
'KRAST' => 28800,
'KRAT' => 25200,
'KST' => 32400,
'LHDT' => 39600,
'LHST' => 37800,
'LINT' => 50400,
'LKT' => 21600,
'MAGST' => 43200,
'MAGT' => 39600,
'MAWT' => 21600,
'MDT' => -21600,
'MESZ' => 7200,
'MEZ' => 3600,
'MHT' => 43200,
'MIT' => -34200,
'MNST' => 32400,
'MSDT' => 14400,
'MSST' => 10800,
'MST' => -25200,
'MUT' => 14400,
'MVT' => 18000,
'MYT' => 28800,
'NCT' => 39600,
'NDT' => -9000,
'NFT' => 41400,
'NMIT' => 36000,
'NOVST' => 25200,
'NOVT' => 21600,
'NPT' => 20700,
'NRT' => 43200,
'NST' => -12600,
'NUT' => -39600,
'NZDT' => 46800,
'NZST' => 43200,
'OMSST' => 25200,
'OMST' => 21600,
'PDT' => -25200,
'PET' => -18000,
'PETST' => 46800,
'PETT' => 43200,
'PGT' => 36000,
'PHOT' => 46800,
'PHT' => 28800,
'PKT' => 18000,
'PMDT' => -7200,
'PMST' => -10800,
'PONT' => 39600,
'PST' => -28800,
'PWT' => 32400,
'PYST' => -10800,
'PYT' => -14400,
'RET' => 14400,
'ROTT' => -10800,
'SAMST' => 18000,
'SAMT' => 14400,
'SAST' => 7200,
'SBT' => 39600,
'SCDT' => 46800,
'SCST' => 43200,
'SCT' => 14400,
'SEST' => 3600,
'SGT' => 28800,
'SIT' => 28800,
'SRT' => -10800,
'SST' => -39600,
'SYST' => 10800,
'SYT' => 7200,
'TFT' => 18000,
'THAT' => -36000,
'TJT' => 18000,
'TKT' => -36000,
'TMT' => 18000,
'TOT' => 46800,
'TPT' => 32400,
'TRUT' => 36000,
'TVT' => 43200,
'TWT' => 28800,
'UYST' => -7200,
'UYT' => -10800,
'UZT' => 18000,
'VET' => -14400,
'VLAST' => 39600,
'VLAT' => 36000,
'VOST' => 21600,
'VUT' => 39600,
'WAST' => 7200,
'WAT' => 3600,
'WDT' => 32400,
'WEST' => 3600,
'WFT' => 43200,
'WIB' => 25200,
'WIT' => 32400,
'WITA' => 28800,
'WKST' => 18000,
'WST' => 28800,
'YAKST' => 36000,
'YAKT' => 32400,
'YAPT' => 36000,
'YEKST' => 21600,
'YEKT' => 18000,
];
/**
* Cached PCRE for Date::$day
*
* @access protected
* @var string
*/
public $day_pcre;
/**
* Cached PCRE for Date::$month
*
* @access protected
* @var string
*/
public $month_pcre;
/**
* Array of user-added callback methods
*
* @access private
* @var array
*/
public $built_in = [];
/**
* Array of user-added callback methods
*
* @access private
* @var array
*/
public $user = [];
/**
* Create new Date object, and set self::day_pcre,
* self::month_pcre, and self::built_in
*
* @access private
*/
public function __construct()
{
$this->day_pcre = '(' . implode('|', array_keys($this->day)) . ')';
$this->month_pcre = '(' . implode('|', array_keys($this->month)) . ')';
static $cache;
if (!isset($cache[get_class($this)])) {
$all_methods = get_class_methods($this);
foreach ($all_methods as $method) {
if (strtolower(substr($method, 0, 5)) === 'date_') {
$cache[get_class($this)][] = $method;
}
}
}
foreach ($cache[get_class($this)] as $method) {
$this->built_in[] = $method;
}
}
/**
* Get the object
*
* @access public
*/
public static function get()
{
static $object;
if (!$object) {
$object = new Date();
}
return $object;
}
/**
* Parse a date
*
* @final
* @access public
* @param string $date Date to parse
* @return int Timestamp corresponding to date string, or false on failure
*/
public function parse($date)
{
foreach ($this->user as $method) {
if (($returned = call_user_func($method, $date)) !== false) {
return $returned;
}
}
foreach ($this->built_in as $method) {
if (($returned = call_user_func([$this, $method], $date)) !== false) {
return $returned;
}
}
return false;
}
/**
* Add a callback method to parse a date
*
* @final
* @access public
* @param callable $callback
*/
public function add_callback($callback)
{
if (is_callable($callback)) {
$this->user[] = $callback;
} else {
trigger_error('User-supplied function must be a valid callback', E_USER_WARNING);
}
}
/**
* Parse a superset of W3C-DTF (allows hyphens and colons to be omitted, as
* well as allowing any of upper or lower case "T", horizontal tabs, or
* spaces to be used as the time separator (including more than one))
*
* @access protected
* @return int Timestamp
*/
public function date_w3cdtf($date)
{
$pcre = <<<'PCRE'
/
^
(?P<year>[0-9]{4})
(?:
-?
(?P<month>[0-9]{2})
(?:
-?
(?P<day>[0-9]{2})
(?:
[Tt\x09\x20]+
(?P<hour>[0-9]{2})
(?:
:?
(?P<minute>[0-9]{2})
(?:
:?
(?P<second>[0-9]{2})
(?:
.
(?P<second_fraction>[0-9]*)
)?
)?
)?
(?:
(?P<zulu>Z)
| (?P<tz_sign>[+\-])
(?P<tz_hour>[0-9]{1,2})
:?
(?P<tz_minute>[0-9]{1,2})
)
)?
)?
)?
$
/x
PCRE;
if (preg_match($pcre, $date, $match)) {
// Fill in empty matches and convert to proper types.
$year = (int) $match['year'];
$month = isset($match['month']) ? (int) $match['month'] : 1;
$day = isset($match['day']) ? (int) $match['day'] : 1;
$hour = isset($match['hour']) ? (int) $match['hour'] : 0;
$minute = isset($match['minute']) ? (int) $match['minute'] : 0;
$second = isset($match['second']) ? (int) $match['second'] : 0;
$second_fraction = isset($match['second_fraction']) ? ((int) $match['second_fraction']) / (10 ** strlen($match['second_fraction'])) : 0;
$tz_sign = ($match['tz_sign'] ?? '') === '-' ? -1 : 1;
$tz_hour = isset($match['tz_hour']) ? (int) $match['tz_hour'] : 0;
$tz_minute = isset($match['tz_minute']) ? (int) $match['tz_minute'] : 0;
// Numeric timezone
$timezone = $tz_hour * 3600;
$timezone += $tz_minute * 60;
$timezone *= $tz_sign;
// Convert the number of seconds to an integer, taking decimals into account
$second = (int) round($second + $second_fraction);
return gmmktime($hour, $minute, $second, $month, $day, $year) - $timezone;
}
return false;
}
/**
* Remove RFC822 comments
*
* @access protected
* @param string $data Data to strip comments from
* @return string Comment stripped string
*/
public function remove_rfc2822_comments($string)
{
$string = (string) $string;
$position = 0;
$length = strlen($string);
$depth = 0;
$output = '';
while ($position < $length && ($pos = strpos($string, '(', $position)) !== false) {
$output .= substr($string, $position, $pos - $position);
$position = $pos + 1;
if ($pos === 0 || $string[$pos - 1] !== '\\') {
$depth++;
while ($depth && $position < $length) {
$position += strcspn($string, '()', $position);
if ($string[$position - 1] === '\\') {
$position++;
continue;
} elseif (isset($string[$position])) {
switch ($string[$position]) {
case '(':
$depth++;
break;
case ')':
$depth--;
break;
}
$position++;
} else {
break;
}
}
} else {
$output .= '(';
}
}
$output .= substr($string, $position);
return $output;
}
/**
* Parse RFC2822's date format
*
* @access protected
* @return int Timestamp
*/
public function date_rfc2822($date)
{
static $pcre;
if (!$pcre) {
$wsp = '[\x09\x20]';
$fws = '(?:' . $wsp . '+|' . $wsp . '*(?:\x0D\x0A' . $wsp . '+)+)';
$optional_fws = $fws . '?';
$day_name = $this->day_pcre;
$month = $this->month_pcre;
$day = '([0-9]{1,2})';
$hour = $minute = $second = '([0-9]{2})';
$year = '([0-9]{2,4})';
$num_zone = '([+\-])([0-9]{2})([0-9]{2})';
$character_zone = '([A-Z]{1,5})';
$zone = '(?:' . $num_zone . '|' . $character_zone . ')';
$pcre = '/(?:' . $optional_fws . $day_name . $optional_fws . ',)?' . $optional_fws . $day . $fws . $month . $fws . $year . $fws . $hour . $optional_fws . ':' . $optional_fws . $minute . '(?:' . $optional_fws . ':' . $optional_fws . $second . ')?' . $fws . $zone . '/i';
}
if (preg_match($pcre, $this->remove_rfc2822_comments($date), $match)) {
/*
Capturing subpatterns:
1: Day name
2: Day
3: Month
4: Year
5: Hour
6: Minute
7: Second
8: Timezone ±
9: Timezone hours
10: Timezone minutes
11: Alphabetic timezone
*/
// Find the month number
$month = $this->month[strtolower($match[3])];
// Numeric timezone
if ($match[8] !== '') {
$timezone = $match[9] * 3600;
$timezone += $match[10] * 60;
if ($match[8] === '-') {
$timezone = 0 - $timezone;
}
}
// Character timezone
elseif (isset($this->timezone[strtoupper($match[11])])) {
$timezone = $this->timezone[strtoupper($match[11])];
}
// Assume everything else to be -0000
else {
$timezone = 0;
}
// Deal with 2/3 digit years
if ($match[4] < 50) {
$match[4] += 2000;
} elseif ($match[4] < 1000) {
$match[4] += 1900;
}
// Second is optional, if it is empty set it to zero
if ($match[7] !== '') {
$second = $match[7];
} else {
$second = 0;
}
return gmmktime(intval($match[5]), intval($match[6]), intval($second), intval($month), intval($match[2]), intval($match[4])) - $timezone;
}
return false;
}
/**
* Parse RFC850's date format
*
* @access protected
* @return int Timestamp
*/
public function date_rfc850($date)
{
static $pcre;
if (!$pcre) {
$space = '[\x09\x20]+';
$day_name = $this->day_pcre;
$month = $this->month_pcre;
$day = '([0-9]{1,2})';
$year = $hour = $minute = $second = '([0-9]{2})';
$zone = '([A-Z]{1,5})';
$pcre = '/^' . $day_name . ',' . $space . $day . '-' . $month . '-' . $year . $space . $hour . ':' . $minute . ':' . $second . $space . $zone . '$/i';
}
if (preg_match($pcre, $date, $match)) {
/*
Capturing subpatterns:
1: Day name
2: Day
3: Month
4: Year
5: Hour
6: Minute
7: Second
8: Timezone
*/
// Month
$month = $this->month[strtolower($match[3])];
// Character timezone
if (isset($this->timezone[strtoupper($match[8])])) {
$timezone = $this->timezone[strtoupper($match[8])];
}
// Assume everything else to be -0000
else {
$timezone = 0;
}
// Deal with 2 digit year
if ($match[4] < 50) {
$match[4] += 2000;
} else {
$match[4] += 1900;
}
return gmmktime($match[5], $match[6], $match[7], $month, $match[2], $match[4]) - $timezone;
}
return false;
}
/**
* Parse C99's asctime()'s date format
*
* @access protected
* @return int Timestamp
*/
public function date_asctime($date)
{
static $pcre;
if (!$pcre) {
$space = '[\x09\x20]+';
$wday_name = $this->day_pcre;
$mon_name = $this->month_pcre;
$day = '([0-9]{1,2})';
$hour = $sec = $min = '([0-9]{2})';
$year = '([0-9]{4})';
$terminator = '\x0A?\x00?';
$pcre = '/^' . $wday_name . $space . $mon_name . $space . $day . $space . $hour . ':' . $min . ':' . $sec . $space . $year . $terminator . '$/i';
}
if (preg_match($pcre, $date, $match)) {
/*
Capturing subpatterns:
1: Day name
2: Month
3: Day
4: Hour
5: Minute
6: Second
7: Year
*/
$month = $this->month[strtolower($match[2])];
return gmmktime((int) $match[4], (int) $match[5], (int) $match[6], $month, (int) $match[3], (int) $match[7]);
}
return false;
}
/**
* Parse dates using strtotime()
*
* @access protected
* @return int Timestamp
*/
public function date_strtotime($date)
{
$strtotime = strtotime($date);
if ($strtotime === -1 || $strtotime === false) {
return false;
}
return $strtotime;
}
}
class_alias('SimplePie\Parse\Date', 'SimplePie_Parse_Date');
Net/IPv6.php 0000644 00000021041 15133170420 0006561 0 ustar 00 <?php
/**
* SimplePie
*
* A PHP-Based RSS and Atom Feed Framework.
* Takes the hard work out of managing a complete RSS/Atom solution.
*
* Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* * Neither the name of the SimplePie Team nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
* AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package SimplePie
* @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
* @author Ryan Parman
* @author Sam Sneddon
* @author Ryan McCue
* @link http://simplepie.org/ SimplePie
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
namespace SimplePie\Net;
/**
* Class to validate and to work with IPv6 addresses.
*
* @package SimplePie
* @subpackage HTTP
* @copyright 2003-2005 The PHP Group
* @license http://www.opensource.org/licenses/bsd-license.php
* @link http://pear.php.net/package/Net_IPv6
* @author Alexander Merz <alexander.merz@web.de>
* @author elfrink at introweb dot nl
* @author Josh Peck <jmp at joshpeck dot org>
* @author Sam Sneddon <geoffers@gmail.com>
*/
class IPv6
{
/**
* Uncompresses an IPv6 address
*
* RFC 4291 allows you to compress concecutive zero pieces in an address to
* '::'. This method expects a valid IPv6 address and expands the '::' to
* the required number of zero pieces.
*
* Example: FF01::101 -> FF01:0:0:0:0:0:0:101
* ::1 -> 0:0:0:0:0:0:0:1
*
* @author Alexander Merz <alexander.merz@web.de>
* @author elfrink at introweb dot nl
* @author Josh Peck <jmp at joshpeck dot org>
* @copyright 2003-2005 The PHP Group
* @license http://www.opensource.org/licenses/bsd-license.php
* @param string $ip An IPv6 address
* @return string The uncompressed IPv6 address
*/
public static function uncompress($ip)
{
$c1 = -1;
$c2 = -1;
if (substr_count($ip, '::') === 1) {
[$ip1, $ip2] = explode('::', $ip);
if ($ip1 === '') {
$c1 = -1;
} else {
$c1 = substr_count($ip1, ':');
}
if ($ip2 === '') {
$c2 = -1;
} else {
$c2 = substr_count($ip2, ':');
}
if (strpos($ip2, '.') !== false) {
$c2++;
}
// ::
if ($c1 === -1 && $c2 === -1) {
$ip = '0:0:0:0:0:0:0:0';
}
// ::xxx
elseif ($c1 === -1) {
$fill = str_repeat('0:', 7 - $c2);
$ip = str_replace('::', $fill, $ip);
}
// xxx::
elseif ($c2 === -1) {
$fill = str_repeat(':0', 7 - $c1);
$ip = str_replace('::', $fill, $ip);
}
// xxx::xxx
else {
$fill = ':' . str_repeat('0:', 6 - $c2 - $c1);
$ip = str_replace('::', $fill, $ip);
}
}
return $ip;
}
/**
* Compresses an IPv6 address
*
* RFC 4291 allows you to compress concecutive zero pieces in an address to
* '::'. This method expects a valid IPv6 address and compresses consecutive
* zero pieces to '::'.
*
* Example: FF01:0:0:0:0:0:0:101 -> FF01::101
* 0:0:0:0:0:0:0:1 -> ::1
*
* @see uncompress()
* @param string $ip An IPv6 address
* @return string The compressed IPv6 address
*/
public static function compress($ip)
{
// Prepare the IP to be compressed
$ip = self::uncompress($ip);
$ip_parts = self::split_v6_v4($ip);
// Replace all leading zeros
$ip_parts[0] = preg_replace('/(^|:)0+([0-9])/', '\1\2', $ip_parts[0]);
// Find bunches of zeros
if (preg_match_all('/(?:^|:)(?:0(?::|$))+/', $ip_parts[0], $matches, PREG_OFFSET_CAPTURE)) {
$max = 0;
$pos = null;
foreach ($matches[0] as $match) {
if (strlen($match[0]) > $max) {
$max = strlen($match[0]);
$pos = $match[1];
}
}
$ip_parts[0] = substr_replace($ip_parts[0], '::', $pos, $max);
}
if ($ip_parts[1] !== '') {
return implode(':', $ip_parts);
}
return $ip_parts[0];
}
/**
* Splits an IPv6 address into the IPv6 and IPv4 representation parts
*
* RFC 4291 allows you to represent the last two parts of an IPv6 address
* using the standard IPv4 representation
*
* Example: 0:0:0:0:0:0:13.1.68.3
* 0:0:0:0:0:FFFF:129.144.52.38
*
* @param string $ip An IPv6 address
* @return array [0] contains the IPv6 represented part, and [1] the IPv4 represented part
*/
private static function split_v6_v4($ip)
{
if (strpos($ip, '.') !== false) {
$pos = strrpos($ip, ':');
$ipv6_part = substr($ip, 0, $pos);
$ipv4_part = substr($ip, $pos + 1);
return [$ipv6_part, $ipv4_part];
}
return [$ip, ''];
}
/**
* Checks an IPv6 address
*
* Checks if the given IP is a valid IPv6 address
*
* @param string $ip An IPv6 address
* @return bool true if $ip is a valid IPv6 address
*/
public static function check_ipv6($ip)
{
$ip = self::uncompress($ip);
[$ipv6, $ipv4] = self::split_v6_v4($ip);
$ipv6 = explode(':', $ipv6);
$ipv4 = explode('.', $ipv4);
if (count($ipv6) === 8 && count($ipv4) === 1 || count($ipv6) === 6 && count($ipv4) === 4) {
foreach ($ipv6 as $ipv6_part) {
// The section can't be empty
if ($ipv6_part === '') {
return false;
}
// Nor can it be over four characters
if (strlen($ipv6_part) > 4) {
return false;
}
// Remove leading zeros (this is safe because of the above)
$ipv6_part = ltrim($ipv6_part, '0');
if ($ipv6_part === '') {
$ipv6_part = '0';
}
// Check the value is valid
$value = hexdec($ipv6_part);
if (dechex($value) !== strtolower($ipv6_part) || $value < 0 || $value > 0xFFFF) {
return false;
}
}
if (count($ipv4) === 4) {
foreach ($ipv4 as $ipv4_part) {
$value = (int) $ipv4_part;
if ((string) $value !== $ipv4_part || $value < 0 || $value > 0xFF) {
return false;
}
}
}
return true;
}
return false;
}
/**
* Checks if the given IP is a valid IPv6 address
*
* @codeCoverageIgnore
* @deprecated Use {@see IPv6::check_ipv6()} instead
* @see check_ipv6
* @param string $ip An IPv6 address
* @return bool true if $ip is a valid IPv6 address
*/
public static function checkIPv6($ip)
{
return self::check_ipv6($ip);
}
}
class_alias('SimplePie\Net\IPv6', 'SimplePie_Net_IPv6');
Rating.php 0000644 00000007151 15133170420 0006501 0 ustar 00 <?php
/**
* SimplePie
*
* A PHP-Based RSS and Atom Feed Framework.
* Takes the hard work out of managing a complete RSS/Atom solution.
*
* Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* * Neither the name of the SimplePie Team nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
* AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package SimplePie
* @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
* @author Ryan Parman
* @author Sam Sneddon
* @author Ryan McCue
* @link http://simplepie.org/ SimplePie
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
namespace SimplePie;
/**
* Handles `<media:rating>` or `<itunes:explicit>` tags as defined in Media RSS and iTunes RSS respectively
*
* Used by {@see \SimplePie\Enclosure::get_rating()} and {@see \SimplePie\Enclosure::get_ratings()}
*
* This class can be overloaded with {@see \SimplePie\SimplePie::set_rating_class()}
*
* @package SimplePie
* @subpackage API
*/
class Rating
{
/**
* Rating scheme
*
* @var string
* @see get_scheme()
*/
public $scheme;
/**
* Rating value
*
* @var string
* @see get_value()
*/
public $value;
/**
* Constructor, used to input the data
*
* For documentation on all the parameters, see the corresponding
* properties and their accessors
*/
public function __construct($scheme = null, $value = null)
{
$this->scheme = $scheme;
$this->value = $value;
}
/**
* String-ified version
*
* @return string
*/
public function __toString()
{
// There is no $this->data here
return md5(serialize($this));
}
/**
* Get the organizational scheme for the rating
*
* @return string|null
*/
public function get_scheme()
{
if ($this->scheme !== null) {
return $this->scheme;
}
return null;
}
/**
* Get the value of the rating
*
* @return string|null
*/
public function get_value()
{
if ($this->value !== null) {
return $this->value;
}
return null;
}
}
class_alias('SimplePie\Rating', 'SimplePie_Rating');
Credit.php 0000644 00000007666 15133170420 0006502 0 ustar 00 <?php
/**
* SimplePie
*
* A PHP-Based RSS and Atom Feed Framework.
* Takes the hard work out of managing a complete RSS/Atom solution.
*
* Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* * Neither the name of the SimplePie Team nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
* AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package SimplePie
* @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
* @author Ryan Parman
* @author Sam Sneddon
* @author Ryan McCue
* @link http://simplepie.org/ SimplePie
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
namespace SimplePie;
/**
* Handles `<media:credit>` as defined in Media RSS
*
* Used by {@see \SimplePie\Enclosure::get_credit()} and {@see \SimplePie\Enclosure::get_credits()}
*
* This class can be overloaded with {@see \SimplePie\SimplePie::set_credit_class()}
*
* @package SimplePie
* @subpackage API
*/
class Credit
{
/**
* Credited role
*
* @var string
* @see get_role()
*/
public $role;
/**
* Organizational scheme
*
* @var string
* @see get_scheme()
*/
public $scheme;
/**
* Credited name
*
* @var string
* @see get_name()
*/
public $name;
/**
* Constructor, used to input the data
*
* For documentation on all the parameters, see the corresponding
* properties and their accessors
*/
public function __construct($role = null, $scheme = null, $name = null)
{
$this->role = $role;
$this->scheme = $scheme;
$this->name = $name;
}
/**
* String-ified version
*
* @return string
*/
public function __toString()
{
// There is no $this->data here
return md5(serialize($this));
}
/**
* Get the role of the person receiving credit
*
* @return string|null
*/
public function get_role()
{
if ($this->role !== null) {
return $this->role;
}
return null;
}
/**
* Get the organizational scheme
*
* @return string|null
*/
public function get_scheme()
{
if ($this->scheme !== null) {
return $this->scheme;
}
return null;
}
/**
* Get the credited person/entity's name
*
* @return string|null
*/
public function get_name()
{
if ($this->name !== null) {
return $this->name;
}
return null;
}
}
class_alias('SimplePie\Credit', 'SimplePie_Credit');
Content/Type/Sniffer.php 0000644 00000022112 15133170420 0011176 0 ustar 00 <?php
/**
* SimplePie
*
* A PHP-Based RSS and Atom Feed Framework.
* Takes the hard work out of managing a complete RSS/Atom solution.
*
* Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* * Neither the name of the SimplePie Team nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
* AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package SimplePie
* @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
* @author Ryan Parman
* @author Sam Sneddon
* @author Ryan McCue
* @link http://simplepie.org/ SimplePie
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
namespace SimplePie\Content\Type;
/**
* Content-type sniffing
*
* Based on the rules in http://tools.ietf.org/html/draft-abarth-mime-sniff-06
*
* This is used since we can't always trust Content-Type headers, and is based
* upon the HTML5 parsing rules.
*
*
* This class can be overloaded with {@see \SimplePie\SimplePie::set_content_type_sniffer_class()}
*
* @package SimplePie
* @subpackage HTTP
*/
class Sniffer
{
/**
* File object
*
* @var \SimplePie\File
*/
public $file;
/**
* Create an instance of the class with the input file
*
* @param Sniffer $file Input file
*/
public function __construct($file)
{
$this->file = $file;
}
/**
* Get the Content-Type of the specified file
*
* @return string Actual Content-Type
*/
public function get_type()
{
if (isset($this->file->headers['content-type'])) {
if (!isset($this->file->headers['content-encoding'])
&& ($this->file->headers['content-type'] === 'text/plain'
|| $this->file->headers['content-type'] === 'text/plain; charset=ISO-8859-1'
|| $this->file->headers['content-type'] === 'text/plain; charset=iso-8859-1'
|| $this->file->headers['content-type'] === 'text/plain; charset=UTF-8')) {
return $this->text_or_binary();
}
if (($pos = strpos($this->file->headers['content-type'], ';')) !== false) {
$official = substr($this->file->headers['content-type'], 0, $pos);
} else {
$official = $this->file->headers['content-type'];
}
$official = trim(strtolower($official));
if ($official === 'unknown/unknown'
|| $official === 'application/unknown') {
return $this->unknown();
} elseif (substr($official, -4) === '+xml'
|| $official === 'text/xml'
|| $official === 'application/xml') {
return $official;
} elseif (substr($official, 0, 6) === 'image/') {
if ($return = $this->image()) {
return $return;
}
return $official;
} elseif ($official === 'text/html') {
return $this->feed_or_html();
}
return $official;
}
return $this->unknown();
}
/**
* Sniff text or binary
*
* @return string Actual Content-Type
*/
public function text_or_binary()
{
if (substr($this->file->body, 0, 2) === "\xFE\xFF"
|| substr($this->file->body, 0, 2) === "\xFF\xFE"
|| substr($this->file->body, 0, 4) === "\x00\x00\xFE\xFF"
|| substr($this->file->body, 0, 3) === "\xEF\xBB\xBF") {
return 'text/plain';
} elseif (preg_match('/[\x00-\x08\x0E-\x1A\x1C-\x1F]/', $this->file->body)) {
return 'application/octet-stream';
}
return 'text/plain';
}
/**
* Sniff unknown
*
* @return string Actual Content-Type
*/
public function unknown()
{
$ws = strspn($this->file->body, "\x09\x0A\x0B\x0C\x0D\x20");
if (strtolower(substr($this->file->body, $ws, 14)) === '<!doctype html'
|| strtolower(substr($this->file->body, $ws, 5)) === '<html'
|| strtolower(substr($this->file->body, $ws, 7)) === '<script') {
return 'text/html';
} elseif (substr($this->file->body, 0, 5) === '%PDF-') {
return 'application/pdf';
} elseif (substr($this->file->body, 0, 11) === '%!PS-Adobe-') {
return 'application/postscript';
} elseif (substr($this->file->body, 0, 6) === 'GIF87a'
|| substr($this->file->body, 0, 6) === 'GIF89a') {
return 'image/gif';
} elseif (substr($this->file->body, 0, 8) === "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A") {
return 'image/png';
} elseif (substr($this->file->body, 0, 3) === "\xFF\xD8\xFF") {
return 'image/jpeg';
} elseif (substr($this->file->body, 0, 2) === "\x42\x4D") {
return 'image/bmp';
} elseif (substr($this->file->body, 0, 4) === "\x00\x00\x01\x00") {
return 'image/vnd.microsoft.icon';
}
return $this->text_or_binary();
}
/**
* Sniff images
*
* @return string Actual Content-Type
*/
public function image()
{
if (substr($this->file->body, 0, 6) === 'GIF87a'
|| substr($this->file->body, 0, 6) === 'GIF89a') {
return 'image/gif';
} elseif (substr($this->file->body, 0, 8) === "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A") {
return 'image/png';
} elseif (substr($this->file->body, 0, 3) === "\xFF\xD8\xFF") {
return 'image/jpeg';
} elseif (substr($this->file->body, 0, 2) === "\x42\x4D") {
return 'image/bmp';
} elseif (substr($this->file->body, 0, 4) === "\x00\x00\x01\x00") {
return 'image/vnd.microsoft.icon';
}
return false;
}
/**
* Sniff HTML
*
* @return string Actual Content-Type
*/
public function feed_or_html()
{
$len = strlen($this->file->body);
$pos = strspn($this->file->body, "\x09\x0A\x0D\x20\xEF\xBB\xBF");
while ($pos < $len) {
switch ($this->file->body[$pos]) {
case "\x09":
case "\x0A":
case "\x0D":
case "\x20":
$pos += strspn($this->file->body, "\x09\x0A\x0D\x20", $pos);
continue 2;
case '<':
$pos++;
break;
default:
return 'text/html';
}
if (substr($this->file->body, $pos, 3) === '!--') {
$pos += 3;
if ($pos < $len && ($pos = strpos($this->file->body, '-->', $pos)) !== false) {
$pos += 3;
} else {
return 'text/html';
}
} elseif (substr($this->file->body, $pos, 1) === '!') {
if ($pos < $len && ($pos = strpos($this->file->body, '>', $pos)) !== false) {
$pos++;
} else {
return 'text/html';
}
} elseif (substr($this->file->body, $pos, 1) === '?') {
if ($pos < $len && ($pos = strpos($this->file->body, '?>', $pos)) !== false) {
$pos += 2;
} else {
return 'text/html';
}
} elseif (substr($this->file->body, $pos, 3) === 'rss'
|| substr($this->file->body, $pos, 7) === 'rdf:RDF') {
return 'application/rss+xml';
} elseif (substr($this->file->body, $pos, 4) === 'feed') {
return 'application/atom+xml';
} else {
return 'text/html';
}
}
return 'text/html';
}
}
class_alias('SimplePie\Content\Type\Sniffer', 'SimplePie_Content_Type_Sniffer');
Parser.php 0000644 00000103543 15133170420 0006513 0 ustar 00 <?php
/**
* SimplePie
*
* A PHP-Based RSS and Atom Feed Framework.
* Takes the hard work out of managing a complete RSS/Atom solution.
*
* Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* * Neither the name of the SimplePie Team nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
* AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package SimplePie
* @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
* @author Ryan Parman
* @author Sam Sneddon
* @author Ryan McCue
* @link http://simplepie.org/ SimplePie
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
namespace SimplePie;
use SimplePie\XML\Declaration\Parser as DeclarationParser;
/**
* Parses XML into something sane
*
*
* This class can be overloaded with {@see \SimplePie\SimplePie::set_parser_class()}
*
* @package SimplePie
* @subpackage Parsing
*/
class Parser implements RegistryAware
{
public $error_code;
public $error_string;
public $current_line;
public $current_column;
public $current_byte;
public $separator = ' ';
public $namespace = [''];
public $element = [''];
public $xml_base = [''];
public $xml_base_explicit = [false];
public $xml_lang = [''];
public $data = [];
public $datas = [[]];
public $current_xhtml_construct = -1;
public $encoding;
protected $registry;
public function set_registry(\SimplePie\Registry $registry)/* : void */
{
$this->registry = $registry;
}
public function parse(&$data, $encoding, $url = '')
{
if (class_exists('DOMXpath') && function_exists('Mf2\parse')) {
$doc = new \DOMDocument();
@$doc->loadHTML($data);
$xpath = new \DOMXpath($doc);
// Check for both h-feed and h-entry, as both a feed with no entries
// and a list of entries without an h-feed wrapper are both valid.
$query = '//*[contains(concat(" ", @class, " "), " h-feed ") or '.
'contains(concat(" ", @class, " "), " h-entry ")]';
$result = $xpath->query($query);
if ($result->length !== 0) {
return $this->parse_microformats($data, $url);
}
}
// Use UTF-8 if we get passed US-ASCII, as every US-ASCII character is a UTF-8 character
if (strtoupper($encoding) === 'US-ASCII') {
$this->encoding = 'UTF-8';
} else {
$this->encoding = $encoding;
}
// Strip BOM:
// UTF-32 Big Endian BOM
if (substr($data, 0, 4) === "\x00\x00\xFE\xFF") {
$data = substr($data, 4);
}
// UTF-32 Little Endian BOM
elseif (substr($data, 0, 4) === "\xFF\xFE\x00\x00") {
$data = substr($data, 4);
}
// UTF-16 Big Endian BOM
elseif (substr($data, 0, 2) === "\xFE\xFF") {
$data = substr($data, 2);
}
// UTF-16 Little Endian BOM
elseif (substr($data, 0, 2) === "\xFF\xFE") {
$data = substr($data, 2);
}
// UTF-8 BOM
elseif (substr($data, 0, 3) === "\xEF\xBB\xBF") {
$data = substr($data, 3);
}
if (substr($data, 0, 5) === '<?xml' && strspn(substr($data, 5, 1), "\x09\x0A\x0D\x20") && ($pos = strpos($data, '?>')) !== false) {
$declaration = $this->registry->create(DeclarationParser::class, [substr($data, 5, $pos - 5)]);
if ($declaration->parse()) {
$data = substr($data, $pos + 2);
$data = '<?xml version="' . $declaration->version . '" encoding="' . $encoding . '" standalone="' . (($declaration->standalone) ? 'yes' : 'no') . '"?>' ."\n". $this->declare_html_entities() . $data;
} else {
$this->error_string = 'SimplePie bug! Please report this!';
return false;
}
}
$return = true;
static $xml_is_sane = null;
if ($xml_is_sane === null) {
$parser_check = xml_parser_create();
xml_parse_into_struct($parser_check, '<foo>&</foo>', $values);
xml_parser_free($parser_check);
$xml_is_sane = isset($values[0]['value']);
}
// Create the parser
if ($xml_is_sane) {
$xml = xml_parser_create_ns($this->encoding, $this->separator);
xml_parser_set_option($xml, XML_OPTION_SKIP_WHITE, 1);
xml_parser_set_option($xml, XML_OPTION_CASE_FOLDING, 0);
xml_set_character_data_handler($xml, [$this, 'cdata']);
xml_set_element_handler($xml, [$this, 'tag_open'], [$this, 'tag_close']);
// Parse!
$wrapper = @is_writable(sys_get_temp_dir()) ? 'php://temp' : 'php://memory';
if (($stream = fopen($wrapper, 'r+')) &&
fwrite($stream, $data) &&
rewind($stream)) {
//Parse by chunks not to use too much memory
do {
$stream_data = fread($stream, 1048576);
if (!xml_parse($xml, $stream_data === false ? '' : $stream_data, feof($stream))) {
$this->error_code = xml_get_error_code($xml);
$this->error_string = xml_error_string($this->error_code);
$return = false;
break;
}
} while (!feof($stream));
fclose($stream);
} else {
$return = false;
}
$this->current_line = xml_get_current_line_number($xml);
$this->current_column = xml_get_current_column_number($xml);
$this->current_byte = xml_get_current_byte_index($xml);
xml_parser_free($xml);
return $return;
}
libxml_clear_errors();
$xml = new \XMLReader();
$xml->xml($data);
while (@$xml->read()) {
switch ($xml->nodeType) {
case constant('XMLReader::END_ELEMENT'):
if ($xml->namespaceURI !== '') {
$tagName = $xml->namespaceURI . $this->separator . $xml->localName;
} else {
$tagName = $xml->localName;
}
$this->tag_close(null, $tagName);
break;
case constant('XMLReader::ELEMENT'):
$empty = $xml->isEmptyElement;
if ($xml->namespaceURI !== '') {
$tagName = $xml->namespaceURI . $this->separator . $xml->localName;
} else {
$tagName = $xml->localName;
}
$attributes = [];
while ($xml->moveToNextAttribute()) {
if ($xml->namespaceURI !== '') {
$attrName = $xml->namespaceURI . $this->separator . $xml->localName;
} else {
$attrName = $xml->localName;
}
$attributes[$attrName] = $xml->value;
}
$this->tag_open(null, $tagName, $attributes);
if ($empty) {
$this->tag_close(null, $tagName);
}
break;
case constant('XMLReader::TEXT'):
case constant('XMLReader::CDATA'):
$this->cdata(null, $xml->value);
break;
}
}
if ($error = libxml_get_last_error()) {
$this->error_code = $error->code;
$this->error_string = $error->message;
$this->current_line = $error->line;
$this->current_column = $error->column;
return false;
}
return true;
}
public function get_error_code()
{
return $this->error_code;
}
public function get_error_string()
{
return $this->error_string;
}
public function get_current_line()
{
return $this->current_line;
}
public function get_current_column()
{
return $this->current_column;
}
public function get_current_byte()
{
return $this->current_byte;
}
public function get_data()
{
return $this->data;
}
public function tag_open($parser, $tag, $attributes)
{
[$this->namespace[], $this->element[]] = $this->split_ns($tag);
$attribs = [];
foreach ($attributes as $name => $value) {
[$attrib_namespace, $attribute] = $this->split_ns($name);
$attribs[$attrib_namespace][$attribute] = $value;
}
if (isset($attribs[\SimplePie\SimplePie::NAMESPACE_XML]['base'])) {
$base = $this->registry->call(Misc::class, 'absolutize_url', [$attribs[\SimplePie\SimplePie::NAMESPACE_XML]['base'], end($this->xml_base)]);
if ($base !== false) {
$this->xml_base[] = $base;
$this->xml_base_explicit[] = true;
}
} else {
$this->xml_base[] = end($this->xml_base);
$this->xml_base_explicit[] = end($this->xml_base_explicit);
}
if (isset($attribs[\SimplePie\SimplePie::NAMESPACE_XML]['lang'])) {
$this->xml_lang[] = $attribs[\SimplePie\SimplePie::NAMESPACE_XML]['lang'];
} else {
$this->xml_lang[] = end($this->xml_lang);
}
if ($this->current_xhtml_construct >= 0) {
$this->current_xhtml_construct++;
if (end($this->namespace) === \SimplePie\SimplePie::NAMESPACE_XHTML) {
$this->data['data'] .= '<' . end($this->element);
if (isset($attribs[''])) {
foreach ($attribs[''] as $name => $value) {
$this->data['data'] .= ' ' . $name . '="' . htmlspecialchars($value, ENT_COMPAT, $this->encoding) . '"';
}
}
$this->data['data'] .= '>';
}
} else {
$this->datas[] = &$this->data;
$this->data = &$this->data['child'][end($this->namespace)][end($this->element)][];
$this->data = ['data' => '', 'attribs' => $attribs, 'xml_base' => end($this->xml_base), 'xml_base_explicit' => end($this->xml_base_explicit), 'xml_lang' => end($this->xml_lang)];
if ((end($this->namespace) === \SimplePie\SimplePie::NAMESPACE_ATOM_03 && in_array(end($this->element), ['title', 'tagline', 'copyright', 'info', 'summary', 'content']) && isset($attribs['']['mode']) && $attribs['']['mode'] === 'xml')
|| (end($this->namespace) === \SimplePie\SimplePie::NAMESPACE_ATOM_10 && in_array(end($this->element), ['rights', 'subtitle', 'summary', 'info', 'title', 'content']) && isset($attribs['']['type']) && $attribs['']['type'] === 'xhtml')
|| (end($this->namespace) === \SimplePie\SimplePie::NAMESPACE_RSS_20 && in_array(end($this->element), ['title']))
|| (end($this->namespace) === \SimplePie\SimplePie::NAMESPACE_RSS_090 && in_array(end($this->element), ['title']))
|| (end($this->namespace) === \SimplePie\SimplePie::NAMESPACE_RSS_10 && in_array(end($this->element), ['title']))) {
$this->current_xhtml_construct = 0;
}
}
}
public function cdata($parser, $cdata)
{
if ($this->current_xhtml_construct >= 0) {
$this->data['data'] .= htmlspecialchars($cdata, ENT_QUOTES, $this->encoding);
} else {
$this->data['data'] .= $cdata;
}
}
public function tag_close($parser, $tag)
{
if ($this->current_xhtml_construct >= 0) {
$this->current_xhtml_construct--;
if (end($this->namespace) === \SimplePie\SimplePie::NAMESPACE_XHTML && !in_array(end($this->element), ['area', 'base', 'basefont', 'br', 'col', 'frame', 'hr', 'img', 'input', 'isindex', 'link', 'meta', 'param'])) {
$this->data['data'] .= '</' . end($this->element) . '>';
}
}
if ($this->current_xhtml_construct === -1) {
$this->data = &$this->datas[count($this->datas) - 1];
array_pop($this->datas);
}
array_pop($this->element);
array_pop($this->namespace);
array_pop($this->xml_base);
array_pop($this->xml_base_explicit);
array_pop($this->xml_lang);
}
public function split_ns($string)
{
static $cache = [];
if (!isset($cache[$string])) {
if ($pos = strpos($string, $this->separator)) {
static $separator_length;
if (!$separator_length) {
$separator_length = strlen($this->separator);
}
$namespace = substr($string, 0, $pos);
$local_name = substr($string, $pos + $separator_length);
if (strtolower($namespace) === \SimplePie\SimplePie::NAMESPACE_ITUNES) {
$namespace = \SimplePie\SimplePie::NAMESPACE_ITUNES;
}
// Normalize the Media RSS namespaces
if ($namespace === \SimplePie\SimplePie::NAMESPACE_MEDIARSS_WRONG ||
$namespace === \SimplePie\SimplePie::NAMESPACE_MEDIARSS_WRONG2 ||
$namespace === \SimplePie\SimplePie::NAMESPACE_MEDIARSS_WRONG3 ||
$namespace === \SimplePie\SimplePie::NAMESPACE_MEDIARSS_WRONG4 ||
$namespace === \SimplePie\SimplePie::NAMESPACE_MEDIARSS_WRONG5) {
$namespace = \SimplePie\SimplePie::NAMESPACE_MEDIARSS;
}
$cache[$string] = [$namespace, $local_name];
} else {
$cache[$string] = ['', $string];
}
}
return $cache[$string];
}
private function parse_hcard($data, $category = false)
{
$name = '';
$link = '';
// Check if h-card is set and pass that information on in the link.
if (isset($data['type']) && in_array('h-card', $data['type'])) {
if (isset($data['properties']['name'][0])) {
$name = $data['properties']['name'][0];
}
if (isset($data['properties']['url'][0])) {
$link = $data['properties']['url'][0];
if ($name === '') {
$name = $link;
} else {
// can't have commas in categories.
$name = str_replace(',', '', $name);
}
$person_tag = $category ? '<span class="person-tag"></span>' : '';
return '<a class="h-card" href="'.$link.'">'.$person_tag.$name.'</a>';
}
}
return $data['value'] ?? '';
}
private function parse_microformats(&$data, $url)
{
$feed_title = '';
$feed_author = null;
$author_cache = [];
$items = [];
$entries = [];
$mf = \Mf2\parse($data, $url);
// First look for an h-feed.
$h_feed = [];
foreach ($mf['items'] as $mf_item) {
if (in_array('h-feed', $mf_item['type'])) {
$h_feed = $mf_item;
break;
}
// Also look for h-feed or h-entry in the children of each top level item.
if (!isset($mf_item['children'][0]['type'])) {
continue;
}
if (in_array('h-feed', $mf_item['children'][0]['type'])) {
$h_feed = $mf_item['children'][0];
// In this case the parent of the h-feed may be an h-card, so use it as
// the feed_author.
if (in_array('h-card', $mf_item['type'])) {
$feed_author = $mf_item;
}
break;
} elseif (in_array('h-entry', $mf_item['children'][0]['type'])) {
$entries = $mf_item['children'];
// In this case the parent of the h-entry list may be an h-card, so use
// it as the feed_author.
if (in_array('h-card', $mf_item['type'])) {
$feed_author = $mf_item;
}
break;
}
}
if (isset($h_feed['children'])) {
$entries = $h_feed['children'];
// Also set the feed title and store author from the h-feed if available.
if (isset($mf['items'][0]['properties']['name'][0])) {
$feed_title = $mf['items'][0]['properties']['name'][0];
}
if (isset($mf['items'][0]['properties']['author'][0])) {
$feed_author = $mf['items'][0]['properties']['author'][0];
}
} elseif (count($entries) === 0) {
$entries = $mf['items'];
}
for ($i = 0; $i < count($entries); $i++) {
$entry = $entries[$i];
if (in_array('h-entry', $entry['type'])) {
$item = [];
$title = '';
$description = '';
if (isset($entry['properties']['url'][0])) {
$link = $entry['properties']['url'][0];
if (isset($link['value'])) {
$link = $link['value'];
}
$item['link'] = [['data' => $link]];
}
if (isset($entry['properties']['uid'][0])) {
$guid = $entry['properties']['uid'][0];
if (isset($guid['value'])) {
$guid = $guid['value'];
}
$item['guid'] = [['data' => $guid]];
}
if (isset($entry['properties']['name'][0])) {
$title = $entry['properties']['name'][0];
if (isset($title['value'])) {
$title = $title['value'];
}
$item['title'] = [['data' => $title]];
}
if (isset($entry['properties']['author'][0]) || isset($feed_author)) {
// author is a special case, it can be plain text or an h-card array.
// If it's plain text it can also be a url that should be followed to
// get the actual h-card.
$author = $entry['properties']['author'][0] ?? $feed_author;
if (!is_string($author)) {
$author = $this->parse_hcard($author);
} elseif (strpos($author, 'http') === 0) {
if (isset($author_cache[$author])) {
$author = $author_cache[$author];
} else {
$mf = \Mf2\fetch($author);
foreach ($mf['items'] as $hcard) {
// Only interested in an h-card by itself in this case.
if (!in_array('h-card', $hcard['type'])) {
continue;
}
// It must have a url property matching what we fetched.
if (!isset($hcard['properties']['url']) ||
!(in_array($author, $hcard['properties']['url']))) {
continue;
}
// Save parse_hcard the trouble of finding the correct url.
$hcard['properties']['url'][0] = $author;
// Cache this h-card for the next h-entry to check.
$author_cache[$author] = $this->parse_hcard($hcard);
$author = $author_cache[$author];
break;
}
}
}
$item['author'] = [['data' => $author]];
}
if (isset($entry['properties']['photo'][0])) {
// If a photo is also in content, don't need to add it again here.
$content = '';
if (isset($entry['properties']['content'][0]['html'])) {
$content = $entry['properties']['content'][0]['html'];
}
$photo_list = [];
for ($j = 0; $j < count($entry['properties']['photo']); $j++) {
$photo = $entry['properties']['photo'][$j];
if (!empty($photo) && strpos($content, $photo) === false) {
$photo_list[] = $photo;
}
}
// When there's more than one photo show the first and use a lightbox.
// Need a permanent, unique name for the image set, but don't have
// anything unique except for the content itself, so use that.
$count = count($photo_list);
if ($count > 1) {
$image_set_id = preg_replace('/[[:^alnum:]]/', '', $photo_list[0]);
$description = '<p>';
for ($j = 0; $j < $count; $j++) {
$hidden = $j === 0 ? '' : 'class="hidden" ';
$description .= '<a href="'.$photo_list[$j].'" '.$hidden.
'data-lightbox="image-set-'.$image_set_id.'">'.
'<img src="'.$photo_list[$j].'"></a>';
}
$description .= '<br><b>'.$count.' photos</b></p>';
} elseif ($count == 1) {
$description = '<p><img src="'.$photo_list[0].'"></p>';
}
}
if (isset($entry['properties']['content'][0]['html'])) {
// e-content['value'] is the same as p-name when they are on the same
// element. Use this to replace title with a strip_tags version so
// that alt text from images is not included in the title.
if ($entry['properties']['content'][0]['value'] === $title) {
$title = strip_tags($entry['properties']['content'][0]['html']);
$item['title'] = [['data' => $title]];
}
$description .= $entry['properties']['content'][0]['html'];
if (isset($entry['properties']['in-reply-to'][0])) {
$in_reply_to = '';
if (is_string($entry['properties']['in-reply-to'][0])) {
$in_reply_to = $entry['properties']['in-reply-to'][0];
} elseif (isset($entry['properties']['in-reply-to'][0]['value'])) {
$in_reply_to = $entry['properties']['in-reply-to'][0]['value'];
}
if ($in_reply_to !== '') {
$description .= '<p><span class="in-reply-to"></span> '.
'<a href="'.$in_reply_to.'">'.$in_reply_to.'</a><p>';
}
}
$item['description'] = [['data' => $description]];
}
if (isset($entry['properties']['category'])) {
$category_csv = '';
// Categories can also contain h-cards.
foreach ($entry['properties']['category'] as $category) {
if ($category_csv !== '') {
$category_csv .= ', ';
}
if (is_string($category)) {
// Can't have commas in categories.
$category_csv .= str_replace(',', '', $category);
} else {
$category_csv .= $this->parse_hcard($category, true);
}
}
$item['category'] = [['data' => $category_csv]];
}
if (isset($entry['properties']['published'][0])) {
$timestamp = strtotime($entry['properties']['published'][0]);
$pub_date = date('F j Y g:ia', $timestamp).' GMT';
$item['pubDate'] = [['data' => $pub_date]];
}
// The title and description are set to the empty string to represent
// a deleted item (which also makes it an invalid rss item).
if (isset($entry['properties']['deleted'][0])) {
$item['title'] = [['data' => '']];
$item['description'] = [['data' => '']];
}
$items[] = ['child' => ['' => $item]];
}
}
// Mimic RSS data format when storing microformats.
$link = [['data' => $url]];
$image = '';
if (!is_string($feed_author) &&
isset($feed_author['properties']['photo'][0])) {
$image = [['child' => ['' => ['url' =>
[['data' => $feed_author['properties']['photo'][0]]]]]]];
}
// Use the name given for the h-feed, or get the title from the html.
if ($feed_title !== '') {
$feed_title = [['data' => htmlspecialchars($feed_title)]];
} elseif ($position = strpos($data, '<title>')) {
$start = $position < 200 ? 0 : $position - 200;
$check = substr($data, $start, 400);
$matches = [];
if (preg_match('/<title>(.+)<\/title>/', $check, $matches)) {
$feed_title = [['data' => htmlspecialchars($matches[1])]];
}
}
$channel = ['channel' => [['child' => ['' =>
['link' => $link, 'image' => $image, 'title' => $feed_title,
'item' => $items]]]]];
$rss = [['attribs' => ['' => ['version' => '2.0']],
'child' => ['' => $channel]]];
$this->data = ['child' => ['' => ['rss' => $rss]]];
return true;
}
private function declare_html_entities()
{
// This is required because the RSS specification says that entity-encoded
// html is allowed, but the xml specification says they must be declared.
return '<!DOCTYPE html [ <!ENTITY nbsp " "> <!ENTITY iexcl "¡"> <!ENTITY cent "¢"> <!ENTITY pound "£"> <!ENTITY curren "¤"> <!ENTITY yen "¥"> <!ENTITY brvbar "¦"> <!ENTITY sect "§"> <!ENTITY uml "¨"> <!ENTITY copy "©"> <!ENTITY ordf "ª"> <!ENTITY laquo "«"> <!ENTITY not "¬"> <!ENTITY shy "­"> <!ENTITY reg "®"> <!ENTITY macr "¯"> <!ENTITY deg "°"> <!ENTITY plusmn "±"> <!ENTITY sup2 "²"> <!ENTITY sup3 "³"> <!ENTITY acute "´"> <!ENTITY micro "µ"> <!ENTITY para "¶"> <!ENTITY middot "·"> <!ENTITY cedil "¸"> <!ENTITY sup1 "¹"> <!ENTITY ordm "º"> <!ENTITY raquo "»"> <!ENTITY frac14 "¼"> <!ENTITY frac12 "½"> <!ENTITY frac34 "¾"> <!ENTITY iquest "¿"> <!ENTITY Agrave "À"> <!ENTITY Aacute "Á"> <!ENTITY Acirc "Â"> <!ENTITY Atilde "Ã"> <!ENTITY Auml "Ä"> <!ENTITY Aring "Å"> <!ENTITY AElig "Æ"> <!ENTITY Ccedil "Ç"> <!ENTITY Egrave "È"> <!ENTITY Eacute "É"> <!ENTITY Ecirc "Ê"> <!ENTITY Euml "Ë"> <!ENTITY Igrave "Ì"> <!ENTITY Iacute "Í"> <!ENTITY Icirc "Î"> <!ENTITY Iuml "Ï"> <!ENTITY ETH "Ð"> <!ENTITY Ntilde "Ñ"> <!ENTITY Ograve "Ò"> <!ENTITY Oacute "Ó"> <!ENTITY Ocirc "Ô"> <!ENTITY Otilde "Õ"> <!ENTITY Ouml "Ö"> <!ENTITY times "×"> <!ENTITY Oslash "Ø"> <!ENTITY Ugrave "Ù"> <!ENTITY Uacute "Ú"> <!ENTITY Ucirc "Û"> <!ENTITY Uuml "Ü"> <!ENTITY Yacute "Ý"> <!ENTITY THORN "Þ"> <!ENTITY szlig "ß"> <!ENTITY agrave "à"> <!ENTITY aacute "á"> <!ENTITY acirc "â"> <!ENTITY atilde "ã"> <!ENTITY auml "ä"> <!ENTITY aring "å"> <!ENTITY aelig "æ"> <!ENTITY ccedil "ç"> <!ENTITY egrave "è"> <!ENTITY eacute "é"> <!ENTITY ecirc "ê"> <!ENTITY euml "ë"> <!ENTITY igrave "ì"> <!ENTITY iacute "í"> <!ENTITY icirc "î"> <!ENTITY iuml "ï"> <!ENTITY eth "ð"> <!ENTITY ntilde "ñ"> <!ENTITY ograve "ò"> <!ENTITY oacute "ó"> <!ENTITY ocirc "ô"> <!ENTITY otilde "õ"> <!ENTITY ouml "ö"> <!ENTITY divide "÷"> <!ENTITY oslash "ø"> <!ENTITY ugrave "ù"> <!ENTITY uacute "ú"> <!ENTITY ucirc "û"> <!ENTITY uuml "ü"> <!ENTITY yacute "ý"> <!ENTITY thorn "þ"> <!ENTITY yuml "ÿ"> <!ENTITY OElig "Œ"> <!ENTITY oelig "œ"> <!ENTITY Scaron "Š"> <!ENTITY scaron "š"> <!ENTITY Yuml "Ÿ"> <!ENTITY fnof "ƒ"> <!ENTITY circ "ˆ"> <!ENTITY tilde "˜"> <!ENTITY Alpha "Α"> <!ENTITY Beta "Β"> <!ENTITY Gamma "Γ"> <!ENTITY Epsilon "Ε"> <!ENTITY Zeta "Ζ"> <!ENTITY Eta "Η"> <!ENTITY Theta "Θ"> <!ENTITY Iota "Ι"> <!ENTITY Kappa "Κ"> <!ENTITY Lambda "Λ"> <!ENTITY Mu "Μ"> <!ENTITY Nu "Ν"> <!ENTITY Xi "Ξ"> <!ENTITY Omicron "Ο"> <!ENTITY Pi "Π"> <!ENTITY Rho "Ρ"> <!ENTITY Sigma "Σ"> <!ENTITY Tau "Τ"> <!ENTITY Upsilon "Υ"> <!ENTITY Phi "Φ"> <!ENTITY Chi "Χ"> <!ENTITY Psi "Ψ"> <!ENTITY Omega "Ω"> <!ENTITY alpha "α"> <!ENTITY beta "β"> <!ENTITY gamma "γ"> <!ENTITY delta "δ"> <!ENTITY epsilon "ε"> <!ENTITY zeta "ζ"> <!ENTITY eta "η"> <!ENTITY theta "θ"> <!ENTITY iota "ι"> <!ENTITY kappa "κ"> <!ENTITY lambda "λ"> <!ENTITY mu "μ"> <!ENTITY nu "ν"> <!ENTITY xi "ξ"> <!ENTITY omicron "ο"> <!ENTITY pi "π"> <!ENTITY rho "ρ"> <!ENTITY sigmaf "ς"> <!ENTITY sigma "σ"> <!ENTITY tau "τ"> <!ENTITY upsilon "υ"> <!ENTITY phi "φ"> <!ENTITY chi "χ"> <!ENTITY psi "ψ"> <!ENTITY omega "ω"> <!ENTITY thetasym "ϑ"> <!ENTITY upsih "ϒ"> <!ENTITY piv "ϖ"> <!ENTITY ensp " "> <!ENTITY emsp " "> <!ENTITY thinsp " "> <!ENTITY zwnj "‌"> <!ENTITY zwj "‍"> <!ENTITY lrm "‎"> <!ENTITY rlm "‏"> <!ENTITY ndash "–"> <!ENTITY mdash "—"> <!ENTITY lsquo "‘"> <!ENTITY rsquo "’"> <!ENTITY sbquo "‚"> <!ENTITY ldquo "“"> <!ENTITY rdquo "”"> <!ENTITY bdquo "„"> <!ENTITY dagger "†"> <!ENTITY Dagger "‡"> <!ENTITY bull "•"> <!ENTITY hellip "…"> <!ENTITY permil "‰"> <!ENTITY prime "′"> <!ENTITY Prime "″"> <!ENTITY lsaquo "‹"> <!ENTITY rsaquo "›"> <!ENTITY oline "‾"> <!ENTITY frasl "⁄"> <!ENTITY euro "€"> <!ENTITY image "ℑ"> <!ENTITY weierp "℘"> <!ENTITY real "ℜ"> <!ENTITY trade "™"> <!ENTITY alefsym "ℵ"> <!ENTITY larr "←"> <!ENTITY uarr "↑"> <!ENTITY rarr "→"> <!ENTITY darr "↓"> <!ENTITY harr "↔"> <!ENTITY crarr "↵"> <!ENTITY lArr "⇐"> <!ENTITY uArr "⇑"> <!ENTITY rArr "⇒"> <!ENTITY dArr "⇓"> <!ENTITY hArr "⇔"> <!ENTITY forall "∀"> <!ENTITY part "∂"> <!ENTITY exist "∃"> <!ENTITY empty "∅"> <!ENTITY nabla "∇"> <!ENTITY isin "∈"> <!ENTITY notin "∉"> <!ENTITY ni "∋"> <!ENTITY prod "∏"> <!ENTITY sum "∑"> <!ENTITY minus "−"> <!ENTITY lowast "∗"> <!ENTITY radic "√"> <!ENTITY prop "∝"> <!ENTITY infin "∞"> <!ENTITY ang "∠"> <!ENTITY and "∧"> <!ENTITY or "∨"> <!ENTITY cap "∩"> <!ENTITY cup "∪"> <!ENTITY int "∫"> <!ENTITY there4 "∴"> <!ENTITY sim "∼"> <!ENTITY cong "≅"> <!ENTITY asymp "≈"> <!ENTITY ne "≠"> <!ENTITY equiv "≡"> <!ENTITY le "≤"> <!ENTITY ge "≥"> <!ENTITY sub "⊂"> <!ENTITY sup "⊃"> <!ENTITY nsub "⊄"> <!ENTITY sube "⊆"> <!ENTITY supe "⊇"> <!ENTITY oplus "⊕"> <!ENTITY otimes "⊗"> <!ENTITY perp "⊥"> <!ENTITY sdot "⋅"> <!ENTITY lceil "⌈"> <!ENTITY rceil "⌉"> <!ENTITY lfloor "⌊"> <!ENTITY rfloor "⌋"> <!ENTITY lang "〈"> <!ENTITY rang "〉"> <!ENTITY loz "◊"> <!ENTITY spades "♠"> <!ENTITY clubs "♣"> <!ENTITY hearts "♥"> <!ENTITY diams "♦"> ]>';
}
}
class_alias('SimplePie\Parser', 'SimplePie_Parser');
Internal/DownloadPermissionsAdjuster.php 0000644 00000013527 15133220024 0014536 0 ustar 00 <?php
/**
* DownloadPermissionsAdjuster class file.
*/
namespace Automattic\WooCommerce\Internal;
use Automattic\WooCommerce\Proxies\LegacyProxy;
defined( 'ABSPATH' ) || exit;
/**
* Class to adjust download permissions on product save.
*/
class DownloadPermissionsAdjuster {
/**
* The downloads data store to use.
*
* @var WC_Data_Store
*/
private $downloads_data_store;
/**
* Class initialization, to be executed when the class is resolved by the container.
*
* @internal
*/
final public function init() {
$this->downloads_data_store = wc_get_container()->get( LegacyProxy::class )->get_instance_of( \WC_Data_Store::class, 'customer-download' );
add_action( 'adjust_download_permissions', array( $this, 'adjust_download_permissions' ), 10, 1 );
}
/**
* Schedule a download permissions adjustment for a product if necessary.
* This should be executed whenever a product is saved.
*
* @param \WC_Product $product The product to schedule a download permission adjustments for.
*/
public function maybe_schedule_adjust_download_permissions( \WC_Product $product ) {
$children_ids = $product->get_children();
if ( ! $children_ids ) {
return;
}
$scheduled_action_args = array( $product->get_id() );
$already_scheduled_actions =
WC()->call_function(
'as_get_scheduled_actions',
array(
'hook' => 'adjust_download_permissions',
'args' => $scheduled_action_args,
'status' => \ActionScheduler_Store::STATUS_PENDING,
),
'ids'
);
if ( empty( $already_scheduled_actions ) ) {
WC()->call_function(
'as_schedule_single_action',
WC()->call_function( 'time' ) + 1,
'adjust_download_permissions',
$scheduled_action_args
);
}
}
/**
* Create additional download permissions for variations if necessary.
*
* When a simple downloadable product is converted to a variable product,
* existing download permissions are still present in the database but they don't apply anymore.
* This method creates additional download permissions for the variations based on
* the old existing ones for the main product.
*
* The procedure is as follows. For each existing download permission for the parent product,
* check if there's any variation offering the same file for download (the file URL, not name, is checked).
* If that is found, check if an equivalent permission exists (equivalent means for the same file and with
* the same order id and customer id). If no equivalent permission exists, create it.
*
* @param int $product_id The id of the product to check permissions for.
*/
public function adjust_download_permissions( int $product_id ) {
$product = wc_get_product( $product_id );
if ( ! $product ) {
return;
}
$children_ids = $product->get_children();
if ( ! $children_ids ) {
return;
}
$parent_downloads = $this->get_download_files_and_permissions( $product );
if ( ! $parent_downloads ) {
return;
}
$children_with_downloads = array();
foreach ( $children_ids as $child_id ) {
$child = wc_get_product( $child_id );
$children_with_downloads[ $child_id ] = $this->get_download_files_and_permissions( $child );
}
foreach ( $parent_downloads['permission_data_by_file_order_user'] as $parent_file_order_and_user => $parent_download_data ) {
foreach ( $children_with_downloads as $child_id => $child_download_data ) {
$file_url = $parent_download_data['file'];
$must_create_permission =
// The variation offers the same file as the parent for download...
in_array( $file_url, array_keys( $child_download_data['download_ids_by_file_url'] ), true ) &&
// ...but no equivalent download permission (same file URL, order id and user id) exists.
! array_key_exists( $parent_file_order_and_user, $child_download_data['permission_data_by_file_order_user'] );
if ( $must_create_permission ) {
// The new child download permission is a copy of the parent's,
// but with the product and download ids changed to match those of the variation.
$new_download_data = $parent_download_data['data'];
$new_download_data['product_id'] = $child_id;
$new_download_data['download_id'] = $child_download_data['download_ids_by_file_url'][ $file_url ];
$this->downloads_data_store->create_from_data( $new_download_data );
}
}
}
}
/**
* Get the existing downloadable files and download permissions for a given product.
* The returned value is an array with two keys:
*
* - download_ids_by_file_url: an associative array of file url => download_id.
* - permission_data_by_file_order_user: an associative array where key is "file_url:customer_id:order_id" and value is the full permission data set.
*
* @param \WC_Product $product The product to get the downloadable files and permissions for.
* @return array[] Information about the downloadable files and permissions for the product.
*/
private function get_download_files_and_permissions( \WC_Product $product ) {
$result = array(
'permission_data_by_file_order_user' => array(),
'download_ids_by_file_url' => array(),
);
$downloads = $product->get_downloads();
foreach ( $downloads as $download ) {
$result['download_ids_by_file_url'][ $download->get_file() ] = $download->get_id();
}
$permissions = $this->downloads_data_store->get_downloads( array( 'product_id' => $product->get_id() ) );
foreach ( $permissions as $permission ) {
$permission_data = (array) $permission->data;
if ( array_key_exists( $permission_data['download_id'], $downloads ) ) {
$file = $downloads[ $permission_data['download_id'] ]->get_file();
$data = array(
'file' => $file,
'data' => (array) $permission->data,
);
$result['permission_data_by_file_order_user'][ "${file}:${permission_data['user_id']}:${permission_data['order_id']}" ] = $data;
}
}
return $result;
}
}
Internal/RestApiUtil.php 0000644 00000013304 15133220024 0011227 0 ustar 00 <?php
/**
* ApiUtil class file.
*/
namespace Automattic\WooCommerce\Internal;
/**
* Helper methos for the REST API.
*
* Class ApiUtil
*
* @package Automattic\WooCommerce\Internal
*/
class RestApiUtil {
/**
* Converts a create refund request from the public API format:
*
* [
* "reason" => "",
* "api_refund" => "x",
* "api_restock" => "x",
* "line_items" => [
* "id" => "111",
* "quantity" => 222,
* "refund_total" => 333,
* "refund_tax" => [
* [
* "id": "444",
* "refund_total": 555
* ],...
* ],...
* ]
*
* ...to the internally used format:
*
* [
* "reason" => null, (if it's missing or any empty value, set as null)
* "api_refund" => true, (if it's missing or non-bool, set as "true")
* "api_restock" => true, (if it's missing or non-bool, set as "true")
* "line_items" => [ (convert sequential array to associative based on "id")
* "111" => [
* "qty" => 222, (rename "quantity" to "qty")
* "refund_total" => 333,
* "refund_tax" => [ (convert sequential array to associative based on "id" and "refund_total)
* "444" => 555,...
* ],...
* ]
* ]
*
* It also calculates the amount if missing and whenever possible, see maybe_calculate_refund_amount_from_line_items.
*
* The conversion is done in a way that if the request is already in the internal format,
* then nothing is changed for compatibility. For example, if the line items array
* is already an associative array or any of its elements
* is missing the "id" key, then the entire array is left unchanged.
* Same for the "refund_tax" array inside each line item.
*
* @param \WP_REST_Request $request The request to adjust.
*/
public static function adjust_create_refund_request_parameters( \WP_REST_Request &$request ) {
if ( empty( $request['reason'] ) ) {
$request['reason'] = null;
}
if ( ! is_bool( $request['api_refund'] ) ) {
$request['api_refund'] = true;
}
if ( ! is_bool( $request['api_restock'] ) ) {
$request['api_restock'] = true;
}
if ( empty( $request['line_items'] ) ) {
$request['line_items'] = array();
} else {
$request['line_items'] = self::adjust_line_items_for_create_refund_request( $request['line_items'] );
}
if ( ! isset( $request['amount'] ) ) {
$amount = self::calculate_refund_amount_from_line_items( $request );
if ( null !== $amount ) {
$request['amount'] = strval( $amount );
}
}
}
/**
* Calculate the "amount" parameter for the request based on the amounts found in line items.
* This will ONLY be possible if ALL of the following is true:
*
* - "line_items" in the request is a non-empty array.
* - All line items have a "refund_total" field with a numeric value.
* - All values inside "refund_tax" in all line items are a numeric value.
*
* The request is assumed to be in internal format already.
*
* @param \WP_REST_Request $request The request to maybe calculate the total amount for.
* @return number|null The calculated amount, or null if it can't be calculated.
*/
private static function calculate_refund_amount_from_line_items( $request ) {
$line_items = $request['line_items'];
if ( ! is_array( $line_items ) || empty( $line_items ) ) {
return null;
}
$amount = 0;
foreach ( $line_items as $item ) {
if ( ! isset( $item['refund_total'] ) || ! is_numeric( $item['refund_total'] ) ) {
return null;
}
$amount += $item['refund_total'];
if ( ! isset( $item['refund_tax'] ) ) {
continue;
}
foreach ( $item['refund_tax'] as $tax ) {
if ( ! is_numeric( $tax ) ) {
return null;
}
$amount += $tax;
}
}
return $amount;
}
/**
* Convert the line items of a refund request to internal format (see adjust_create_refund_request_parameters).
*
* @param array $line_items The line items to convert.
* @return array The converted line items.
*/
private static function adjust_line_items_for_create_refund_request( $line_items ) {
if ( ! is_array( $line_items ) || empty( $line_items ) || self::is_associative( $line_items ) ) {
return $line_items;
}
$new_array = array();
foreach ( $line_items as $item ) {
if ( ! isset( $item['id'] ) ) {
return $line_items;
}
if ( isset( $item['quantity'] ) && ! isset( $item['qty'] ) ) {
$item['qty'] = $item['quantity'];
}
unset( $item['quantity'] );
if ( isset( $item['refund_tax'] ) ) {
$item['refund_tax'] = self::adjust_taxes_for_create_refund_request_line_item( $item['refund_tax'] );
}
$id = $item['id'];
$new_array[ $id ] = $item;
unset( $new_array[ $id ]['id'] );
}
return $new_array;
}
/**
* Adjust the taxes array from a line item in a refund request, see adjust_create_refund_parameters.
*
* @param array $taxes_array The array to adjust.
* @return array The adjusted array.
*/
private static function adjust_taxes_for_create_refund_request_line_item( $taxes_array ) {
if ( ! is_array( $taxes_array ) || empty( $taxes_array ) || self::is_associative( $taxes_array ) ) {
return $taxes_array;
}
$new_array = array();
foreach ( $taxes_array as $item ) {
if ( ! isset( $item['id'] ) || ! isset( $item['refund_total'] ) ) {
return $taxes_array;
}
$id = $item['id'];
$refund_total = $item['refund_total'];
$new_array[ $id ] = $refund_total;
}
return $new_array;
}
/**
* Is an array sequential or associative?
*
* @param array $array The array to check.
* @return bool True if the array is associative, false if it's sequential.
*/
private static function is_associative( array $array ) {
return array_keys( $array ) !== range( 0, count( $array ) - 1 );
}
}
Internal/ProductAttributesLookup/DataRegenerator.php 0000644 00000032722 15133220024 0016757 0 ustar 00 <?php
/**
* DataRegenerator class file.
*/
namespace Automattic\WooCommerce\Internal\ProductAttributesLookup;
use Automattic\WooCommerce\Internal\ProductAttributesLookup\LookupDataStore;
use Automattic\WooCommerce\Utilities\ArrayUtil;
defined( 'ABSPATH' ) || exit;
/**
* This class handles the (re)generation of the product attributes lookup table.
* It schedules the regeneration in small product batches by itself, so it can be used outside the
* regular WooCommerce data regenerations mechanism.
*
* After the regeneration is completed a wp_wc_product_attributes_lookup table will exist with entries for
* all the products that existed when initiate_regeneration was invoked; entries for products created after that
* are supposed to be created/updated by the appropriate data store classes (or by the code that uses
* the data store classes) whenever a product is created/updated.
*
* Additionally, after the regeneration is completed a 'woocommerce_attribute_lookup_enabled' option
* with a value of 'no' will have been created.
*
* This class also adds two entries to the Status - Tools menu: one for manually regenerating the table contents,
* and another one for enabling or disabling the actual lookup table usage.
*/
class DataRegenerator {
const PRODUCTS_PER_GENERATION_STEP = 10;
/**
* The data store to use.
*
* @var LookupDataStore
*/
private $data_store;
/**
* The lookup table name.
*
* @var string
*/
private $lookup_table_name;
/**
* DataRegenerator constructor.
*/
public function __construct() {
global $wpdb;
$this->lookup_table_name = $wpdb->prefix . 'wc_product_attributes_lookup';
add_filter(
'woocommerce_debug_tools',
function( $tools ) {
return $this->add_initiate_regeneration_entry_to_tools_array( $tools );
},
1,
999
);
add_action(
'woocommerce_run_product_attribute_lookup_regeneration_callback',
function () {
$this->run_regeneration_step_callback();
}
);
}
/**
* Class initialization, invoked by the DI container.
*
* @internal
* @param LookupDataStore $data_store The data store to use.
*/
final public function init( LookupDataStore $data_store ) {
$this->data_store = $data_store;
}
/**
* Initialize the regeneration procedure:
* deletes the lookup table and related options if they exist,
* then it creates the table and runs the first step of the regeneration process.
*
* This is the method that should be used as a callback for a data regeneration in wc-update-functions, e.g.:
*
* function wc_update_XX_regenerate_product_attributes_lookup_table() {
* wc_get_container()->get(DataRegenerator::class)->initiate_regeneration();
* return false;
* }
*
* (Note how we are returning "false" since the class handles the step scheduling by itself).
*/
public function initiate_regeneration() {
$this->enable_or_disable_lookup_table_usage( false );
$this->delete_all_attributes_lookup_data();
$products_exist = $this->initialize_table_and_data();
if ( $products_exist ) {
$this->enqueue_regeneration_step_run();
} else {
$this->finalize_regeneration();
}
}
/**
* Delete all the existing data related to the lookup table, including the table itself.
*
* Shortcut to run this method in case the debug tools UI isn't available or for quick debugging:
*
* wp eval "wc_get_container()->get(Automattic\WooCommerce\Internal\ProductAttributesLookup\DataRegenerator::class)->delete_all_attributes_lookup_data();"
*/
public function delete_all_attributes_lookup_data() {
global $wpdb;
delete_option( 'woocommerce_attribute_lookup_enabled' );
delete_option( 'woocommerce_attribute_lookup_last_product_id_to_process' );
delete_option( 'woocommerce_attribute_lookup_last_products_page_processed' );
$this->data_store->unset_regeneration_in_progress_flag();
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
$wpdb->query( 'DROP TABLE IF EXISTS ' . $this->lookup_table_name );
}
/**
* Create the lookup table and initialize the options that will be temporarily used
* while the regeneration is in progress.
*
* @return bool True if there's any product at all in the database, false otherwise.
*/
private function initialize_table_and_data() {
global $wpdb;
// phpcs:disable WordPress.DB.PreparedSQL.NotPrepared
$wpdb->query(
'
CREATE TABLE ' . $this->lookup_table_name . '(
product_id bigint(20) NOT NULL,
product_or_parent_id bigint(20) NOT NULL,
taxonomy varchar(32) NOT NULL,
term_id bigint(20) NOT NULL,
is_variation_attribute tinyint(1) NOT NULL,
in_stock tinyint(1) NOT NULL
);
'
);
// phpcs:enable WordPress.DB.PreparedSQL.NotPrepared
$last_existing_product_id =
WC()->call_function(
'wc_get_products',
array(
'return' => 'ids',
'limit' => 1,
'orderby' => array(
'ID' => 'DESC',
),
)
);
if ( ! $last_existing_product_id ) {
// No products exist, nothing to (re)generate.
return false;
}
$this->data_store->set_regeneration_in_progress_flag();
update_option( 'woocommerce_attribute_lookup_last_product_id_to_process', current( $last_existing_product_id ) );
update_option( 'woocommerce_attribute_lookup_last_products_page_processed', 0 );
return true;
}
/**
* Action scheduler callback, performs one regeneration step and then
* schedules the next step if necessary.
*/
private function run_regeneration_step_callback() {
if ( ! $this->data_store->regeneration_is_in_progress() ) {
return;
}
$result = $this->do_regeneration_step();
if ( $result ) {
$this->enqueue_regeneration_step_run();
} else {
$this->finalize_regeneration();
}
}
/**
* Enqueue one regeneration step in action scheduler.
*/
private function enqueue_regeneration_step_run() {
$queue = WC()->get_instance_of( \WC_Queue::class );
$queue->schedule_single(
WC()->call_function( 'time' ) + 1,
'woocommerce_run_product_attribute_lookup_regeneration_callback',
array(),
'woocommerce-db-updates'
);
}
/**
* Perform one regeneration step: grabs a chunk of products and creates
* the appropriate entries for them in the lookup table.
*
* @return bool True if more steps need to be run, false otherwise.
*/
private function do_regeneration_step() {
$last_products_page_processed = get_option( 'woocommerce_attribute_lookup_last_products_page_processed' );
$current_products_page = (int) $last_products_page_processed + 1;
$product_ids = WC()->call_function(
'wc_get_products',
array(
'limit' => self::PRODUCTS_PER_GENERATION_STEP,
'page' => $current_products_page,
'orderby' => array(
'ID' => 'ASC',
),
'return' => 'ids',
)
);
if ( ! $product_ids ) {
return false;
}
foreach ( $product_ids as $id ) {
$this->data_store->create_data_for_product( $id );
}
update_option( 'woocommerce_attribute_lookup_last_products_page_processed', $current_products_page );
$last_product_id_to_process = get_option( 'woocommerce_attribute_lookup_last_product_id_to_process' );
return end( $product_ids ) < $last_product_id_to_process;
}
/**
* Cleanup/final option setup after the regeneration has been completed.
*/
private function finalize_regeneration() {
delete_option( 'woocommerce_attribute_lookup_last_product_id_to_process' );
delete_option( 'woocommerce_attribute_lookup_last_products_page_processed' );
update_option( 'woocommerce_attribute_lookup_enabled', 'no' );
$this->data_store->unset_regeneration_in_progress_flag();
}
/**
* Add a 'Regenerate product attributes lookup table' entry to the Status - Tools page.
*
* @param array $tools_array The tool definitions array that is passed ro the woocommerce_debug_tools filter.
* @return array The tools array with the entry added.
*/
private function add_initiate_regeneration_entry_to_tools_array( array $tools_array ) {
if ( ! $this->data_store->is_feature_visible() ) {
return $tools_array;
}
$lookup_table_exists = $this->data_store->check_lookup_table_exists();
$generation_is_in_progress = $this->data_store->regeneration_is_in_progress();
// Regenerate table.
if ( $lookup_table_exists ) {
$generate_item_name = __( 'Regenerate the product attributes lookup table', 'woocommerce' );
$generate_item_desc = __( 'This tool will regenerate the product attributes lookup table data from existing product(s) data. This process may take a while.', 'woocommerce' );
$generate_item_return = __( 'Product attributes lookup table data is regenerating', 'woocommerce' );
$generate_item_button = __( 'Regenerate', 'woocommerce' );
} else {
$generate_item_name = __( 'Create and fill product attributes lookup table', 'woocommerce' );
$generate_item_desc = __( 'This tool will create the product attributes lookup table data and fill it with existing products data. This process may take a while.', 'woocommerce' );
$generate_item_return = __( 'Product attributes lookup table is being filled', 'woocommerce' );
$generate_item_button = __( 'Create', 'woocommerce' );
}
$entry = array(
'name' => $generate_item_name,
'desc' => $generate_item_desc,
'requires_refresh' => true,
'callback' => function() use ( $generate_item_return ) {
$this->initiate_regeneration_from_tools_page();
return $generate_item_return;
},
);
if ( $lookup_table_exists ) {
$entry['selector'] = array(
'description' => __( 'Select a product to regenerate the data for, or leave empty for a full table regeneration:', 'woocommerce' ),
'class' => 'wc-product-search',
'search_action' => 'woocommerce_json_search_products',
'name' => 'regenerate_product_attribute_lookup_data_product_id',
'placeholder' => esc_attr__( 'Search for a product…', 'woocommerce' ),
);
}
if ( $generation_is_in_progress ) {
$entry['button'] = sprintf(
/* translators: %d: How many products have been processed so far. */
__( 'Filling in progress (%d)', 'woocommerce' ),
get_option( 'woocommerce_attribute_lookup_last_products_page_processed', 0 ) * self::PRODUCTS_PER_GENERATION_STEP
);
$entry['disabled'] = true;
} else {
$entry['button'] = $generate_item_button;
}
$tools_array['regenerate_product_attributes_lookup_table'] = $entry;
if ( $lookup_table_exists ) {
// Delete the table.
$tools_array['delete_product_attributes_lookup_table'] = array(
'name' => __( 'Delete the product attributes lookup table', 'woocommerce' ),
'desc' => sprintf(
'<strong class="red">%1$s</strong> %2$s',
__( 'Note:', 'woocommerce' ),
__( 'This will delete the product attributes lookup table. You can create it again with the "Create and fill product attributes lookup table" tool.', 'woocommerce' )
),
'button' => __( 'Delete', 'woocommerce' ),
'requires_refresh' => true,
'callback' => function () {
$this->delete_all_attributes_lookup_data();
return __( 'Product attributes lookup table has been deleted.', 'woocommerce' );
},
);
}
return $tools_array;
}
/**
* Callback to initiate the regeneration process from the Status - Tools page.
*
* @throws \Exception The regeneration is already in progress.
*/
private function initiate_regeneration_from_tools_page() {
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput
if ( ! isset( $_REQUEST['_wpnonce'] ) || false === wp_verify_nonce( $_REQUEST['_wpnonce'], 'debug_action' ) ) {
throw new \Exception( 'Invalid nonce' );
}
if ( isset( $_REQUEST['regenerate_product_attribute_lookup_data_product_id'] ) ) {
$product_id = (int) $_REQUEST['regenerate_product_attribute_lookup_data_product_id'];
$this->check_can_do_lookup_table_regeneration( $product_id );
$this->data_store->create_data_for_product( $product_id );
} else {
$this->check_can_do_lookup_table_regeneration();
$this->initiate_regeneration();
}
}
/**
* Enable or disable the actual lookup table usage.
*
* @param bool $enable True to enable, false to disable.
* @throws \Exception A lookup table regeneration is currently in progress.
*/
private function enable_or_disable_lookup_table_usage( $enable ) {
if ( $this->data_store->regeneration_is_in_progress() ) {
throw new \Exception( "Can't enable or disable the attributes lookup table usage while it's regenerating." );
}
update_option( 'woocommerce_attribute_lookup_enabled', $enable ? 'yes' : 'no' );
}
/**
* Check if everything is good to go to perform a complete or per product lookup table data regeneration
* and throw an exception if not.
*
* @param mixed $product_id The product id to check the regeneration viability for, or null to check if a complete regeneration is possible.
* @throws \Exception Something prevents the regeneration from starting.
*/
private function check_can_do_lookup_table_regeneration( $product_id = null ) {
if ( ! $this->data_store->is_feature_visible() ) {
throw new \Exception( "Can't do product attribute lookup data regeneration: feature is not visible" );
}
if ( $product_id && ! $this->data_store->check_lookup_table_exists() ) {
throw new \Exception( "Can't do product attribute lookup data regeneration: lookup table doesn't exist" );
}
if ( $this->data_store->regeneration_is_in_progress() ) {
throw new \Exception( "Can't do product attribute lookup data regeneration: regeneration is already in progress" );
}
if ( $product_id && ! wc_get_product( $product_id ) ) {
throw new \Exception( "Can't do product attribute lookup data regeneration: product doesn't exist" );
}
}
}
Internal/ProductAttributesLookup/LookupDataStore.php 0000644 00000052600 15133220024 0016765 0 ustar 00 <?php
/**
* LookupDataStore class file.
*/
namespace Automattic\WooCommerce\Internal\ProductAttributesLookup;
use Automattic\WooCommerce\Utilities\ArrayUtil;
defined( 'ABSPATH' ) || exit;
/**
* Data store class for the product attributes lookup table.
*/
class LookupDataStore {
/**
* Types of updates to perform depending on the current changest
*/
const ACTION_NONE = 0;
const ACTION_INSERT = 1;
const ACTION_UPDATE_STOCK = 2;
const ACTION_DELETE = 3;
/**
* The lookup table name.
*
* @var string
*/
private $lookup_table_name;
/**
* Is the feature visible?
*
* @var bool
*/
private $is_feature_visible;
/**
* LookupDataStore constructor. Makes the feature hidden by default.
*/
public function __construct() {
global $wpdb;
$this->lookup_table_name = $wpdb->prefix . 'wc_product_attributes_lookup';
$this->is_feature_visible = false;
$this->init_hooks();
}
/**
* Initialize the hooks used by the class.
*/
private function init_hooks() {
add_action(
'woocommerce_run_product_attribute_lookup_update_callback',
function ( $product_id, $action ) {
$this->run_update_callback( $product_id, $action );
},
10,
2
);
add_filter(
'woocommerce_get_sections_products',
function ( $products ) {
if ( $this->is_feature_visible() && $this->check_lookup_table_exists() ) {
$products['advanced'] = __( 'Advanced', 'woocommerce' );
}
return $products;
},
100,
1
);
add_filter(
'woocommerce_get_settings_products',
function ( $settings, $section_id ) {
if ( 'advanced' === $section_id && $this->is_feature_visible() && $this->check_lookup_table_exists() ) {
$title_item = array(
'title' => __( 'Product attributes lookup table', 'woocommerce' ),
'type' => 'title',
);
$regeneration_is_in_progress = $this->regeneration_is_in_progress();
if ( $regeneration_is_in_progress ) {
$title_item['desc'] = __( 'These settings are not available while the lookup table regeneration is in progress.', 'woocommerce' );
}
$settings[] = $title_item;
if ( ! $regeneration_is_in_progress ) {
$settings[] = array(
'title' => __( 'Enable table usage', 'woocommerce' ),
'desc' => __( 'Use the product attributes lookup table for catalog filtering.', 'woocommerce' ),
'id' => 'woocommerce_attribute_lookup_enabled',
'default' => 'no',
'type' => 'checkbox',
'checkboxgroup' => 'start',
);
$settings[] = array(
'title' => __( 'Direct updates', 'woocommerce' ),
'desc' => __( 'Update the table directly upon product changes, instead of scheduling a deferred update.', 'woocommerce' ),
'id' => 'woocommerce_attribute_lookup_direct_updates',
'default' => 'no',
'type' => 'checkbox',
'checkboxgroup' => 'start',
);
}
$settings[] = array( 'type' => 'sectionend' );
}
return $settings;
},
100,
2
);
}
/**
* Check if the lookup table exists in the database.
*
* TODO: Remove this method and references to it once the lookup table is created via data migration.
*
* @return bool
*/
public function check_lookup_table_exists() {
global $wpdb;
$query = $wpdb->prepare( 'SHOW TABLES LIKE %s', $wpdb->esc_like( $this->lookup_table_name ) );
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
return $this->lookup_table_name === $wpdb->get_var( $query );
}
/**
* Checks if the feature is visible (so that dedicated entries will be added to the debug tools page).
*
* @return bool True if the feature is visible.
*/
public function is_feature_visible() {
return $this->is_feature_visible;
}
/**
* Makes the feature visible, so that dedicated entries will be added to the debug tools page.
*/
public function show_feature() {
$this->is_feature_visible = true;
}
/**
* Hides the feature, so that no entries will be added to the debug tools page.
*/
public function hide_feature() {
$this->is_feature_visible = false;
}
/**
* Get the name of the lookup table.
*
* @return string
*/
public function get_lookup_table_name() {
return $this->lookup_table_name;
}
/**
* Insert/update the appropriate lookup table entries for a new or modified product or variation.
* This must be invoked after a product or a variation is created (including untrashing and duplication)
* or modified.
*
* @param int|\WC_Product $product Product object or product id.
* @param null|array $changeset Changes as provided by 'get_changes' method in the product object, null if it's being created.
*/
public function on_product_changed( $product, $changeset = null ) {
if ( ! $this->check_lookup_table_exists() ) {
return;
}
if ( ! is_a( $product, \WC_Product::class ) ) {
$product = WC()->call_function( 'wc_get_product', $product );
}
$action = $this->get_update_action( $changeset );
if ( self::ACTION_NONE !== $action ) {
$this->maybe_schedule_update( $product->get_id(), $action );
}
}
/**
* Schedule an update of the product attributes lookup table for a given product.
* If an update for the same action is already scheduled, nothing is done.
*
* If the 'woocommerce_attribute_lookup_direct_update' option is set to 'yes',
* the update is done directly, without scheduling.
*
* @param int $product_id The product id to schedule the update for.
* @param int $action The action to perform, one of the ACTION_ constants.
*/
private function maybe_schedule_update( int $product_id, int $action ) {
if ( 'yes' === get_option( 'woocommerce_attribute_lookup_direct_updates' ) ) {
$this->run_update_callback( $product_id, $action );
return;
}
$args = array( $product_id, $action );
$queue = WC()->get_instance_of( \WC_Queue::class );
$already_scheduled = $queue->search(
array(
'hook' => 'woocommerce_run_product_attribute_lookup_update_callback',
'args' => $args,
'status' => \ActionScheduler_Store::STATUS_PENDING,
),
'ids'
);
if ( empty( $already_scheduled ) ) {
$queue->schedule_single(
WC()->call_function( 'time' ) + 1,
'woocommerce_run_product_attribute_lookup_update_callback',
$args,
'woocommerce-db-updates'
);
}
}
/**
* Perform an update of the lookup table for a specific product.
*
* @param int $product_id The product id to perform the update for.
* @param int $action The action to perform, one of the ACTION_ constants.
*/
private function run_update_callback( int $product_id, int $action ) {
if ( ! $this->check_lookup_table_exists() ) {
return;
}
$product = WC()->call_function( 'wc_get_product', $product_id );
if ( ! $product ) {
$action = self::ACTION_DELETE;
}
switch ( $action ) {
case self::ACTION_INSERT:
$this->delete_data_for( $product_id );
$this->create_data_for( $product );
break;
case self::ACTION_UPDATE_STOCK:
$this->update_stock_status_for( $product );
break;
case self::ACTION_DELETE:
$this->delete_data_for( $product_id );
break;
}
}
/**
* Determine the type of action to perform depending on the received changeset.
*
* @param array|null $changeset The changeset received by on_product_changed.
* @return int One of the ACTION_ constants.
*/
private function get_update_action( $changeset ) {
if ( is_null( $changeset ) ) {
// No changeset at all means that the product is new.
return self::ACTION_INSERT;
}
$keys = array_keys( $changeset );
// Order matters:
// - The change with the most precedence is a change in catalog visibility
// (which will result in all data being regenerated or deleted).
// - Then a change in attributes (all data will be regenerated).
// - And finally a change in stock status (existing data will be updated).
// Thus these conditions must be checked in that same order.
if ( in_array( 'catalog_visibility', $keys, true ) ) {
$new_visibility = $changeset['catalog_visibility'];
if ( 'visible' === $new_visibility || 'catalog' === $new_visibility ) {
return self::ACTION_INSERT;
} else {
return self::ACTION_DELETE;
}
}
if ( in_array( 'attributes', $keys, true ) ) {
return self::ACTION_INSERT;
}
if ( array_intersect( $keys, array( 'stock_quantity', 'stock_status', 'manage_stock' ) ) ) {
return self::ACTION_UPDATE_STOCK;
}
return self::ACTION_NONE;
}
/**
* Update the stock status of the lookup table entries for a given product.
*
* @param \WC_Product $product The product to update the entries for.
*/
private function update_stock_status_for( \WC_Product $product ) {
global $wpdb;
$in_stock = $product->is_in_stock();
// phpcs:disable WordPress.DB.PreparedSQL.NotPrepared
$wpdb->query(
$wpdb->prepare(
'UPDATE ' . $this->lookup_table_name . ' SET in_stock = %d WHERE product_id = %d',
$in_stock ? 1 : 0,
$product->get_id()
)
);
// phpcs:enable WordPress.DB.PreparedSQL.NotPrepared
}
/**
* Delete the lookup table contents related to a given product or variation,
* if it's a variable product it deletes the information for variations too.
* This must be invoked after a product or a variation is trashed or deleted.
*
* @param int|\WC_Product $product Product object or product id.
*/
public function on_product_deleted( $product ) {
if ( ! $this->check_lookup_table_exists() ) {
return;
}
if ( is_a( $product, \WC_Product::class ) ) {
$product_id = $product->get_id();
} else {
$product_id = $product;
}
$this->maybe_schedule_update( $product_id, self::ACTION_DELETE );
}
/**
* Create the lookup data for a given product, if a variable product is passed
* the information is created for all of its variations.
* This method is intended to be called from the data regenerator.
*
* @param int|WC_Product $product Product object or id.
* @throws \Exception A variation object is passed.
*/
public function create_data_for_product( $product ) {
if ( ! is_a( $product, \WC_Product::class ) ) {
$product = WC()->call_function( 'wc_get_product', $product );
}
if ( $this->is_variation( $product ) ) {
throw new \Exception( "LookupDataStore::create_data_for_product can't be called for variations." );
}
$this->delete_data_for( $product->get_id() );
$this->create_data_for( $product );
}
/**
* Create lookup table data for a given product.
*
* @param \WC_Product $product The product to create the data for.
*/
private function create_data_for( \WC_Product $product ) {
if ( $this->is_variation( $product ) ) {
$this->create_data_for_variation( $product );
} elseif ( $this->is_variable_product( $product ) ) {
$this->create_data_for_variable_product( $product );
} else {
$this->create_data_for_simple_product( $product );
}
}
/**
* Delete all the lookup table entries for a given product,
* if it's a variable product information for variations is deleted too.
*
* @param int $product_id Simple product id, or main/parent product id for variable products.
*/
private function delete_data_for( int $product_id ) {
global $wpdb;
// phpcs:disable WordPress.DB.PreparedSQL.NotPrepared
$wpdb->query(
$wpdb->prepare(
'DELETE FROM ' . $this->lookup_table_name . ' WHERE product_id = %d OR product_or_parent_id = %d',
$product_id,
$product_id
)
);
// phpcs:enable WordPress.DB.PreparedSQL.NotPrepared
}
/**
* Create lookup table entries for a simple (non variable) product.
* Assumes that no entries exist yet.
*
* @param \WC_Product $product The product to create the entries for.
*/
private function create_data_for_simple_product( \WC_Product $product ) {
$product_attributes_data = $this->get_attribute_taxonomies( $product );
$has_stock = $product->is_in_stock();
$product_id = $product->get_id();
foreach ( $product_attributes_data as $taxonomy => $data ) {
$term_ids = $data['term_ids'];
foreach ( $term_ids as $term_id ) {
$this->insert_lookup_table_data( $product_id, $product_id, $taxonomy, $term_id, false, $has_stock );
}
}
}
/**
* Create lookup table entries for a variable product.
* Assumes that no entries exist yet.
*
* @param \WC_Product_Variable $product The product to create the entries for.
*/
private function create_data_for_variable_product( \WC_Product_Variable $product ) {
$product_attributes_data = $this->get_attribute_taxonomies( $product );
$variation_attributes_data = array_filter(
$product_attributes_data,
function( $item ) {
return $item['used_for_variations'];
}
);
$non_variation_attributes_data = array_filter(
$product_attributes_data,
function( $item ) {
return ! $item['used_for_variations'];
}
);
$main_product_has_stock = $product->is_in_stock();
$main_product_id = $product->get_id();
foreach ( $non_variation_attributes_data as $taxonomy => $data ) {
$term_ids = $data['term_ids'];
foreach ( $term_ids as $term_id ) {
$this->insert_lookup_table_data( $main_product_id, $main_product_id, $taxonomy, $term_id, false, $main_product_has_stock );
}
}
$term_ids_by_slug_cache = $this->get_term_ids_by_slug_cache( array_keys( $variation_attributes_data ) );
$variations = $this->get_variations_of( $product );
foreach ( $variation_attributes_data as $taxonomy => $data ) {
foreach ( $variations as $variation ) {
$this->insert_lookup_table_data_for_variation( $variation, $taxonomy, $main_product_id, $data['term_ids'], $term_ids_by_slug_cache );
}
}
}
/**
* Create all the necessary lookup data for a given variation.
*
* @param \WC_Product_Variation $variation The variation to create entries for.
*/
private function create_data_for_variation( \WC_Product_Variation $variation ) {
$main_product = WC()->call_function( 'wc_get_product', $variation->get_parent_id() );
$product_attributes_data = $this->get_attribute_taxonomies( $main_product );
$variation_attributes_data = array_filter(
$product_attributes_data,
function( $item ) {
return $item['used_for_variations'];
}
);
$term_ids_by_slug_cache = $this->get_term_ids_by_slug_cache( array_keys( $variation_attributes_data ) );
foreach ( $variation_attributes_data as $taxonomy => $data ) {
$this->insert_lookup_table_data_for_variation( $variation, $taxonomy, $main_product->get_id(), $data['term_ids'], $term_ids_by_slug_cache );
}
}
/**
* Create lookup table entries for a given variation, corresponding to a given taxonomy and a set of term ids.
*
* @param \WC_Product_Variation $variation The variation to create entries for.
* @param string $taxonomy The taxonomy to create the entries for.
* @param int $main_product_id The parent product id.
* @param array $term_ids The term ids to create entries for.
* @param array $term_ids_by_slug_cache A dictionary of term ids by term slug, as returned by 'get_term_ids_by_slug_cache'.
*/
private function insert_lookup_table_data_for_variation( \WC_Product_Variation $variation, string $taxonomy, int $main_product_id, array $term_ids, array $term_ids_by_slug_cache ) {
$variation_id = $variation->get_id();
$variation_has_stock = $variation->is_in_stock();
$variation_definition_term_id = $this->get_variation_definition_term_id( $variation, $taxonomy, $term_ids_by_slug_cache );
if ( $variation_definition_term_id ) {
$this->insert_lookup_table_data( $variation_id, $main_product_id, $taxonomy, $variation_definition_term_id, true, $variation_has_stock );
} else {
$term_ids_for_taxonomy = $term_ids;
foreach ( $term_ids_for_taxonomy as $term_id ) {
$this->insert_lookup_table_data( $variation_id, $main_product_id, $taxonomy, $term_id, true, $variation_has_stock );
}
}
}
/**
* Get a cache of term ids by slug for a set of taxonomies, with this format:
*
* [
* 'taxonomy' => [
* 'slug_1' => id_1,
* 'slug_2' => id_2,
* ...
* ], ...
* ]
*
* @param array $taxonomies List of taxonomies to build the cache for.
* @return array A dictionary of taxonomies => dictionary of term slug => term id.
*/
private function get_term_ids_by_slug_cache( $taxonomies ) {
$result = array();
foreach ( $taxonomies as $taxonomy ) {
$terms = WC()->call_function(
'get_terms',
array(
'taxonomy' => $taxonomy,
'hide_empty' => false,
'fields' => 'id=>slug',
)
);
$result[ $taxonomy ] = array_flip( $terms );
}
return $result;
}
/**
* Get the id of the term that defines a variation for a given taxonomy,
* or null if there's no such defining id (for variations having "Any <taxonomy>" as the definition)
*
* @param \WC_Product_Variation $variation The variation to get the defining term id for.
* @param string $taxonomy The taxonomy to get the defining term id for.
* @param array $term_ids_by_slug_cache A term ids by slug as generated by get_term_ids_by_slug_cache.
* @return int|null The term id, or null if there's no defining id for that taxonomy in that variation.
*/
private function get_variation_definition_term_id( \WC_Product_Variation $variation, string $taxonomy, array $term_ids_by_slug_cache ) {
$variation_attributes = $variation->get_attributes();
$term_slug = ArrayUtil::get_value_or_default( $variation_attributes, $taxonomy );
if ( $term_slug ) {
return $term_ids_by_slug_cache[ $taxonomy ][ $term_slug ];
} else {
return null;
}
}
/**
* Get the variations of a given variable product.
*
* @param \WC_Product_Variable $product The product to get the variations for.
* @return array An array of WC_Product_Variation objects.
*/
private function get_variations_of( \WC_Product_Variable $product ) {
$variation_ids = $product->get_children();
return array_map(
function( $id ) {
return WC()->call_function( 'wc_get_product', $id );
},
$variation_ids
);
}
/**
* Check if a given product is a variable product.
*
* @param \WC_Product $product The product to check.
* @return bool True if it's a variable product, false otherwise.
*/
private function is_variable_product( \WC_Product $product ) {
return is_a( $product, \WC_Product_Variable::class );
}
/**
* Check if a given product is a variation.
*
* @param \WC_Product $product The product to check.
* @return bool True if it's a variation, false otherwise.
*/
private function is_variation( \WC_Product $product ) {
return is_a( $product, \WC_Product_Variation::class );
}
/**
* Return the list of taxonomies used for variations on a product together with
* the associated term ids, with the following format:
*
* [
* 'taxonomy_name' =>
* [
* 'term_ids' => [id, id, ...],
* 'used_for_variations' => true|false
* ], ...
* ]
*
* @param \WC_Product $product The product to get the attribute taxonomies for.
* @return array Information about the attribute taxonomies of the product.
*/
private function get_attribute_taxonomies( \WC_Product $product ) {
$product_attributes = $product->get_attributes();
$result = array();
foreach ( $product_attributes as $taxonomy_name => $attribute_data ) {
if ( ! $attribute_data->get_id() ) {
// Custom product attribute, not suitable for attribute-based filtering.
continue;
}
$result[ $taxonomy_name ] = array(
'term_ids' => $attribute_data->get_options(),
'used_for_variations' => $attribute_data->get_variation(),
);
}
return $result;
}
/**
* Insert one entry in the lookup table.
*
* @param int $product_id The product id.
* @param int $product_or_parent_id The product id for non-variable products, the main/parent product id for variations.
* @param string $taxonomy Taxonomy name.
* @param int $term_id Term id.
* @param bool $is_variation_attribute True if the taxonomy corresponds to an attribute used to define variations.
* @param bool $has_stock True if the product is in stock.
*/
private function insert_lookup_table_data( int $product_id, int $product_or_parent_id, string $taxonomy, int $term_id, bool $is_variation_attribute, bool $has_stock ) {
global $wpdb;
// phpcs:disable WordPress.DB.PreparedSQL.NotPrepared
$wpdb->query(
$wpdb->prepare(
'INSERT INTO ' . $this->lookup_table_name . ' (
product_id,
product_or_parent_id,
taxonomy,
term_id,
is_variation_attribute,
in_stock)
VALUES
( %d, %d, %s, %d, %d, %d )',
$product_id,
$product_or_parent_id,
$taxonomy,
$term_id,
$is_variation_attribute ? 1 : 0,
$has_stock ? 1 : 0
)
);
// phpcs:enable WordPress.DB.PreparedSQL.NotPrepared
}
/**
* Tells if a lookup table regeneration is currently in progress.
*
* @return bool True if a lookup table regeneration is already in progress.
*/
public function regeneration_is_in_progress() {
return 'yes' === get_option( 'woocommerce_attribute_lookup_regeneration_in_progress', null );
}
/**
* Set a permanent flag (via option) indicating that the lookup table regeneration is in process.
*/
public function set_regeneration_in_progress_flag() {
update_option( 'woocommerce_attribute_lookup_regeneration_in_progress', 'yes' );
}
/**
* Remove the flag indicating that the lookup table regeneration is in process.
*/
public function unset_regeneration_in_progress_flag() {
delete_option( 'woocommerce_attribute_lookup_regeneration_in_progress' );
}
}
Internal/ProductAttributesLookup/Filterer.php 0000644 00000027011 15133220024 0015457 0 ustar 00 <?php
/**
* Filterer class file.
*/
namespace Automattic\WooCommerce\Internal\ProductAttributesLookup;
defined( 'ABSPATH' ) || exit;
/**
* Helper class for filtering products using the product attributes lookup table.
*/
class Filterer {
/**
* The product attributes lookup data store to use.
*
* @var LookupDataStore
*/
private $data_store;
/**
* The name of the product attributes lookup table.
*
* @var string
*/
private $lookup_table_name;
/**
* Class initialization, invoked by the DI container.
*
* @internal
* @param LookupDataStore $data_store The data store to use.
*/
final public function init( LookupDataStore $data_store ) {
$this->data_store = $data_store;
$this->lookup_table_name = $data_store->get_lookup_table_name();
}
/**
* Checks if the product attribute filtering via lookup table feature is enabled.
*
* @return bool
*/
public function filtering_via_lookup_table_is_active() {
return 'yes' === get_option( 'woocommerce_attribute_lookup_enabled' );
}
/**
* Adds post clauses for filtering via lookup table.
* This method should be invoked within a 'posts_clauses' filter.
*
* @param array $args Product query clauses as supplied to the 'posts_clauses' filter.
* @param \WP_Query $wp_query Current product query as supplied to the 'posts_clauses' filter.
* @param array $attributes_to_filter_by Attribute filtering data as generated by WC_Query::get_layered_nav_chosen_attributes.
* @return array The updated product query clauses.
*/
public function filter_by_attribute_post_clauses( array $args, \WP_Query $wp_query, array $attributes_to_filter_by ) {
global $wpdb;
if ( ! $wp_query->is_main_query() || ! $this->filtering_via_lookup_table_is_active() ) {
return $args;
}
$clause_root = " {$wpdb->prefix}posts.ID IN (";
if ( 'yes' === get_option( 'woocommerce_hide_out_of_stock_items' ) ) {
$in_stock_clause = ' AND in_stock = 1';
} else {
$in_stock_clause = '';
}
foreach ( $attributes_to_filter_by as $taxonomy => $data ) {
$all_terms = get_terms( $taxonomy, array( 'hide_empty' => false ) );
$term_ids_by_slug = wp_list_pluck( $all_terms, 'term_id', 'slug' );
$term_ids_to_filter_by = array_values( array_intersect_key( $term_ids_by_slug, array_flip( $data['terms'] ) ) );
$term_ids_to_filter_by = array_map( 'absint', $term_ids_to_filter_by );
$term_ids_to_filter_by_list = '(' . join( ',', $term_ids_to_filter_by ) . ')';
$is_and_query = 'and' === $data['query_type'];
$count = count( $term_ids_to_filter_by );
if ( 0 !== $count ) {
if ( $is_and_query ) {
$clauses[] = "
{$clause_root}
SELECT product_or_parent_id
FROM {$this->lookup_table_name} lt
WHERE is_variation_attribute=0
{$in_stock_clause}
AND term_id in {$term_ids_to_filter_by_list}
GROUP BY product_id
HAVING COUNT(product_id)={$count}
UNION
SELECT product_or_parent_id
FROM {$this->lookup_table_name} lt
WHERE is_variation_attribute=1
{$in_stock_clause}
AND term_id in {$term_ids_to_filter_by_list}
)";
} else {
$clauses[] = "
{$clause_root}
SELECT product_or_parent_id
FROM {$this->lookup_table_name} lt
WHERE term_id in {$term_ids_to_filter_by_list}
{$in_stock_clause}
)";
}
}
}
if ( ! empty( $clauses ) ) {
$args['where'] .= ' AND (' . join( ' AND ', $clauses ) . ')';
} elseif ( ! empty( $attributes_to_filter_by ) ) {
$args['where'] .= ' AND 1=0';
}
return $args;
}
/**
* Count products within certain terms, taking the main WP query into consideration,
* for the WC_Widget_Layered_Nav widget.
*
* This query allows counts to be generated based on the viewed products, not all products.
*
* @param array $term_ids Term IDs.
* @param string $taxonomy Taxonomy.
* @param string $query_type Query Type.
* @return array
*/
public function get_filtered_term_product_counts( $term_ids, $taxonomy, $query_type ) {
global $wpdb;
$use_lookup_table = $this->filtering_via_lookup_table_is_active();
$tax_query = \WC_Query::get_main_tax_query();
$meta_query = \WC_Query::get_main_meta_query();
if ( 'or' === $query_type ) {
foreach ( $tax_query as $key => $query ) {
if ( is_array( $query ) && $taxonomy === $query['taxonomy'] ) {
unset( $tax_query[ $key ] );
}
}
}
$meta_query = new \WP_Meta_Query( $meta_query );
$tax_query = new \WP_Tax_Query( $tax_query );
if ( $use_lookup_table ) {
$query = $this->get_product_counts_query_using_lookup_table( $tax_query, $meta_query, $taxonomy, $term_ids );
} else {
$query = $this->get_product_counts_query_not_using_lookup_table( $tax_query, $meta_query, $term_ids );
}
$query = apply_filters( 'woocommerce_get_filtered_term_product_counts_query', $query );
$query_sql = implode( ' ', $query );
// We have a query - let's see if cached results of this query already exist.
$query_hash = md5( $query_sql );
// Maybe store a transient of the count values.
$cache = apply_filters( 'woocommerce_layered_nav_count_maybe_cache', true );
if ( true === $cache ) {
$cached_counts = (array) get_transient( 'wc_layered_nav_counts_' . sanitize_title( $taxonomy ) );
} else {
$cached_counts = array();
}
if ( ! isset( $cached_counts[ $query_hash ] ) ) {
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
$results = $wpdb->get_results( $query_sql, ARRAY_A );
$counts = array_map( 'absint', wp_list_pluck( $results, 'term_count', 'term_count_id' ) );
$cached_counts[ $query_hash ] = $counts;
if ( true === $cache ) {
set_transient( 'wc_layered_nav_counts_' . sanitize_title( $taxonomy ), $cached_counts, DAY_IN_SECONDS );
}
}
return array_map( 'absint', (array) $cached_counts[ $query_hash ] );
}
/**
* Get the query for counting products by terms using the product attributes lookup table.
*
* @param \WP_Tax_Query $tax_query The current main tax query.
* @param \WP_Meta_Query $meta_query The current main meta query.
* @param string $taxonomy The attribute name to get the term counts for.
* @param string $term_ids The term ids to include in the search.
* @return array An array of SQL query parts.
*/
private function get_product_counts_query_using_lookup_table( $tax_query, $meta_query, $taxonomy, $term_ids ) {
global $wpdb;
$meta_query_sql = $meta_query->get_sql( 'post', $this->lookup_table_name, 'product_or_parent_id' );
$tax_query_sql = $tax_query->get_sql( $this->lookup_table_name, 'product_or_parent_id' );
$hide_out_of_stock = 'yes' === get_option( 'woocommerce_hide_out_of_stock_items' );
$in_stock_clause = $hide_out_of_stock ? ' AND in_stock = 1' : '';
$query['select'] = 'SELECT COUNT(DISTINCT product_or_parent_id) as term_count, term_id as term_count_id';
$query['from'] = "FROM {$this->lookup_table_name}";
$query['join'] = "
{$tax_query_sql['join']} {$meta_query_sql['join']}
INNER JOIN {$wpdb->posts} ON {$wpdb->posts}.ID = {$this->lookup_table_name}.product_or_parent_id";
$term_ids_sql = $this->get_term_ids_sql( $term_ids );
$query['where'] = "
WHERE {$wpdb->posts}.post_type IN ( 'product' )
AND {$wpdb->posts}.post_status = 'publish'
{$tax_query_sql['where']} {$meta_query_sql['where']}
AND {$this->lookup_table_name}.taxonomy='{$taxonomy}'
AND {$this->lookup_table_name}.term_id IN $term_ids_sql
{$in_stock_clause}";
if ( ! empty( $term_ids ) ) {
$attributes_to_filter_by = \WC_Query::get_layered_nav_chosen_attributes();
if ( ! empty( $attributes_to_filter_by ) ) {
$and_term_ids = array();
$or_term_ids = array();
foreach ( $attributes_to_filter_by as $taxonomy => $data ) {
$all_terms = get_terms( $taxonomy, array( 'hide_empty' => false ) );
$term_ids_by_slug = wp_list_pluck( $all_terms, 'term_id', 'slug' );
$term_ids_to_filter_by = array_values( array_intersect_key( $term_ids_by_slug, array_flip( $data['terms'] ) ) );
if ( 'and' === $data['query_type'] ) {
$and_term_ids = array_merge( $and_term_ids, $term_ids_to_filter_by );
} else {
$or_term_ids = array_merge( $or_term_ids, $term_ids_to_filter_by );
}
}
if ( ! empty( $and_term_ids ) ) {
$terms_count = count( $and_term_ids );
$term_ids_list = '(' . join( ',', $and_term_ids ) . ')';
$query['where'] .= "
AND product_or_parent_id IN (
SELECT product_or_parent_id
FROM {$this->lookup_table_name} lt
WHERE is_variation_attribute=0
{$in_stock_clause}
AND term_id in {$term_ids_list}
GROUP BY product_id
HAVING COUNT(product_id)={$terms_count}
UNION
SELECT product_or_parent_id
FROM {$this->lookup_table_name} lt
WHERE is_variation_attribute=1
{$in_stock_clause}
AND term_id in {$term_ids_list}
)";
}
if ( ! empty( $or_term_ids ) ) {
$term_ids_list = '(' . join( ',', $or_term_ids ) . ')';
$query['where'] .= "
AND product_or_parent_id IN (
SELECT product_or_parent_id FROM {$this->lookup_table_name}
WHERE term_id in {$term_ids_list}
{$in_stock_clause}
)";
}
} else {
$query['where'] .= $in_stock_clause;
}
} elseif ( $hide_out_of_stock ) {
$query['where'] .= " AND {$this->lookup_table_name}.in_stock=1";
}
$search_query_sql = \WC_Query::get_main_search_query_sql();
if ( $search_query_sql ) {
$query['where'] .= ' AND ' . $search_query_sql;
}
$query['group_by'] = 'GROUP BY terms.term_id';
$query['group_by'] = "GROUP BY {$this->lookup_table_name}.term_id";
return $query;
}
/**
* Get the query for counting products by terms NOT using the product attributes lookup table.
*
* @param \WP_Tax_Query $tax_query The current main tax query.
* @param \WP_Meta_Query $meta_query The current main meta query.
* @param string $term_ids The term ids to include in the search.
* @return array An array of SQL query parts.
*/
private function get_product_counts_query_not_using_lookup_table( $tax_query, $meta_query, $term_ids ) {
global $wpdb;
$meta_query_sql = $meta_query->get_sql( 'post', $wpdb->posts, 'ID' );
$tax_query_sql = $tax_query->get_sql( $wpdb->posts, 'ID' );
// Generate query.
$query = array();
$query['select'] = "SELECT COUNT( DISTINCT {$wpdb->posts}.ID ) AS term_count, terms.term_id AS term_count_id";
$query['from'] = "FROM {$wpdb->posts}";
$query['join'] = "
INNER JOIN {$wpdb->term_relationships} AS term_relationships ON {$wpdb->posts}.ID = term_relationships.object_id
INNER JOIN {$wpdb->term_taxonomy} AS term_taxonomy USING( term_taxonomy_id )
INNER JOIN {$wpdb->terms} AS terms USING( term_id )
" . $tax_query_sql['join'] . $meta_query_sql['join'];
$term_ids_sql = $this->get_term_ids_sql( $term_ids );
$query['where'] = "
WHERE {$wpdb->posts}.post_type IN ( 'product' )
AND {$wpdb->posts}.post_status = 'publish'
{$tax_query_sql['where']} {$meta_query_sql['where']}
AND terms.term_id IN $term_ids_sql";
$search_query_sql = \WC_Query::get_main_search_query_sql();
if ( $search_query_sql ) {
$query['where'] .= ' AND ' . $search_query_sql;
}
$query['group_by'] = 'GROUP BY terms.term_id';
return $query;
}
/**
* Formats a list of term ids as "(id,id,id)".
*
* @param array $term_ids The list of terms to format.
* @return string The formatted list.
*/
private function get_term_ids_sql( $term_ids ) {
return '(' . implode( ',', array_map( 'absint', $term_ids ) ) . ')';
}
}
Internal/AssignDefaultCategory.php 0000644 00000003631 15133220024 0013253 0 ustar 00 <?php
/**
* AssignDefaultCategory class file.
*/
namespace Automattic\WooCommerce\Internal;
defined( 'ABSPATH' ) || exit;
/**
* Class to assign default category to products.
*/
class AssignDefaultCategory {
/**
* Class initialization, to be executed when the class is resolved by the container.
*
* @internal
*/
final public function init() {
add_action( 'wc_schedule_update_product_default_cat', array( $this, 'maybe_assign_default_product_cat' ) );
}
/**
* When a product category is deleted, we need to check
* if the product has no categories assigned. Then assign
* it a default category. We delay this with a scheduled
* action job to not block the response.
*
* @return void
*/
public function schedule_action() {
WC()->queue()->schedule_single(
time(),
'wc_schedule_update_product_default_cat',
array(),
'wc_update_product_default_cat'
);
}
/**
* Assigns default product category for products
* that have no categories.
*
* @return void
*/
public function maybe_assign_default_product_cat() {
global $wpdb;
$default_category = get_option( 'default_product_cat', 0 );
if ( $default_category ) {
$wpdb->query(
$wpdb->prepare(
"INSERT INTO {$wpdb->term_relationships} (object_id, term_taxonomy_id)
SELECT DISTINCT posts.ID, %s FROM {$wpdb->posts} posts
LEFT JOIN
(
SELECT object_id FROM {$wpdb->term_relationships} term_relationships
LEFT JOIN {$wpdb->term_taxonomy} term_taxonomy ON term_relationships.term_taxonomy_id = term_taxonomy.term_taxonomy_id
WHERE term_taxonomy.taxonomy = 'product_cat'
) AS tax_query
ON posts.ID = tax_query.object_id
WHERE posts.post_type = 'product'
AND tax_query.object_id IS NULL",
$default_category
)
);
wp_cache_flush();
delete_transient( 'wc_term_counts' );
wp_update_term_count_now( array( $default_category ), 'product_cat' );
}
}
}
Internal/RestockRefundedItemsAdjuster.php 0000644 00000004121 15133220024 0014612 0 ustar 00 <?php
/**
* RestockRefundedItemsAdjuster class file.
*/
namespace Automattic\WooCommerce\Internal;
use Automattic\WooCommerce\Proxies\LegacyProxy;
defined( 'ABSPATH' ) || exit;
/**
* Class to adjust or initialize the restock refunded items.
*/
class RestockRefundedItemsAdjuster {
/**
* The order factory to use.
*
* @var WC_Order_Factory
*/
private $order_factory;
/**
* Class initialization, to be executed when the class is resolved by the container.
*
* @internal
*/
final public function init() {
$this->order_factory = wc_get_container()->get( LegacyProxy::class )->get_instance_of( \WC_Order_Factory::class );
add_action( 'woocommerce_before_save_order_items', array( $this, 'initialize_restock_refunded_items' ), 10, 2 );
}
/**
* Initializes the restock refunded items meta for order version less than 5.5.
*
* @see https://github.com/woocommerce/woocommerce/issues/29502
*
* @param int $order_id Order ID.
* @param array $items Order items to save.
*/
public function initialize_restock_refunded_items( $order_id, $items ) {
$order = wc_get_order( $order_id );
$order_version = $order->get_version();
if ( version_compare( $order_version, '5.5', '>=' ) ) {
return;
}
// If there are no refund lines, then this migration isn't necessary because restock related meta's wouldn't be set.
if ( 0 === count( $order->get_refunds() ) ) {
return;
}
if ( isset( $items['order_item_id'] ) ) {
foreach ( $items['order_item_id'] as $item_id ) {
$item = $this->order_factory::get_order_item( absint( $item_id ) );
if ( ! $item ) {
continue;
}
if ( 'line_item' !== $item->get_type() ) {
continue;
}
// There could be code paths in custom code which don't update version number but still update the items.
if ( '' !== $item->get_meta( '_restock_refunded_items', true ) ) {
continue;
}
$refunded_item_quantity = abs( $order->get_qty_refunded_for_item( $item->get_id() ) );
$item->add_meta_data( '_restock_refunded_items', $refunded_item_quantity, false );
$item->save();
}
}
}
}
Internal/DependencyManagement/ExtendedContainer.php 0000644 00000013676 15133220024 0016514 0 ustar 00 <?php
/**
* ExtendedContainer class file.
*/
namespace Automattic\WooCommerce\Internal\DependencyManagement;
use Automattic\WooCommerce\Utilities\StringUtil;
use Automattic\WooCommerce\Vendor\League\Container\Container as BaseContainer;
use Automattic\WooCommerce\Vendor\League\Container\Definition\DefinitionInterface;
/**
* This class extends the original League's Container object by adding some functionality
* that we need for WooCommerce.
*/
class ExtendedContainer extends BaseContainer {
/**
* The root namespace of all WooCommerce classes in the `src` directory.
*
* @var string
*/
private $woocommerce_namespace = 'Automattic\\WooCommerce\\';
/**
* Whitelist of classes that we can register using the container
* despite not belonging to the WooCommerce root namespace.
*
* In general we allow only the registration of classes in the
* WooCommerce root namespace to prevent registering 3rd party code
* (which doesn't really belong to this container) or old classes
* (which may be eventually deprecated, also the LegacyProxy
* should be used for those).
*
* @var string[]
*/
private $registration_whitelist = array(
\Psr\Container\ContainerInterface::class,
);
/**
* Register a class in the container.
*
* @param string $class_name Class name.
* @param mixed $concrete How to resolve the class with `get`: a factory callback, a concrete instance, another class name, or null to just create an instance of the class.
* @param bool|null $shared Whether the resolution should be performed only once and cached.
*
* @return DefinitionInterface The generated definition for the container.
* @throws ContainerException Invalid parameters.
*/
public function add( string $class_name, $concrete = null, bool $shared = null ) : DefinitionInterface {
if ( ! $this->is_class_allowed( $class_name ) ) {
throw new ContainerException( "You cannot add '$class_name', only classes in the {$this->woocommerce_namespace} namespace are allowed." );
}
$concrete_class = $this->get_class_from_concrete( $concrete );
if ( isset( $concrete_class ) && ! $this->is_class_allowed( $concrete_class ) ) {
throw new ContainerException( "You cannot add concrete '$concrete_class', only classes in the {$this->woocommerce_namespace} namespace are allowed." );
}
// We want to use a definition class that does not support constructor injection to avoid accidental usage.
if ( ! $concrete instanceof DefinitionInterface ) {
$concrete = new Definition( $class_name, $concrete );
}
return parent::add( $class_name, $concrete, $shared );
}
/**
* Replace an existing registration with a different concrete.
*
* @param string $class_name The class name whose definition will be replaced.
* @param mixed $concrete The new concrete (same as "add").
*
* @return DefinitionInterface The modified definition.
* @throws ContainerException Invalid parameters.
*/
public function replace( string $class_name, $concrete ) : DefinitionInterface {
if ( ! $this->has( $class_name ) ) {
throw new ContainerException( "The container doesn't have '$class_name' registered, please use 'add' instead of 'replace'." );
}
$concrete_class = $this->get_class_from_concrete( $concrete );
if ( isset( $concrete_class ) && ! $this->is_class_allowed( $concrete_class ) && ! $this->is_anonymous_class( $concrete_class ) ) {
throw new ContainerException( "You cannot use concrete '$concrete_class', only classes in the {$this->woocommerce_namespace} namespace are allowed." );
}
return $this->extend( $class_name )->setConcrete( $concrete );
}
/**
* Reset all the cached resolutions, so any further "get" for shared definitions will generate the instance again.
*/
public function reset_all_resolved() {
foreach ( $this->definitions->getIterator() as $definition ) {
// setConcrete causes the cached resolved value to be forgotten.
$concrete = $definition->getConcrete();
$definition->setConcrete( $concrete );
}
}
/**
* Get an instance of a registered class.
*
* @param string $id The class name.
* @param bool $new True to generate a new instance even if the class was registered as shared.
*
* @return object An instance of the requested class.
* @throws ContainerException Attempt to get an instance of a non-namespaced class.
*/
public function get( $id, bool $new = false ) {
if ( false === strpos( $id, '\\' ) ) {
throw new ContainerException( "Attempt to get an instance of the non-namespaced class '$id' from the container, did you forget to add a namespace import?" );
}
return parent::get( $id, $new );
}
/**
* Gets the class from the concrete regardless of type.
*
* @param mixed $concrete The concrete that we want the class from..
*
* @return string|null The class from the concrete if one is available, null otherwise.
*/
protected function get_class_from_concrete( $concrete ) {
if ( is_object( $concrete ) && ! is_callable( $concrete ) ) {
if ( $concrete instanceof DefinitionInterface ) {
return $this->get_class_from_concrete( $concrete->getConcrete() );
}
return get_class( $concrete );
}
if ( is_string( $concrete ) && class_exists( $concrete ) ) {
return $concrete;
}
return null;
}
/**
* Checks to see whether or not a class is allowed to be registered.
*
* @param string $class_name The class to check.
*
* @return bool True if the class is allowed to be registered, false otherwise.
*/
protected function is_class_allowed( string $class_name ): bool {
return StringUtil::starts_with( $class_name, $this->woocommerce_namespace, false ) || in_array( $class_name, $this->registration_whitelist, true );
}
/**
* Check if a class name corresponds to an anonymous class.
*
* @param string $class_name The class name to check.
* @return bool True if the name corresponds to an anonymous class.
*/
protected function is_anonymous_class( string $class_name ): bool {
return StringUtil::starts_with( $class_name, 'class@anonymous' );
}
}
Internal/DependencyManagement/AbstractServiceProvider.php 0000644 00000016177 15133220024 0017707 0 ustar 00 <?php
/**
* AbstractServiceProvider class file.
*/
namespace Automattic\WooCommerce\Internal\DependencyManagement;
use Automattic\WooCommerce\Vendor\League\Container\Argument\RawArgument;
use Automattic\WooCommerce\Vendor\League\Container\Definition\DefinitionInterface;
use Automattic\WooCommerce\Vendor\League\Container\ServiceProvider\AbstractServiceProvider as BaseServiceProvider;
/**
* Base class for the service providers used to register classes in the container.
*
* See the documentation of the original class this one is based on (https://container.thephpleague.com/3.x/service-providers)
* for basic usage details. What this class adds is:
*
* - The `add_with_auto_arguments` method that allows to register classes without having to specify the injection method arguments.
* - The `share_with_auto_arguments` method, sibling of the above.
* - Convenience `add` and `share` methods that are just proxies for the same methods in `$this->getContainer()`.
*/
abstract class AbstractServiceProvider extends BaseServiceProvider {
/**
* Register a class in the container and use reflection to guess the injection method arguments.
*
* WARNING: this method uses reflection, so please have performance in mind when using it.
*
* @param string $class_name Class name to register.
* @param mixed $concrete The concrete to register. Can be a shared instance, a factory callback, or a class name.
* @param bool $shared Whether to register the class as shared (`get` always returns the same instance) or not.
*
* @return DefinitionInterface The generated container definition.
*
* @throws ContainerException Error when reflecting the class, or class injection method is not public, or an argument has no valid type hint.
*/
protected function add_with_auto_arguments( string $class_name, $concrete = null, bool $shared = false ) : DefinitionInterface {
$definition = new Definition( $class_name, $concrete );
$function = $this->reflect_class_or_callable( $class_name, $concrete );
if ( ! is_null( $function ) ) {
$arguments = $function->getParameters();
foreach ( $arguments as $argument ) {
if ( $argument->isDefaultValueAvailable() ) {
$default_value = $argument->getDefaultValue();
$definition->addArgument( new RawArgument( $default_value ) );
} else {
$argument_class = $this->get_class( $argument );
if ( is_null( $argument_class ) ) {
throw new ContainerException( "Argument '{$argument->getName()}' of class '$class_name' doesn't have a type hint or has one that doesn't specify a class." );
}
$definition->addArgument( $argument_class->name );
}
}
}
// Register the definition only after being sure that no exception will be thrown.
$this->getContainer()->add( $definition->getAlias(), $definition, $shared );
return $definition;
}
/**
* Gets the class of a parameter.
*
* This method is a replacement for ReflectionParameter::getClass,
* which is deprecated as of PHP 8.
*
* @param \ReflectionParameter $parameter The parameter to get the class for.
*
* @return \ReflectionClass|null The class of the parameter, or null if it hasn't any.
*/
private function get_class( \ReflectionParameter $parameter ) {
// TODO: Remove this 'if' block once minimum PHP version for WooCommerce is bumped to at least 7.1.
if ( version_compare( PHP_VERSION, '7.1', '<' ) ) {
return $parameter->getClass();
}
return $parameter->getType() && ! $parameter->getType()->isBuiltin()
? new \ReflectionClass( $parameter->getType()->getName() )
: null;
}
/**
* Check if a combination of class name and concrete is valid for registration.
* Also return the class injection method if the concrete is either a class name or null (then use the supplied class name).
*
* @param string $class_name The class name to check.
* @param mixed $concrete The concrete to check.
*
* @return \ReflectionFunctionAbstract|null A reflection instance for the $class_name injection method or $concrete injection method or callable; null otherwise.
* @throws ContainerException Class has a private injection method, can't reflect class, or the concrete is invalid.
*/
private function reflect_class_or_callable( string $class_name, $concrete ) {
if ( ! isset( $concrete ) || is_string( $concrete ) && class_exists( $concrete ) ) {
try {
$class = $concrete ?? $class_name;
$method = new \ReflectionMethod( $class, Definition::INJECTION_METHOD );
if ( ! isset( $method ) ) {
return null;
}
$missing_modifiers = array();
if ( ! $method->isFinal() ) {
$missing_modifiers[] = 'final';
}
if ( ! $method->isPublic() ) {
$missing_modifiers[] = 'public';
}
if ( ! empty( $missing_modifiers ) ) {
throw new ContainerException( "Method '" . Definition::INJECTION_METHOD . "' of class '$class' isn't '" . implode( ' ', $missing_modifiers ) . "', instances can't be created." );
}
return $method;
} catch ( \ReflectionException $ex ) {
return null;
}
} elseif ( is_callable( $concrete ) ) {
try {
return new \ReflectionFunction( $concrete );
} catch ( \ReflectionException $ex ) {
throw new ContainerException( "Error when reflecting callable: {$ex->getMessage()}" );
}
}
return null;
}
/**
* Register a class in the container and use reflection to guess the injection method arguments.
* The class is registered as shared, so `get` on the container always returns the same instance.
*
* WARNING: this method uses reflection, so please have performance in mind when using it.
*
* @param string $class_name Class name to register.
* @param mixed $concrete The concrete to register. Can be a shared instance, a factory callback, or a class name.
*
* @return DefinitionInterface The generated container definition.
*
* @throws ContainerException Error when reflecting the class, or class injection method is not public, or an argument has no valid type hint.
*/
protected function share_with_auto_arguments( string $class_name, $concrete = null ) : DefinitionInterface {
return $this->add_with_auto_arguments( $class_name, $concrete, true );
}
/**
* Register an entry in the container.
*
* @param string $id Entry id (typically a class or interface name).
* @param mixed|null $concrete Concrete entity to register under that id, null for automatic creation.
* @param bool|null $shared Whether to register the class as shared (`get` always returns the same instance) or not.
*
* @return DefinitionInterface The generated container definition.
*/
protected function add( string $id, $concrete = null, bool $shared = null ) : DefinitionInterface {
return $this->getContainer()->add( $id, $concrete, $shared );
}
/**
* Register a shared entry in the container (`get` always returns the same instance).
*
* @param string $id Entry id (typically a class or interface name).
* @param mixed|null $concrete Concrete entity to register under that id, null for automatic creation.
*
* @return DefinitionInterface The generated container definition.
*/
protected function share( string $id, $concrete = null ) : DefinitionInterface {
return $this->add( $id, $concrete, true );
}
}
Internal/DependencyManagement/Definition.php 0000644 00000002177 15133220024 0015173 0 ustar 00 <?php
/**
* An extension to the Definition class to prevent constructor injection from being possible.
*/
namespace Automattic\WooCommerce\Internal\DependencyManagement;
use Automattic\WooCommerce\Vendor\League\Container\Definition\Definition as BaseDefinition;
/**
* An extension of the definition class that replaces constructor injection with method injection.
*/
class Definition extends BaseDefinition {
/**
* The standard method that we use for dependency injection.
*/
const INJECTION_METHOD = 'init';
/**
* Resolve a class using method injection instead of constructor injection.
*
* @param string $concrete The concrete to instantiate.
*
* @return object
*/
protected function resolveClass( string $concrete ) {
$resolved = $this->resolveArguments( $this->arguments );
$concrete = new $concrete();
// Constructor injection causes backwards compatibility problems
// so we will rely on method injection via an internal method.
if ( method_exists( $concrete, static::INJECTION_METHOD ) ) {
call_user_func_array( array( $concrete, static::INJECTION_METHOD ), $resolved );
}
return $concrete;
}
}
Internal/DependencyManagement/ServiceProviders/ProxiesServiceProvider.php 0000644 00000001464 15133220024 0023064 0 ustar 00 <?php
/**
* ProxiesServiceProvider class file.
*/
namespace Automattic\WooCommerce\Internal\DependencyManagement\ServiceProviders;
use Automattic\WooCommerce\Internal\DependencyManagement\AbstractServiceProvider;
use Automattic\WooCommerce\Proxies\LegacyProxy;
use Automattic\WooCommerce\Proxies\ActionsProxy;
/**
* Service provider for the classes in the Automattic\WooCommerce\Proxies namespace.
*/
class ProxiesServiceProvider extends AbstractServiceProvider {
/**
* The classes/interfaces that are serviced by this service provider.
*
* @var array
*/
protected $provides = array(
LegacyProxy::class,
ActionsProxy::class,
);
/**
* Register the classes.
*/
public function register() {
$this->share( ActionsProxy::class );
$this->share_with_auto_arguments( LegacyProxy::class );
}
}
Internal/DependencyManagement/ServiceProviders/DownloadPermissionsAdjusterServiceProvider.php 0000644 00000001364 15133220024 0027137 0 ustar 00 <?php
/**
* DownloadPermissionsAdjusterServiceProvider class file.
*/
namespace Automattic\WooCommerce\Internal\DependencyManagement\ServiceProviders;
use Automattic\WooCommerce\Internal\DependencyManagement\AbstractServiceProvider;
use Automattic\WooCommerce\Internal\DownloadPermissionsAdjuster;
/**
* Service provider for the DownloadPermissionsAdjuster class.
*/
class DownloadPermissionsAdjusterServiceProvider extends AbstractServiceProvider {
/**
* The classes/interfaces that are serviced by this service provider.
*
* @var array
*/
protected $provides = array(
DownloadPermissionsAdjuster::class,
);
/**
* Register the classes.
*/
public function register() {
$this->share( DownloadPermissionsAdjuster::class );
}
}
Internal/DependencyManagement/ServiceProviders/RestockRefundedItemsAdjusterServiceProvider.php 0000644 00000001372 15133220024 0027224 0 ustar 00 <?php
/**
* RestockRefundedItemsAdjusterServiceProvider class file.
*/
namespace Automattic\WooCommerce\Internal\DependencyManagement\ServiceProviders;
use Automattic\WooCommerce\Internal\DependencyManagement\AbstractServiceProvider;
use Automattic\WooCommerce\Internal\RestockRefundedItemsAdjuster;
/**
* Service provider for the RestockRefundedItemsAdjuster class.
*/
class RestockRefundedItemsAdjusterServiceProvider extends AbstractServiceProvider {
/**
* The classes/interfaces that are serviced by this service provider.
*
* @var array
*/
protected $provides = array(
RestockRefundedItemsAdjuster::class,
);
/**
* Register the classes.
*/
public function register() {
$this->share( RestockRefundedItemsAdjuster::class );
}
}
Internal/DependencyManagement/ServiceProviders/AssignDefaultCategoryServiceProvider.php 0000644 00000001320 15133220024 0025651 0 ustar 00 <?php
/**
* AssignDefaultCategoryServiceProvider class file.
*/
namespace Automattic\WooCommerce\Internal\DependencyManagement\ServiceProviders;
use Automattic\WooCommerce\Internal\DependencyManagement\AbstractServiceProvider;
use Automattic\WooCommerce\Internal\AssignDefaultCategory;
/**
* Service provider for the AssignDefaultCategory class.
*/
class AssignDefaultCategoryServiceProvider extends AbstractServiceProvider {
/**
* The classes/interfaces that are serviced by this service provider.
*
* @var array
*/
protected $provides = array(
AssignDefaultCategory::class,
);
/**
* Register the classes.
*/
public function register() {
$this->share( AssignDefaultCategory::class );
}
}
Internal/DependencyManagement/ServiceProviders/ProductAttributesLookupServiceProvider.php 0000644 00000002112 15133220024 0026303 0 ustar 00 <?php
/**
* ProductAttributesLookupServiceProvider class file.
*/
namespace Automattic\WooCommerce\Internal\DependencyManagement\ServiceProviders;
use Automattic\WooCommerce\Internal\DependencyManagement\AbstractServiceProvider;
use Automattic\WooCommerce\Internal\ProductAttributesLookup\DataRegenerator;
use Automattic\WooCommerce\Internal\ProductAttributesLookup\Filterer;
use Automattic\WooCommerce\Internal\ProductAttributesLookup\LookupDataStore;
/**
* Service provider for the ProductAttributesLookupServiceProvider namespace.
*/
class ProductAttributesLookupServiceProvider extends AbstractServiceProvider {
/**
* The classes/interfaces that are serviced by this service provider.
*
* @var array
*/
protected $provides = array(
DataRegenerator::class,
Filterer::class,
LookupDataStore::class,
);
/**
* Register the classes.
*/
public function register() {
$this->share( DataRegenerator::class )->addArgument( LookupDataStore::class );
$this->share( Filterer::class )->addArgument( LookupDataStore::class );
$this->share( LookupDataStore::class );
}
}
Internal/DependencyManagement/ContainerException.php 0000644 00000001252 15133220024 0016675 0 ustar 00 <?php
/**
* ExtendedContainer class file.
*/
namespace Automattic\WooCommerce\Internal\DependencyManagement;
/**
* Class ContainerException.
* Used to signal error conditions related to the dependency injection container.
*/
class ContainerException extends \Exception {
/**
* Create a new instance of the class.
*
* @param null $message The exception message to throw.
* @param int $code The error code.
* @param Exception|null $previous The previous throwable used for exception chaining.
*/
public function __construct( $message = null, $code = 0, Exception $previous = null ) {
parent::__construct( $message, $code, $previous );
}
}
Internal/WCCom/ConnectionHelper.php 0000644 00000001216 15133220024 0013230 0 ustar 00 <?php
/**
* Helpers for managing connection to WooCommerce.com.
*/
namespace Automattic\WooCommerce\Internal\WCCom;
defined( 'ABSPATH' ) || exit;
/**
* Class WCConnectionHelper.
*
* Helpers for managing connection to WooCommerce.com.
*/
final class ConnectionHelper {
/**
* Check if WooCommerce.com account is connected.
*
* @since 4.4.0
* @return bool Whether account is connected.
*/
public static function is_connected() {
$helper_options = get_option( 'woocommerce_helper_data', array() );
if ( array_key_exists( 'auth', $helper_options ) && ! empty( $helper_options['auth'] ) ) {
return true;
}
return false;
}
}
Autoloader.php 0000644 00000003701 15133220024 0007345 0 ustar 00 <?php
/**
* Includes the composer Autoloader used for packages and classes in the src/ directory.
*/
namespace Automattic\WooCommerce;
defined( 'ABSPATH' ) || exit;
/**
* Autoloader class.
*
* @since 3.7.0
*/
class Autoloader {
/**
* Static-only class.
*/
private function __construct() {}
/**
* Require the autoloader and return the result.
*
* If the autoloader is not present, let's log the failure and display a nice admin notice.
*
* @return boolean
*/
public static function init() {
$autoloader = dirname( __DIR__ ) . '/vendor/autoload_packages.php';
if ( ! is_readable( $autoloader ) ) {
self::missing_autoloader();
return false;
}
$autoloader_result = require $autoloader;
if ( ! $autoloader_result ) {
return false;
}
return $autoloader_result;
}
/**
* If the autoloader is missing, add an admin notice.
*/
protected static function missing_autoloader() {
if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
error_log( // phpcs:ignore
esc_html__( 'Your installation of WooCommerce is incomplete. If you installed WooCommerce from GitHub, please refer to this document to set up your development environment: https://github.com/woocommerce/woocommerce/wiki/How-to-set-up-WooCommerce-development-environment', 'woocommerce' )
);
}
add_action(
'admin_notices',
function() {
?>
<div class="notice notice-error">
<p>
<?php
printf(
/* translators: 1: is a link to a support document. 2: closing link */
esc_html__( 'Your installation of WooCommerce is incomplete. If you installed WooCommerce from GitHub, %1$splease refer to this document%2$s to set up your development environment.', 'woocommerce' ),
'<a href="' . esc_url( 'https://github.com/woocommerce/woocommerce/wiki/How-to-set-up-WooCommerce-development-environment' ) . '" target="_blank" rel="noopener noreferrer">',
'</a>'
);
?>
</p>
</div>
<?php
}
);
}
}
Container.php 0000644 00000007255 15133220024 0007200 0 ustar 00 <?php
/**
* Container class file.
*/
namespace Automattic\WooCommerce;
use Automattic\WooCommerce\Internal\DependencyManagement\ExtendedContainer;
use Automattic\WooCommerce\Internal\DependencyManagement\ServiceProviders\DownloadPermissionsAdjusterServiceProvider;
use Automattic\WooCommerce\Internal\DependencyManagement\ServiceProviders\AssignDefaultCategoryServiceProvider;
use Automattic\WooCommerce\Internal\DependencyManagement\ServiceProviders\ProductAttributesLookupServiceProvider;
use Automattic\WooCommerce\Internal\DependencyManagement\ServiceProviders\ProxiesServiceProvider;
use Automattic\WooCommerce\Internal\DependencyManagement\ServiceProviders\RestockRefundedItemsAdjusterServiceProvider;
/**
* PSR11 compliant dependency injection container for WooCommerce.
*
* Classes in the `src` directory should specify dependencies from that directory via an 'init' method having arguments
* with type hints. If an instance of the container itself is needed, the type hint to use is \Psr\Container\ContainerInterface.
*
* Classes in the `src` directory should interact with anything outside (especially code in the `includes` directory
* and WordPress functions) by using the classes in the `Proxies` directory. The exception is idempotent
* functions (e.g. `wp_parse_url`), those can be used directly.
*
* Classes in the `includes` directory should use the `wc_get_container` function to get the instance of the container when
* they need to get an instance of a class from the `src` directory.
*
* Class registration should be done via service providers that inherit from Automattic\WooCommerce\Internal\DependencyManagement
* and those should go in the `src\Internal\DependencyManagement\ServiceProviders` folder unless there's a good reason
* to put them elsewhere. All the service provider class names must be in the `SERVICE_PROVIDERS` constant.
*/
final class Container implements \Psr\Container\ContainerInterface {
/**
* The list of service provider classes to register.
*
* @var string[]
*/
private $service_providers = array(
AssignDefaultCategoryServiceProvider::class,
DownloadPermissionsAdjusterServiceProvider::class,
ProductAttributesLookupServiceProvider::class,
ProxiesServiceProvider::class,
RestockRefundedItemsAdjusterServiceProvider::class,
);
/**
* The underlying container.
*
* @var \League\Container\Container
*/
private $container;
/**
* Class constructor.
*/
public function __construct() {
$this->container = new ExtendedContainer();
// Add ourselves as the shared instance of ContainerInterface,
// register everything else using service providers.
$this->container->share( \Psr\Container\ContainerInterface::class, $this );
foreach ( $this->service_providers as $service_provider_class ) {
$this->container->addServiceProvider( $service_provider_class );
}
}
/**
* Finds an entry of the container by its identifier and returns it.
*
* @param string $id Identifier of the entry to look for.
*
* @throws NotFoundExceptionInterface No entry was found for **this** identifier.
* @throws Psr\Container\ContainerExceptionInterface Error while retrieving the entry.
*
* @return mixed Entry.
*/
public function get( $id ) {
return $this->container->get( $id );
}
/**
* Returns true if the container can return an entry for the given identifier.
* Returns false otherwise.
*
* `has($id)` returning true does not mean that `get($id)` will not throw an exception.
* It does however mean that `get($id)` will not throw a `NotFoundExceptionInterface`.
*
* @param string $id Identifier of the entry to look for.
*
* @return bool
*/
public function has( $id ) {
return $this->container->has( $id );
}
}
Packages.php 0000644 00000007617 15133220024 0006776 0 ustar 00 <?php
/**
* Loads WooCommece packages from the /packages directory. These are packages developed outside of core.
*/
namespace Automattic\WooCommerce;
defined( 'ABSPATH' ) || exit;
/**
* Packages class.
*
* @since 3.7.0
*/
class Packages {
/**
* Static-only class.
*/
private function __construct() {}
/**
* Array of package names and their main package classes.
*
* @var array Key is the package name/directory, value is the main package class which handles init.
*/
protected static $packages = array(
'woocommerce-blocks' => '\\Automattic\\WooCommerce\\Blocks\\Package',
'woocommerce-admin' => '\\Automattic\\WooCommerce\\Admin\\Composer\\Package',
);
/**
* Init the package loader.
*
* @since 3.7.0
*/
public static function init() {
add_action( 'plugins_loaded', array( __CLASS__, 'on_init' ) );
}
/**
* Callback for WordPress init hook.
*/
public static function on_init() {
self::load_packages();
}
/**
* Checks a package exists by looking for it's directory.
*
* @param string $package Package name.
* @return boolean
*/
public static function package_exists( $package ) {
return file_exists( dirname( __DIR__ ) . '/packages/' . $package );
}
/**
* Loads packages after plugins_loaded hook.
*
* Each package should include an init file which loads the package so it can be used by core.
*/
protected static function load_packages() {
foreach ( self::$packages as $package_name => $package_class ) {
if ( ! self::package_exists( $package_name ) ) {
self::missing_package( $package_name );
continue;
}
call_user_func( array( $package_class, 'init' ) );
}
// Proxies "activated_plugin" hook for embedded packages listen on WC plugin activation
// https://github.com/woocommerce/woocommerce/issues/28697.
if ( is_admin() ) {
$activated_plugin = get_transient( 'woocommerce_activated_plugin' );
if ( $activated_plugin ) {
delete_transient( 'woocommerce_activated_plugin' );
/**
* WooCommerce is activated hook.
*
* @since 5.0.0
* @param bool $activated_plugin Activated plugin path,
* generally woocommerce/woocommerce.php.
*/
do_action( 'woocommerce_activated_plugin', $activated_plugin );
}
}
}
/**
* If a package is missing, add an admin notice.
*
* @param string $package Package name.
*/
protected static function missing_package( $package ) {
if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
error_log( // phpcs:ignore
sprintf(
/* Translators: %s package name. */
esc_html__( 'Missing the WooCommerce %s package', 'woocommerce' ),
'<code>' . esc_html( $package ) . '</code>'
) . ' - ' . esc_html__( 'Your installation of WooCommerce is incomplete. If you installed WooCommerce from GitHub, please refer to this document to set up your development environment: https://github.com/woocommerce/woocommerce/wiki/How-to-set-up-WooCommerce-development-environment', 'woocommerce' )
);
}
add_action(
'admin_notices',
function() use ( $package ) {
?>
<div class="notice notice-error">
<p>
<strong>
<?php
printf(
/* Translators: %s package name. */
esc_html__( 'Missing the WooCommerce %s package', 'woocommerce' ),
'<code>' . esc_html( $package ) . '</code>'
);
?>
</strong>
<br>
<?php
printf(
/* translators: 1: is a link to a support document. 2: closing link */
esc_html__( 'Your installation of WooCommerce is incomplete. If you installed WooCommerce from GitHub, %1$splease refer to this document%2$s to set up your development environment.', 'woocommerce' ),
'<a href="' . esc_url( 'https://github.com/woocommerce/woocommerce/wiki/How-to-set-up-WooCommerce-development-environment' ) . '" target="_blank" rel="noopener noreferrer">',
'</a>'
);
?>
</p>
</div>
<?php
}
);
}
}
Utilities/NumberUtil.php 0000644 00000002230 15133220024 0011303 0 ustar 00 <?php
/**
* A class of utilities for dealing with numbers.
*/
namespace Automattic\WooCommerce\Utilities;
/**
* A class of utilities for dealing with numbers.
*/
final class NumberUtil {
/**
* Round a number using the built-in `round` function, but unless the value to round is numeric
* (a number or a string that can be parsed as a number), apply 'floatval' first to it
* (so it will convert it to 0 in most cases).
*
* This is needed because in PHP 7 applying `round` to a non-numeric value returns 0,
* but in PHP 8 it throws an error. Specifically, in WooCommerce we have a few places where
* round('') is often executed.
*
* @param mixed $val The value to round.
* @param int $precision The optional number of decimal digits to round to.
* @param int $mode A constant to specify the mode in which rounding occurs.
*
* @return float The value rounded to the given precision as a float, or the supplied default value.
*/
public static function round( $val, int $precision = 0, int $mode = PHP_ROUND_HALF_UP ) : float {
if ( ! is_numeric( $val ) ) {
$val = floatval( $val );
}
return round( $val, $precision, $mode );
}
}
Utilities/ArrayUtil.php 0000644 00000004351 15133220024 0011137 0 ustar 00 <?php
/**
* A class of utilities for dealing with arrays.
*/
namespace Automattic\WooCommerce\Utilities;
/**
* A class of utilities for dealing with arrays.
*/
class ArrayUtil {
/**
* Get a value from an nested array by specifying the entire key hierarchy with '::' as separator.
*
* E.g. for [ 'foo' => [ 'bar' => [ 'fizz' => 'buzz' ] ] ] the value for key 'foo::bar::fizz' would be 'buzz'.
*
* @param array $array The array to get the value from.
* @param string $key The complete key hierarchy, using '::' as separator.
* @param mixed $default The value to return if the key doesn't exist in the array.
*
* @return mixed The retrieved value, or the supplied default value.
* @throws \Exception $array is not an array.
*/
public static function get_nested_value( array $array, string $key, $default = null ) {
$key_stack = explode( '::', $key );
$subkey = array_shift( $key_stack );
if ( isset( $array[ $subkey ] ) ) {
$value = $array[ $subkey ];
if ( count( $key_stack ) ) {
foreach ( $key_stack as $subkey ) {
if ( is_array( $value ) && isset( $value[ $subkey ] ) ) {
$value = $value[ $subkey ];
} else {
$value = $default;
break;
}
}
}
} else {
$value = $default;
}
return $value;
}
/**
* Checks if a given key exists in an array and its value can be evaluated as 'true'.
*
* @param array $array The array to check.
* @param string $key The key for the value to check.
* @return bool True if the key exists in the array and the value can be evaluated as 'true'.
*/
public static function is_truthy( array $array, string $key ) {
return isset( $array[ $key ] ) && $array[ $key ];
}
/**
* Gets the value for a given key from an array, or a default value if the key doesn't exist in the array.
*
* @param array $array The array to get the value from.
* @param string $key The key to use to retrieve the value.
* @param null $default The default value to return if the key doesn't exist in the array.
* @return mixed|null The value for the key, or the default value passed.
*/
public static function get_value_or_default( array $array, string $key, $default = null ) {
return isset( $array[ $key ] ) ? $array[ $key ] : $default;
}
}
Utilities/StringUtil.php 0000644 00000003273 15133220024 0011331 0 ustar 00 <?php
/**
* A class of utilities for dealing with strings.
*/
namespace Automattic\WooCommerce\Utilities;
/**
* A class of utilities for dealing with strings.
*/
final class StringUtil {
/**
* Checks to see whether or not a string starts with another.
*
* @param string $string The string we want to check.
* @param string $starts_with The string we're looking for at the start of $string.
* @param bool $case_sensitive Indicates whether the comparison should be case-sensitive.
*
* @return bool True if the $string starts with $starts_with, false otherwise.
*/
public static function starts_with( string $string, string $starts_with, bool $case_sensitive = true ): bool {
$len = strlen( $starts_with );
if ( $len > strlen( $string ) ) {
return false;
}
$string = substr( $string, 0, $len );
if ( $case_sensitive ) {
return strcmp( $string, $starts_with ) === 0;
}
return strcasecmp( $string, $starts_with ) === 0;
}
/**
* Checks to see whether or not a string ends with another.
*
* @param string $string The string we want to check.
* @param string $ends_with The string we're looking for at the end of $string.
* @param bool $case_sensitive Indicates whether the comparison should be case-sensitive.
*
* @return bool True if the $string ends with $ends_with, false otherwise.
*/
public static function ends_with( string $string, string $ends_with, bool $case_sensitive = true ): bool {
$len = strlen( $ends_with );
if ( $len > strlen( $string ) ) {
return false;
}
$string = substr( $string, -$len );
if ( $case_sensitive ) {
return strcmp( $string, $ends_with ) === 0;
}
return strcasecmp( $string, $ends_with ) === 0;
}
}
Proxies/LegacyProxy.php 0000644 00000007251 15133220024 0011151 0 ustar 00 <?php
/**
* LegacyProxy class file.
*/
namespace Automattic\WooCommerce\Proxies;
use Automattic\WooCommerce\Internal\DependencyManagement\Definition;
use \Psr\Container\ContainerInterface;
/**
* Proxy class to access legacy WooCommerce functionality.
*
* This class should be used to interact with code outside the `src` directory, especially functions and classes
* in the `includes` directory, unless a more specific proxy exists for the functionality at hand (e.g. `ActionsProxy`).
* Idempotent functions can be executed directly.
*/
class LegacyProxy {
/**
* Gets an instance of a given legacy class.
* This must not be used to get instances of classes in the `src` directory.
*
* If a given class needs a special procedure to get an instance of it,
* please add a private get_instance_of_(lowercased_class_name) and it will be
* automatically invoked. See also how objects of classes having a static `instance`
* method are retrieved, similar approaches can be used as needed to make use
* of existing factory methods such as e.g. 'load'.
*
* @param string $class_name The name of the class to get an instance for.
* @param mixed ...$args Parameters to be passed to the class constructor or to the appropriate internal 'get_instance_of_' method.
*
* @return object The instance of the class.
* @throws \Exception The requested class belongs to the `src` directory, or there was an error creating an instance of the class.
*/
public function get_instance_of( string $class_name, ...$args ) {
if ( false !== strpos( $class_name, '\\' ) ) {
throw new \Exception(
'The LegacyProxy class is not intended for getting instances of classes in the src directory, please use ' .
Definition::INJECTION_METHOD . ' method injection or the instance of ' . ContainerInterface::class . ' for that.'
);
}
// If a class has a dedicated method to obtain a instance, use it.
$method = 'get_instance_of_' . strtolower( $class_name );
if ( method_exists( __CLASS__, $method ) ) {
return $this->$method( ...$args );
}
// If the class is a singleton, use the "instance" method.
if ( method_exists( $class_name, 'instance' ) ) {
return $class_name::instance( ...$args );
}
// If the class has a "load" method, use it.
if ( method_exists( $class_name, 'load' ) ) {
return $class_name::load( ...$args );
}
// Fallback to simply creating a new instance of the class.
return new $class_name( ...$args );
}
/**
* Get an instance of a class implementing WC_Queue_Interface.
*
* @return \WC_Queue_Interface The instance.
*/
private function get_instance_of_wc_queue_interface() {
return \WC_Queue::instance();
}
/**
* Call a user function. This should be used to execute any non-idempotent function, especially
* those in the `includes` directory or provided by WordPress.
*
* @param string $function_name The function to execute.
* @param mixed ...$parameters The parameters to pass to the function.
*
* @return mixed The result from the function.
*/
public function call_function( $function_name, ...$parameters ) {
return call_user_func_array( $function_name, $parameters );
}
/**
* Call a static method in a class. This should be used to execute any non-idempotent method in classes
* from the `includes` directory.
*
* @param string $class_name The name of the class containing the method.
* @param string $method_name The name of the method.
* @param mixed ...$parameters The parameters to pass to the method.
*
* @return mixed The result from the method.
*/
public function call_static( $class_name, $method_name, ...$parameters ) {
return call_user_func_array( "$class_name::$method_name", $parameters );
}
}
Proxies/ActionsProxy.php 0000644 00000002120 15133220024 0011333 0 ustar 00 <?php
/**
* ActionsProxy class file.
*/
namespace Automattic\WooCommerce\Proxies;
/**
* Proxy for interacting with WordPress actions and filters.
*
* This class should be used instead of directly accessing the WordPress functions, to ease unit testing.
*/
class ActionsProxy {
/**
* Retrieve the number of times an action is fired.
*
* @param string $tag The name of the action hook.
*
* @return int The number of times action hook $tag is fired.
*/
public function did_action( $tag ) {
return did_action( $tag );
}
/**
* Calls the callback functions that have been added to a filter hook.
*
* @param string $tag The name of the filter hook.
* @param mixed $value The value to filter.
* @param mixed ...$parameters Additional parameters to pass to the callback functions.
*
* @return mixed The filtered value after all hooked functions are applied to it.
*/
public function apply_filters( $tag, $value, ...$parameters ) {
return apply_filters( $tag, $value, ...$parameters );
}
// TODO: Add the rest of the actions and filters related methods.
}
Checkout/Helpers/ReserveStock.php 0000644 00000015446 15133220024 0013045 0 ustar 00 <?php
/**
* Handle product stock reservation during checkout.
*/
namespace Automattic\WooCommerce\Checkout\Helpers;
defined( 'ABSPATH' ) || exit;
/**
* Stock Reservation class.
*/
final class ReserveStock {
/**
* Is stock reservation enabled?
*
* @var boolean
*/
private $enabled = true;
/**
* Constructor
*/
public function __construct() {
// Table needed for this feature are added in 4.3.
$this->enabled = get_option( 'woocommerce_schema_version', 0 ) >= 430;
}
/**
* Is stock reservation enabled?
*
* @return boolean
*/
protected function is_enabled() {
return $this->enabled;
}
/**
* Query for any existing holds on stock for this item.
*
* @param \WC_Product $product Product to get reserved stock for.
* @param integer $exclude_order_id Optional order to exclude from the results.
*
* @return integer Amount of stock already reserved.
*/
public function get_reserved_stock( $product, $exclude_order_id = 0 ) {
global $wpdb;
if ( ! $this->is_enabled() ) {
return 0;
}
// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared
return (int) $wpdb->get_var( $this->get_query_for_reserved_stock( $product->get_stock_managed_by_id(), $exclude_order_id ) );
}
/**
* Put a temporary hold on stock for an order if enough is available.
*
* @throws ReserveStockException If stock cannot be reserved.
*
* @param \WC_Order $order Order object.
* @param int $minutes How long to reserve stock in minutes. Defaults to woocommerce_hold_stock_minutes.
*/
public function reserve_stock_for_order( $order, $minutes = 0 ) {
$minutes = $minutes ? $minutes : (int) get_option( 'woocommerce_hold_stock_minutes', 60 );
if ( ! $minutes || ! $this->is_enabled() ) {
return;
}
try {
$items = array_filter(
$order->get_items(),
function( $item ) {
return $item->is_type( 'line_item' ) && $item->get_product() instanceof \WC_Product && $item->get_quantity() > 0;
}
);
$rows = array();
foreach ( $items as $item ) {
$product = $item->get_product();
if ( ! $product->is_in_stock() ) {
throw new ReserveStockException(
'woocommerce_product_out_of_stock',
sprintf(
/* translators: %s: product name */
__( '"%s" is out of stock and cannot be purchased.', 'woocommerce' ),
$product->get_name()
),
403
);
}
// If stock management is off, no need to reserve any stock here.
if ( ! $product->managing_stock() || $product->backorders_allowed() ) {
continue;
}
$managed_by_id = $product->get_stock_managed_by_id();
/**
* Filter order item quantity.
*
* @param int|float $quantity Quantity.
* @param WC_Order $order Order data.
* @param WC_Order_Item_Product $item Order item data.
*/
$item_quantity = apply_filters( 'woocommerce_order_item_quantity', $item->get_quantity(), $order, $item );
$rows[ $managed_by_id ] = isset( $rows[ $managed_by_id ] ) ? $rows[ $managed_by_id ] + $item_quantity : $item_quantity;
}
if ( ! empty( $rows ) ) {
foreach ( $rows as $product_id => $quantity ) {
$this->reserve_stock_for_product( $product_id, $quantity, $order, $minutes );
}
}
} catch ( ReserveStockException $e ) {
$this->release_stock_for_order( $order );
throw $e;
}
}
/**
* Release a temporary hold on stock for an order.
*
* @param \WC_Order $order Order object.
*/
public function release_stock_for_order( $order ) {
global $wpdb;
if ( ! $this->is_enabled() ) {
return;
}
$wpdb->delete(
$wpdb->wc_reserved_stock,
array(
'order_id' => $order->get_id(),
)
);
}
/**
* Reserve stock for a product by inserting rows into the DB.
*
* @throws ReserveStockException If a row cannot be inserted.
*
* @param int $product_id Product ID which is having stock reserved.
* @param int $stock_quantity Stock amount to reserve.
* @param \WC_Order $order Order object which contains the product.
* @param int $minutes How long to reserve stock in minutes.
*/
private function reserve_stock_for_product( $product_id, $stock_quantity, $order, $minutes ) {
global $wpdb;
$product_data_store = \WC_Data_Store::load( 'product' );
$query_for_stock = $product_data_store->get_query_for_stock( $product_id );
$query_for_reserved_stock = $this->get_query_for_reserved_stock( $product_id, $order->get_id() );
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared
$result = $wpdb->query(
$wpdb->prepare(
"
INSERT INTO {$wpdb->wc_reserved_stock} ( `order_id`, `product_id`, `stock_quantity`, `timestamp`, `expires` )
SELECT %d, %d, %d, NOW(), ( NOW() + INTERVAL %d MINUTE ) FROM DUAL
WHERE ( $query_for_stock FOR UPDATE ) - ( $query_for_reserved_stock FOR UPDATE ) >= %d
ON DUPLICATE KEY UPDATE `expires` = VALUES( `expires` ), `stock_quantity` = VALUES( `stock_quantity` )
",
$order->get_id(),
$product_id,
$stock_quantity,
$minutes,
$stock_quantity
)
);
// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared
if ( ! $result ) {
$product = wc_get_product( $product_id );
throw new ReserveStockException(
'woocommerce_product_not_enough_stock',
sprintf(
/* translators: %s: product name */
__( 'Not enough units of %s are available in stock to fulfil this order.', 'woocommerce' ),
$product ? $product->get_name() : '#' . $product_id
),
403
);
}
}
/**
* Returns query statement for getting reserved stock of a product.
*
* @param int $product_id Product ID.
* @param integer $exclude_order_id Optional order to exclude from the results.
* @return string|void Query statement.
*/
private function get_query_for_reserved_stock( $product_id, $exclude_order_id = 0 ) {
global $wpdb;
$query = $wpdb->prepare(
"
SELECT COALESCE( SUM( stock_table.`stock_quantity` ), 0 ) FROM $wpdb->wc_reserved_stock stock_table
LEFT JOIN $wpdb->posts posts ON stock_table.`order_id` = posts.ID
WHERE posts.post_status IN ( 'wc-checkout-draft', 'wc-pending' )
AND stock_table.`expires` > NOW()
AND stock_table.`product_id` = %d
AND stock_table.`order_id` != %d
",
$product_id,
$exclude_order_id
);
/**
* Filter: woocommerce_query_for_reserved_stock
* Allows to filter the query for getting reserved stock of a product.
*
* @since 4.5.0
* @param string $query The query for getting reserved stock of a product.
* @param int $product_id Product ID.
* @param int $exclude_order_id Order to exclude from the results.
*/
return apply_filters( 'woocommerce_query_for_reserved_stock', $query, $product_id, $exclude_order_id );
}
}
Checkout/Helpers/ReserveStockException.php 0000644 00000002311 15133220024 0014707 0 ustar 00 <?php
/**
* Exceptions for stock reservation.
*/
namespace Automattic\WooCommerce\Checkout\Helpers;
defined( 'ABSPATH' ) || exit;
/**
* ReserveStockException class.
*/
class ReserveStockException extends \Exception {
/**
* Sanitized error code.
*
* @var string
*/
protected $error_code;
/**
* Error extra data.
*
* @var array
*/
protected $error_data;
/**
* Setup exception.
*
* @param string $code Machine-readable error code, e.g `woocommerce_invalid_product_id`.
* @param string $message User-friendly translated error message, e.g. 'Product ID is invalid'.
* @param int $http_status_code Proper HTTP status code to respond with, e.g. 400.
* @param array $data Extra error data.
*/
public function __construct( $code, $message, $http_status_code = 400, $data = array() ) {
$this->error_code = $code;
$this->error_data = $data;
parent::__construct( $message, $http_status_code );
}
/**
* Returns the error code.
*
* @return string
*/
public function getErrorCode() {
return $this->error_code;
}
/**
* Returns error data.
*
* @return array
*/
public function getErrorData() {
return $this->error_data;
}
}
Forms/Loader.php 0000644 00000003007 15133255232 0007552 0 ustar 00 <?php
namespace WPForms\Forms;
/**
* Class Loader gives ability to track/load all forms modules.
*
* @since 1.5.1
*/
class Loader {
/**
* Get the instance of a class and store it in itself.
*
* @since 1.5.1
*/
public static function get_instance() {
static $instance;
if ( ! $instance ) {
$instance = new self();
}
return $instance;
}
/**
* Loader constructor.
*
* @since 1.5.1
*/
public function __construct() {
$core_class_names = array(
'Preview',
);
$class_names = \apply_filters( 'wpforms_forms_classes_available', $core_class_names );
foreach ( $class_names as $class_name ) {
$this->register_class( $class_name );
}
}
/**
* Register a new class.
*
* @since 1.5.1
*
* @param string $class_name Class name to register.
*/
public function register_class( $class_name ) {
$class_name = sanitize_text_field( $class_name );
// Load Lite class if exists.
if ( class_exists( 'WPForms\Lite\Forms\\' . $class_name ) && ! wpforms()->is_pro() ) {
$class_name = 'WPForms\Lite\Forms\\' . $class_name;
new $class_name();
return;
}
// Load Pro class if exists.
if ( class_exists( 'WPForms\Pro\Forms\\' . $class_name ) && wpforms()->is_pro() ) {
$class_name = 'WPForms\Pro\Forms\\' . $class_name;
new $class_name();
return;
}
// Load general class if neither Pro nor Lite class exists.
if ( class_exists( __NAMESPACE__ . '\\' . $class_name ) ) {
$class_name = __NAMESPACE__ . '\\' . $class_name;
new $class_name();
}
}
}
Forms/Submission.php 0000644 00000007624 15133255232 0010510 0 ustar 00 <?php
namespace WPForms\Forms;
/**
* Class Submission.
*
* @since 1.7.4
*/
class Submission {
/**
* The form fields.
*
* @since 1.7.4
*
* @var array
*/
protected $fields;
/**
* The form entry.
*
* @since 1.7.4
*
* @var array
*/
private $entry;
/**
* The form ID.
*
* @since 1.7.4
*
* @var int
*/
private $form_id;
/**
* The form data.
*
* @since 1.7.4
*
* @var array
*/
protected $form_data;
/**
* The date.
*
* @since 1.7.4
*
* @var string
*/
private $date;
/**
* Register the submission data.
*
* @since 1.7.4
*
* @param array $fields The form fields.
* @param array $entry The form entry.
* @param int $form_id The form ID.
* @param array $form_data The form data.
*/
public function register( array $fields, array $entry, $form_id, array $form_data = [] ) {
$this->fields = $fields;
$this->entry = $entry;
$this->form_id = $form_id;
$this->form_data = $form_data;
$this->date = gmdate( 'Y-m-d H:i:s' );
}
/**
* Prepare the submission data.
*
* @since 1.7.4
*
* @return array|void
*/
public function prepare_entry_data() {
// phpcs:disable WPForms.PHP.ValidateHooks.InvalidHookName, WPForms.Comments.ParamTagHooks.InvalidParamTagsQuantity
/**
* Provide the opportunity to disable entry saving.
*
* @since 1.0.0
*
* @param bool $entry_save Entry save flag. Defaults to true.
* @param array $fields Fields data.
* @param array $entry Entry data.
* @param array $form_data Form data.
*/
if ( ! apply_filters( 'wpforms_entry_save', true, $this->fields, $this->entry, $this->form_data ) ) {
return;
}
/**
* Filter the entry data before saving.
*
* @since 1.0.0
*
* @param array $fields Fields data.
* @param array $entry Entry data.
* @param array $form_data Form data.
*/
$fields = apply_filters( 'wpforms_entry_save_data', $this->fields, $this->entry, $this->form_data );
$user_id = is_user_logged_in() ? get_current_user_id() : 0;
$user_ip = wpforms_get_ip();
$user_agent = ! empty( $_SERVER['HTTP_USER_AGENT'] ) ? substr( sanitize_text_field( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ), 0, 256 ) : '';
$user_uuid = wpforms_is_collecting_cookies_allowed() && ! empty( $_COOKIE['_wpfuuid'] ) ? sanitize_key( $_COOKIE['_wpfuuid'] ) : '';
/**
* Allow developers disable saving user IP and User Agent within the entry.
*
* @since 1.5.1
*
* @param bool $disable True if you need to disable storing IP and UA within the entry. Defaults to false.
* @param array $fields Fields data.
* @param array $form_data Form data.
*/
$is_ip_disabled = apply_filters( 'wpforms_disable_entry_user_ip', '__return_false', $fields, $this->form_data );
// If GDPR enhancements are enabled and user details are disabled
// globally or in the form settings, discard the IP and UA.
if (
! $is_ip_disabled ||
! wpforms_is_collecting_ip_allowed( $this->form_data )
) {
$user_agent = '';
$user_ip = '';
}
/**
* Information about the entry, that is ready to be saved into the main entries table,
* which is used for displaying a list of entries and partially for search.
*
* @since 1.5.9
*
* @param array $entry_data Information about the entry, that will be saved into the DB.
* @param array $form_data Form data.
*/
return (array) apply_filters(
'wpforms_entry_save_args',
[
'form_id' => absint( $this->form_id ),
'user_id' => absint( $user_id ),
'fields' => wp_json_encode( $fields ),
'ip_address' => sanitize_text_field( $user_ip ),
'user_agent' => sanitize_text_field( $user_agent ),
'date' => $this->date,
'user_uuid' => sanitize_text_field( $user_uuid ),
],
$this->form_data
);
// phpcs:enable WPForms.PHP.ValidateHooks.InvalidHookName, WPForms.Comments.ParamTagHooks.InvalidParamTagsQuantity
}
}
Forms/Honeypot.php 0000644 00000003675 15133255232 0010164 0 ustar 00 <?php
namespace WPForms\Forms;
/**
* Class Honeypot.
*
* @since 1.6.2
*/
class Honeypot {
/**
* Initialise the actions for the Honeypot.
*
* @since 1.6.2
*/
public function init() {
$this->hooks();
}
/**
* Register hooks.
*
* @since 1.6.2
*/
public function hooks() {
add_action( 'wpforms_frontend_output', [ $this, 'render' ], 15, 5 );
}
/**
* Return function to render the honeypot.
*
* @since 1.6.2
*
* @param array $form_data Form data and settings.
*/
public function render( $form_data ) {
if (
empty( $form_data['settings']['honeypot'] ) ||
'1' !== $form_data['settings']['honeypot']
) {
return;
}
$names = [ 'Name', 'Phone', 'Comment', 'Message', 'Email', 'Website' ];
echo '<div class="wpforms-field wpforms-field-hp">';
echo '<label for="wpforms-' . $form_data['id'] . '-field-hp" class="wpforms-field-label">' . $names[ array_rand( $names ) ] . '</label>'; // phpcs:ignore
echo '<input type="text" name="wpforms[hp]" id="wpforms-' . $form_data['id'] . '-field-hp" class="wpforms-field-medium">'; // phpcs:ignore
echo '</div>';
}
/**
* Validate honeypot.
*
* @since 1.6.2
*
* @param array $form_data Form data.
* @param array $fields Fields.
* @param array $entry Form entry.
*
* @return bool|string False or an string with the error.
*/
public function validate( array $form_data, array $fields, array $entry ) {
$honeypot = false;
if (
! empty( $form_data['settings']['honeypot'] ) &&
'1' === $form_data['settings']['honeypot'] &&
! empty( $entry['hp'] )
) {
$honeypot = esc_html__( 'WPForms honeypot field triggered.', 'wpforms-lite' );
}
// If we get passed an empty fields array, but we have the data in our form data, use it.
if ( empty( $fields ) && isset( $form_data['fields'] ) ) {
$fields = $form_data['fields'];
}
return apply_filters( 'wpforms_process_honeypot', $honeypot, $fields, $entry, $form_data );
}
}
Forms/Akismet.php 0000644 00000012403 15133255232 0007741 0 ustar 00 <?php
namespace WPForms\Forms;
use Akismet as AkismetPlugin;
/**
* Class Akismet.
*
* @since 1.7.6
*/
class Akismet {
/**
* Is the Akismet plugin installed?
*
* @since 1.7.6
*
* @return bool
*/
public static function is_installed() {
return file_exists( WP_PLUGIN_DIR . '/akismet/akismet.php' );
}
/**
* Is the Akismet plugin activated?
*
* @since 1.7.6
*
* @return bool
*/
public static function is_activated() {
return is_callable( [ 'Akismet', 'get_api_key' ] ) && is_callable( [ 'Akismet', 'http_post' ] );
}
/**
* Has the Akismet plugin been configured wih a valid API key?
*
* @since 1.7.6
*
* @return bool
*/
public static function is_configured() {
// Akismet will only allow an API key to be saved if it is a valid key.
// We can assume that if there is an API key saved, it is valid.
return self::is_activated() && ! empty( AkismetPlugin::get_api_key() );
}
/**
* Get the list of field types that are allowed to be sent to Akismet.
*
* @since 1.7.6
*
* @return array List of field types that are allowed to be sent to Akismet
*/
private function get_field_type_allowlist() {
$field_type_allowlist = [
'text',
'textarea',
'name',
'email',
'phone',
'address',
'url',
'richtext',
];
/**
* Filters the field types that are allowed to be sent to Akismet.
*
* @since 1.7.6
*
* @param array $field_type_allowlist Field types allowed to be sent to Akismet.
*/
return apply_filters( 'wpforms_forms_akismet_get_field_type_allowlist', $field_type_allowlist );
}
/**
* Get the entry data to be sent to Akismet.
*
* @since 1.7.6
*
* @param array $fields Field data for the current form.
* @param array $entry Entry data for the current entry.
*
* @return array $entry_data Entry data to be sent to Akismet.
*/
private function get_entry_data( $fields, $entry ) {
$field_type_allowlist = $this->get_field_type_allowlist();
$entry_data = [];
$entry_content = [];
foreach ( $fields as $key => $field ) {
$field_type = $field['type'];
$field_content = is_array( $entry['fields'][ $key ] ) ? implode( ' ', $entry['fields'][ $key ] ) : $entry['fields'][ $key ];
if ( ! in_array( $field_type, $field_type_allowlist, true ) ) {
continue;
}
if ( in_array( $field_type, [ 'name', 'email', 'url' ], true ) && ! isset( $entry_data[ $field_type ] ) ) {
$entry_data[ $field_type ] = $field_content;
continue;
}
$entry_content[] = $field_content;
}
$entry_data['content'] = implode( ' ', $entry_content );
return $entry_data;
}
/**
* Is the entry marked as spam by Akismet?
*
* @since 1.7.6
*
* @param array $form_data Form data for the current form.
* @param array $entry Entry data for the current entry.
*
* @return bool
*/
private function entry_is_spam( $form_data, $entry ) {
$entry_data = $this->get_entry_data( $form_data['fields'], $entry );
$request = [
'blog' => get_option( 'home' ),
'user_ip' => wpforms_is_collecting_ip_allowed( $form_data ) ? wpforms_get_ip() : null,
// phpcs:disable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
'user_agent' => isset( $_SERVER['HTTP_USER_AGENT'] ) ? wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) : null,
'referrer' => wp_get_referer() ? wp_get_referer() : null,
// phpcs:enable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
'permalink' => wpforms_current_url(),
'comment_type' => 'contact-form',
'comment_author' => isset( $entry_data['name'] ) ? $entry_data['name'] : '',
'comment_author_email' => isset( $entry_data['email'] ) ? $entry_data['email'] : '',
'comment_author_url' => isset( $entry_data['url'] ) ? $entry_data['url'] : '',
'comment_content' => isset( $entry_data['content'] ) ? $entry_data['content'] : '',
'blog_lang' => get_locale(),
'blog_charset' => get_bloginfo( 'charset' ),
'user_role' => wp_get_current_user()->roles[0],
'honypot_field_name' => 'wpforms["hp"]',
];
// phpcs:disable WordPress.Security.NonceVerification.Recommended
// If we are on a form preview page, tell Akismet that this is a test submission.
if ( isset( $_GET['wpforms_form_preview'] ) ) {
$request['is_test'] = true;
}
// phpcs:enable WordPress.Security.NonceVerification.Recommended
$response = AkismetPlugin::http_post( build_query( $request ), 'comment-check' );
return ! empty( $response ) && isset( $response[1] ) && 'true' === trim( $response[1] );
}
/**
* Validate entry.
*
* @since 1.7.6
*
* @param array $form_data Form data for the current form.
* @param array $entry Entry data for the current entry.
*
* @return string|bool
*/
public function validate( array $form_data, array $entry ) {
// If Akismet is turned on in form settings, is activated, is configured and the entry is spam.
if (
! empty( $form_data['settings']['akismet'] ) &&
self::is_configured() &&
$this->entry_is_spam( $form_data, $entry )
) {
// This string is being logged not printed, so it does not need to be translatable.
return esc_html__( 'Anti-spam verification failed, please try again later.', 'wpforms-lite' );
}
return false;
}
}
Forms/Locator.php 0000644 00000064670 15133255232 0007764 0 ustar 00 <?php
// phpcs:disable Generic.Commenting.DocComment.MissingShort
/** @noinspection PhpUnnecessaryCurlyVarSyntaxInspection */
// phpcs:enable Generic.Commenting.DocComment.MissingShort
namespace WPForms\Forms;
use WP_Post;
use WPForms\Tasks\Actions\FormsLocatorScanTask;
/**
* Class Locator.
*
* @since 1.7.4
*/
class Locator {
/**
* Column name on Forms Overview admin page.
*
* @since 1.7.4
*/
const COLUMN_NAME = 'locations';
/**
* Locations meta key.
*
* @since 1.7.4
*/
const LOCATIONS_META = 'wpforms_form_locations';
/**
* WPForms widget name.
*
* @since 1.7.4
*/
const WPFORMS_WIDGET_NAME = 'wpforms-widget';
/**
* WPForms widget prefix.
*
* @since 1.7.4
*/
const WPFORMS_WIDGET_PREFIX = self::WPFORMS_WIDGET_NAME . '-';
/**
* WPForms widgets option name.
*
* @since 1.7.4
*/
const WPFORMS_WIDGET_OPTION = 'widget_' . self::WPFORMS_WIDGET_NAME;
/**
* Text widget name.
*
* @since 1.7.4
*/
const TEXT_WIDGET_NAME = 'text';
/**
* Text widget prefix.
*
* @since 1.7.4
*/
const TEXT_WIDGET_PREFIX = self::TEXT_WIDGET_NAME . '-';
/**
* Text widgets option name.
*
* @since 1.7.4
*/
const TEXT_WIDGET_OPTION = 'widget_' . self::TEXT_WIDGET_NAME;
/**
* Block widget name.
*
* @since 1.7.4
*/
const BLOCK_WIDGET_NAME = 'block';
/**
* Block widget prefix.
*
* @since 1.7.4
*/
const BLOCK_WIDGET_PREFIX = self::BLOCK_WIDGET_NAME . '-';
/**
* Block widgets option name.
*
* @since 1.7.4
*/
const BLOCK_WIDGET_OPTION = 'widget_' . self::BLOCK_WIDGET_NAME;
/**
* Location type for widget.
* For a page/post, the location type is the post type.
*
* @since 1.7.4
*/
const WIDGET = 'widget';
/**
* WP template post type.
*
* @since 1.7.4
*/
const WP_TEMPLATE = 'wp_template';
/**
* WP template post type.
*
* @since 1.7.4.1
*/
const WP_TEMPLATE_PART = 'wp_template_part';
/**
* Default title for WPForms widget.
* For WPForms widget, we extract title from the widget. If it is empty, we use the default one.
*
* @since 1.7.4
*
* @var string
*/
private $wpforms_widget_title = '';
/**
* Default title for text widget.
* For text widget, we extract title from the widget. If it is empty, we use the default one.
*
* @since 1.7.4
*
* @var string
*/
private $text_widget_title = '';
/**
* Fixed title for block widget.
*
* @since 1.7.4
*
* @var string
*/
private $block_widget_title = '';
/**
* Home url.
*
* @since 1.7.4
*
* @var string
*/
private $home_url;
/**
* Scan status.
*
* @since 1.7.4
*
* @var string
*/
private $scan_status;
/**
* Init class.
*
* @since 1.7.4
*/
public function init() {
$this->home_url = home_url();
$this->scan_status = (string) get_option( FormsLocatorScanTask::SCAN_STATUS );
$this->wpforms_widget_title = __( 'WPForms Widget', 'wpforms-lite' );
$this->text_widget_title = __( 'Text Widget', 'wpforms-lite' );
$this->block_widget_title = __( 'Block Widget', 'wpforms-lite' );
$this->hooks();
}
/**
* Register hooks.
*
* @since 1.7.4
*/
private function hooks() {
// View hooks.
add_filter( 'wpforms_overview_table_columns', [ $this, 'add_column' ] );
add_filter( 'wpforms_overview_table_column_value', [ $this, 'column_value' ], 10, 3 );
add_filter( 'wpforms_overview_row_actions', [ $this, 'row_actions_all' ], 10, 2 );
add_action( 'wpforms_overview_enqueue', [ $this, 'localize_overview_script' ] );
// Monitoring hooks.
add_action( 'save_post', [ $this, 'save_post' ], 10, 3 );
add_action( 'post_updated', [ $this, 'post_updated' ], 10, 3 );
add_action( 'wp_trash_post', [ $this, 'trash_post' ] );
add_action( 'untrash_post', [ $this, 'untrash_post' ] );
add_action( 'delete_post', [ $this, 'trash_post' ] );
add_action( 'permalink_structure_changed', [ $this, 'permalink_structure_changed' ], 10, 2 );
$wpforms_widget_option = self::WPFORMS_WIDGET_OPTION;
$text_widget_option = self::TEXT_WIDGET_OPTION;
$block_widget_option = self::BLOCK_WIDGET_OPTION;
add_action( "update_option_{$wpforms_widget_option}" , [ $this, 'update_option' ], 10, 3 );
add_action( "update_option_{$text_widget_option}" , [ $this, 'update_option' ], 10, 3 );
add_action( "update_option_{$block_widget_option}", [ $this, 'update_option' ], 10, 3 );
}
/**
* Add locations column to the view.
*
* @since 1.7.4
*
* @param array $columns Columns.
*
* @return array
*/
public function add_column( $columns ) {
$columns[ self::COLUMN_NAME ] =
sprintf(
'<span class="wpforms-locations-column-title">%1$s</span>' .
'<span class="wpforms-locations-column-icon" title="%2$s"></span>',
esc_html__( 'Locations', 'wpforms-lite' ),
esc_html__( 'Form locations', 'wpforms-lite' )
);
return $columns;
}
/**
* Display column value.
*
* @since 1.7.4
*
* @param mixed $value Column value.
* @param WP_Post $form Form.
* @param string $column_name Column name.
*
* @return mixed
*/
public function column_value( $value, $form, $column_name ) {
if ( $column_name !== self::COLUMN_NAME ) {
return $value;
}
$form_locations = get_post_meta( $form->ID, self::LOCATIONS_META, true );
if ( $form_locations === '' ) {
$empty_values = [
'' => '—',
FormsLocatorScanTask::SCAN_STATUS_IN_PROGRESS => '...',
FormsLocatorScanTask::SCAN_STATUS_COMPLETED => '0',
];
return $empty_values[ $this->scan_status ];
}
$values = $this->get_location_rows( $form_locations );
if ( ! $values ) {
return '0';
}
$column_value = sprintf(
'<span class="wpforms-locations-count"><a href="#" title="%s">%d</a></span>',
esc_attr__( 'View form locations', 'wpforms-lite' ),
count( $values )
);
$column_value .= '<p class="locations-list">' . implode( '', $values ) . '</p>';
return $column_value;
}
/**
* Row actions for view "All".
*
* @since 1.7.4
*
* @param array $row_actions Row actions.
* @param WP_Post $form Form object.
*
* @return array
*/
public function row_actions_all( $row_actions, $form ) { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh
$form_locations = get_post_meta( $form->ID, self::LOCATIONS_META, true );
if ( ! $form_locations ) {
return $row_actions;
}
$locations = [
'locations' => sprintf(
'<a href="#" title="%s">%s</a>',
esc_attr__( 'View form locations', 'wpforms-lite' ),
esc_html__( 'Locations', 'wpforms-lite' )
),
];
// Insert Locations action before the first available position in the positions list or at the end of $row_actions.
$positions = [
'preview_',
'duplicate',
'trash',
];
$keys = array_keys( $row_actions );
foreach ( $positions as $position ) {
$pos = array_search( $position, $keys, true );
if ( $pos !== false ) {
break;
}
}
$pos = $pos === false ? count( $row_actions ) : $pos;
return array_slice( $row_actions, 0, $pos ) + $locations + array_slice( $row_actions, $pos );
}
/**
* Localize overview script to pass translation strings.
*
* @since 1.7.4
*/
public function localize_overview_script() {
wp_localize_script(
'wpforms-admin-forms-overview',
'wpforms_forms_locator',
[
'paneTitle' => __( 'Form Locations', 'wpforms-lite' ),
'close' => __( 'Close', 'wpforms-lite' ),
]
);
}
/**
* Get id of the sidebar where widget is positioned.
*
* @since 1.7.4
*
* @param string $widget_id Widget id.
*
* @return string
*/
private function get_widget_sidebar_id( $widget_id ) {
$sidebars_widgets = wp_get_sidebars_widgets();
foreach ( $sidebars_widgets as $sidebar_id => $sidebar_widgets ) {
foreach ( $sidebar_widgets as $sidebar_widget ) {
if ( $widget_id === $sidebar_widget ) {
return (string) $sidebar_id;
}
}
}
return '';
}
/**
* Get name of the sidebar where widget is positioned.
*
* @since 1.7.4
*
* @param string $widget_id Widget id.
*
* @return string
*/
private function get_widget_sidebar_name( $widget_id ) {
$sidebar_id = $this->get_widget_sidebar_id( $widget_id );
if ( ! $sidebar_id ) {
return '';
}
$sidebar = $this->get_sidebar( $sidebar_id );
return isset( $sidebar['name'] ) ? (string) $sidebar['name'] : '';
}
/**
* Retrieves the registered sidebar with the given ID.
*
* @since 1.7.4
*
* @global array $wp_registered_sidebars The registered sidebars.
*
* @param string $id The sidebar ID.
*
* @return array|null The discovered sidebar, or null if it is not registered.
*/
private function get_sidebar( $id ) {
if ( function_exists( 'wp_get_sidebar' ) ) {
return wp_get_sidebar( $id );
}
global $wp_registered_sidebars;
if ( ! $wp_registered_sidebars ) {
return null;
}
foreach ( $wp_registered_sidebars as $sidebar ) {
if ( $sidebar['id'] === $id ) {
return $sidebar;
}
}
if ( $id === 'wp_inactive_widgets' ) {
return [
'id' => 'wp_inactive_widgets',
'name' => __( 'Inactive widgets', 'wpforms-lite' ),
];
}
return null;
}
/**
* Get post location title.
*
* @since 1.7.4
*
* @param array $form_location Form location.
*
* @return string
*/
private function get_post_location_title( $form_location ) {
$title = $form_location['title'];
if ( $this->is_wp_template( $form_location['type'] ) ) {
return __( 'Site editor template', 'wpforms-lite' ) . ': ' . $title;
}
return $title;
}
/**
* Whether locations type is WP Template.
*
* @since 1.7.4.1
*
* @param string $location_type Location type.
*
* @return bool
*/
private function is_wp_template( $location_type ) {
return in_array( $location_type, [ self::WP_TEMPLATE, self::WP_TEMPLATE_PART ], true );
}
/**
* Get location title.
*
* @since 1.7.4
*
* @param array $form_location Form location.
*
* @return string
*/
private function get_location_title( $form_location ) {
if ( $form_location['type'] !== self::WIDGET ) {
return $this->get_post_location_title( $form_location );
}
$sidebar_name = $this->get_widget_sidebar_name( $form_location['id'] );
if ( ! $sidebar_name ) {
// Widget not found.
return '';
}
$title = $form_location['title'];
if ( ! $title ) {
if ( strpos( $form_location['id'], self::WPFORMS_WIDGET_PREFIX ) === 0 ) {
$title = $this->wpforms_widget_title;
}
if ( strpos( $form_location['id'], 'text-' ) === 0 ) {
$title = $this->text_widget_title;
}
}
return $sidebar_name . ': ' . $title;
}
/**
* Get location url.
*
* @since 1.7.4
*
* @param array $form_location Form location.
*
* @return string
*/
private function get_location_url( $form_location ) {
// Get widget or wp_template url.
if ( $form_location['type'] === self::WIDGET || $this->is_wp_template( $form_location['type'] ) ) {
return '';
}
// Get post url.
if ( ! $this->is_post_visible( $form_location ) ) {
return '';
}
return $form_location['url'];
}
/**
* Get location edit url.
*
* @since 1.7.4
*
* @param array $form_location Form location.
*
* @return string
*/
private function get_location_edit_url( $form_location ) {
// Get widget url.
if ( $form_location['type'] === self::WIDGET ) {
return current_user_can( 'edit_theme_options' ) ? admin_url( 'widgets.php' ) : '';
}
// Get post url.
if ( ! $this->is_post_visible( $form_location ) ) {
return '';
}
if ( $this->is_wp_template( $form_location['type'] ) ) {
return add_query_arg(
[
'postType' => $form_location['type'],
'postId' => get_stylesheet() . '//' . str_replace( '/', '', $form_location['url'] ),
],
admin_url( 'site-editor.php' )
);
}
return (string) get_edit_post_link( $form_location['id'], '' );
}
/**
* Get location information to output as a row in the location pane.
*
* @since 1.7.4
*
* @param array $form_location Form location.
*
* @return string
* @noinspection PhpTernaryExpressionCanBeReducedToShortVersionInspection
* @noinspection ElvisOperatorCanBeUsedInspection
*/
private function get_location_row( $form_location ) {
$title = $this->get_location_title( $form_location );
$title = $title ? $title : __( '(no title)', 'wpforms-lite' );
$location_url = $this->get_location_url( $form_location );
$location_link = '';
if ( $location_url ) {
$location_full_url = $this->home_url . $location_url;
// phpcs:ignore Generic.Commenting.DocComment.MissingShort
/** @noinspection HtmlUnknownTarget */
$location_link = sprintf(
' <a href="%1$s" target="_blank" class="wpforms-locations-link">%2$s <i class="fa fa-external-link" aria-hidden="true"></i></a>',
esc_url( $location_full_url ),
esc_url( $location_url )
);
}
$location_edit_url = $this->get_location_edit_url( $form_location );
$location_edit_url = $location_edit_url ? $location_edit_url : '#';
// phpcs:ignore Generic.Commenting.DocComment.MissingShort
/** @noinspection HtmlUnknownTarget */
$location_edit_link = sprintf(
'<a href="%1$s">%2$s</a>',
esc_url( $location_edit_url ),
esc_html( $title )
);
// Escaped above.
return sprintf(
'<span class="wpforms-locations-list-item">%s</span>',
$location_edit_link . $location_link
);
}
/**
* Get location information to output as rows in the location pane.
*
* @since 1.7.4
*
* @param array $form_locations Form locations.
*
* @return array
*/
private function get_location_rows( $form_locations ) {
$rows = [];
foreach ( $form_locations as $form_location ) {
$rows[] = $this->get_location_row( $form_location );
}
$rows = array_unique( array_filter( $rows ) );
uasort(
$rows,
static function( $a, $b ) {
$pattern = '/href=".+widgets.php">(.+?)</i';
$widget_title_a = preg_match( $pattern, $a, $ma ) ? $ma[1] : '';
$widget_title_b = preg_match( $pattern, $b, $mb ) ? $mb[1] : '';
return strcmp( $widget_title_a, $widget_title_b );
}
);
return $rows;
}
/**
* Update form location on save_post action.
*
* @since 1.7.4
*
* @param int $post_ID Post ID.
* @param WP_Post $post Post object.
* @param bool $update Whether this is an existing post being updated.
*
* @noinspection PhpUnusedParameterInspection
*/
public function save_post( $post_ID, $post, $update ) {
if (
$update ||
! in_array( $post->post_type, $this->get_post_types(), true ) ||
! in_array( $post->post_status, $this->get_post_statuses(), true )
) {
return;
}
$form_ids = $this->get_form_ids( $post->post_content );
$this->update_form_locations_metas( $post, [], $form_ids );
}
/**
* Update form location on post_updated action.
*
* @since 1.7.4
*
* @param int $post_id Post id.
* @param WP_Post $post_after Post after the update.
* @param WP_Post $post_before Post before the update.
*
* @noinspection PhpUnusedParameterInspection
*/
public function post_updated( $post_id, $post_after, $post_before ) {
if (
! in_array( $post_after->post_type, $this->get_post_types(), true ) ||
! in_array( $post_after->post_status, $this->get_post_statuses(), true )
) {
return;
}
$form_ids_before = $this->get_form_ids( $post_before->post_content );
$form_ids_after = $this->get_form_ids( $post_after->post_content );
$this->update_form_locations_metas( $post_after, $form_ids_before, $form_ids_after );
}
/**
* Update form locations on trash_post action.
*
* @since 1.7.4
*
* @param int $post_id Post id.
*/
public function trash_post( $post_id ) {
$post = get_post( $post_id );
$form_ids_before = $this->get_form_ids( $post->post_content );
$form_ids_after = [];
$this->update_form_locations_metas( $post, $form_ids_before, $form_ids_after );
}
/**
* Update form locations on untrash_post action.
*
* @since 1.7.4
*
* @param int $post_id Post id.
*/
public function untrash_post( $post_id ) {
$post = get_post( $post_id );
$form_ids_before = [];
$form_ids_after = $this->get_form_ids( $post->post_content );
$this->update_form_locations_metas( $post, $form_ids_before, $form_ids_after );
}
/**
* Prepare widgets for further search.
*
* @since 1.7.4
*
* @param array|null $widgets Widgets.
* @param string $type Widget type.
*
* @return array
*/
private function prepare_widgets( $widgets, $type ) {
$params = [
'wpforms' => [
'option' => self::WPFORMS_WIDGET_OPTION,
'content' => 'form_id',
],
'text' => [
'option' => self::TEXT_WIDGET_OPTION,
'content' => 'text',
],
'block' => [
'option' => self::BLOCK_WIDGET_OPTION,
'content' => 'content',
],
];
if ( ! array_key_exists( $type, $params ) ) {
return [];
}
$option = $params[ $type ]['option'];
$content = $params[ $type ]['content'];
if ( $widgets === null ) {
$widgets = get_option( $option );
}
if ( ! is_array( $widgets ) ) {
return [];
}
return array_filter(
$widgets,
static function ( $widget ) use ( $content ) {
return isset( $widget[ $content ] );
}
);
}
/**
* Search forms in WPForms widgets.
*
* @since 1.7.4
*
* @param array $widgets Widgets.
*
* @return array
*/
private function search_in_wpforms_widgets( $widgets = null ) {
$widgets = $this->prepare_widgets( $widgets, 'wpforms' );
$locations = [];
foreach ( $widgets as $id => $widget ) {
$locations[] = [
'type' => self::WIDGET,
'title' => $widget['title'],
'form_id' => $widget['form_id'],
'id' => self::WPFORMS_WIDGET_PREFIX . $id,
];
}
return $locations;
}
/**
* Search forms in text widgets.
*
* @since 1.7.4
*
* @param array $widgets Widgets.
*
* @return array
*/
private function search_in_text_widgets( $widgets = null ) {
$widgets = $this->prepare_widgets( $widgets, 'text' );
$locations = [];
foreach ( $widgets as $id => $widget ) {
$form_ids = $this->get_form_ids( $widget['text'] );
foreach ( $form_ids as $form_id ) {
$locations[] = [
'type' => self::WIDGET,
'title' => $widget['title'],
'form_id' => $form_id,
'id' => self::TEXT_WIDGET_PREFIX . $id,
];
}
}
return $locations;
}
/**
* Search forms in block widgets.
*
* @since 1.7.4
*
* @param array $widgets Widgets.
*
* @return array
*/
private function search_in_block_widgets( $widgets = null ) {
$widgets = $this->prepare_widgets( $widgets, 'block' );
$locations = [];
foreach ( $widgets as $id => $widget ) {
$form_ids = $this->get_form_ids( $widget['content'] );
foreach ( $form_ids as $form_id ) {
$locations[] = [
'type' => self::WIDGET,
'title' => $this->block_widget_title,
'form_id' => $form_id,
'id' => self::BLOCK_WIDGET_PREFIX . $id,
];
}
}
return $locations;
}
/**
* Search forms in widgets.
*
* @since 1.7.4
*
* @return array
*/
public function search_in_widgets() {
return array_merge(
$this->search_in_wpforms_widgets(),
$this->search_in_text_widgets(),
$this->search_in_block_widgets()
);
}
/**
* Get difference of two arrays containing locations.
*
* @since 1.7.4
*
* @param array $locations1 Locations to subtract from.
* @param array $locations2 Locations to subtract.
*
* @return array
*/
private function array_udiff( $locations1, $locations2 ) {
return array_udiff(
$locations1,
$locations2,
static function ( $a, $b ) {
return ( $a === $b ) ? 0 : - 1;
}
);
}
/**
* Remove locations from metas.
*
* @since 1.7.4
*
* @param array $locations_to_remove Locations to remove.
*
* @return void
*/
private function remove_locations( $locations_to_remove ) {
foreach ( $locations_to_remove as $location_to_remove ) {
$locations = get_post_meta( $location_to_remove['form_id'], self::LOCATIONS_META, true );
if ( ! $locations ) {
continue;
}
foreach ( $locations as $key => $location ) {
if ( $location['id'] === $location_to_remove['id'] ) {
unset( $locations[ $key ] );
}
}
update_post_meta( $location_to_remove['form_id'], self::LOCATIONS_META, $locations );
}
}
/**
* Add locations to metas.
*
* @since 1.7.4
*
* @param array $locations_to_add Locations to add.
*
* @return void
*/
private function add_locations( $locations_to_add ) {
foreach ( $locations_to_add as $location_to_add ) {
$locations = get_post_meta( $location_to_add['form_id'], self::LOCATIONS_META, true );
if ( ! $locations ) {
$locations = [];
}
$locations[] = $location_to_add;
update_post_meta( $location_to_add['form_id'], self::LOCATIONS_META, $locations );
}
}
/**
* Update form locations on widget update.
*
* @since 1.7.4
*
* @param mixed $old_value The old option value.
* @param mixed $value The new option value.
* @param string $option Option name.
*/
public function update_option( $old_value, $value, $option ) {
switch ( $option ) {
case self::WPFORMS_WIDGET_OPTION:
$old_locations = $this->search_in_wpforms_widgets( $old_value );
$new_locations = $this->search_in_wpforms_widgets( $value );
break;
case self::TEXT_WIDGET_OPTION:
$old_locations = $this->search_in_text_widgets( $old_value );
$new_locations = $this->search_in_text_widgets( $value );
break;
case self::BLOCK_WIDGET_OPTION:
$old_locations = $this->search_in_block_widgets( $old_value );
$new_locations = $this->search_in_block_widgets( $value );
break;
default:
// phpcs:ignore WPForms.Formatting.EmptyLineBeforeReturn.AddEmptyLineBeforeReturnStatement
return;
}
$this->remove_locations( $this->array_udiff( $old_locations, $new_locations ) );
$this->add_locations( $this->array_udiff( $new_locations, $old_locations ) );
}
/**
* Delete locations and schedule new rescan on change of permalink structure.
*
* @since 1.7.4
*
* @param string $old_permalink_structure The previous permalink structure.
* @param string $permalink_structure The new permalink structure.
*
* @noinspection PhpUnusedParameterInspection
*/
public function permalink_structure_changed( $old_permalink_structure, $permalink_structure ) {
/**
* Run Forms Locator delete action.
*
* @since 1.7.4
*/
do_action( FormsLocatorScanTask::DELETE_ACTION ); // phpcs:ignore WPForms.PHP.ValidateHooks.InvalidHookName
/**
* Run Forms Locator scan action.
*
* @since 1.7.4
*/
do_action( FormsLocatorScanTask::RESCAN_ACTION ); // phpcs:ignore WPForms.PHP.ValidateHooks.InvalidHookName
}
/**
* Update form locations metas.
*
* @since 1.7.4
*
* @param WP_Post $post_after Post after the update.
* @param array $form_ids_before Form ids before the update.
* @param array $form_ids_after Form ids after the update.
*/
private function update_form_locations_metas( $post_after, $form_ids_before, $form_ids_after ) {
$post_id = $post_after->ID;
$url = get_permalink( $post_id );
$url = ( $url === false || is_wp_error( $url ) ) ? '' : $url;
$url = str_replace( $this->home_url, '', $url );
$form_ids_to_remove = array_diff( $form_ids_before, $form_ids_after );
$form_ids_to_add = array_diff( $form_ids_after, $form_ids_before );
foreach ( $form_ids_to_remove as $form_id ) {
$locations = $this->get_locations_without_current_post( $form_id, $post_id );
update_post_meta( $form_id, self::LOCATIONS_META, $locations );
}
foreach ( $form_ids_to_add as $form_id ) {
$locations = $this->get_locations_without_current_post( $form_id, $post_id );
$locations[] = [
'type' => $post_after->post_type,
'title' => $post_after->post_title,
'form_id' => $form_id,
'id' => $post_id,
'status' => $post_after->post_status,
'url' => $url,
];
update_post_meta( $form_id, self::LOCATIONS_META, $locations );
}
}
/**
* Get post types for search in.
*
* @since 1.7.4
*
* @return string[]
*/
public function get_post_types() {
$args = [
'public' => true,
'publicly_queryable' => true,
];
$post_types = get_post_types( $args, 'names', 'or' );
unset( $post_types['attachment'] );
$post_types[] = self::WP_TEMPLATE;
$post_types[] = self::WP_TEMPLATE_PART;
return $post_types;
}
/**
* Get post statuses for search in.
*
* @since 1.7.4
*
* @return string[]
*/
public function get_post_statuses() {
return [ 'publish', 'pending', 'draft', 'future', 'private' ];
}
/**
* Get form ids from the content.
*
* @since 1.7.4
*
* @param string $content Content.
*
* @return int[]
*/
public function get_form_ids( $content ) {
$form_ids = [];
if (
preg_match_all(
/**
* Extract id from conventional wpforms shortcode or wpforms block.
* Examples:
* [wpforms id="32" title="true" description="true"]
* <!-- wp:wpforms/form-selector {"formId":"32","displayTitle":true,"displayDesc":true} /-->
* In both, we should find 32.
*/
'#\[\s*wpforms.+id\s*=\s*"(\d+?)".*]|<!-- wp:wpforms/form-selector {"formId":"(\d+?)".*?} /-->#',
$content,
$matches
)
) {
array_shift( $matches );
$form_ids = array_map(
'intval',
array_unique( array_filter( array_merge( ...$matches ) ) )
);
}
return $form_ids;
}
/**
* Get form locations without current post.
*
* @since 1.7.4
*
* @param int $form_id Form id.
* @param int $post_id Post id.
*
* @return array
*/
private function get_locations_without_current_post( $form_id, $post_id ) {
$locations = get_post_meta( $form_id, self::LOCATIONS_META, true );
if ( ! is_array( $locations ) ) {
$locations = [];
}
return array_filter(
$locations,
static function ( $location ) use ( $post_id ) {
return $location['id'] !== $post_id;
}
);
}
/**
* Determine whether a post is visible.
*
* @since 1.7.4
*
* @param array $location Post location.
*
* @return bool
*/
private function is_post_visible( $location ) {
$edit_cap = 'edit_post';
$read_cap = 'read_post';
$post_id = $location['id'];
$post_status_obj = get_post_status_object( $location['status'] );
// Post status is not registered, assume it's not public.
if ( ! $post_status_obj ) {
return current_user_can( $edit_cap, $post_id );
}
if ( $post_status_obj->public ) {
return true;
}
if ( ! is_user_logged_in() ) {
// User must be logged in to view unpublished posts.
return false;
}
if ( $post_status_obj->protected ) {
// User must have edit permissions on the draft to preview.
return current_user_can( $edit_cap, $post_id );
}
if ( $post_status_obj->private ) {
return current_user_can( $read_cap, $post_id );
}
return false;
}
}
Forms/Token.php 0000644 00000017333 15133255232 0007433 0 ustar 00 <?php
namespace WPForms\Forms;
/**
* Class Token.
*
* This token class generates tokens that are used in our Anti-Spam checking mechanism.
*
* @since 1.6.2
*/
class Token {
/**
* Initialise the actions for the Anti-spam.
*
* @since 1.6.2
*/
public function init() {
$this->hooks();
}
/**
* Register hooks.
*
* @since 1.6.2
*/
public function hooks() {
add_filter( 'wpforms_frontend_form_atts', [ $this, 'add_token_to_form_atts' ], 10, 2 );
}
/**
* Return a valid token.
*
* @since 1.6.2
* @since 1.7.1 Added the $form_data argument.
*
* @param mixed $current True to use current time, otherwise a timestamp string.
* @param array $form_data Form data and settings.
*
* @return string Token.
*/
public function get( $current = true, $form_data = [] ) {
// If $current was not passed, or it is true, we use the current timestamp.
// If $current was passed in as a string, we'll use that passed in timestamp.
if ( $current !== true ) {
$time = $current;
} else {
$time = time();
}
// Format the timestamp to be less exact, as we want to deal in days.
// June 19th, 2020 would get formatted as: 1906202017125.
// Day of the month, month number, year, day number of the year, week number of the year.
$token_data = gmdate( 'dmYzW', $time );
if ( ! empty( $form_data['id'] ) ) {
$token_data .= "::{$form_data['id']}";
}
// Combine our token date and our token salt, and md5 it.
return md5( $token_data . \WPForms\Helpers\Crypto::get_secret_key() );
}
/**
* Generate the array of valid tokens to check for. These include two days
* before the current date to account for long cache times.
*
* These two filters are available if a user wants to extend the times.
* 'wpforms_form_token_check_before_today'
* 'wpforms_form_token_check_after_today'
*
* @since 1.6.2
* @since 1.7.1 Added the $form_data argument.
*
* @param array $form_data Form data and settings.
*
* @return array Array of all valid tokens to check against.
*/
public function get_valid_tokens( $form_data = [] ) {
$current_date = time();
// Create our array of times to check before today. A user with a longer
// cache time can extend this. A user with a shorter cache time can remove times.
$valid_token_times_before = apply_filters(
'wpforms_form_token_check_before_today',
[
( 2 * DAY_IN_SECONDS ), // Two days ago.
( 1 * DAY_IN_SECONDS ), // One day ago.
]
);
// Mostly to catch edge cases like the form page loading and submitting on two different days.
// This probably won't be filtered by users too much, but they could extend it.
$valid_token_times_after = apply_filters(
'wpforms_form_token_check_after_today',
[
( 45 * MINUTE_IN_SECONDS ), // Add in 45 minutes past today to catch some midnight edge cases.
]
);
// Built up our valid tokens.
$valid_tokens = [];
// Add in all the previous times we check.
foreach ( $valid_token_times_before as $time ) {
$valid_tokens[] = $this->get( $current_date - $time, $form_data );
}
// Add in our current date.
$valid_tokens[] = $this->get( $current_date, $form_data );
// Add in the times after our check.
foreach ( $valid_token_times_after as $time ) {
$valid_tokens[] = $this->get( $current_date + $time, $form_data );
}
return $valid_tokens;
}
/**
* Check if the given token is valid or not.
*
* Tokens are valid for some period of time (see wpforms_token_validity_in_hours
* and wpforms_token_validity_in_days to extend the validation period).
* By default tokens are valid for day.
*
* @since 1.6.2
* @since 1.7.1 Added the $form_data argument.
*
* @param string $token Token to validate.
* @param array $form_data Form data and settings.
*
* @return bool Whether the token is valid or not.
*/
public function verify( $token, $form_data = [] ) {
// Check to see if our token is inside the valid tokens.
return in_array( $token, $this->get_valid_tokens( $form_data ), true );
}
/**
* Add the token to the form attributes.
*
* @since 1.6.2
* @since 1.7.1 Added the $form_data argument.
*
* @param array $attrs Form attributes.
* @param array $form_data Form data and settings.
*
* @return array Form attributes.
*/
public function add_token_to_form_atts( array $attrs, array $form_data ) {
$attrs['atts']['data-token'] = $this->get( true, $form_data );
return $attrs;
}
/**
* Validate Anti-spam if enabled.
*
* @since 1.6.2
*
* @param array $form_data Form data.
* @param array $fields Fields.
* @param array $entry Form entry.
*
* @return bool|string True or a string with the error.
*/
public function validate( array $form_data, array $fields, array $entry ) {
// Bail out if we don't have the antispam setting.
if ( empty( $form_data['settings']['antispam'] ) ) {
return true;
}
// Bail out if the antispam setting isn't enabled.
if ( $form_data['settings']['antispam'] !== '1' ) {
return true;
}
$valid = isset( $entry['token'] ) && $this->verify( $entry['token'], $form_data );
$valid = $this->process_antispam_filter_wrapper( $valid, $fields, $entry, $form_data );
if ( ! $valid ) {
return $this->get_missing_token_message();
}
return true;
}
/**
* Helper to run our filter on all the responses for the antispam checks.
*
* @since 1.6.2
*
* @param bool $is_valid_not_spam Is valid entry or not.
* @param array $fields Form Fields.
* @param array $entry Form entry.
* @param array $form_data Form Data.
*
* @return bool Is valid or not.
*/
public function process_antispam_filter_wrapper( $is_valid_not_spam, array $fields, array $entry, array $form_data ) {
return apply_filters( 'wpforms_process_antispam', $is_valid_not_spam, $fields, $entry, $form_data );
}
/**
* Helper to get the missing token message.
*
* @since 1.6.2.1
*
* @return string missing token message.
*/
private function get_missing_token_message() {
return $this->get_error_message( esc_html__( 'This page isn\'t loading JavaScript properly, and the form will not be able to submit.', 'wpforms-lite' ) );
}
/**
* Helper to get the invalid token message.
*
* @since 1.6.2.1
*
* @return string Invalid token message.
*/
private function get_invalid_token_message() {
return $this->get_error_message( esc_html__( 'Form token is invalid. Please refresh the page.', 'wpforms-lite' ) );
}
/**
* Get error message depends on user.
*
* @since 1.6.4.1
*
* @param string $text Message text.
*
* @return string
*/
private function get_error_message( $text ) {
return wpforms_current_user_can() ? $text . $this->maybe_get_support_text() : $this->get_non_logged_user_error_message();
}
/**
* Non logged user error message.
*
* @since 1.6.4.1
*
* @return string
*/
private function get_non_logged_user_error_message() {
return esc_html__( 'The form was unable to submit. Please contact the site administrator.', 'wpforms-lite' );
}
/**
* If a user is a super admin, add a support link to the message.
*
* @since 1.6.2.1
*
* @return string Support text if super admin, empty string if not.
*/
private function maybe_get_support_text() {
// If user isn't a super admin, don't return any text.
if ( ! is_super_admin() ) {
return '';
}
// If the user is an admin, return text with a link to support.
// We add a space here to separate the sentences, but outside of the localized
// text to avoid it being removed.
return ' ' . sprintf(
// translators: placeholders are links.
esc_html__( 'Please check out our %1$stroubleshooting guide%2$s for details on resolving this issue.', 'wpforms-lite' ),
'<a href="https://wpforms.com/docs/getting-support-wpforms/">',
'</a>'
);
}
}
Forms/Preview.php 0000644 00000013733 15133255232 0007774 0 ustar 00 <?php
namespace WPForms\Forms;
/**
* Form preview.
*
* @since 1.5.1
*/
class Preview {
/**
* Form data.
*
* @since 1.5.1
*
* @var array
*/
public $form_data;
/**
* Constructor.
*
* @since 1.5.1
*/
public function __construct() {
if ( ! $this->is_preview_page() ) {
return;
}
$this->hooks();
}
/**
* Check if current page request meets requirements for form preview page.
*
* @since 1.5.1
*
* @return bool
*/
public function is_preview_page() {
// Only proceed for the form preview page.
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
if ( empty( $_GET['wpforms_form_preview'] ) ) {
return false;
}
// Check for logged-in user with correct capabilities.
if ( ! is_user_logged_in() ) {
return false;
}
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
$form_id = absint( $_GET['wpforms_form_preview'] );
if ( ! wpforms_current_user_can( 'view_form_single', $form_id ) ) {
return false;
}
// Fetch form details.
$this->form_data = wpforms()->get( 'form' )->get( $form_id, [ 'content_only' => true ] );
// Check valid form was found.
if ( empty( $this->form_data ) || empty( $this->form_data['id'] ) ) {
return false;
}
return true;
}
/**
* Hooks.
*
* @since 1.5.1
*/
public function hooks() {
add_action( 'pre_get_posts', [ $this, 'pre_get_posts' ] );
add_filter( 'the_title', [ $this, 'the_title' ], 100, 1 );
add_filter( 'the_content', [ $this, 'the_content' ], 999 );
add_filter( 'get_the_excerpt', [ $this, 'the_content' ], 999 );
add_filter( 'home_template_hierarchy', [ $this, 'force_page_template_hierarchy' ] );
add_filter( 'frontpage_template_hierarchy', [ $this, 'force_page_template_hierarchy' ] );
add_filter( 'post_thumbnail_html', '__return_empty_string' );
}
/**
* Modify query, limit to one post.
*
* @since 1.5.1
* @since 1.7.0 Added `page_id`, `post_type` and `post__in` query variables.
*
* @param \WP_Query $query The WP_Query instance.
*/
public function pre_get_posts( $query ) {
if ( is_admin() || ! $query->is_main_query() ) {
return;
}
$query->set( 'page_id', '' );
$query->set( 'post_type', 'wpforms' );
$query->set( 'post__in', empty( $this->form_data['id'] ) ? [] : [ (int) $this->form_data['id'] ] );
$query->set( 'posts_per_page', 1 );
}
/**
* Customize form preview page title.
*
* @since 1.5.1
*
* @param string $title Page title.
*
* @return string
*/
public function the_title( $title ) {
if ( in_the_loop() ) {
$title = sprintf( /* translators: %s - form title. */
esc_html__( '%s Preview', 'wpforms-lite' ),
! empty( $this->form_data['settings']['form_title'] ) ? sanitize_text_field( $this->form_data['settings']['form_title'] ) : esc_html__( 'Form', 'wpforms-lite' )
);
}
return $title;
}
/**
* Customize form preview page content.
*
* @since 1.5.1
*
* @return string
*/
public function the_content() {
if ( ! isset( $this->form_data['id'] ) ) {
return '';
}
if ( ! wpforms_current_user_can( 'view_form_single', $this->form_data['id'] ) ) {
return '';
}
$links = [];
if ( wpforms_current_user_can( 'edit_form_single', $this->form_data['id'] ) ) {
$links[] = [
'url' => esc_url(
add_query_arg(
[
'page' => 'wpforms-builder',
'view' => 'fields',
'form_id' => absint( $this->form_data['id'] ),
],
admin_url( 'admin.php' )
)
),
'text' => esc_html__( 'Edit Form', 'wpforms-lite' ),
];
}
if ( wpforms()->is_pro() && wpforms_current_user_can( 'view_entries_form_single', $this->form_data['id'] ) ) {
$links[] = [
'url' => esc_url(
add_query_arg(
[
'page' => 'wpforms-entries',
'view' => 'list',
'form_id' => absint( $this->form_data['id'] ),
],
admin_url( 'admin.php' )
)
),
'text' => esc_html__( 'View Entries', 'wpforms-lite' ),
];
}
if ( ! empty( $_GET['new_window'] ) ) { // phpcs:ignore
$links[] = [
'url' => 'javascript:window.close();',
'text' => esc_html__( 'Close this window', 'wpforms-lite' ),
];
}
$content = '<p>';
$content .= esc_html__( 'This is a preview of the latest saved revision of your form. If this preview does not match your form, save your changes and then refresh this page. This form preview is not publicly accessible.', 'wpforms-lite' );
if ( ! empty( $links ) ) {
$content .= '<br>';
$content .= '<span class="wpforms-preview-notice-links">';
foreach ( $links as $key => $link ) {
$content .= '<a href="' . $link['url'] . '">' . $link['text'] . '</a>';
$l = array_keys( $links );
if ( end( $l ) !== $key ) {
$content .= ' <span style="display:inline-block;margin:0 6px;opacity: 0.5">|</span> ';
}
}
$content .= '</span>';
}
$content .= '</p>';
$content .= '<p>';
$content .= sprintf(
wp_kses(
/* translators: %s - WPForms doc link. */
__( 'For form testing tips, check out our <a href="%s" target="_blank" rel="noopener noreferrer">complete guide!</a>', 'wpforms-lite' ),
[
'a' => [
'href' => [],
'target' => [],
'rel' => [],
],
]
),
'https://wpforms.com/docs/how-to-properly-test-your-wordpress-forms-before-launching-checklist/'
);
$content .= '</p>';
$content .= do_shortcode( '[wpforms id="' . absint( $this->form_data['id'] ) . '"]' );
return $content;
}
/**
* Force page template types.
*
* @since 1.7.2
*
* @param array $templates A list of template candidates, in descending order of priority.
*
* @return array
*/
public function force_page_template_hierarchy( $templates ) {
return [ 'page.php', 'single.php', 'index.php' ];
}
/**
* Force page template types.
*
* @since 1.5.1
* @deprecated 1.7.2
*
* @return string
*/
public function template_include() {
_deprecated_function( __METHOD__, '1.7.2 of the WPForms plugin' );
return locate_template( [ 'page.php', 'single.php', 'index.php' ] );
}
}
Integrations/DefaultContent/DefaultContent.php 0000644 00000003267 15133255232 0015572 0 ustar 00 <?php
namespace WPForms\Integrations\DefaultContent;
use WPForms\Integrations\IntegrationInterface;
/**
* Class DefaultContent.
*
* @since 1.7.2
*/
class DefaultContent implements IntegrationInterface {
/**
* Indicate if current integration is allowed to load.
*
* @since 1.7.2
*
* @return bool
*/
public function allow_load() {
global $pagenow;
return get_option( 'fresh_site' ) && $pagenow === 'customize.php';
}
/**
* Load an integration.
*
* @since 1.7.2
*/
public function load() {
add_filter( 'get_theme_starter_content', [ $this, 'modify_starter_content' ], 1000, 2 );
}
/**
* Append education text to Contact page content.
*
* @since 1.7.2
*
* @param array $content Array of starter content.
* @param array $config Array of theme-specific starter content configuration.
*
* @return array
*/
public function modify_starter_content( $content, $config ) {
global $wp_version;
if ( ! isset( $content['posts']['contact'] ) ) {
return $content;
}
// Use Paragraph blocks for WP 5.0+.
$format = version_compare( $wp_version, '5.0', '>=' ) ? "<!-- wp:paragraph -->\n<p>%s</p>\n<!-- /wp:paragraph -->" : '<p>%s</p>';
$content['posts']['contact']['post_content'] .= sprintf(
$format,
wp_kses(
sprintf( /* translators: %s - The WPForms Overview page URL. */
_x( 'Create your <a href="%s" target="_blank" rel="noopener noreferrer">contact form</a> with WPForms in minutes.', 'Theme starter content', 'wpforms-lite' ),
esc_url( admin_url( 'admin.php?page=wpforms-overview' ) )
),
[
'a' => [
'href' => [],
'rel' => [],
'target' => [],
],
]
)
);
return $content;
}
}
Integrations/IntegrationInterface.php 0000644 00000000642 15133255232 0014032 0 ustar 00 <?php
namespace WPForms\Integrations;
/**
* Interface IntegrationInterface defines required methods for integrations to work properly.
*
* @since 1.4.8
*/
interface IntegrationInterface {
/**
* Indicate if current integration is allowed to load.
*
* @since 1.4.8
*
* @return bool
*/
public function allow_load();
/**
* Load an integration.
*
* @since 1.4.8
*/
public function load();
}
Integrations/Loader.php 0000644 00000004503 15133255232 0011134 0 ustar 00 <?php
namespace WPForms\Integrations;
/**
* Class Loader gives ability to track/load all integrations.
*
* @since 1.4.8
*/
class Loader {
/**
* Get the instance of a class and store it in itself.
*
* @since 1.4.8
*/
public static function get_instance() {
static $instance;
if ( ! $instance ) {
$instance = new Loader();
}
return $instance;
}
/**
* Loader constructor.
*
* @since 1.4.8
*/
public function __construct() {
$core_class_names = [
'SMTP\Notifications',
'LiteConnect\LiteConnect',
'Divi\Divi',
'Elementor\Elementor',
'Gutenberg\FormSelector',
'WPMailSMTP\Notifications',
'WPorg\Translations',
'UncannyAutomator\UncannyAutomator',
'UsageTracking\UsageTracking',
'DefaultThemes\DefaultThemes',
'TranslationsPress\Translations',
'DefaultContent\DefaultContent',
];
$class_names = (array) apply_filters( 'wpforms_integrations_available', $core_class_names );
foreach ( $class_names as $class_name ) {
$integration = $this->register_class( $class_name );
if ( ! empty( $integration ) ) {
$this->load_integration( $integration );
}
}
}
/**
* Load an integration.
*
* @param IntegrationInterface $integration Instance of an integration class.
*
* @since 1.4.8
*/
protected function load_integration( IntegrationInterface $integration ) {
if ( $integration->allow_load() ) {
$integration->load();
}
}
/**
* Register a new class.
*
* @since 1.5.6
*
* @param string $class_name Class name to register.
*
* @return IntegrationInterface Instance of class.
*/
public function register_class( $class_name ) {
$class_name = sanitize_text_field( $class_name );
// Load Lite class if exists.
if ( class_exists( 'WPForms\Lite\Integrations\\' . $class_name ) && ! wpforms()->is_pro() ) {
$class_name = 'WPForms\Lite\Integrations\\' . $class_name;
return new $class_name();
}
// Load Pro class if exists.
if ( class_exists( 'WPForms\Pro\Integrations\\' . $class_name ) && wpforms()->is_pro() ) {
$class_name = 'WPForms\Pro\Integrations\\' . $class_name;
return new $class_name();
}
// Load general class if neither Pro nor Lite class exists.
if ( class_exists( __NAMESPACE__ . '\\' . $class_name ) ) {
$class_name = __NAMESPACE__ . '\\' . $class_name;
return new $class_name();
}
}
}
Integrations/SMTP/Notifications.php 0000644 00000013216 15133255232 0013323 0 ustar 00 <?php
namespace WPForms\Integrations\SMTP;
use WPForms\Integrations\IntegrationInterface;
/**
* Notifications class.
*
* @since 1.7.6
*/
class Notifications implements IntegrationInterface {
/**
* Determine if the class is allowed to load.
*
* @since 1.7.6
*
* @return bool
*/
public function allow_load() {
return wpforms_is_admin_page( 'builder' );
}
/**
* Load the class.
*
* @since 1.7.6
*/
public function load() {
$this->hooks();
}
/**
* Hooks.
*
* @since 1.7.6
*/
private function hooks() {
add_filter( 'wpforms_builder_notifications_sender_address_settings', [ $this, 'change_from_email_settings' ], 10, 3 );
}
/**
* Add warning message when email doesn't match site domain.
*
* @since 1.7.6
*
* @param array $args Field settings.
* @param array $form_data Form data.
* @param int $id Notification ID.
*
* @return array
*/
public function change_from_email_settings( $args, $form_data, $id ) {
$email = empty( $form_data['settings']['notifications'][ $id ]['sender_address'] ) ? '{admin_email}' : $form_data['settings']['notifications'][ $id ]['sender_address'];
if ( $this->email_domain_matches_site_domain( $email ) || $this->has_active_smtp_plugin() ) {
return $args;
}
$site_domain = wp_parse_url( get_bloginfo( 'wpurl' ) )['host'];
$email_does_not_match_text = sprintf( /* translators: %1$s - WordPress site domain. */
__( 'The current \'From Email\' address does not match your website domain name (%1$s). This can cause your notification emails to be blocked or marked as spam.', 'wpforms-lite' ),
esc_html( $site_domain )
);
$install_wp_mail_smtp_text = sprintf(
wp_kses( /* translators: %1$s - WP Mail SMTP install page URL. */
__(
'We strongly recommend that you install the free <a href="%1$s" target="_blank">WP Mail SMTP</a> plugin! The Setup Wizard makes it easy to fix your emails.',
'wpforms-lite'
),
[
'a' => [
'href' => [],
'target' => [],
],
]
),
esc_url( admin_url( 'admin.php?page=wpforms-smtp' ) )
);
$address_match_text = sprintf( /* translators: %1$s - WordPress site domain. */
__( 'Alternately, try using a From Address that matches your website domain (no-reply@%1$s).', 'wpforms-lite' ),
esc_html( $site_domain )
);
$fix_email_delivery_text = sprintf(
wp_kses( /* translators: %1$s - Fixing email delivery issues doc URL. */
__(
'Please check out our <a href="%1$s" target="_blank" rel="noopener noreferrer">doc on fixing email delivery issues</a> for more details.',
'wpforms-lite'
),
[
'a' => [
'href' => [],
'target' => [],
'rel' => [],
],
]
),
esc_url( wpforms_utm_link( 'https://wpforms.com/docs/how-to-fix-wordpress-contact-form-not-sending-email-with-smtp/', 'Block Settings', 'Fixing Email Delivery Issues' ) )
);
$from_email_after = sprintf(
'<p>%1$s</p> <p>%2$s</p> <p>%3$s</p> <p>%4$s</p>',
$email_does_not_match_text,
$install_wp_mail_smtp_text,
$address_match_text,
$fix_email_delivery_text
);
$args['after'] = '<div class="wpforms-alert wpforms-alert-warning wpforms-alert-warning-wide">' . $from_email_after . '</div>';
$args['class'] .= ' wpforms-panel-field-warning';
return $args;
}
/**
* Check if the domain name in an email address matches the WordPress site domain.
*
* @since 1.7.6
*
* @param string $email The email address to check against the WordPress site domain.
*
* @return bool
*/
private function email_domain_matches_site_domain( $email ) {
// Process smart tags if they are used as a value.
$email = wpforms_process_smart_tags( $email, [] );
// Skip processing when email is empty or does not set.
// e.g. {field_id="3"} which we don't have at the moment.
if ( empty( $email ) ) {
return true;
}
$email_domain = substr( strrchr( $email, '@' ), 1 );
$site_domain = wp_parse_url( get_bloginfo( 'wpurl' ) )['host'];
return $email_domain === $site_domain;
}
/**
* Check if the site has any active SMTP plugins.
*
* @since 1.7.6
*
* @return bool
*/
private function has_active_smtp_plugin() {
// List of plugins from \WPMailSMTP\Conflicts.
$smtp_plugin_list = [
'branda-white-labeling/ultimate-branding.php',
'bws-smtp/bws-smtp.php',
'cimy-swift-smtp/cimy_swift_smtp.php',
'disable-emails/disable-emails.php',
'easy-wp-smtp/easy-wp-smtp.php',
'fluent-smtp/fluent-smtp.php',
'gmail-smtp/main.php',
'mailgun/mailgun.php',
'my-smtp-wp/my-smtp-wp.php',
'post-smtp/postman-smtp.php',
'postman-smtp/postman-smtp.php',
'postmark-approved-wordpress-plugin/postmark.php',
'sar-friendly-smtp/sar-friendly-smtp.php',
'sendgrid-email-delivery-simplified/wpsendgrid.php',
'smtp-mail/index.php',
'smtp-mailer/main.php',
'sparkpost/wordpress-sparkpost.php',
'turbosmtp/turbo-smtp-plugin.php',
'woocommerce-sendinblue-newsletter-subscription/woocommerce-sendinblue.php',
'wp-amazon-ses-smtp/wp-amazon-ses.php',
'wp-easy-smtp/wp-easy-smtp.php',
'wp-gmail-smtp/wp-gmail-smtp.php',
'wp-html-mail/wp-html-mail.php',
'wp-mail-bank/wp-mail-bank.php',
'wp-mail-booster/wp-mail-booster.php',
'wp-mail-smtp-mailer/wp-mail-smtp-mailer.php',
'wp-mail-smtp-pro/wp_mail_smtp.php',
'wp-mail-smtp/wp_mail_smtp.php',
'wp-mailgun-smtp/wp-mailgun-smtp.php',
'wp-offload-ses/wp-offload-ses.php',
'wp-sendgrid-smtp/wp-sendgrid-smtp.php',
'wp-ses/wp-ses.php',
'wp-smtp/wp-smtp.php',
'wp-yahoo-smtp/wp-yahoo-smtp.php',
];
foreach ( $smtp_plugin_list as $smtp_plugin ) {
if ( is_plugin_active( $smtp_plugin ) ) {
return true;
}
}
return false;
}
}
Integrations/DefaultThemes/DefaultThemes.php 0000644 00000013014 15133255232 0015207 0 ustar 00 <?php
namespace WPForms\Integrations\DefaultThemes;
use WPForms\Integrations\IntegrationInterface;
/**
* Class DefaultThemes.
*
* @since 1.6.6
*/
class DefaultThemes implements IntegrationInterface {
/**
* Twenty Twenty theme name.
*
* @since 1.6.6
*/
const TT = 'twentytwenty';
/**
* Twenty Twenty-One theme name.
*
* @since 1.6.6
*/
const TT1 = 'twentytwentyone';
/**
* Current theme name.
*
* @since 1.6.6
*
* @var string
*/
private $current_theme;
/**
* Determine if WordPress default theme is used.
*
* @since 1.6.6
*
* @return string
*/
private function get_current_default_theme() {
$allowed_themes = [ self::TT, self::TT1 ];
$theme = wp_get_theme();
$theme_name = $theme->get_template();
$theme_parent = $theme->parent();
$default_themes = array_intersect( array_filter( [ $theme_name, $theme_parent ] ), $allowed_themes );
return ! empty( $default_themes[0] ) ? $default_themes[0] : '';
}
/**
* Indicate if current integration is allowed to load.
*
* @since 1.6.6
*
* @return bool
*/
public function allow_load() {
$this->current_theme = $this->get_current_default_theme();
return ! empty( $this->current_theme );
}
/**
* Load an integration.
*
* @since 1.6.6
*/
public function load() {
if ( $this->current_theme === self::TT ) {
$this->tt_hooks();
return;
}
if ( $this->current_theme === self::TT1 ) {
$this->tt1_hooks();
return;
}
}
/**
* Hooks for the Twenty Twenty theme.
*
* @since 1.6.6
*/
private function tt_hooks() { // phpcs:disable WPForms.PHP.HooksMethod.InvalidPlaceForAddingHooks
add_action( 'wp_enqueue_scripts', [ $this, 'tt_iframe_fix' ], 11 );
add_action( 'wpforms_frontend_css', [ $this, 'tt_dropdown_fix' ] );
} // phpcs:enable WPForms.PHP.HooksMethod.InvalidPlaceForAddingHooks
/**
* Hooks for the Twenty Twenty-One theme.
*
* @since 1.6.6
*/
private function tt1_hooks() { // phpcs:disable WPForms.PHP.HooksMethod.InvalidPlaceForAddingHooks
$form_styling = wpforms_setting( 'disable-css', '1' );
if ( $form_styling === '1' ) {
add_action( 'wp_enqueue_scripts', [ $this, 'tt1_multiple_fields_fix' ], 11 );
add_action( 'wp_enqueue_scripts', [ $this, 'tt1_dropdown_fix' ], 11 );
}
if ( $form_styling === '2' ) {
add_action( 'wp_enqueue_scripts', [ $this, 'tt1_base_style_fix' ], 11 );
}
} // phpcs:enable WPForms.PHP.HooksMethod.InvalidPlaceForAddingHooks
/**
* Apply fix for Checkboxes and Radio fields in the Twenty Twenty-One theme.
*
* @since 1.6.6
*/
public function tt1_multiple_fields_fix() {
wp_add_inline_style(
'twenty-twenty-one-style',
/** @lang CSS */
'@supports (-webkit-appearance: none) or (-moz-appearance: none) {
div.wpforms-container-full .wpforms-form input[type=checkbox] {
-webkit-appearance: checkbox;
-moz-appearance: checkbox;
}
div.wpforms-container-full .wpforms-form input[type=radio] {
-webkit-appearance: radio;
-moz-appearance: radio;
}
div.wpforms-container-full .wpforms-form input[type=checkbox]:after,
div.wpforms-container-full .wpforms-form input[type=radio]:after {
content: none;
}
}'
);
}
/**
* Apply fix for Dropdown field arrow, when it disappeared from select in the Twenty Twenty-One theme.
*
* @since 1.6.8
*/
public function tt1_dropdown_fix() {
wp_add_inline_style(
'twenty-twenty-one-style',
/** @lang CSS */
'div.wpforms-container-full form.wpforms-form select {
background-image: url("data:image/svg+xml;utf8,<svg xmlns=\'http://www.w3.org/2000/svg\' width=\'10\' height=\'10\' fill=\'%2328303d\'><polygon points=\'0,0 10,0 5,5\'/></svg>");
background-repeat: no-repeat;
background-position: right var(--form--spacing-unit) top 60%;
}'
);
}
/**
* Apply fix for Checkboxes and Radio fields width in the Twenty Twenty-One theme, when the user uses only base styles.
*
* @since 1.6.8
*/
public function tt1_base_style_fix() {
wp_add_inline_style(
'twenty-twenty-one-style',
/** @lang CSS */
'.wpforms-container .wpforms-field input[type=checkbox],
.wpforms-container .wpforms-field input[type=radio] {
width: 25px;
height: 25px;
}
.wpforms-container .wpforms-field input[type=checkbox] + label,
.wpforms-container .wpforms-field input[type=radio] + label {
vertical-align: top;
}'
);
}
/**
* Apply resize fix for iframe HTML element, when the next page was clicked in the Twenty Twenty theme.
*
* @since 1.6.6
*/
public function tt_iframe_fix() {
wp_add_inline_script(
'twentytwenty-js',
/** @lang JavaScript */
'window.addEventListener( "load", function() {
if ( typeof jQuery === "undefined" ) {
return;
}
jQuery( document ).on( "wpformsPageChange wpformsShowConditionalsField", function() {
if ( typeof twentytwenty === "undefined" || typeof twentytwenty.intrinsicRatioVideos === "undefined" || typeof twentytwenty.intrinsicRatioVideos.makeFit === "undefined" ) {
return;
}
twentytwenty.intrinsicRatioVideos.makeFit();
} );
jQuery( document ).on( "wpformsRichTextEditorInit", function( e, editor ) {
jQuery( editor.container ).find( "iframe" ).addClass( "intrinsic-ignore" );
} );
} );'
);
}
/**
* Apply fix for the dropdown list in Twenty Twenty theme.
*
* @since 1.7.3
*/
public function tt_dropdown_fix() {
static $fixed = false;
if ( $fixed ) {
return;
}
?>
<style>
#site-content {
overflow: visible !important;
}
</style>
<?php
$fixed = true;
}
}
Integrations/WPorg/Translations.php 0000644 00000007155 15133255232 0013453 0 ustar 00 <?php
namespace WPForms\Integrations\WPorg;
use Language_Pack_Upgrader;
use Automatic_Upgrader_Skin;
use WPForms\Integrations\IntegrationInterface;
/**
* Load translations from WordPress.org for the Lite version.
*
* @since 1.6.9
*/
class Translations implements IntegrationInterface {
/**
* Full wp.org API URL for the plugin.
*
* @since 1.6.9
*/
const API_URL = 'https://api.wordpress.org/plugins/update-check/1.1/';
/**
* Indicate if current integration is allowed to load.
*
* @since 1.6.9
*
* @return bool
*/
public function allow_load() {
if ( ! is_admin() ) {
return false;
}
// For WordPress versions 4.9.0-4.9.4 this file must be included before the current_user_can() check.
require_once ABSPATH . 'wp-admin/includes/template.php';
if ( ! current_user_can( 'install_languages' ) ) {
return false;
}
require_once ABSPATH . 'wp-admin/includes/file.php';
require_once ABSPATH . 'wp-admin/includes/translation-install.php';
return wp_can_install_language_pack();
}
/**
* Load an integration.
*
* @since 1.6.9
*/
public function load() {
// Download translations for all addons when language for the site has been changed.
add_action( 'update_option_WPLANG', [ $this, 'download_translations' ] );
}
/**
* Get translation packages from the wp.org API.
*
* @since 1.6.9
*
* @return array
*/
private function get_translation_packages() {
$plugin_data = get_plugin_data( WPFORMS_PLUGIN_FILE );
$plugin_data['Name'] = 'WPForms Lite';
$plugin_data['Version'] = '9999.0';
$request = wp_remote_post(
self::API_URL,
[
'body' => [
'plugins' => wp_json_encode(
[
'plugins' => [
'wpforms-lite/wpforms.php' => $plugin_data,
],
]
),
'locale' => wp_json_encode( get_available_languages() ),
],
]
);
$code = wp_remote_retrieve_response_code( $request );
$body = wp_remote_retrieve_body( $request );
if ( $code !== 200 || $body === 'error' || is_wp_error( $body ) ) {
return [];
}
$body = json_decode( $body, true );
return ! empty( $body['translations'] ) ? $body['translations'] : [];
}
/**
* Download translations for all available languages.
*
* @since 1.6.9
*/
public function download_translations() {
$translations = $this->get_translation_packages();
if ( empty( $translations ) ) {
return;
}
$skin = new Automatic_Upgrader_Skin();
$upgrader = new Language_Pack_Upgrader( $skin );
foreach ( $translations as $language ) {
// Sometimes a language can be passed as array.
$this->download_package( (object) $language, $upgrader, $skin );
}
}
/**
* Download translation for the language.
*
* @since 1.6.9
*
* @param object $language Language package.
* @param Language_Pack_Upgrader $upgrader The instance of the core class used for updating/installing language packs (translations).
* @param Automatic_Upgrader_Skin $skin Upgrader Skin for Automatic WordPress Upgrades.
*/
private function download_package( $language, Language_Pack_Upgrader $upgrader, Automatic_Upgrader_Skin $skin ) {
if ( ! property_exists( $language, 'package' ) || empty( $language->package ) ) {
return;
}
$skin->language_update = $language;
$upgrader->run(
[
'package' => $language->package,
'destination' => WP_LANG_DIR . '/plugins',
'abort_if_destination_exists' => false,
'is_multi' => true,
'hook_extra' => [
'language_update_type' => $language->type,
'language_update' => $language,
],
]
);
}
}
Integrations/UsageTracking/UsageTracking.php 0000644 00000034153 15133255232 0015210 0 ustar 00 <?php
namespace WPForms\Integrations\UsageTracking;
use WPForms\Integrations\IntegrationInterface;
/**
* Usage Tracker functionality to understand what's going on on client's sites.
*
* @since 1.6.1
*/
class UsageTracking implements IntegrationInterface {
/**
* The slug that will be used to save the option of Usage Tracker.
*
* @since 1.6.1
*/
const SETTINGS_SLUG = 'usage-tracking-enabled';
/**
* Indicate if current integration is allowed to load.
*
* @since 1.6.1
*
* @return bool
*/
public function allow_load() {
/**
* Whether the Usage Tracking code is allowed to be loaded.
*
* Description.
*
* @since 1.6.1
*
* @param bool $var Boolean value.
*/
return (bool) apply_filters( 'wpforms_usagetracking_is_allowed', true );
}
/**
* Whether Usage Tracking is enabled.
*
* @since 1.6.1
*
* @return bool
*/
public function is_enabled() {
/**
* Whether the Usage Tracking is enabled.
*
* Description.
*
* @since 1.6.1
*
* @param bool $var Boolean value taken from the DB.
*/
return (bool) apply_filters( 'wpforms_integrations_usagetracking_is_enabled', wpforms_setting( self::SETTINGS_SLUG ) );
}
/**
* Load an integration.
*
* @since 1.6.1
*/
public function load() {
add_filter( 'wpforms_settings_defaults', [ $this, 'settings_misc_option' ], 4 );
// Deregister the action if option is disabled.
add_action(
'wpforms_settings_updated',
function () {
if ( ! $this->is_enabled() ) {
( new SendUsageTask() )->cancel();
}
}
);
// Register the action handler only if enabled.
if ( $this->is_enabled() ) {
add_filter(
'wpforms_tasks_get_tasks',
static function ( $tasks ) {
$tasks[] = SendUsageTask::class;
return $tasks;
}
);
}
}
/**
* Add "Allow Usage Tracking" to WPForms settings.
*
* @since 1.6.1
*
* @param array $settings WPForms settings.
*
* @return array
*/
public function settings_misc_option( $settings ) {
$settings['misc'][ self::SETTINGS_SLUG ] = [
'id' => self::SETTINGS_SLUG,
'name' => esc_html__( 'Allow Usage Tracking', 'wpforms-lite' ),
'desc' => esc_html__( 'By allowing us to track usage data, we can better help you, as we will know which WordPress configurations, themes, and plugins we should test.', 'wpforms-lite' ),
'type' => 'checkbox',
];
return $settings;
}
/**
* Get the User Agent string that will be sent to the API.
*
* @since 1.6.1
*
* @return string
*/
public function get_user_agent() {
return 'WPForms/' . WPFORMS_VERSION . '; ' . get_bloginfo( 'url' );
}
/**
* Get data for sending to the server.
*
* @since 1.6.1
*
* @return array
*/
public function get_data() {
global $wpdb;
$theme_data = wp_get_theme();
$activated_dates = get_option( 'wpforms_activated', [] );
$first_form_date = get_option( 'wpforms_forms_first_created' );
$forms = $this->get_all_forms();
$forms_total = count( $forms );
$entries_total = $this->get_entries_total();
$data = [
// Generic data (environment).
'url' => home_url(),
'php_version' => PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION,
'wp_version' => get_bloginfo( 'version' ),
'mysql_version' => $wpdb->db_version(),
'server_version' => isset( $_SERVER['SERVER_SOFTWARE'] ) ? sanitize_text_field( wp_unslash( $_SERVER['SERVER_SOFTWARE'] ) ) : '',
'is_ssl' => is_ssl(),
'is_multisite' => is_multisite(),
'is_wpcom' => defined( 'IS_WPCOM' ) && IS_WPCOM,
'is_wpcom_vip' => ( defined( 'WPCOM_IS_VIP_ENV' ) && WPCOM_IS_VIP_ENV ) || ( function_exists( 'wpcom_is_vip' ) && wpcom_is_vip() ),
'sites_count' => $this->get_sites_total(),
'active_plugins' => $this->get_active_plugins(),
'theme_name' => $theme_data->name,
'theme_version' => $theme_data->version,
'locale' => get_locale(),
'timezone_offset' => $this->get_timezone_offset(),
// WPForms-specific data.
'wpforms_version' => WPFORMS_VERSION,
'wpforms_license_key' => wpforms_get_license_key(),
'wpforms_license_type' => $this->get_license_type(),
'wpforms_is_pro' => wpforms()->is_pro(),
'wpforms_entries_avg' => $this->get_entries_avg( $forms_total, $entries_total ),
'wpforms_entries_total' => $entries_total,
'wpforms_entries_last_7days' => $this->get_entries_total( '7days' ),
'wpforms_entries_last_30days' => $this->get_entries_total( '30days' ),
'wpforms_forms_total' => $forms_total,
'wpforms_challenge_stats' => get_option( 'wpforms_challenge', [] ),
'wpforms_lite_installed_date' => $this->get_installed( $activated_dates, 'lite' ),
'wpforms_pro_installed_date' => $this->get_installed( $activated_dates, 'pro' ),
'wpforms_builder_opened_date' => (int) get_option( 'wpforms_builder_opened_date', 0 ),
'wpforms_settings' => $this->get_settings(),
'wpforms_integration_active' => $this->get_forms_integrations( $forms ),
'wpforms_payments_active' => $this->get_payments_active( $forms ),
'wpforms_multiple_confirmations' => count( $this->get_forms_with_multiple_confirmations( $forms ) ),
'wpforms_multiple_notifications' => count( $this->get_forms_with_multiple_notifications( $forms ) ),
'wpforms_ajax_form_submissions' => count( $this->get_ajax_form_submissions( $forms ) ),
];
if ( ! empty( $first_form_date ) ) {
$data['wpforms_forms_first_created'] = $first_form_date;
}
return $data;
}
/**
* Get license type.
*
* @since 1.6.1
* @since 1.7.2 Clarified the license type.
*
* @return string
*/
private function get_license_type() {
if ( ! wpforms()->is_pro() ) {
return 'lite';
}
$license_type = wpforms_get_license_type();
$license_key = wpforms_get_license_key();
if ( ! $license_type ) {
return empty( $license_key ) ? 'no license' : 'not verified';
}
if ( wpforms_setting( 'is_expired', false, 'wpforms_license' ) ) {
return 'expired';
}
if ( wpforms_setting( 'is_disabled', false, 'wpforms_license' ) ) {
return 'disabled';
}
if ( wpforms_setting( 'is_invalid', false, 'wpforms_license' ) ) {
return 'invalid';
}
return $license_type;
}
/**
* Get all settings, except those with sensitive data.
*
* @since 1.6.1
*
* @return array
*/
private function get_settings() {
// Remove keys with exact names that we don't need.
$settings = array_diff_key(
get_option( 'wpforms_settings', [] ),
array_flip(
[
'stripe-test-secret-key',
'stripe-test-publishable-key',
'stripe-live-secret-key',
'stripe-live-publishable-key',
'authorize_net-test-api-login-id',
'authorize_net-test-transaction-key',
'authorize_net-live-api-login-id',
'authorize_net-live-transaction-key',
'square-location-id-sandbox',
'square-location-id-production',
'geolocation-google-places-api-key',
'geolocation-algolia-places-application-id',
'geolocation-algolia-places-search-only-api-key',
'geolocation-mapbox-search-access-token',
'recaptcha-site-key',
'recaptcha-secret-key',
'recaptcha-fail-msg',
'hcaptcha-site-key',
'hcaptcha-secret-key',
'hcaptcha-fail-msg',
]
)
);
$data = [];
// Remove keys with a vague names that we don't need.
foreach ( $settings as $key => $value ) {
if ( strpos( $key, 'validation-' ) !== false ) {
continue;
}
$data[ $key ] = $value;
}
return $data;
}
/**
* Get timezone offset.
* We use `wp_timezone_string()` when it's available (WP 5.3+),
* otherwise fallback to the same code, copy-pasted.
*
* @see wp_timezone_string()
*
* @since 1.6.1
*
* @return string
*/
private function get_timezone_offset() {
// It was added in WordPress 5.3.
if ( function_exists( 'wp_timezone_string' ) ) {
return wp_timezone_string();
}
/*
* The code below is basically a copy-paste from that function.
*/
$timezone_string = get_option( 'timezone_string' );
if ( $timezone_string ) {
return $timezone_string;
}
$offset = (float) get_option( 'gmt_offset' );
$hours = (int) $offset;
$minutes = ( $offset - $hours );
$sign = ( $offset < 0 ) ? '-' : '+';
$abs_hour = abs( $hours );
$abs_mins = abs( $minutes * 60 );
$tz_offset = sprintf( '%s%02d:%02d', $sign, $abs_hour, $abs_mins );
return $tz_offset;
}
/**
* Get the list of active plugins.
*
* @since 1.6.1
*
* @return array
*/
private function get_active_plugins() {
if ( ! function_exists( 'get_plugins' ) ) {
include ABSPATH . '/wp-admin/includes/plugin.php';
}
$active = get_option( 'active_plugins', [] );
$plugins = array_intersect_key( get_plugins(), array_flip( $active ) );
return array_map(
static function ( $plugin ) {
if ( isset( $plugin['Version'] ) ) {
return $plugin['Version'];
}
return 'Not Set';
},
$plugins
);
}
/**
* Installed date.
*
* @since 1.6.1
*
* @param array $activated_dates Input array with dates.
* @param string $key Input key what you want to get.
*
* @return mixed
*/
private function get_installed( $activated_dates, $key ) {
if ( ! empty( $activated_dates[ $key ] ) ) {
return $activated_dates[ $key ];
}
return false;
}
/**
* Number of forms with some integrations active.
*
* @since 1.6.1
*
* @param array $forms List of forms.
*
* @return array List of forms with active integrations count.
*/
private function get_forms_integrations( $forms ) {
$integrations = array_map(
static function ( $form ) {
if ( ! empty( $form->post_content['providers'] ) ) {
return array_keys( $form->post_content['providers'] );
}
return false;
},
$forms
);
$integrations = array_filter( $integrations );
if ( count( $integrations ) > 0 ) {
$integrations = call_user_func_array( 'array_merge', array_values( $integrations ) );
}
return array_count_values( $integrations );
}
/**
* Number of forms with active payments.
*
* @since 1.6.1
*
* @param array $forms Input forms list.
*
* @return array List of forms with active payments count.
*/
private function get_payments_active( $forms ) {
$payments = array_map(
static function ( $form ) {
if ( empty( $form->post_content['payments'] ) ) {
return false;
}
$enabled = [];
foreach ( $form->post_content['payments'] as $key => $value ) {
if ( ! empty( $value['enable'] ) ) {
$enabled[] = $key;
}
}
return empty( $enabled ) ? false : $enabled;
},
$forms
);
$payments = array_filter( $payments );
if ( count( $payments ) > 0 ) {
$payments = call_user_func_array( 'array_merge', array_values( $payments ) );
}
return array_count_values( $payments );
}
/**
* Forms with multiple notifications.
*
* @since 1.6.1
*
* @param array $forms List of forms to check.
*
* @return array List of forms with multiple notifications.
*/
private function get_forms_with_multiple_notifications( $forms ) {
return array_filter(
$forms,
static function ( $form ) {
return ! empty( $form->post_content['settings']['notifications'] ) && count( $form->post_content['settings']['notifications'] ) > 1;
}
);
}
/**
* Forms with multiple confirmations.
*
* @since 1.6.1
*
* @param array $forms List of forms to check.
*
* @return array List of forms with multiple confirmations.
*/
private function get_forms_with_multiple_confirmations( $forms ) {
return array_filter(
$forms,
static function ( $form ) {
return ! empty( $form->post_content['settings']['confirmations'] ) && count( $form->post_content['settings']['confirmations'] ) > 1;
}
);
}
/**
* Forms with ajax submission option enabled.
*
* @since 1.6.1
*
* @param array $forms All forms.
*
* @return array
*/
private function get_ajax_form_submissions( $forms ) {
return array_filter(
$forms,
static function ( $form ) {
return ! empty( $form->post_content['settings']['ajax_submit'] );
}
);
}
/**
* Total number of sites.
*
* @since 1.6.1
*
* @return int
*/
private function get_sites_total() {
return function_exists( 'get_blog_count' ) ? (int) get_blog_count() : 1;
}
/**
* Total number of entries.
*
* @since 1.6.1
*
* @param string $period Which period should be counted? Possible values: 7days, 30days.
* Everything else will mean "all" entries.
*
* @return int
*/
private function get_entries_total( $period = 'all' ) {
if ( ! wpforms()->is_pro() ) {
switch ( $period ) {
case '7days':
case '30days':
$count = 0;
break;
default:
global $wpdb;
$count = $wpdb->get_var( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching
"SELECT SUM(meta_value)
FROM {$wpdb->postmeta}
WHERE meta_key = 'wpforms_entries_count';"
);
}
return $count;
}
$args = [];
switch ( $period ) {
case '7days':
$args = [
'date' => [
gmdate( 'Y-m-d', strtotime( '-7 days' ) ),
gmdate( 'Y-m-d' ),
],
];
break;
case '30days':
$args = [
'date' => [
gmdate( 'Y-m-d', strtotime( '-30 days' ) ),
gmdate( 'Y-m-d' ),
],
];
break;
}
return wpforms()->entry->get_entries( $args, true );
}
/**
* Average entries count.
*
* @since 1.6.1
*
* @param int $forms Total forms count.
* @param int $entries Total entries count.
*
* @return int
*/
private function get_entries_avg( $forms, $entries ) {
return $forms ? round( $entries / $forms ) : 0;
}
/**
* Get all forms.
*
* @since 1.6.1
*
* @return array
*/
private function get_all_forms() {
$forms = wpforms()->form->get( '' );
if ( ! is_array( $forms ) ) {
return [];
}
return array_map(
static function ( $form ) {
$form->post_content = wpforms_decode( $form->post_content );
return $form;
},
$forms
);
}
}
Integrations/UsageTracking/SendUsageTask.php 0000644 00000004605 15133255232 0015161 0 ustar 00 <?php
namespace WPForms\Integrations\UsageTracking;
use WPForms\Tasks\Task;
/**
* Class SendUsageTask.
*
* @since 1.6.1
*/
class SendUsageTask extends Task {
/**
* Action name for this task.
*
* @since 1.6.1
*/
const ACTION = 'wpforms_send_usage_data';
/**
* Server URL to send requests to.
*
* @since 1.6.1
*/
const TRACK_URL = 'https://wpformsusage.com/v1/track';
/**
* Option name to store the timestamp of the last run.
*
* @since 1.6.3
*/
const LAST_RUN = 'wpforms_send_usage_last_run';
/**
* Class constructor.
*
* @since 1.6.1
*/
public function __construct() {
parent::__construct( self::ACTION );
$this->init();
}
/**
* Initialize the task with all the proper checks.
*
* @since 1.6.1
*/
public function init() {
// Register the action handler.
$this->hooks();
$tasks = wpforms()->get( 'tasks' );
// Add new if none exists.
if ( $tasks->is_scheduled( self::ACTION ) !== false ) {
return;
}
$this->recurring( $this->generate_start_date(), WEEK_IN_SECONDS )->register();
}
/**
* Add hooks.
*
* @since 1.7.3
*/
private function hooks() {
add_action( self::ACTION, [ $this, 'process' ] );
}
/**
* Randomly pick a timestamp
* which is not more than 1 week in the future
* starting from next sunday.
*
* @since 1.6.1
*
* @return int
*/
private function generate_start_date() {
$tracking = [];
$tracking['days'] = wp_rand( 0, 6 ) * DAY_IN_SECONDS;
$tracking['hours'] = wp_rand( 0, 23 ) * HOUR_IN_SECONDS;
$tracking['minutes'] = wp_rand( 0, 59 ) * MINUTE_IN_SECONDS;
$tracking['seconds'] = wp_rand( 0, 59 );
return strtotime( 'next sunday' ) + array_sum( $tracking );
}
/**
* Send the actual data in a POST request.
*
* @since 1.6.1
*/
public function process() {
$last_run = get_option( self::LAST_RUN );
// Make sure we do not run it more than once a day.
if (
$last_run !== false &&
( time() - $last_run ) < DAY_IN_SECONDS
) {
return;
}
// Send data to the usage tracking API.
$ut = new UsageTracking();
wp_remote_post(
self::TRACK_URL,
[
'timeout' => 5,
'redirection' => 5,
'httpversion' => '1.1',
'blocking' => true,
'body' => $ut->get_data(),
'user-agent' => $ut->get_user_agent(),
]
);
// Update the last run option to the current timestamp.
update_option( self::LAST_RUN, time() );
}
}
Integrations/UncannyAutomator/UncannyAutomator.php 0000644 00000012226 15133255232 0016545 0 ustar 00 <?php
namespace WPForms\Integrations\UncannyAutomator;
use WPForms\Integrations\IntegrationInterface;
/**
* UncannyAutomator class.
*
* @since 1.7.0
*/
class UncannyAutomator implements IntegrationInterface {
/**
* Custom priority for a provider, that will affect loading/placement order.
*
* @since 1.7.0
*
* @var int
*/
const PRIORITY = 15;
/**
* Unique provider slug.
*
* @since 1.7.0
*
* @var string
*/
const SLUG = 'uncanny-automator';
/**
* Translatable provider name.
*
* @since 1.7.0
*
* @var string
*/
private $name;
/**
* Custom provider icon (logo).
*
* @since 1.7.0
*
* @var string
*/
private $icon;
/**
* Indicate if current integration is allowed to load.
*
* @since 1.7.0
*
* @return bool
*/
public function allow_load() {
global $wp_version;
return PHP_VERSION_ID >= 50600 && version_compare( $wp_version, '5.3', '>=' ) && ! function_exists( 'Automator' );
}
/**
* Load the integration.
*
* @since 1.7.0
*/
public function load() {
$this->name = esc_html__( 'Uncanny Automator', 'wpforms-lite' );
$this->icon = WPFORMS_PLUGIN_URL . 'assets/images/icon-provider-uncanny-automator.png';
$this->hooks();
}
/**
* Register all hooks.
*
* @since 1.7.0
*/
private function hooks() {
add_action( 'wpforms_providers_panel_sidebar', [ $this, 'display_sidebar' ], self::PRIORITY );
add_action( 'wpforms_providers_panel_content', [ $this, 'display_content' ], self::PRIORITY );
add_filter( 'automator_on_activate_redirect_to_dashboard', '__return_false' );
add_action( 'wpforms_plugin_activated', [ $this, 'update_source' ] );
}
/**
* Display content inside the panel sidebar area.
*
* @since 1.7.0
*/
public function display_sidebar() {
printf(
'<a href="#" class="wpforms-panel-sidebar-section icon wpforms-panel-sidebar-section-%1$s" data-section="%1$s">
<img src="%2$s" alt="%4$s">%3$s<i class="fa fa-angle-right wpforms-toggle-arrow"></i>
</a>',
esc_attr( self::SLUG ),
esc_url( $this->icon ),
esc_html( $this->name ),
esc_attr( $this->name )
);
}
/**
* Display content inside the panel area.
*
* @since 1.7.0
*/
public function display_content() {
$plugins = get_plugins();
$is_installed = ! empty( $plugins[ sprintf( '%1$s/%1$s.php', self::SLUG ) ] );
$button_label = $is_installed ? esc_html__( 'Activate Now', 'wpforms-lite' ) : esc_html__( 'Install Now', 'wpforms-lite' );
$learn_more_url = esc_url(
add_query_arg(
[
'utm_source' => 'wpforms',
'utm_medium' => 'form_marketing',
'utm_content' => 'learn_more_btn_before_install',
'utm_r' => 150,
],
'https://automatorplugin.com/wpforms-automation/'
)
);
?>
<div
class="wpforms-panel-content-section wpforms-builder-provider wpforms-panel-content-section-<?php echo esc_attr( self::SLUG ); ?>"
id="<?php echo esc_attr( self::SLUG ); ?>-provider"
data-provider="<?php echo esc_attr( self::SLUG ); ?>">
<div class="wpforms-builder-provider-title wpforms-panel-content-section-title">
<?php echo esc_html( $this->name ); ?>
<?php
printf(
'<button class="wpforms-builder-provider-title-add education-modal"
data-name="%1$s"
data-slug="%2$s"
data-action="%3$s"
data-path="%2$s/%2$s.php"
data-type="plugin"
data-url="https://downloads.wordpress.org/plugin/%2$s.zip"
data-nonce="%4$s"
data-hide-on-success="true">%5$s</button>',
esc_attr(
sprintf( /* translators: %s - plugin name. */
__( '%s plugin', 'wpforms-lite' ),
$this->name
)
),
esc_attr( self::SLUG ),
$is_installed ? 'activate' : 'install',
esc_attr( wp_create_nonce( 'wpforms-admin' ) ),
esc_html( $button_label )
);
?>
</div>
<div class="wpforms-builder-provider-connections-default">
<img src="<?php echo esc_url( $this->icon ); ?>" alt="<?php echo esc_attr( $this->name ); ?>">
<div class="wpforms-builder-provider-settings-default-content">
<h2><?php esc_html_e( 'Put Your WordPress Site on Autopilot', 'wpforms-lite' ); ?></h2>
<p><?php esc_html_e( 'Build powerful automations that control what happens on form submission. Connect your forms to Google Sheets, Zoom, social media, membership plugins, elearning platforms, and more with Uncanny Automator.', 'wpforms-lite' ); ?></p>
<p>
<a href="<?php echo esc_url( $learn_more_url ); ?>"
class="wpforms-btn wpforms-btn-md wpforms-btn-orange"
target="_blank"
rel="noopener noreferrer">
<?php esc_html_e( 'Learn More', 'wpforms-lite' ); ?>
</a>
</p>
</div>
</div>
<div class="wpforms-builder-provider-body">
<div class="wpforms-provider-connections-wrap wpforms-clear">
<div class="wpforms-builder-provider-connections"></div>
</div>
</div>
</div>
<?php
}
/**
* Update source.
*
* @since 1.7.0
*
* @param string $plugin_base Path to the plugin file relative to the plugins' directory.
*/
public function update_source( $plugin_base ) {
if ( sprintf( '%1$s/%1$s.php', self::SLUG ) !== $plugin_base ) {
return;
}
update_option( 'uncannyautomator_source', 'wpforms' );
}
}
Integrations/Gutenberg/FormSelector.php 0000644 00000022451 15133255232 0014256 0 ustar 00 <?php
namespace WPForms\Integrations\Gutenberg;
use WPForms\Integrations\IntegrationInterface;
/**
* Form Selector Gutenberg block with live preview.
*
* @since 1.4.8
*/
class FormSelector implements IntegrationInterface {
/**
* Callbacks registered for wpforms_frontend_container_class filter.
*
* @since 1.7.5
*
* @var array
*/
private $callbacks = [];
/**
* Indicate if current integration is allowed to load.
*
* @since 1.4.8
*
* @return bool
*/
public function allow_load() {
return function_exists( 'register_block_type' );
}
/**
* Load an integration.
*
* @since 1.4.8
*/
public function load() {
$this->hooks();
}
/**
* Integration hooks.
*
* @since 1.4.8
*/
protected function hooks() {
add_action( 'init', [ $this, 'register_block' ] );
add_action( 'enqueue_block_editor_assets', [ $this, 'enqueue_block_editor_assets' ] );
add_action( 'wpforms_frontend_output_container_after', [ $this, 'replace_wpforms_frontend_container_class_filter' ] );
}
/**
* Replace the filter registered for wpforms_frontend_container_class.
*
* @since 1.7.5
*
* @param array $form_data Form data.
*
* @return void
*/
public function replace_wpforms_frontend_container_class_filter( $form_data ) { // phpcs:ignore WPForms.PHP.HooksMethod.InvalidPlaceForAddingHooks
if ( empty( $this->callbacks[ $form_data['id'] ] ) ) {
return;
}
$callback = array_shift( $this->callbacks[ $form_data['id'] ] );
remove_filter( 'wpforms_frontend_container_class', $callback );
if ( ! empty( $this->callbacks[ $form_data['id'] ] ) ) {
add_filter( 'wpforms_frontend_container_class', reset( $this->callbacks[ $form_data['id'] ] ), 10, 2 );
}
}
/**
* Register WPForms Gutenberg block on the backend.
*
* @since 1.4.8
*/
public function register_block() {
$attributes = [
'formId' => [
'type' => 'string',
],
'displayTitle' => [
'type' => 'boolean',
],
'displayDesc' => [
'type' => 'boolean',
],
'className' => [
'type' => 'string',
],
];
$this->register_styles();
register_block_type(
'wpforms/form-selector',
[
/**
* Modify wpforms block attributes.
*
* @since 1.5.8.2
*
* @param array $attributes Attributes.
*/
'attributes' => apply_filters( 'wpforms_gutenberg_form_selector_attributes', $attributes ), // phpcs:ignore WPForms.PHP.ValidateHooks.InvalidHookName
'style' => 'wpforms-gutenberg-form-selector',
'editor_style' => 'wpforms-integrations',
'render_callback' => [ $this, 'get_form_html' ],
]
);
}
/**
* Register WPForms Gutenberg block styles.
*
* @since 1.7.4.2
*/
protected function register_styles() {
if ( ! is_admin() ) {
return;
}
$min = wpforms_get_min_suffix();
wp_register_style(
'wpforms-integrations',
WPFORMS_PLUGIN_URL . "assets/css/admin-integrations{$min}.css",
[],
WPFORMS_VERSION
);
$disable_css_setting = (int) wpforms_setting( 'disable-css', '1' );
if ( $disable_css_setting === 3 ) {
return;
}
$css_file = $disable_css_setting === 2 ? 'base' : 'full';
wp_register_style(
'wpforms-gutenberg-form-selector',
WPFORMS_PLUGIN_URL . "assets/css/wpforms-{$css_file}{$min}.css",
[ 'wp-edit-blocks', 'wpforms-integrations' ],
WPFORMS_VERSION
);
}
/**
* Load WPForms Gutenberg block scripts.
*
* @since 1.4.8
*/
public function enqueue_block_editor_assets() {
$i18n = [
'title' => esc_html__( 'WPForms', 'wpforms-lite' ),
'description' => esc_html__( 'Select and display one of your forms.', 'wpforms-lite' ),
'form_keywords' => [
esc_html__( 'form', 'wpforms-lite' ),
esc_html__( 'contact', 'wpforms-lite' ),
esc_html__( 'survey', 'wpforms-lite' ),
'the dude',
],
'form_select' => esc_html__( 'Select a Form', 'wpforms-lite' ),
'form_settings' => esc_html__( 'Form Settings', 'wpforms-lite' ),
'form_selected' => esc_html__( 'Form', 'wpforms-lite' ),
'show_title' => esc_html__( 'Show Title', 'wpforms-lite' ),
'show_description' => esc_html__( 'Show Description', 'wpforms-lite' ),
'panel_notice_head' => esc_html__( 'Heads up!', 'wpforms-lite' ),
'panel_notice_text' => esc_html__( 'Do not forget to test your form.', 'wpforms-lite' ),
'panel_notice_link' => esc_html__( 'Check out our complete guide!', 'wpforms-lite' ),
];
if ( version_compare( $GLOBALS['wp_version'], '5.1.1', '<=' ) ) {
array_pop( $i18n['form_keywords'] );
}
wp_enqueue_style( 'wpforms-integrations' );
wp_enqueue_script(
'wpforms-gutenberg-form-selector',
// The unminified version is not supported by the browser.
WPFORMS_PLUGIN_URL . 'assets/js/components/admin/gutenberg/formselector.min.js',
[ 'wp-blocks', 'wp-i18n', 'wp-element' ],
WPFORMS_VERSION,
true
);
$forms = wpforms()->form->get( '', [ 'order' => 'DESC' ] );
$forms = ! empty( $forms ) ? $forms : [];
$forms = array_map(
static function( $form ) {
$form->post_title = htmlspecialchars_decode( $form->post_title, ENT_QUOTES );
return $form;
},
$forms
);
wp_localize_script(
'wpforms-gutenberg-form-selector',
'wpforms_gutenberg_form_selector',
[
'logo_url' => WPFORMS_PLUGIN_URL . 'assets/images/sullie-alt.png',
'block_preview_url' => WPFORMS_PLUGIN_URL . 'assets/images/integrations/gutenberg/block-preview.png',
'wpnonce' => wp_create_nonce( 'wpforms-gutenberg-form-selector' ),
'forms' => $forms,
'i18n' => $i18n,
]
);
}
/**
* Get form HTML to display in a WPForms Gutenberg block.
*
* @since 1.4.8
*
* @param array $attr Attributes passed by WPForms Gutenberg block.
*
* @return string
*/
public function get_form_html( $attr ) { // phpcs:ignore WPForms.PHP.HooksMethod.InvalidPlaceForAddingHooks
$id = ! empty( $attr['formId'] ) ? absint( $attr['formId'] ) : 0;
if ( empty( $id ) ) {
return '';
}
$title = ! empty( $attr['displayTitle'] );
$desc = ! empty( $attr['displayDesc'] );
if ( $this->is_gb_editor() ) {
$this->disable_fields_in_gb_editor();
}
if ( ! empty( $attr['className'] ) ) {
$class_callback = static function ( $classes, $form_data ) use ( $id, $attr ) {
if ( (int) $form_data['id'] !== $id ) {
return $classes;
}
$cls = array_map( 'esc_attr', explode( ' ', $attr['className'] ) );
return array_unique( array_merge( $classes, $cls ) );
};
if ( empty( $this->callbacks[ $id ] ) ) {
add_filter( 'wpforms_frontend_container_class', $class_callback, 10, 2 );
}
$this->callbacks[ $id ][] = $class_callback;
}
ob_start();
// phpcs:disable WPForms.PHP.ValidateHooks.InvalidHookName
/**
* Fires before Gutenberg block output.
*
* @since 1.5.8.2
*/
do_action( 'wpforms_gutenberg_block_before' );
/**
* Filter block title display flag.
*
* @since 1.5.8.2
*
* @param bool $title Title display flag.
* @param int $id Form id.
*/
$title = apply_filters( 'wpforms_gutenberg_block_form_title', $title, $id );
/**
* Filter block description display flag.
*
* @since 1.5.8.2
*
* @param bool $desc Description display flag.
* @param int $id Form id.
*/
$desc = apply_filters( 'wpforms_gutenberg_block_form_desc', $desc, $id );
if ( $this->is_gb_editor() ) {
wpforms_display(
$id,
$title,
$desc
);
} else {
printf(
'[wpforms id="%s" title="%d" description="%d"]',
absint( $id ),
(bool) $title,
(bool) $desc
);
}
/**
* Fires after Gutenberg block output.
*
* @since 1.5.8.2
*/
do_action( 'wpforms_gutenberg_block_after' );
$content = ob_get_clean();
if ( empty( $content ) ) {
$content = '<div class="components-placeholder"><div class="components-placeholder__label"></div>' .
'<div class="components-placeholder__fieldset">' .
esc_html__( 'The form cannot be displayed.', 'wpforms-lite' ) .
'</div></div>';
}
/**
* Filter Gutenberg block content.
*
* @since 1.5.8.2
*
* @param string $content Block content.
* @param int $id Form id.
*/
return apply_filters( 'wpforms_gutenberg_block_form_content', $content, $id );
// phpcs:enable WPForms.PHP.ValidateHooks.InvalidHookName
}
/**
* Checking if is Gutenberg REST API call.
*
* @since 1.5.7
*
* @return bool True if is Gutenberg REST API call.
*/
public function is_gb_editor() {
// TODO: Find a better way to check if is GB editor API call.
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
return defined( 'REST_REQUEST' ) && REST_REQUEST && ! empty( $_REQUEST['context'] ) && $_REQUEST['context'] === 'edit';
}
/**
* Disable form fields if called from the Gutenberg editor.
*
* @since 1.7.5
*
* @return void
*/
private function disable_fields_in_gb_editor() { // phpcs:ignore WPForms.PHP.HooksMethod.InvalidPlaceForAddingHooks
add_filter(
'wpforms_frontend_container_class',
function ( $classes ) {
$classes[] = 'wpforms-gutenberg-form-selector';
$classes[] = 'wpforms-container-full';
return $classes;
}
);
add_action(
'wpforms_frontend_output',
function () {
echo '<fieldset disabled>';
},
3
);
add_action(
'wpforms_frontend_output',
function () {
echo '</fieldset>';
},
30
);
}
}
Integrations/LiteConnect/LiteConnect.php 0000644 00000013434 15133255232 0014347 0 ustar 00 <?php
namespace WPForms\Integrations\LiteConnect;
use WPForms\Integrations\IntegrationInterface;
/**
* Class LiteConnect.
*
* @since 1.7.4
*/
abstract class LiteConnect implements IntegrationInterface {
/**
* The slug that will be used to save the option of Lite Connect.
*
* @since 1.7.4
*
* @var string
*/
const SETTINGS_SLUG = 'lite-connect-enabled';
/**
* The $_GET argument to trigger the auth key endpoint.
*
* @since 1.7.4.1
*
* @var string
*/
const AUTH_KEY_ARG = 'wpforms-liteconnect-auth-key';
/**
* Indicate if current integration is allowed to load.
*
* @since 1.7.4
*
* @return bool
*/
public function allow_load() {
return self::is_allowed();
}
/**
* Whether Lite Connect is allowed.
*
* @since 1.7.4
*
* @return bool
*/
public static function is_allowed() {
// Disable Lite Connect integration for local hosts.
$allowed = ! self::is_local_not_debug() && self::is_production();
// phpcs:disable WPForms.PHP.ValidateHooks.InvalidHookName
/**
* Determine whether Lite Connect integration is allowed to load.
*
* @since 1.7.4
*
* @param bool $is_allowed Is LiteConnect allowed? Value by default: true.
*/
return (bool) apply_filters( 'wpforms_integrations_lite_connect_is_allowed', $allowed );
// phpcs:enable WPForms.PHP.ValidateHooks.InvalidHookName
}
/**
* Whether Lite Connect is enabled.
*
* @since 1.7.4
*
* @return bool
*/
public static function is_enabled() {
// phpcs:disable WPForms.PHP.ValidateHooks.InvalidHookName
/**
* Determine whether LiteConnect is enabled on the WPForms > Settings admin page.
*
* @since 1.7.4
*
* @param bool $is_enabled Is LiteConnect enabled on WPForms > Settings page?
*/
return (bool) apply_filters( 'wpforms_integrations_lite_connect_is_enabled', wpforms_setting( self::SETTINGS_SLUG ) );
// phpcs:enable WPForms.PHP.ValidateHooks.InvalidHookName
}
/**
* Load an integration.
*
* @since 1.7.4
*/
public function load() {
$this->endpoints();
}
/**
* Whether Lite Connect is running locally and not in the debug mode.
*
* @since 1.7.4
*
* @return bool
*/
private static function is_local_not_debug() {
return ! defined( 'WPFORMS_DEBUG_LITE_CONNECT' ) && self::is_localhost();
}
/**
* Whether Lite Connect is running locally.
*
* @since 1.7.4
*
* @return bool
*/
private static function is_localhost() {
// Check for local IPs.
$ip = wpforms_get_ip();
if ( ! filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE ) ) {
return true;
}
// Check for local TLDs.
if ( ! empty( $_SERVER['HTTP_HOST'] ) ) {
$host = sanitize_text_field( wp_unslash( $_SERVER['HTTP_HOST'] ) );
$local_tlds = [
'.local',
'.invalid',
'.example',
'.test',
];
foreach ( $local_tlds as $tld ) {
if ( preg_match( '/' . $tld . '$/', $host ) ) {
return true;
}
}
}
// Return false if IP and TLD are not local.
return false;
}
/**
* Whether Lite Connect is running on production website.
*
* @since 1.7.6
*
* @return bool
*/
private static function is_production() {
return wp_get_environment_type() === 'production';
}
/**
* Provide responses to endpoint requests.
*
* @since 1.7.4
*/
private function endpoints() {
// We check nonce in the endpoint_key().
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
if ( ! isset( $_GET[ self::AUTH_KEY_ARG ] ) ) {
return;
}
$this->endpoint_key();
}
/**
* Process endpoint for callback on generate_site_key().
*
* @since 1.7.4
*/
private function endpoint_key() {
$json = file_get_contents( 'php://input' );
$response = json_decode( $json, true );
if ( ! $response ) {
$this->endpoint_die( 'Lite Connect: No response' );
}
if ( isset( $response['error'] ) ) {
$this->endpoint_die(
'Lite Connect: unable to add the site to system',
$response
);
}
if ( ! isset( $response['key'], $response['id'], $response['nonce'] ) ) {
$this->endpoint_die(
'Lite Connect: unknown communication error',
$response
);
}
if ( ! wp_verify_nonce( $response['nonce'], API::KEY_NONCE_ACTION ) ) {
$this->endpoint_die(
'Lite Connect: nonce verification failed',
$response
);
}
unset( $response['nonce'] );
$settings = get_option( Integration::get_option_name(), [] );
$settings['site'] = $response;
update_option( API::GENERATE_KEY_ATTEMPT_COUNTER_OPTION, 0 );
update_option( Integration::get_option_name(), $settings );
exit();
}
/**
* Finish the endpoint execution with wp_die().
*
* @since 1.7.4
*
* @param string $title Log message title.
* @param array $response Response.
*
* @noinspection ForgottenDebugOutputInspection
*/
private function endpoint_die( $title = '', $response = [] ) { // phpcs:ignore WPForms.PHP.HooksMethod.InvalidPlaceForAddingHooks
$this->log( $title, $response );
// We call wp_die too early, before the query is run.
// So, we should remove some filters to avoid having PHP notices in error log.
remove_filter( 'wp_robots', 'wp_robots_noindex_embeds' );
remove_filter( 'wp_robots', 'wp_robots_noindex_search' );
wp_die(
esc_html__( 'This is the Lite Connect endpoint page.', 'wpforms-lite' ),
'Lite Connect endpoint',
400
);
}
/**
* Log message.
*
* @since 1.7.4
*
* @param string $title Log message title.
* @param array $response Response.
*/
private function log( $title = '', $response = [] ) {
if ( ! $title ) {
return;
}
wpforms_log(
$title,
[
'response' => $response,
'request' => [
'domain' => isset( $response['domain'] ) ? $response['domain'] : '',
'admin_email' => Integration::get_enabled_email(),
],
],
[ 'type' => [ 'error' ] ]
);
}
}
Integrations/LiteConnect/Integration.php 0000644 00000022713 15133255232 0014423 0 ustar 00 <?php
namespace WPForms\Integrations\LiteConnect;
use WPForms\Admin\Notice;
use WPForms\Helpers\Transient;
use WPForms\Tasks\Tasks;
/**
* Class Integration.
*
* Base integration between Lite Connect API and WPForms.
*
* @since 1.7.4
*/
class Integration extends API {
/**
* Authentication data.
*
* @since 1.7.4
*
* @var array
*/
protected $auth = [];
/**
* Option name to store the total count of Lite Connect entries.
*
* @since 1.7.4
*
* @var string
*/
const LITE_CONNECT_ENTRIES_COUNT_OPTION = 'wpforms_lite_connect_entries_count';
/**
* Integration constructor.
*
* @since 1.7.4
*/
public function __construct() {
static $updated;
parent::__construct();
$this->hooks();
// Update the site key and access token.
if (
! $updated &&
( is_admin() && ! wp_doing_ajax() ) &&
( ( wpforms()->is_pro() && self::get_enabled_since() ) || LiteConnect::is_enabled() )
) {
$this->maybe_update_access_token();
$this->update_keys();
$updated = true;
}
}
/**
* Hooks.
*
* @since 1.7.5
*/
private function hooks() {
add_action( 'admin_init', [ $this, 'max_attempts_notice' ], 10 );
}
/**
* Update the site key and access token if they do not exist.
*
* @since 1.7.4
*/
public function update_keys() {
if ( isset( $this->auth['site_key'], $this->auth['access_token'] ) ) {
return;
}
$site_key = $this->get_site_key();
$this->auth = [
'site_key' => $site_key,
'access_token' => $this->get_access_token( $site_key ),
];
}
/**
* Get the site key.
*
* @since 1.7.4
*
* @return string|false|array The site key, or false on error.
*/
protected function get_site_key() {
// At first, try to get the site key from the wp-config.php file.
$debug_site_key = $this->get_debug_setting( 'key' );
if ( $debug_site_key !== false ) {
return $debug_site_key;
}
// If site key already exists, then we won't need to regenerate it.
$curr_key = wpforms_setting( 'site', false, self::get_option_name() );
if ( ! empty( $curr_key['key'] ) ) {
return $curr_key['key'];
}
// Generate the site key.
return $this->generate_site_key();
}
/**
* Get the access token.
*
* @since 1.7.4
*
* @param string|array $site_key The site key.
* @param bool $force True to force generate a new access token.
*
* @return string|false|void The access token, or false on error.
*/
protected function get_access_token( $site_key, $force = false ) {
if ( ! $site_key ) {
return false;
}
$curr_token = wpforms_setting( 'access_token', false, self::get_option_name() );
// It won't regenerate the access token if $force is false, and the current token is not expired.
if ( $force === false && isset( $curr_token['expires_at'] ) && (int) $curr_token['expires_at'] - time() > 0 ) {
return $curr_token['access_token'];
}
// Generate the access token.
$response = $this->generate_access_token( $site_key );
if ( $response ) {
$response = json_decode( $response, true );
if ( isset( $response['access_token'] ) ) {
$settings = get_option( self::get_option_name(), [] );
$settings['access_token'] = $response;
update_option( self::get_option_name(), $settings );
// Create task to refresh access token in 6 days.
$this->refresh_access_token_task();
return $response['access_token'];
}
wpforms_log(
'Lite Connect: unable to generate access token',
[
'response' => $response,
'request' => [
'domain' => $this->domain,
'site_id' => $this->site_id,
'wp_version' => get_bloginfo( 'version' ),
],
],
[ 'type' => [ 'error' ] ]
);
}
return false;
}
/**
* Create a task to refresh the access token.
*
* @since 1.7.4
*/
private function refresh_access_token_task() {
$tasks = wpforms()->get( 'tasks' );
if ( $tasks instanceof Tasks && ! $tasks->is_scheduled( RefreshAccessTokenTask::LITE_CONNECT_TASK ) ) {
( new RefreshAccessTokenTask() )->create();
}
}
/**
* Get the name for the Lite Connect's option.
*
* @since 1.7.4
*
* @return string
*/
public static function get_option_name() {
if ( defined( 'WPFORMS_LITE_CONNECT_STAGING' ) && WPFORMS_LITE_CONNECT_STAGING ) {
return API::STAGING_LITE_CONNECT_OPTION;
}
return API::LITE_CONNECT_OPTION;
}
/**
* Get the Lite Connect entries count.
*
* @since 1.7.4
*
* @return int The entries count.
*/
public static function get_entries_count() {
return (int) get_option( self::LITE_CONNECT_ENTRIES_COUNT_OPTION, 0 );
}
/**
* Get the Lite Connect new entries count (since previous import).
*
* @since 1.7.4
*
* @return int The new entries count.
*/
public static function get_new_entries_count() {
// Get current total entries count.
$count = self::get_entries_count();
// Reduces the entries that were already imported previously from the count.
$import = wpforms_setting( 'import', false, self::get_option_name() );
$prev_count = 0;
if ( isset( $import['previous_import_count'] ) ) {
$prev_count = (int) $import['previous_import_count'];
}
if ( isset( $import['previous_failed_count'] ) ) {
$prev_count += (int) $import['previous_failed_count'];
}
return $count < $prev_count ? 0 : $count - $prev_count;
}
/**
* Maybe restart the import flag (for when the user re-upgrades to pro).
*
* @since 1.7.4
*/
public static function maybe_restart_import_flag() {
$settings = get_option( self::get_option_name(), [] );
if ( empty( $settings ) ) {
return;
}
$status = isset( $settings['import']['status'] ) ? $settings['import']['status'] : false;
if ( $status === 'done' ) {
$previous_imported_entries = Transient::get( 'lite_connect_imported_entries' );
$settings['import']['previous_import_count'] = is_array( $previous_imported_entries ) ? count( $previous_imported_entries ) : 0;
$previous_failed_entries = Transient::get( 'lite_connect_failed_entries' );
$settings['import']['previous_failed_count'] = is_array( $previous_failed_entries ) ? count( $previous_failed_entries ) : 0;
}
self::maybe_set_entries_count();
// Reset import status to be able to restart import process.
unset(
$settings['import']['status'],
$settings['import']['user_notified']
);
update_option( self::get_option_name(), $settings );
if ( Transient::get( 'lite_connect_error' ) !== false ) {
Transient::delete( 'lite_connect_error' );
}
}
/**
* Get the Lite Connect enabled since timestamp.
*
* @since 1.7.4
*
* @return bool|int
*/
public static function get_enabled_since() {
return wpforms_setting( LiteConnect::SETTINGS_SLUG . '-since' );
}
/**
* Get the Email of the user who enabled Lite Connect.
*
* @since 1.7.4
*
* @return bool|string
*/
public static function get_enabled_email() {
return wpforms_setting( LiteConnect::SETTINGS_SLUG . '-email' );
}
/**
* Normalize Lite Connect entries counter when their value is wrong.
*
* @since 1.7.4
*/
public static function maybe_set_entries_count() {
$settings = get_option( self::get_option_name(), [] );
if ( empty( $settings ) ) {
return;
}
$previous_import_count = isset( $settings['import']['previous_import_count'] ) ? (int) $settings['import']['previous_import_count'] : 0;
$previous_failed_count = isset( $settings['import']['previous_failed_count'] ) ? (int) $settings['import']['previous_failed_count'] : 0;
$previous_import_count += $previous_failed_count;
// When the entries counter was manually deleted from options OR it was modified by another process,
// we are setting the counter to the value of the previous imported entries.
// In this way, the next form submission will increase counter properly, and user will see value of the backed up entries.
// Obviously, this solution is not perfect, but we don't have another source of the total entries count.
if ( $previous_import_count > self::get_entries_count() ) {
update_option( self::LITE_CONNECT_ENTRIES_COUNT_OPTION, $previous_import_count );
}
}
/**
* Show the Lite Connect notice about the max attempts to generate the API key.
*
* @since 1.7.5
*/
public function max_attempts_notice() {
$attempts_count = get_option( self::GENERATE_KEY_ATTEMPT_COUNTER_OPTION, 0 );
$notice_text = sprintf(
wp_kses( /* translators: %s - WPForms documentation link. */
__( 'Your form entries can’t be backed up because WPForms can’t connect to the backup server. If you’d like to back up your entries, find out how to <a href="%s" target="_blank" rel="noopener noreferrer">fix entry backup issues</a>.', 'wpforms-lite' ),
[
'a' => [
'href' => [],
'target' => [],
'rel' => [],
],
]
),
wpforms_utm_link( 'https://wpforms.com/docs/how-to-use-lite-connect-for-wpforms/#backup-issues', 'Admin Notice' )
);
if ( $attempts_count >= self::MAX_GENERATE_KEY_ATTEMPTS ) {
Notice::warning(
$notice_text,
[
'dismiss' => Notice::DISMISS_GLOBAL,
'slug' => 'max_attempts',
]
);
}
}
/**
* Maybe update access token.
*
* @since 1.7.6
*/
public function maybe_update_access_token() {
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
$action = isset( $_GET['wpforms_lite_connect_action'] ) ? sanitize_key( $_GET['wpforms_lite_connect_action'] ) : '';
if ( $action !== 'update-access-token' || ! current_user_can( 'manage_options' ) ) {
return;
}
$this->get_access_token( $this->get_site_key(), true );
}
}
Integrations/LiteConnect/API.php 0000644 00000023776 15133255232 0012563 0 ustar 00 <?php
namespace WPForms\Integrations\LiteConnect;
use WPForms\Helpers\Transient;
/**
* Class API.
*
* @since 1.7.4
*/
class API {
/**
* Option name.
*
* @since 1.7.4
*
* @var string
*/
const LITE_CONNECT_OPTION = 'wpforms_lite_connect';
/**
* Staging option name.
*
* @since 1.7.4
*
* @var string
*/
const STAGING_LITE_CONNECT_OPTION = 'wpforms_lite_connect_staging';
/**
* Lite Connect API URL.
*
* @since 1.7.4
*
* @var string
*/
const API_URL = 'https://wpformsliteconnect.com';
/**
* Lite Connect staging API URL.
*
* @since 1.7.4
*
* @var string
*/
const STAGING_API_URL = 'https://staging.wpformsliteconnect.com';
/**
* Lite Connect generate_site_key() lock transient name.
*
* @since 1.7.4
*
* @var string
*/
const LITE_CONNECT_SITE_KEY_LOCK = 'lite_connect_site_key_lock';
/**
* Lite Connect generate_access_token() lock transient name.
*
* @since 1.7.4
*/
const LITE_CONNECT_ACCESS_TOKEN_LOCK = 'lite_connect_access_token_lock';
/**
* Lite Connect create_not_logged_in_nonce() action.
*
* @since 1.7.4
*
* @var string
*/
const KEY_NONCE_ACTION = 'lite_connect_key_action';
/**
* Max number of attempts for generate_site_key().
*
* @since 1.7.5
*
* @var integer
*/
const MAX_GENERATE_KEY_ATTEMPTS = 20;
/**
* Generate key attempt counter.
*
* @since 1.7.5
*
* @var string
*/
const GENERATE_KEY_ATTEMPT_COUNTER_OPTION = 'wpforms_lite_connect_generate_key_attempt_counter';
/**
* Lite Connect API URL.
*
* @since 1.7.4
*
* @var string
*/
protected $api_url;
/**
* The site domain.
*
* @since 1.7.4
*
* @var string
*/
protected $domain;
/**
* The site ID.
*
* @since 1.7.4
*
* @var string
*/
protected $site_id;
/**
* API constructor.
*
* @since 1.7.4
*/
public function __construct() {
// Get the domain name.
// Strip protocol `http(s)://` and `www.` from the site URL.
$this->domain = preg_replace( '/(https?:\/\/)?(www\.)?(.*)\/?/', '$3', home_url() );
$this->api_url = self::API_URL;
if ( defined( 'WPFORMS_LITE_CONNECT_STAGING' ) && WPFORMS_LITE_CONNECT_STAGING ) {
$this->api_url = self::STAGING_API_URL;
}
$this->set_site_id();
}
/**
* Generate the site key.
*
* @since 1.7.4
*
* @return false
*/
protected function generate_site_key() {
if ( $this->is_max_generate_key_attempts_reached() ) {
return false;
}
if ( Transient::get( self::LITE_CONNECT_SITE_KEY_LOCK ) ) {
return false;
}
Transient::set( self::LITE_CONNECT_SITE_KEY_LOCK, true, MINUTE_IN_SECONDS );
$admin_email = Integration::get_enabled_email();
$user = get_user_by( 'email', $admin_email );
$data = [
'domain' => $this->domain,
'admin_email' => $admin_email,
'first_name' => ! empty( $user->first_name ) ? $user->first_name : '',
'last_name' => ! empty( $user->last_name ) ? $user->last_name : '',
'nonce' => $this->create_not_logged_in_nonce(),
'callback' => add_query_arg( [ LiteConnect::AUTH_KEY_ARG => '' ], trailingslashit( home_url() ) ),
];
$response = $this->request(
'/auth/key',
$data
);
if ( $response !== false ) {
Transient::delete( self::LITE_CONNECT_SITE_KEY_LOCK );
}
$this->update_generate_key_attempts_count();
// At this point, we do not have the site key.
// It will be sent to us in the 'wpforms/auth/key/nonce' callback.
return false;
}
/**
* Generate the access token.
*
* @since 1.7.4
*
* @param string $site_key The site key.
*
* @return false|string
*/
protected function generate_access_token( $site_key ) {
// Verify if an access token is already being generated.
if ( Transient::get( self::LITE_CONNECT_ACCESS_TOKEN_LOCK ) ) {
return false;
}
// Set a lock to avoid multiple requests to generate the access token.
Transient::set( self::LITE_CONNECT_ACCESS_TOKEN_LOCK, true, MINUTE_IN_SECONDS );
$response = $this->request(
'/auth/access_token',
[
'domain' => $this->domain,
'site_id' => $this->site_id,
'wp_version' => get_bloginfo( 'version' ),
],
[
'X-WPForms-Lite-Connect-Site-Key' => $site_key,
]
);
if ( $response && strpos( $response, '{"error":' ) === false ) {
// Delete lock.
Transient::delete( self::LITE_CONNECT_ACCESS_TOKEN_LOCK );
}
return $response;
}
/**
* Add an entry to the Lite Connect API.
*
* @since 1.7.4
*
* @param string $access_token The access token.
* @param int $form_id The form ID.
* @param string $entry_data The entry data.
*
* @return false|string
*/
public function add_form_entry( $access_token, $form_id, $entry_data ) {
return $this->request(
'/storage/entries',
[
'site_id' => $this->site_id,
'form_id' => $form_id,
'data' => $entry_data,
],
[
'X-WPForms-Lite-Connect-Access-Token' => $access_token,
]
);
}
/**
* Send a request to the Lite Connect API.
*
* @since 1.7.4
*
* @param string $uri The request's URI.
* @param array $body The request's body.
* @param array $headers The HTTP headers.
*
* @return false|string
*/
protected function request( $uri, $body, $headers = [] ) {
$url = $this->api_url . $uri;
$user_agent = 'WPForms/' . WPFORMS_VERSION . '; ' . home_url();
$response = wp_remote_post(
$url,
[
'method' => 'POST',
'timeout' => 15,
'headers' => $headers,
'body' => $body,
'user-agent' => $user_agent,
]
);
if (
is_wp_error( $response ) ||
(
isset( $response['response']['code'] ) &&
(int) $response['response']['code'] !== 200
)
) {
if ( ! is_wp_error( $response ) ) {
unset( $response['headers'], $response['http_response'], $response['cookies'], $response['filename'] );
}
$args = [
'type' => [ 'error' ],
];
if ( isset( $body['form_id'] ) ) {
$args['form_id'] = $body['form_id'];
}
wpforms_log(
'Lite Connect: remote API request error',
[
'response' => $response,
'request' => [
'url' => $url,
'body' => $this->prepare_log_data( $body ),
'headers' => $this->prepare_log_data( $headers ),
'user-agent' => $user_agent,
],
],
$args
);
}
if ( is_wp_error( $response ) ) {
return false;
}
return wp_remote_retrieve_body( $response );
}
/**
* Prepare data for logging.
*
* @since 1.7.4
*
* @param mixed $data Data to log.
*
* @return mixed
*/
private function prepare_log_data( $data ) {
$asterisks = '***';
if ( ! empty( $data['X-WPForms-Lite-Connect-Access-Token'] ) ) {
$data['X-WPForms-Lite-Connect-Access-Token'] = $asterisks;
}
if ( ! empty( $data['X-WPForms-Lite-Connect-Site-Key'] ) ) {
$data['X-WPForms-Lite-Connect-Site-Key'] = $asterisks;
}
if ( ! empty( $data['nonce'] ) ) {
$data['nonce'] = $asterisks;
}
return $data;
}
/**
* Get debug setting.
*
* @since 1.7.4
*
* @param string $name Setting name.
*
* @return false|mixed
*/
protected function get_debug_setting( $name ) {
// To be defined in wp-config.php.
if ( ! defined( 'WPFORMS_DEBUG_LITE_CONNECT' ) || ! is_array( WPFORMS_DEBUG_LITE_CONNECT ) ) {
return false;
}
return ! empty( WPFORMS_DEBUG_LITE_CONNECT[ $name ] ) ? WPFORMS_DEBUG_LITE_CONNECT[ $name ] : false;
}
/**
* Create not logged in nonce.
* We need it, because callback from the server to the wpforms/auth/key/nonce will be processed as not logged in.
*
* @since 1.7.4
*
* @return string
*/
private function create_not_logged_in_nonce() {
$user = wp_get_current_user();
$user_id = $user ? $user->ID : 0;
wp_set_current_user( 0 );
$saved_cookie = $_COOKIE;
$_COOKIE = [];
$nonce = wp_create_nonce( self::KEY_NONCE_ACTION );
$_COOKIE = $saved_cookie;
wp_set_current_user( $user_id );
return $nonce;
}
/**
* Set site ID.
*
* @since 1.7.4
*
* @return void
*/
private function set_site_id() {
// At first, try to use the site ID from the wp-config.php file.
$debug_site_id = $this->get_debug_setting( 'id' );
if ( $debug_site_id !== false ) {
$this->site_id = $debug_site_id;
return;
}
// Otherwise, use the site ID generated and saved as setting.
$site = wpforms_setting( 'site', false, Integration::get_option_name() );
if ( ! isset( $site['id'] ) ) {
return;
}
$this->site_id = $site['id'];
}
/**
* Check that we have not reached the max number of attempts to get keys from API using generate_keys().
*
* @since 1.7.5
*
* @return bool
*/
private function is_max_generate_key_attempts_reached() {
$attempts_count = get_option( self::GENERATE_KEY_ATTEMPT_COUNTER_OPTION, 0 );
return $attempts_count >= self::MAX_GENERATE_KEY_ATTEMPTS;
}
/**
* Update count of the attempts to get keys from API using generate_keys().
* It allows us to prevent sending requests to the API server infinitely.
*
* @since 1.7.5
*/
private function update_generate_key_attempts_count() {
global $wpdb;
$counter = get_option( self::GENERATE_KEY_ATTEMPT_COUNTER_OPTION, 0 );
if ( $counter >= self::MAX_GENERATE_KEY_ATTEMPTS - 1 ) {
// Disable Lite Connect.
$wpforms_settings = get_option( 'wpforms_settings', [] );
$wpforms_settings[ LiteConnect::SETTINGS_SLUG ] = 0;
update_option( 'wpforms_settings', $wpforms_settings );
}
// Store actual attempt counter value to the option.
// We need here an atomic operation to avoid race conditions with getting site key via callback.
// phpcs:disable WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching
$wpdb->query(
$wpdb->prepare(
"INSERT INTO $wpdb->options
(option_name, option_value, autoload)
VALUES ( %s, 1, 'no' )
ON DUPLICATE KEY UPDATE
option_value = option_value + 1",
self::GENERATE_KEY_ATTEMPT_COUNTER_OPTION
)
);
// phpcs:enable WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize
wp_cache_delete( self::GENERATE_KEY_ATTEMPT_COUNTER_OPTION, 'options' );
}
}
Integrations/LiteConnect/RefreshAccessTokenTask.php 0000644 00000002417 15133255232 0016503 0 ustar 00 <?php
namespace WPForms\Integrations\LiteConnect;
/**
* Class RefreshAccessTokenTask.
*
* @since 1.7.4
*/
class RefreshAccessTokenTask extends Integration {
/**
* Task name.
*
* @since 1.7.4
*
* @var string
*/
const LITE_CONNECT_TASK = 'wpforms_lite_connect_refresh_access_token';
/**
* RefreshAccessTokenTask constructor.
*
* @since 1.7.4
*/
public function __construct() {
parent::__construct();
$this->hooks();
}
/**
* Initialize the hooks.
*
* @since 1.7.4
*/
private function hooks() {
// Process the tasks as needed.
add_action( self::LITE_CONNECT_TASK, [ $this, 'process' ] );
}
/**
* Creates a task to refresh the Lite Connect access token via Action Scheduler.
*
* @since 1.7.4
*/
public function create() {
$action_id = wpforms()->get( 'tasks' )
->create( self::LITE_CONNECT_TASK )
->once( time() + 6 * DAY_IN_SECONDS )
->register();
if ( is_null( $action_id ) ) {
wpforms_log(
'Lite Connect: error creating the AS task',
[
'task' => self::LITE_CONNECT_TASK,
],
[ 'type' => [ 'error' ] ]
);
}
}
/**
* Process the task to regenerate the access token.
*
* @since 1.7.4
*/
public function process() {
$this->get_access_token( $this->get_site_key(), true );
}
}
Integrations/Elementor/Elementor.php 0000644 00000011653 15133255232 0013616 0 ustar 00 <?php
namespace WPForms\Integrations\Elementor;
use Elementor\Plugin as ElementorPlugin;
use WPForms\Integrations\IntegrationInterface;
/**
* Improve Elementor Compatibility.
*
* @since 1.6.0
*/
class Elementor implements IntegrationInterface {
/**
* Indicates if current integration is allowed to load.
*
* @since 1.6.0
*
* @return bool
*/
public function allow_load() {
return (bool) did_action( 'elementor/loaded' );
}
/**
* Load an integration.
*
* @since 1.6.0
*/
public function load() {
$this->hooks();
}
/**
* Integration hooks.
*
* @since 1.6.0
*/
protected function hooks() {
// Skip if Elementor is not available.
if ( ! class_exists( '\Elementor\Plugin' ) ) {
return;
}
add_action( 'elementor/preview/init', [ $this, 'init' ] );
add_action( 'elementor/frontend/after_enqueue_scripts', [ $this, 'preview_assets' ] );
add_action( 'elementor/frontend/after_enqueue_scripts', [ $this, 'frontend_assets' ] );
add_action( 'elementor/editor/after_enqueue_styles', [ $this, 'editor_assets' ] );
version_compare( ELEMENTOR_VERSION, '3.5.0', '>=' ) ?
add_action( 'elementor/widgets/register', [ $this, 'register_widget' ] ) :
add_action( 'elementor/widgets/widgets_registered', [ $this, 'register_widget' ] );
add_action( 'wp_ajax_wpforms_admin_get_form_selector_options', [ $this, 'ajax_get_form_selector_options' ] );
}
/**
* Init the main logic.
*
* @since 1.6.0
*/
public function init() {
/**
* Allow developers to determine whether the compatibility layer should be applied.
* We do this check here because we want this filter to be available for theme developers too.
*
* @since 1.6.0
*
* @param bool $use_compat Use compatibility.
*/
$use_compat = (bool) apply_filters( 'wpforms_apply_elementor_preview_compat', true );
if ( $use_compat !== true ) {
return;
}
// Load WPForms assets globally on Elementor Preview panel only.
add_filter( 'wpforms_global_assets', '__return_true' );
// Hide CAPTCHA badge on Elementor Preview panel.
add_filter( 'wpforms_frontend_recaptcha_disable', '__return_true' );
}
/**
* Load assets in the preview panel.
*
* @since 1.6.2
*/
public function preview_assets() {
if ( ! ElementorPlugin::$instance->preview->is_preview_mode() ) {
return;
}
$min = wpforms_get_min_suffix();
wp_enqueue_style(
'wpforms-integrations',
WPFORMS_PLUGIN_URL . "assets/css/admin-integrations{$min}.css",
null,
WPFORMS_VERSION
);
wp_enqueue_script(
'wpforms-elementor',
WPFORMS_PLUGIN_URL . "assets/js/integrations/elementor/editor{$min}.js",
[ 'elementor-frontend', 'jquery', 'wp-util' ],
WPFORMS_VERSION,
true
);
wp_localize_script(
'wpforms-elementor',
'wpformsElementorVars',
[
'ajax_url' => admin_url( 'admin-ajax.php' ),
'nonce' => wp_create_nonce( 'wpforms-elementor-integration' ),
'edit_form_url' => admin_url( 'admin.php?page=wpforms-builder&view=fields&form_id=' ),
'add_form_url' => admin_url( 'admin.php?page=wpforms-builder&view=setup' ),
'css_url' => WPFORMS_PLUGIN_URL . "assets/css/admin-integrations{$min}.css",
'debug' => wpforms_debug(),
]
);
}
/**
* Load an integration assets on the frontend.
*
* @since 1.6.2
*/
public function frontend_assets() {
if ( ElementorPlugin::$instance->preview->is_preview_mode() ) {
return;
}
$min = wpforms_get_min_suffix();
wp_enqueue_script(
'wpforms-elementor',
WPFORMS_PLUGIN_URL . "assets/js/integrations/elementor/frontend{$min}.js",
[ 'elementor-frontend', 'jquery', 'wp-util' ],
WPFORMS_VERSION,
true
);
wp_localize_script(
'wpforms-elementor',
'wpformsElementorVars',
[
'captcha_provider' => wpforms_setting( 'captcha-provider', 'recaptcha' ),
'recaptcha_type' => wpforms_setting( 'recaptcha-type', 'v2' ),
]
);
}
/**
* Load assets in the elementor document.
*
* @since 1.6.2
*/
public function editor_assets() {
if ( empty( $_GET['action'] ) || $_GET['action'] !== 'elementor' ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
return;
}
$min = wpforms_get_min_suffix();
wp_enqueue_style(
'wpforms-integrations',
WPFORMS_PLUGIN_URL . "assets/css/admin-integrations{$min}.css",
null,
WPFORMS_VERSION
);
}
/**
* Register WPForms Widget.
*
* @since 1.6.2
* @since 1.7.6 Added support for new registration method since 3.5.0.
*/
public function register_widget() {
version_compare( ELEMENTOR_VERSION, '3.5.0', '>=' ) ?
ElementorPlugin::instance()->widgets_manager->register( new Widget() ) :
ElementorPlugin::instance()->widgets_manager->register_widget_type( new Widget() );
}
/**
* Get form selector options.
*
* @since 1.6.2
*/
public function ajax_get_form_selector_options() {
check_ajax_referer( 'wpforms-elementor-integration', 'nonce' );
wp_send_json_success( ( new Widget() )->get_form_selector_options() );
}
}
Integrations/Elementor/Widget.php 0000644 00000017746 15133255232 0013120 0 ustar 00 <?php
namespace WPForms\Integrations\Elementor;
use Elementor\Plugin;
use Elementor\Widget_Base;
use Elementor\Controls_Manager;
/**
* WPForms widget for Elementor page builder.
*
* @since 1.6.2
*/
class Widget extends Widget_Base {
/**
* Get widget name.
*
* Retrieve shortcode widget name.
*
* @since 1.6.2
*
* @return string Widget name.
*/
public function get_name() {
return 'wpforms';
}
/**
* Get widget title.
*
* Retrieve shortcode widget title.
*
* @since 1.6.2
*
* @return string Widget title.
*/
public function get_title() {
return __( 'WPForms', 'wpforms-lite' );
}
/**
* Get widget icon.
*
* Retrieve shortcode widget icon.
*
* @since 1.6.2
*
* @return string Widget icon.
*/
public function get_icon() {
return 'icon-wpforms';
}
/**
* Get widget keywords.
*
* Retrieve the list of keywords the widget belongs to.
*
* @since 1.6.2
*
* @return array Widget keywords.
*/
public function get_keywords() {
return [
'form',
'forms',
'wpforms',
'contact form',
'sullie',
'the dude',
];
}
/**
* Get widget categories.
*
* @since 1.6.2
*
* @return array Widget categories.
*/
public function get_categories() {
return [
'basic',
];
}
/**
* Register widget controls.
*
* Adds different input fields to allow the user to change and customize the widget settings.
*
* @since 1.6.2
*/
protected function register_controls() { // phpcs:ignore PSR2.Methods.MethodDeclaration.Underscore
$this->content_controls();
}
/**
* Register content tab controls.
*
* @since 1.6.2
*/
protected function content_controls() {
$this->start_controls_section(
'section_form',
[
'label' => esc_html__( 'Form', 'wpforms-lite' ),
'tab' => Controls_Manager::TAB_CONTENT,
]
);
$forms = $this->get_forms();
if ( empty( $forms ) ) {
$this->add_control(
'add_form_notice',
[
'show_label' => false,
'type' => Controls_Manager::RAW_HTML,
'raw' => wp_kses(
__( '<b>You haven\'t created a form yet.</b><br> What are you waiting for?', 'wpforms-lite' ),
[
'b' => [],
'br' => [],
]
),
'content_classes' => 'elementor-panel-alert elementor-panel-alert-info',
]
);
}
$this->add_control(
'form_id',
[
'label' => esc_html__( 'Form', 'wpforms-lite' ),
'type' => Controls_Manager::SELECT,
'label_block' => true,
'options' => $forms,
'default' => '0',
]
);
$this->add_control(
'edit_form',
[
'show_label' => false,
'type' => Controls_Manager::RAW_HTML,
'raw' => wp_kses( /* translators: %s - WPForms documentation link. */
__( 'Need to make changes? <a href="#">Edit the selected form.</a>', 'wpforms-lite' ),
[ 'a' => [] ]
),
'condition' => [
'form_id!' => '0',
],
]
);
$this->add_control(
'test_form_notice',
[
'show_label' => false,
'type' => Controls_Manager::RAW_HTML,
'raw' => sprintf(
wp_kses( /* translators: %s - WPForms documentation link. */
__( '<b>Heads up!</b> Don\'t forget to test your form. <a href="%s" target="_blank" rel="noopener noreferrer">Check out our complete guide!</a>', 'wpforms-lite' ),
[
'b' => [],
'br' => [],
'a' => [
'href' => [],
'rel' => [],
'target' => [],
],
]
),
'https://wpforms.com/docs/how-to-properly-test-your-wordpress-forms-before-launching-checklist/'
),
'condition' => [
'form_id!' => '0',
],
'content_classes' => 'elementor-panel-alert elementor-panel-alert-info',
]
);
$this->add_control(
'add_form_btn',
[
'show_label' => false,
'label_block' => false,
'type' => Controls_Manager::BUTTON,
'button_type' => 'default',
'separator' => 'before',
'text' => '<b>+</b>' . esc_html__( 'New form', 'wpforms-lite' ),
'event' => 'elementorWPFormsAddFormBtnClick',
]
);
$this->end_controls_section();
$this->start_controls_section(
'section_display',
[
'label' => esc_html__( 'Display Options', 'wpforms-lite' ),
'tab' => Controls_Manager::TAB_CONTENT,
'condition' => [
'form_id!' => '0',
],
]
);
$this->add_control(
'display_form_name',
[
'label' => esc_html__( 'Form Name', 'wpforms-lite' ),
'type' => Controls_Manager::SWITCHER,
'label_on' => esc_html__( 'Show', 'wpforms-lite' ),
'label_off' => esc_html__( 'Hide', 'wpforms-lite' ),
'return_value' => 'yes',
'condition' => [
'form_id!' => '0',
],
]
);
$this->add_control(
'display_form_description',
[
'label' => esc_html__( 'Form Description', 'wpforms-lite' ),
'type' => Controls_Manager::SWITCHER,
'label_on' => esc_html__( 'Show', 'wpforms-lite' ),
'label_off' => esc_html__( 'Hide', 'wpforms-lite' ),
'separator' => 'after',
'return_value' => 'yes',
'condition' => [
'form_id!' => '0',
],
]
);
$this->end_controls_section();
}
/**
* Render widget output.
*
* @since 1.6.2
*/
protected function render() {
if ( Plugin::$instance->editor->is_edit_mode() ) {
$this->render_edit_mode();
} else {
$this->render_frontend();
}
}
/**
* Render widget output in edit mode.
*
* @since 1.6.3.1
*/
protected function render_edit_mode() {
$form_id = $this->get_settings_for_display( 'form_id' );
// Popup markup template.
echo wpforms_render( 'integrations/elementor/popup' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
if ( count( $this->get_forms() ) < 2 ) {
// No forms block.
echo wpforms_render( 'integrations/elementor/no-forms' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
return;
}
if ( empty( $form_id ) ) {
// Render form selector.
echo wpforms_render( // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
'integrations/elementor/form-selector',
[
'forms' => $this->get_form_selector_options(),
],
true
);
return;
}
// Finally, render selected form.
$this->render_frontend();
}
/**
* Render widget output on the frontend.
*
* @since 1.6.3.1
*/
protected function render_frontend() {
// Render selected form.
echo do_shortcode( $this->render_shortcode() );
}
/**
* Render widget as plain content.
*
* @since 1.6.2
*/
public function render_plain_content() {
echo $this->render_shortcode(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
/**
* Render shortcode.
*
* @since 1.6.2
*/
public function render_shortcode() {
return sprintf(
'[wpforms id="%1$d" title="%2$s" description="%3$s"]',
absint( $this->get_settings_for_display( 'form_id' ) ),
sanitize_key( $this->get_settings_for_display( 'display_form_name' ) === 'yes' ? 'true' : 'false' ),
sanitize_key( $this->get_settings_for_display( 'display_form_description' ) === 'yes' ? 'true' : 'false' )
);
}
/**
* Get forms list.
*
* @since 1.6.2
*
* @returns array Array of forms.
*/
public function get_forms() {
static $forms_list = [];
if ( empty( $forms_list ) ) {
$forms = wpforms()->form->get();
if ( ! empty( $forms ) ) {
$forms_list[0] = esc_html__( 'Select a form', 'wpforms-lite' );
foreach ( $forms as $form ) {
$forms_list[ $form->ID ] = mb_strlen( $form->post_title ) > 100 ? mb_substr( $form->post_title, 0, 97 ) . '...' : $form->post_title;
}
}
}
return $forms_list;
}
/**
* Get form selector options.
*
* @since 1.6.2
*
* @returns string Rendered options for the select tag.
*/
public function get_form_selector_options() {
$forms = $this->get_forms();
$options = '';
foreach ( $forms as $form_id => $form ) {
$options .= sprintf(
'<option value="%d">%s</option>',
(int) $form_id,
esc_html( $form )
);
}
return $options;
}
}
Integrations/Divi/Divi.php 0000644 00000013716 15133255232 0011522 0 ustar 00 <?php
namespace WPForms\Integrations\Divi;
use WPForms\Integrations\IntegrationInterface;
/**
* Class Divi.
*
* @since 1.6.3
*/
class Divi implements IntegrationInterface {
/**
* Indicate if current integration is allowed to load.
*
* @since 1.6.3
*
* @return bool
*/
public function allow_load() {
if ( function_exists( 'et_divi_builder_init_plugin' ) ) {
return true;
}
$allow_themes = [ 'Divi', 'Extra' ];
$theme = wp_get_theme();
$theme_name = $theme->get_template();
$theme_parent = $theme->parent();
return (bool) array_intersect( [ $theme_name, $theme_parent ], $allow_themes );
}
/**
* Load an integration.
*
* @since 1.6.3
*/
public function load() {
$this->hooks();
}
/**
* Hooks.
*
* @since 1.6.3
*/
public function hooks() {
add_action( 'et_builder_ready', [ $this, 'register_module' ] );
add_action( 'wp_enqueue_scripts', [ $this, 'frontend_styles' ], 12 );
if ( wp_doing_ajax() ) {
add_action( 'wp_ajax_wpforms_divi_preview', [ $this, 'preview' ] );
}
if ( $this->is_divi_builder() ) {
add_action( 'wp_enqueue_scripts', [ $this, 'builder_styles' ], 12 );
add_action( 'wp_enqueue_scripts', [ $this, 'builder_scripts' ] );
add_filter( 'wpforms_global_assets', '__return_true' );
add_filter( 'wpforms_frontend_missing_assets_error_js_disable', '__return_true', PHP_INT_MAX );
}
}
/**
* Determine if a current page is opened in the Divi Builder.
*
* @since 1.6.3
*
* @return bool
*/
private function is_divi_builder() {
return ! empty( $_GET['et_fb'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
}
/**
* Get current style name.
*
* Overwrite styles for the Divi Builder.
*
* @since 1.6.3
*
* @return string
*/
public function get_current_styles_name() {
$disable_css = absint( wpforms_setting( 'disable-css', 1 ) );
if ( $disable_css === 1 ) {
return 'full';
}
if ( $disable_css === 2 ) {
return 'base';
}
return '';
}
/**
* Determine if the Divi Builder plugin is loaded.
*
* @since 1.6.3
*
* @return bool
*/
protected function is_divi_plugin_loaded() {
if ( ! is_singular() ) {
return false;
}
return function_exists( 'et_is_builder_plugin_active' ) && et_is_builder_plugin_active();
}
/**
* Register frontend styles.
* Required for the plugin version of builder only.
*
* @since 1.6.3
*/
public function frontend_styles() {
if ( ! $this->is_divi_plugin_loaded() ) {
return;
}
$min = wpforms_get_min_suffix();
$styles_name = $this->get_current_styles_name();
if ( $styles_name ) {
// Load CSS per global setting.
wp_register_style(
"wpforms-{$styles_name}",
WPFORMS_PLUGIN_URL . "assets/css/integrations/divi/wpforms-{$styles_name}{$min}.css",
[],
WPFORMS_VERSION
);
}
wp_register_style(
'wpforms-choicesjs',
WPFORMS_PLUGIN_URL . "assets/css/integrations/divi/choices{$min}.css",
[],
\WPForms_Field_Select::CHOICES_VERSION
);
}
/**
* Load styles.
*
* @since 1.6.3
*/
public function builder_styles() {
$min = wpforms_get_min_suffix();
wp_enqueue_style(
'wpforms-integrations',
WPFORMS_PLUGIN_URL . "assets/css/admin-integrations{$min}.css",
null,
WPFORMS_VERSION
);
$styles_name = $this->get_current_styles_name();
if ( $styles_name ) {
// Load CSS per global setting.
wp_register_style(
"wpforms-{$styles_name}",
WPFORMS_PLUGIN_URL . "assets/css/integrations/divi/wpforms-{$styles_name}{$min}.css",
[],
WPFORMS_VERSION
);
}
}
/**
* Load scripts.
*
* @since 1.6.3
*/
public function builder_scripts() {
wp_enqueue_script(
'wpforms-divi',
// The unminified version is not supported by the browser.
WPFORMS_PLUGIN_URL . 'assets/js/integrations/divi/formselector.min.js',
[ 'react', 'react-dom' ],
WPFORMS_VERSION,
true
);
wp_localize_script(
'wpforms-divi',
'wpforms_divi_builder',
[
'ajax_url' => admin_url( 'admin-ajax.php' ),
'nonce' => wp_create_nonce( 'wpforms_divi_builder' ),
'placeholder' => WPFORMS_PLUGIN_URL . 'assets/images/sullie-alt.png',
'placeholder_title' => esc_html__( 'WPForms', 'wpforms-lite' ),
]
);
}
/**
* Register module.
*
* @since 1.6.3
*/
public function register_module() {
if ( ! class_exists( 'ET_Builder_Module' ) ) {
return;
}
new WPFormsSelector();
}
/**
* Ajax handler for the form preview.
*
* @since 1.6.3
*/
public function preview() {
check_ajax_referer( 'wpforms_divi_builder', 'nonce' );
$form_id = absint( filter_input( INPUT_POST, 'form_id', FILTER_SANITIZE_NUMBER_INT ) );
$show_title = 'on' === filter_input( INPUT_POST, 'show_title', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
$show_desc = 'on' === filter_input( INPUT_POST, 'show_desc', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
add_filter(
'wpforms_frontend_container_class',
function( $classes ) {
$classes[] = 'wpforms-gutenberg-form-selector';
$classes[] = 'wpforms-container-full';
return $classes;
}
);
add_action(
'wpforms_frontend_output',
function() {
echo '<fieldset disabled>';
},
3
);
add_action(
'wpforms_frontend_output',
function() {
echo '</fieldset>';
// This empty image is needed to execute JS code that triggers the custom event.
// Unfortunately, <script> tag doesn't work in the Divi Builder.
echo "<img src='data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=='
height='0'
width='0'
onLoad=\"jQuery( document ).trigger( 'wpformsDiviModuleDisplay' );\"
/>";
},
30
);
wp_send_json_success(
do_shortcode(
sprintf(
'[wpforms id="%1$s" title="%2$s" description="%3$s"]',
absint( $form_id ),
(bool) apply_filters( 'wpforms_divi_builder_form_title', $show_title, $form_id ),
(bool) apply_filters( 'wpforms_divi_builder_form_desc', $show_desc, $form_id )
)
)
);
}
}
Integrations/Divi/WPFormsSelector.php 0000644 00000006210 15133255232 0013654 0 ustar 00 <?php
namespace WPForms\Integrations\Divi;
use ET_Builder_Module;
/**
* Class WPFormsSelector.
*
* @since 1.6.3
*/
class WPFormsSelector extends ET_Builder_Module {
/**
* Module slug.
*
* @var string
*/
public $slug = 'wpforms_selector';
/**
* VB support.
*
* @var string
*/
public $vb_support = 'on';
/**
* Init module.
*
* @since 1.6.3
*/
public function init() {
$this->name = esc_html__( 'WPForms', 'wpforms-lite' );
}
/**
* Get list of settings.
*
* @since 1.6.3
*
* @return array
*/
public function get_fields() {
$forms = wpforms()->form->get( '', [ 'order' => 'DESC' ] );
$forms = ! empty( $forms ) ? wp_list_pluck( $forms, 'post_title', 'ID' ) : [];
$forms = array_map(
function ( $form ) {
return htmlspecialchars_decode( $form, ENT_QUOTES );
},
$forms
);
$forms[0] = esc_html__( 'Select form', 'wpforms-lite' );
return [
'form_id' => [
'label' => esc_html__( 'Form', 'wpforms-lite' ),
'type' => 'select',
'option_category' => 'basic_option',
'toggle_slug' => 'main_content',
'options' => $forms,
],
'show_title' => [
'label' => esc_html__( 'Show Title', 'wpforms-lite' ),
'type' => 'yes_no_button',
'option_category' => 'basic_option',
'toggle_slug' => 'main_content',
'options' => [
'off' => esc_html__( 'Off', 'wpforms-lite' ),
'on' => esc_html__( 'On', 'wpforms-lite' ),
],
],
'show_desc' => [
'label' => esc_html__( 'Show Description', 'wpforms-lite' ),
'option_category' => 'basic_option',
'type' => 'yes_no_button',
'toggle_slug' => 'main_content',
'options' => [
'off' => esc_html__( 'Off', 'wpforms-lite' ),
'on' => esc_html__( 'On', 'wpforms-lite' ),
],
],
];
}
/**
* Disable advanced fields configuration.
*
* @since 1.6.3
*
* @return array
*/
public function get_advanced_fields_config() {
return [
'link_options' => false,
'text' => false,
'background' => false,
'borders' => false,
'box_shadow' => false,
'button' => false,
'filters' => false,
'fonts' => false,
];
}
/**
* Render module on the frontend.
*
* @since 1.6.3
*
* @param array $attrs List of unprocessed attributes.
* @param string $content Content being processed.
* @param string $render_slug Slug of module that is used for rendering output.
*
* @return string
*/
public function render( $attrs, $content = null, $render_slug = '' ) {
if ( empty( $this->props['form_id'] ) ) {
return '';
}
return do_shortcode(
sprintf(
'[wpforms id="%1$s" title="%2$s" description="%3$s"]',
absint( $this->props['form_id'] ),
(bool) apply_filters( 'wpforms_divi_builder_form_title', ! empty( $this->props['show_title'] ) && 'on' === $this->props['show_title'], absint( $this->props['form_id'] ) ),
(bool) apply_filters( 'wpforms_divi_builder_form_desc', ! empty( $this->props['show_desc'] ) && 'on' === $this->props['show_desc'], absint( $this->props['form_id'] ) )
)
);
}
}
Integrations/WPMailSMTP/Notifications.php 0000644 00000012640 15133255232 0014375 0 ustar 00 <?php
namespace WPForms\Integrations\WPMailSMTP;
use WPMailSMTP\Options;
use WPForms\Integrations\IntegrationInterface;
/**
* WP Mail SMTP hints inside form builder notifications.
*
* @since 1.4.8
*/
class Notifications implements IntegrationInterface {
/**
* WP Mail SMTP options.
*
* @since 1.4.8
*
* @var Options
*/
public $options;
/**
* Indicate if current integration is allowed to load.
*
* @since 1.4.8
*
* @return bool
*/
public function allow_load() {
return wpforms_is_admin_page( 'builder' ) && function_exists( 'wp_mail_smtp' );
}
/**
* Load an integration.
*
* @since 1.4.8
*/
public function load() {
$this->options = new Options();
$this->hooks();
}
/**
* Integration filters.
*
* @since 1.4.8
*/
protected function hooks() {
add_filter( 'wpforms_builder_notifications_from_name_after', [ $this, 'from_name_after' ] );
add_filter( 'wpforms_builder_notifications_from_email_after', [ $this, 'from_email_after' ] );
add_filter( 'wpforms_builder_notifications_sender_name_settings', [ $this, 'change_from_name_settings' ], 10, 3 );
add_filter( 'wpforms_builder_notifications_sender_address_settings', [ $this, 'change_from_email_settings' ], 10, 3 );
add_action( 'wpforms_form_settings_notifications_single_after', [ $this, 'add_hidden_from_name_field' ], 10, 2 );
add_action( 'wpforms_form_settings_notifications_single_after', [ $this, 'add_hidden_from_email_field' ], 10, 2 );
}
/**
* Redefine From Name settings with data from WP Mail SMTP.
*
* @since 1.7.6
*
* @param array $args Field settings.
* @param array $form_data Form data.
* @param int $id Notification ID.
*
* @return array
*/
public function change_from_name_settings( $args, $form_data, $id ) {
if ( ! $this->options->get( 'mail', 'from_name_force' ) ) {
return $args;
}
$args['value'] = $this->options->get( 'mail', 'from_name' );
unset( $args['smarttags'] );
return $args;
}
/**
* Redefine From Email settings with data from WP Mail SMTP.
*
* @since 1.7.6
*
* @param array $args Field settings.
* @param array $form_data Form data.
* @param int $id Notification ID.
*
* @return array
*/
public function change_from_email_settings( $args, $form_data, $id ) {
if ( ! $this->options->get( 'mail', 'from_email_force' ) ) {
return $args;
}
$args['value'] = $this->options->get( 'mail', 'from_email' );
unset( $args['smarttags'] );
return $args;
}
/**
* Add hidden From Name field to overwrite value from WP Mail SMTP.
*
* @since 1.7.6
*
* @param array $settings Form settings.
* @param int $id Notification id.
*/
public function add_hidden_from_name_field( $settings, $id ) {
if ( empty( $settings->form_data['settings']['notifications'][ $id ]['sender_name'] ) || ! $this->options->get( 'mail', 'from_name_force' ) ) {
return;
}
wpforms_panel_field(
'text',
'notifications',
'sender_name',
$settings->form_data,
'',
[
'parent' => 'settings',
'subsection' => $id,
'readonly' => true,
'class' => 'wpforms-hidden',
'value' => $settings->form_data['settings']['notifications'][ $id ]['sender_name'],
]
);
}
/**
* Add hidden From Email field to overwrite value from WP Mail SMTP.
*
* @since 1.7.6
*
* @param array $settings Form settings.
* @param int $id Notification id.
*/
public function add_hidden_from_email_field( $settings, $id ) {
if ( empty( $settings->form_data['settings']['notifications'][ $id ]['sender_address'] ) || ! $this->options->get( 'mail', 'from_email_force' ) ) {
return;
}
wpforms_panel_field(
'text',
'notifications',
'sender_address',
$settings->form_data,
'',
[
'parent' => 'settings',
'subsection' => $id,
'readonly' => true,
'class' => 'wpforms-hidden',
'value' => $settings->form_data['settings']['notifications'][ $id ]['sender_address'],
]
);
}
/**
* Display hint if WP Mail SMTP is forcing from name.
*
* @since 1.4.8
*
* @param string $after Text displayed after setting.
*
* @return string
*/
public function from_name_after( $after ) {
if ( ! $this->options->get( 'mail', 'from_name_force' ) ) {
return $after;
}
return sprintf(
wp_kses( /* translators: %s - URL WP Mail SMTP settings. */
__( 'This setting is disabled because you have the "Force From Name" setting enabled in the <a href="%s" target="_blank">WP Mail SMTP</a> plugin.', 'wpforms-lite' ),
[
'a' => [
'href' => [],
'target' => [],
],
]
),
esc_url( admin_url( 'options-general.php?page=wp-mail-smtp#wp-mail-smtp-setting-row-from_name' ) )
);
}
/**
* Display hint if WP Mail SMTP is forcing from email.
*
* @since 1.4.8
*
* @param string $after Text displayed after setting.
*
* @return string
*/
public function from_email_after( $after ) {
if ( ! $this->options->get( 'mail', 'from_email_force' ) ) {
return $after;
}
return sprintf(
wp_kses( /* translators: %s - URL WP Mail SMTP settings. */
__( 'This setting is disabled because you have the "Force From Email" setting enabled in the <a href="%s" target="_blank">WP Mail SMTP</a> plugin.', 'wpforms-lite' ),
[
'a' => [
'href' => [],
'target' => [],
],
]
),
esc_url( admin_url( 'options-general.php?page=wp-mail-smtp#wp-mail-smtp-setting-row-from_email' ) )
);
}
}
WPForms.php 0000644 00000024740 15133255232 0006622 0 ustar 00 <?php
namespace WPForms {
use stdClass;
/**
* Main WPForms class.
*
* @since 1.0.0
*/
final class WPForms {
/**
* One is the loneliest number that you'll ever do.
*
* @since 1.0.0
*
* @var \WPForms\WPForms
*/
private static $instance;
/**
* Plugin version for enqueueing, etc.
* The value is got from WPFORMS_VERSION constant.
*
* @since 1.0.0
*
* @var string
*/
public $version = '';
/**
* Classes registry.
*
* @since 1.5.7
*
* @var array
*/
private $registry = [];
/**
* List of legacy public properties.
*
* @since 1.6.8
*
* @var string[]
*/
private $legacy_properties = [
'form',
'entry',
'entry_fields',
'entry_meta',
'frontend',
'process',
'smart_tags',
'license',
];
/**
* Paid returns true, free (Lite) returns false.
*
* @since 1.3.9
* @since 1.7.3 changed to private.
*
* @var bool
*/
private $pro = false;
/**
* Backward compatibility method for accessing the class registry in an old way,
* e.g. 'wpforms()->form' or 'wpforms()->entry'.
*
* @since 1.5.7
*
* @param string $name Name of the object to get.
*
* @return mixed|null
*/
public function __get( $name ) {
if ( $name === 'smart_tags' ) {
_deprecated_argument(
'wpforms()->smart_tags',
'1.6.7 of the WPForms plugin',
"Please use `wpforms()->get( 'smart_tags' )` instead."
);
}
if ( $name === 'pro' ) {
return wpforms()->is_pro();
}
return $this->get( $name );
}
/**
* Main WPForms Instance.
*
* Only one instance of WPForms exists in memory at any one time.
* Also prevent the need to define globals all over the place.
*
* @since 1.0.0
*
* @return WPForms
*/
public static function instance() {
if (
self::$instance === null ||
! self::$instance instanceof self
) {
self::$instance = new self();
self::$instance->constants();
self::$instance->includes();
// Load Pro or Lite specific files.
if ( self::$instance->is_pro() ) {
self::$instance->registry['pro'] = require_once WPFORMS_PLUGIN_DIR . 'pro/wpforms-pro.php';
} else {
require_once WPFORMS_PLUGIN_DIR . 'lite/wpforms-lite.php';
}
add_action( 'plugins_loaded', [ self::$instance, 'objects' ], 10 );
}
return self::$instance;
}
/**
* Setup plugin constants.
* All the path/URL related constants are defined in main plugin file.
*
* @since 1.0.0
*/
private function constants() {
$this->version = WPFORMS_VERSION;
// Plugin Slug - Determine plugin type and set slug accordingly.
// This filter is documented in \WPForms\WPForms::is_pro.
if ( apply_filters( 'wpforms_allow_pro_version', file_exists( WPFORMS_PLUGIN_DIR . 'pro/wpforms-pro.php' ) ) ) {
$this->pro = true;
define( 'WPFORMS_PLUGIN_SLUG', 'wpforms' );
} else {
define( 'WPFORMS_PLUGIN_SLUG', 'wpforms-lite' );
}
}
/**
* Include files.
*
* @since 1.0.0
*/
private function includes() {
require_once WPFORMS_PLUGIN_DIR . 'includes/class-db.php';
require_once WPFORMS_PLUGIN_DIR . 'includes/functions.php';
require_once WPFORMS_PLUGIN_DIR . 'includes/compat.php';
$this->includes_magic();
// Global includes.
require_once WPFORMS_PLUGIN_DIR . 'includes/functions-list.php';
require_once WPFORMS_PLUGIN_DIR . 'includes/class-install.php';
require_once WPFORMS_PLUGIN_DIR . 'includes/class-form.php';
require_once WPFORMS_PLUGIN_DIR . 'includes/class-fields.php';
require_once WPFORMS_PLUGIN_DIR . 'includes/class-frontend.php';
// TODO: class-templates.php should be loaded in admin area only.
require_once WPFORMS_PLUGIN_DIR . 'includes/class-templates.php';
// TODO: class-providers.php should be loaded in admin area only.
require_once WPFORMS_PLUGIN_DIR . 'includes/class-providers.php';
require_once WPFORMS_PLUGIN_DIR . 'includes/class-process.php';
require_once WPFORMS_PLUGIN_DIR . 'includes/class-widget.php';
require_once WPFORMS_PLUGIN_DIR . 'includes/emails/class-emails.php';
require_once WPFORMS_PLUGIN_DIR . 'includes/integrations.php';
require_once WPFORMS_PLUGIN_DIR . 'includes/deprecated.php';
// Admin/Dashboard only includes, also in ajax.
if ( is_admin() ) {
require_once WPFORMS_PLUGIN_DIR . 'includes/admin/admin.php';
require_once WPFORMS_PLUGIN_DIR . 'includes/admin/class-notices.php';
require_once WPFORMS_PLUGIN_DIR . 'includes/admin/class-menu.php';
require_once WPFORMS_PLUGIN_DIR . 'includes/admin/overview/class-overview.php';
require_once WPFORMS_PLUGIN_DIR . 'includes/admin/builder/class-builder.php';
require_once WPFORMS_PLUGIN_DIR . 'includes/admin/builder/functions.php';
require_once WPFORMS_PLUGIN_DIR . 'includes/admin/class-settings.php';
require_once WPFORMS_PLUGIN_DIR . 'includes/admin/class-welcome.php';
require_once WPFORMS_PLUGIN_DIR . 'includes/admin/class-editor.php';
require_once WPFORMS_PLUGIN_DIR . 'includes/admin/class-review.php';
require_once WPFORMS_PLUGIN_DIR . 'includes/admin/class-about.php';
require_once WPFORMS_PLUGIN_DIR . 'includes/admin/ajax-actions.php';
}
}
/**
* Including the new files with PHP 5.3 style.
*
* @since 1.4.7
*/
private function includes_magic() {
// Action Scheduler requires a special loading procedure.
require_once WPFORMS_PLUGIN_DIR . 'vendor/woocommerce/action-scheduler/action-scheduler.php';
// Autoload Composer packages.
require_once WPFORMS_PLUGIN_DIR . 'vendor/autoload.php';
// Load the class loader.
$this->register(
[
'name' => 'Loader',
'hook' => false,
]
);
if ( version_compare( phpversion(), '5.5', '>=' ) ) {
/*
* Load PHP 5.5 email subsystem.
*/
add_action( 'wpforms_loaded', [ '\WPForms\Emails\Summaries', 'get_instance' ] );
}
/*
* Load admin components. Exclude from frontend.
*/
if ( is_admin() ) {
add_action( 'wpforms_loaded', [ '\WPForms\Admin\Loader', 'get_instance' ] );
}
/*
* Load form components.
*/
add_action( 'wpforms_loaded', [ '\WPForms\Forms\Loader', 'get_instance' ] );
/*
* Properly init the providers loader, that will handle all the related logic and further loading.
*/
add_action( 'wpforms_loaded', [ '\WPForms\Providers\Providers', 'get_instance' ] );
/*
* Properly init the integrations loader, that will handle all the related logic and further loading.
*/
add_action( 'wpforms_loaded', [ '\WPForms\Integrations\Loader', 'get_instance' ] );
}
/**
* Setup objects.
*
* @since 1.0.0
*/
public function objects() {
// Global objects.
$this->form = new \WPForms_Form_Handler();
$this->frontend = new \WPForms_Frontend();
$this->process = new \WPForms_Process();
// Hook now that all of the WPForms stuff is loaded.
do_action( 'wpforms_loaded' );
}
/**
* Register a class.
*
* @since 1.5.7
*
* @param array $class Class registration info.
*/
public function register( $class ) {
if ( empty( $class['name'] ) || ! is_string( $class['name'] ) ) {
return;
}
if ( isset( $class['condition'] ) && empty( $class['condition'] ) ) {
return;
}
$full_name = $this->is_pro() ? '\WPForms\Pro\\' . $class['name'] : '\WPForms\Lite\\' . $class['name'];
$full_name = class_exists( $full_name ) ? $full_name : '\WPForms\\' . $class['name'];
if ( ! class_exists( $full_name ) ) {
return;
}
$pattern = '/[^a-zA-Z0-9_\\\-]/';
$id = isset( $class['id'] ) ? $class['id'] : '';
$id = $id ? preg_replace( $pattern, '', (string) $id ) : $id;
$hook = isset( $class['hook'] ) ? $class['hook'] : 'wpforms_loaded';
$hook = $hook ? preg_replace( $pattern, '', (string) $hook ) : $hook;
$run = isset( $class['run'] ) ? $class['run'] : 'init';
$priority = isset( $class['priority'] ) && is_int( $class['priority'] ) ? $class['priority'] : 10;
$callback = function () use ( $full_name, $id, $run ) {
$instance = new $full_name();
if ( $id && ! array_key_exists( $id, $this->registry ) ) {
$this->registry[ $id ] = $instance;
}
if ( $run && method_exists( $instance, $run ) ) {
$instance->{$run}();
}
};
if ( $hook ) {
add_action( $hook, $callback, $priority );
} else {
$callback();
}
}
/**
* Register classes in bulk.
*
* @since 1.5.7
*
* @param array $classes Classes to register.
*/
public function register_bulk( $classes ) {
if ( ! is_array( $classes ) ) {
return;
}
foreach ( $classes as $class ) {
$this->register( $class );
}
}
/**
* Get a class instance from a registry.
*
* @since 1.5.7
*
* @param string $name Class name or an alias.
*
* @return mixed|stdClass|null
*/
public function get( $name ) {
if ( ! empty( $this->registry[ $name ] ) ) {
return $this->registry[ $name ];
}
// Backward compatibility for old public properties.
// Return null to save old condition for these properties.
if ( in_array( $name, $this->legacy_properties, true ) ) {
return isset( $this->{$name} ) ? $this->{$name} : null;
}
return new stdClass();
}
/**
* Get the list of all custom tables starting with `wpforms_*`.
*
* @since 1.6.3
*
* @return array List of table names.
*/
public function get_existing_custom_tables() {
global $wpdb;
$tables = $wpdb->get_results( "SHOW TABLES LIKE '" . $wpdb->prefix . "wpforms_%'", 'ARRAY_N' ); // phpcs:ignore
return ! empty( $tables ) ? wp_list_pluck( $tables, 0 ) : [];
}
/**
* Whether the current instance of the plugin is a paid version, or free.
*
* @since 1.7.3
*
* @return bool
*/
public function is_pro() {
/**
* Filters whether the current plugin version is pro.
*
* @since 1.7.3
*
* @param bool $pro Whether the current plugin version is pro.
*/
return (bool) apply_filters( 'wpforms_allow_pro_version', $this->pro );
}
}
}
namespace {
/**
* The function which returns the one WPForms instance.
*
* @since 1.0.0
*
* @return WPForms\WPForms
*/
function wpforms() {
return WPForms\WPForms::instance();
}
/**
* Adding an alias for backward-compatibility with plugins
* that still use class_exists( 'WPForms' )
* instead of function_exists( 'wpforms' ), which is preferred.
*
* In 1.5.0 we removed support for PHP 5.2
* and moved former WPForms class to a namespace: WPForms\WPForms.
*
* @since 1.5.1
*/
class_alias( 'WPForms\WPForms', 'WPForms' );
}
Migrations/Upgrade168.php 0000644 00000002364 15133255232 0011225 0 ustar 00 <?php
namespace WPForms\Migrations;
/**
* Class v1.6.8 upgrade.
*
* @since 1.7.5
*
* @noinspection PhpUnused
*/
class Upgrade168 extends UpgradeBase {
/**
* Run upgrade.
*
* @since 1.7.5
*
* @return bool|null Upgrade result:
* true - the upgrade completed successfully,
* false - in the case of failure,
* null - upgrade started but not yet finished (background task).
*/
public function run() {
$current_opened_date = get_option( 'wpforms_builder_opened_date', null );
// Do not run migration twice as 0 is a default value for all old users.
if ( $current_opened_date === '0' ) {
return true;
}
// We don't want users to report to us if they already previously used the builder by creating a form.
$form_handler = wpforms()->get( 'form' );
if ( ! $form_handler ) {
return false;
}
$forms = $form_handler->get(
'',
[
'posts_per_page' => 1,
'nopaging' => false,
'fields' => 'ids',
'update_post_meta_cache' => false,
]
);
// At least 1 form exists - set the default value.
if ( ! empty( $forms ) ) {
add_option( 'wpforms_builder_opened_date', 0, '', 'no' );
}
return true;
}
}
Migrations/Migrations.php 0000644 00000001325 15133255232 0011507 0 ustar 00 <?php
namespace WPForms\Migrations;
use WPForms\Tasks\Meta;
/**
* Class Migrations handles Lite plugin upgrade routines.
*
* @since 1.7.5
*/
class Migrations extends Base {
/**
* WP option name to store the migration version.
*
* @since 1.5.9
*/
const MIGRATED_OPTION_NAME = 'wpforms_versions_lite';
/**
* Name of the core plugin used in log messages.
*
* @since 1.7.5
*/
const PLUGIN_NAME = 'WPForms';
/**
* Upgrade classes.
*
* @since 1.7.5
*/
const UPGRADE_CLASSES = [
'Upgrade159',
'Upgrade1672',
'Upgrade168',
'Upgrade175',
'Upgrade1751',
];
/**
* Custom table handler classes.
*
* @since 1.7.6
*/
const CUSTOM_TABLE_HANDLER_CLASSES = [
Meta::class,
];
}
Migrations/UpgradeBase.php 0000644 00000004666 15133255232 0011570 0 ustar 00 <?php
// phpcs:disable Generic.Commenting.DocComment.MissingShort
/** @noinspection PhpExpressionResultUnusedInspection */
/** @noinspection PhpPropertyOnlyWrittenInspection */
/** @noinspection UnusedConstructorDependenciesInspection */
/** @noinspection PhpUnusedAliasInspection */
// phpcs:enable Generic.Commenting.DocComment.MissingShort
namespace WPForms\Migrations;
// phpcs:disable WPForms.PHP.UseStatement.UnusedUseStatement
use WPForms\Migrations\Migrations;
use WPForms\Pro\Migrations\Migrations as MigrationsPro;
// phpcs:enable WPForms.PHP.UseStatement.UnusedUseStatement
/**
* Class UpgradeBase contains both Lite and Pro plugin upgrade methods.
*
* @since 1.7.5
*/
abstract class UpgradeBase {
/**
* Migration class instance.
*
* @since 1.7.5
*
* @var Migrations|MigrationsPro
*/
protected $migrations;
/**
* Primary class constructor.
*
* @since 1.7.5
*
* @param Migrations|MigrationsPro $migrations Instance of Migrations class.
*/
public function __construct( $migrations ) {
$this->migrations = $migrations;
}
/**
* Run upgrade.
*
* @since 1.7.5
*
* @return bool|null Upgrade result:
* true - the upgrade completed successfully,
* false - in the case of failure,
* null - upgrade started but not yet finished (background task).
*/
abstract public function run();
/**
* Run the async upgrade via Action Scheduler (AS) task.
* The AS task has to support STATUS option with START, IN_PROGRESS, and COMPLETED values.
* Also, the AS task must have the init() method.
*
* @since 1.7.5
*
* @param string $class Classname of async AS task.
*
* @return bool|null Upgrade result:
* true - the upgrade completed successfully,
* false - in the case of failure,
* null - upgrade started but not yet finished (background task).
*/
protected function run_async( $class ) { // phpcs:ignore WPForms.PHP.HooksMethod.InvalidPlaceForAddingHooks
$status = get_option( $class::STATUS );
if ( $status === $class::COMPLETED ) {
delete_option( $class::STATUS );
return true;
}
if ( ! $status ) {
update_option( $class::STATUS, $class::START );
}
// Class Tasks does not exist at this point, so we have to add an action on init.
add_action(
'init',
function () use ( $class ) {
( new $class() )->init();
},
PHP_INT_MAX
);
return null;
}
}
Migrations/Upgrade159.php 0000644 00000001303 15133255232 0011215 0 ustar 00 <?php
namespace WPForms\Migrations;
/**
* Class v1.5.9 upgrade.
*
* @since 1.7.5
*
* @noinspection PhpUnused
*/
class Upgrade159 extends UpgradeBase {
/**
* Create tasks_meta table.
*
* @since 1.7.5
*
* @return bool|null Upgrade result:
* true - the upgrade completed successfully,
* false - in the case of failure,
* null - upgrade started but not yet finished (background task).
*/
public function run() {
$meta = wpforms()->get( 'tasks_meta' );
if ( ! $meta ) {
return false;
}
// Create the table if it doesn't exist.
if ( ! $meta->table_exists() ) {
$meta->create_table();
}
return true;
}
}
Migrations/Base.php 0000644 00000023266 15133255232 0010255 0 ustar 00 <?php
namespace WPForms\Migrations;
use ReflectionClass;
/**
* Class Migrations handles both Lite and Pro plugin upgrade routines.
*
* @since 1.7.5
*/
abstract class Base {
/**
* WP option name to store the migration versions.
* Must have 'versions' in the name defined in extending classes,
* like 'wpforms_versions', 'wpforms_versions_lite, 'wpforms_stripe_versions' etc.
*
* @since 1.7.5
*/
const MIGRATED_OPTION_NAME = '';
/**
* Current plugin version.
*
* @since 1.7.5
*/
const CURRENT_VERSION = WPFORMS_VERSION;
/**
* Name of the core plugin used in log messages.
*
* @since 1.7.5
*/
const PLUGIN_NAME = '';
/**
* Upgrade classes.
*
* @since 1.7.5
*/
const UPGRADE_CLASSES = [];
/**
* Custom table handler classes.
*
* @since 1.7.6
*/
const CUSTOM_TABLE_HANDLER_CLASSES = [];
/**
* Migration started status.
*
* @since 1.7.5
*/
const STARTED = - 1;
/**
* Migration failed status.
*
* @since 1.7.5
*/
const FAILED = - 2;
/**
* Initial fake version for comparisons.
*
* @since 1.7.5
*/
const INITIAL_FAKE_VERSION = '0.0.1';
/**
* Reflection class instance.
*
* @since 1.7.5
*
* @var ReflectionClass
*/
protected $reflector;
/**
* Migrated versions.
*
* @since 1.7.5
*
* @var string[]
*/
protected $migrated = [];
/**
* Custom tables.
*
* @since 1.7.6
*
* @var array
*/
private static $custom_tables;
/**
* Primary class constructor.
*
* @since 1.7.5
*/
public function __construct() {
$this->reflector = new ReflectionClass( $this );
}
/**
* Class init.
*
* @since 1.7.5
*/
public function init() {
if ( ! $this->is_allowed() ) {
return;
}
$this->maybe_create_tables();
$this->maybe_convert_migration_option();
$this->hooks();
}
/**
* General hooks.
*
* @since 1.7.5
*/
protected function hooks() {
$priority = $this->is_core_plugin() ? - 9999 : 100;
add_action( 'wpforms_loaded', [ $this, 'migrate' ], $priority );
add_action( 'wpforms_loaded', [ $this, 'update_versions' ], $priority + 1 );
}
/**
* Run the migrations of the core plugin for a specific version.
*
* @since 1.7.5
*
* @noinspection NotOptimalIfConditionsInspection
*/
public function migrate() {
$classes = $this->get_upgrade_classes();
$namespace = $this->reflector->getNamespaceName() . '\\';
foreach ( $classes as $class ) {
$upgrade_version = $this->get_upgrade_version( $class );
$plugin_name = $this->get_plugin_name( $class );
$class = $namespace . $class;
if (
( isset( $this->migrated[ $upgrade_version ] ) && $this->migrated[ $upgrade_version ] >= 0 ) ||
version_compare( $upgrade_version, static::CURRENT_VERSION, '>' ) ||
! class_exists( $class )
) {
continue;
}
if ( ! isset( $this->migrated[ $upgrade_version ] ) ) {
$this->migrated[ $upgrade_version ] = static::STARTED;
$this->log( sprintf( 'Migration of %1$s to %2$s started.', $plugin_name, $upgrade_version ) );
}
// Run upgrade.
$migrated = ( new $class( $this ) )->run();
// Some migration methods can be called several times to support AS action,
// so do not log their completion here.
if ( $migrated === null ) {
continue;
}
$this->migrated[ $upgrade_version ] = $migrated ? time() : static::FAILED;
$message = $migrated ?
sprintf( 'Migration of %1$s to %2$s completed.', $plugin_name, $upgrade_version ) :
sprintf( 'Migration of %1$s to %2$s failed.', $plugin_name, $upgrade_version );
$this->log( $message );
}
}
/**
* If upgrade has occurred, update versions option in the database.
*
* @since 1.7.5
*/
public function update_versions() {
// Retrieve the last migrated versions.
$last_migrated = get_option( static::MIGRATED_OPTION_NAME, [] );
$migrated = array_merge( $last_migrated, $this->migrated );
/**
* Store current version upgrade timestamp even if there were no migrations to it.
* We need it in wpforms_get_upgraded_timestamp() for further usage in Event Driven Plugin Notifications.
*/
$migrated[ static::CURRENT_VERSION ] = isset( $migrated[ static::CURRENT_VERSION ] ) ?
$migrated[ static::CURRENT_VERSION ] :
time();
ksort( $last_migrated );
ksort( $migrated );
if ( $migrated === $last_migrated ) {
return;
}
update_option( static::MIGRATED_OPTION_NAME, $migrated );
$fully_completed = array_reduce(
$migrated,
static function ( $carry, $status ) {
return $carry && ( $status >= 0 );
},
true
);
if ( ! $fully_completed ) {
return;
}
$this->log(
sprintf( 'Migration of %1$s to %2$s is fully completed.', static::PLUGIN_NAME, static::CURRENT_VERSION )
);
// We need to run further only for core plugin (Lite and Pro).
if ( ! $this->is_core_plugin() ) {
return;
}
$last_completed = array_filter(
$last_migrated,
static function( $status ) {
return $status >= 0;
}
);
if ( ! $last_completed ) {
return;
}
update_option( 'wpforms_version_upgraded_from', $this->get_max_version( $last_completed ) );
}
/**
* Get upgrade classes.
*
* @since 1.7.5
*
* @return string[]
*/
protected function get_upgrade_classes() {
$classes = static::UPGRADE_CLASSES;
sort( $classes );
return $classes;
}
/**
* Get upgrade version from the class name.
*
* @since 1.7.5
*
* @param string $class Class name.
*
* @return string
*/
protected function get_upgrade_version( $class ) {
// Find only the digits to get version number.
if ( ! preg_match( '/\d+/', $class, $matches ) ) {
return '';
}
return implode( '.', str_split( $matches[0] ) );
}
/**
* Get plugin/addon name.
*
* @since 1.7.5
*
* @param string $class Upgrade class name.
*
* @return string
*/
protected function get_plugin_name( $class ) {
return static::PLUGIN_NAME;
}
/**
* Log message to WPForms logger and standard debug.log file.
*
* @since 1.7.5
*
* @param string $message The error message that should be logged.
*
* @noinspection ForgottenDebugOutputInspection
*/
protected function log( $message ) {
if ( defined( 'WPFORMS_DEBUG' ) && WPFORMS_DEBUG ) {
// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
error_log( $message );
wpforms_log( 'Migration', $message, [ 'type' => 'log' ] );
}
}
/**
* Determine if migration is allowed.
*
* @since 1.7.5
*/
private function is_allowed() {
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
if ( isset( $_GET['service-worker'] ) ) {
return false;
}
return ( defined( 'DOING_CRON' ) && DOING_CRON ) || is_admin();
}
/**
* Maybe create custom plugin tables.
*
* @since 1.7.6
*/
private function maybe_create_tables() {
if ( self::$custom_tables === null ) {
self::$custom_tables = wpforms()->get_existing_custom_tables();
}
foreach ( static::CUSTOM_TABLE_HANDLER_CLASSES as $custom_table_handler_class ) {
if ( ! class_exists( $custom_table_handler_class ) ) {
continue;
}
$custom_table_handler = new $custom_table_handler_class();
if ( ! in_array( $custom_table_handler->table_name, self::$custom_tables, true ) ) {
$custom_table_handler->create_table();
}
}
}
/**
* Maybe convert migration option format.
*
* @since 1.7.5
*/
private function maybe_convert_migration_option() {
/**
* Retrieve the migration option and check its format.
* Old format: a string 'x.y.z' containing last migrated version.
* New format: [ 'x.y.z' => {status}, 'x1.y1.z1' => {status}... ],
* where {status} is a migration status.
* Negative means some status (-1 for 'started' etc.),
* zero means completed earlier at unknown time,
* positive means completion timestamp.
*/
$this->migrated = get_option( static::MIGRATED_OPTION_NAME );
// If option is an array, it means that it is already converted to the new format.
if ( is_array( $this->migrated ) ) {
return;
}
/**
* Convert option to the new format.
*
* Old option names contained 'version',
* like 'wpforms_version', 'wpforms_version_lite', 'wpforms_stripe_version' etc.
* We preserve old options for downgrade cases.
* New option names should contain 'versions' and be like 'wpforms_versions' etc.
*/
$this->migrated = get_option(
str_replace( 'versions', 'version', static::MIGRATED_OPTION_NAME )
);
$version = $this->migrated === false ? self::INITIAL_FAKE_VERSION : (string) $this->migrated;
$timestamp = $version === static::CURRENT_VERSION ? time() : 0;
$this->migrated = [ $version => $timestamp ];
$max_version = $this->get_max_version( $this->migrated );
$upgrade_classes = $this->get_upgrade_classes();
foreach ( $upgrade_classes as $upgrade_class ) {
$upgrade_version = $this->get_upgrade_version( $upgrade_class );
if (
! isset( $this->migrated[ $upgrade_version ] ) &&
version_compare( $upgrade_version, $max_version, '<' )
) {
$this->migrated[ $upgrade_version ] = 0;
}
}
unset( $this->migrated[ self::INITIAL_FAKE_VERSION ] );
ksort( $this->migrated );
update_option( static::MIGRATED_OPTION_NAME, $this->migrated );
}
/**
* Get max version.
*
* @since 1.7.5
*
* @param array $versions Versions.
*
* @return string
*/
private function get_max_version( $versions ) {
return array_reduce(
array_keys( $versions ),
static function( $carry, $version ) {
return version_compare( $version, $carry, '>' ) ? $version : $carry;
},
self::INITIAL_FAKE_VERSION
);
}
/**
* Determine if it is the core plugin (Lite or Pro).
*
* @since 1.7.5
*
* @return bool True if it is the core plugin.
*/
protected function is_core_plugin() {
return strpos( static::MIGRATED_OPTION_NAME, 'wpforms_versions' ) === 0;
}
}
Migrations/Upgrade175.php 0000644 00000004730 15133255232 0011222 0 ustar 00 <?php
namespace WPForms\Migrations;
use WPForms\Tasks\Meta;
use WPForms\Tasks\Tasks;
/**
* Class v1.7.5 upgrade.
*
* @since 1.7.5
*
* @noinspection PhpUnused
*/
class Upgrade175 extends UpgradeBase {
/**
* Delete all task meta of not active tasks.
*
* @since 1.7.5
*
* @noinspection ElvisOperatorCanBeUsedInspection
*
* @return bool|null Upgrade result:
* true - the upgrade completed successfully,
* false - in the case of failure,
* null - upgrade started but not yet finished (background task).
*/
public function run() {
global $wpdb;
if ( ! $this->as_tables_exist() ) {
return true;
}
$group = Tasks::GROUP;
$sql = "SELECT DISTINCT a.args FROM {$wpdb->prefix}actionscheduler_actions a
JOIN {$wpdb->prefix}actionscheduler_groups g ON g.group_id = a.group_id
WHERE g.slug = '$group' AND a.status IN ('pending', 'in-progress')";
// phpcs:disable WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared
$results = $wpdb->get_results( $sql, 'ARRAY_A' );
// phpcs:enable WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared
$results = $results ? $results : [];
$meta_ids = [];
foreach ( $results as $result ) {
$args = isset( $result['args'] ) ? json_decode( $result['args'], true ) : null;
if ( $args && ! empty( $args['tasks_meta_id'] ) ) {
$meta_ids[] = $args['tasks_meta_id'];
}
}
$table_name = Meta::get_table_name();
$not_in = $meta_ids ? wpforms_wpdb_prepare_in( $meta_ids ) : '0';
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.NoCaching
$wpdb->query(
"DELETE FROM {$table_name} WHERE id NOT IN ( {$not_in} )"
);
// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.NoCaching
return true;
}
/**
* Check whether AS tables exist.
*
* @since 1.7.6
*
* @return bool
*/
private function as_tables_exist() {
global $wpdb;
$required_tables = [
$wpdb->prefix . 'actionscheduler_actions',
$wpdb->prefix . 'actionscheduler_groups',
];
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching
$tables = $wpdb->get_col( "SHOW TABLES LIKE '" . $wpdb->prefix . "actionscheduler%'" );
$intersect = array_values( array_intersect( $tables, $required_tables ) );
sort( $intersect );
sort( $required_tables );
return $intersect === $required_tables;
}
}
Migrations/Upgrade1751.php 0000644 00000001062 15133255232 0011276 0 ustar 00 <?php
namespace WPForms\Migrations;
/**
* Class v1.7.5.1 upgrade.
*
* @since 1.7.5.1
*
* @noinspection PhpUnused
*/
class Upgrade1751 extends UpgradeBase {
/**
* Repeat 1.7.5 migration.
*
* @since 1.7.5.1
*
* @return bool|null Upgrade result:
* true - the upgrade completed successfully,
* false - in the case of failure,
* null - upgrade started but not yet finished (background task).
*/
public function run() {
return ( new Upgrade175( $this->migrations ) )->run();
}
}
Migrations/Upgrade1672.php 0000644 00000001537 15133255232 0011307 0 ustar 00 <?php
namespace WPForms\Migrations;
/**
* Class v1.6.7.2 upgrade.
*
* @since 1.7.5
*
* @noinspection PhpUnused
*/
class Upgrade1672 extends UpgradeBase {
/**
* Run upgrade.
*
* @since 1.7.5
*
* @return bool|null Upgrade result:
* true - the upgrade completed successfully,
* false - in the case of failure,
* null - upgrade started but not yet finished (background task).
*/
public function run() {
$review = get_option( 'wpforms_review' );
if ( empty( $review ) ) {
return true;
}
$notices = get_option( 'wpforms_admin_notices', [] );
if ( isset( $notices['review_request'] ) ) {
return true;
}
$notices['review_request'] = $review;
update_option( 'wpforms_admin_notices', $notices, true );
delete_option( 'wpforms_review' );
return true;
}
}
Providers/Providers.php 0000644 00000003306 15133255232 0011212 0 ustar 00 <?php
namespace WPForms\Providers;
/**
* Class Providers gives ability to track/load all providers.
*
* @since 1.4.7
* @since 1.7.3 Renamed from `Loader` to `Providers`.
*/
class Providers {
/**
* Get the instance of a class and store it in itself.
* Later we will be able to use this class as `$providers_loader = \WPForms\Providers\Providers::get_instance();`.
*
* @since 1.4.7
*/
public static function get_instance() {
static $instance;
if ( ! $instance ) {
$instance = new Providers();
}
return $instance;
}
/**
* Loader constructor.
*
* @since 1.4.7
*/
public function __construct() {
}
/**
* Register a provider.
*
* @since 1.4.7
*
* @param \WPForms\Providers\Provider\Core $provider The core class of a single provider.
*/
public function register( Provider\Core $provider ) {
\add_filter( 'wpforms_providers_available', array( $provider, 'register_provider' ) );
// WPForms > Settings > Integrations page.
$integration = $provider->get_page_integrations();
if ( null !== $integration ) {
\add_action( 'wpforms_settings_providers', array( $integration, 'display' ), $provider::PRIORITY, 2 );
}
// Editing Single Form > Form Builder.
$form_builder = $provider->get_form_builder();
if ( null !== $form_builder ) {
\add_action( 'wpforms_providers_panel_sidebar', array( $form_builder, 'display_sidebar' ), $provider::PRIORITY );
\add_action( 'wpforms_providers_panel_content', array( $form_builder, 'display_content' ), $provider::PRIORITY );
}
// Process entry submission.
$process = $provider->get_process();
if ( null !== $process ) {
\add_action( 'wpforms_process_complete', array( $process, 'process' ), 5, 4 );
}
}
}
Providers/Provider/Status.php 0000644 00000006000 15133255232 0012304 0 ustar 00 <?php
namespace WPForms\Providers\Provider;
use stdClass;
/**
* Class Status gives ability to check/work with provider statuses.
* Might be used later to track Provider errors on data-delivery.
*
* @since 1.4.8
*/
class Status {
/**
* Provider identifier, its slug.
*
* @since 1.4.8
*
* @var string
*/
private $provider;
/**
* Form data and settings.
*
* @since 1.4.8
*
* @var array
*/
protected $form_data = [];
/**
* Status constructor.
*
* @since 1.4.8
*
* @param string $provider Provider slug.
*/
public function __construct( $provider ) {
$this->provider = sanitize_key( (string) $provider );
}
/**
* Provide ability to statically init the object.
* Useful for inline-invocations.
*
* @example: Status::init( 'drip' )->is_ready();
*
* @since 1.4.8
* @since 1.5.9 Added a check on provider.
*
* @param string $provider Provider slug.
*
* @return Status
*/
public static function init( $provider ) {
static $instance;
if ( ! $instance || $provider !== $instance->provider ) {
$instance = new self( $provider );
}
return $instance;
}
/**
* Check whether the defined provider is configured or not.
* "Configured" means has an account, that might be checked/updated on Settings > Integrations.
*
* @since 1.4.8
*
* @return bool
*/
public function is_configured() {
$options = \wpforms_get_providers_options();
// We meed to leave this filter for BC.
$is_configured = \apply_filters(
'wpforms_providers_' . $this->provider . '_configured',
! empty( $options[ $this->provider ] )
);
// Use this filter to change the configuration status of the provider.
return apply_filters( 'wpforms_providers_status_is_configured', $is_configured, $this->provider );
}
/**
* Check whether the defined provider is connected to some form.
* "Connected" means it has a Connection in Form Builder > Providers > Provider tab.
*
* @since 1.4.8
*
* @param int $form_id Form ID to check the status against.
*
* @return bool
*/
public function is_connected( $form_id ) {
$is_connected = false;
$revisions = wpforms()->get( 'revisions' );
$revision = $revisions ? $revisions->get_revision() : null;
if ( $revision ) {
$form_id = $revision->ID;
}
$this->form_data = wpforms()->get( 'form' )->get( (int) $form_id );
$content = isset( $this->form_data->post_content ) ? json_decode( $this->form_data->post_content ) : new stdClass();
if (
! empty( $content->providers->{$this->provider} ) ||
! empty( $content->payments->{$this->provider} )
) {
$is_connected = true;
}
return apply_filters( 'wpforms_providers_status_is_connected', $is_connected, $this->provider );
}
/**
* Is the current provider ready to be used?
* It means both configured and connected.
*
* @since 1.4.8
*
* @param int $form_id Form ID to check the status against.
*
* @return bool
*/
public function is_ready( $form_id ) {
return $this->is_configured() && $this->is_connected( $form_id );
}
}
Providers/Provider/Core.php 0000644 00000006447 15133255232 0011730 0 ustar 00 <?php
namespace WPForms\Providers\Provider;
/**
* Class Core stores the basic information about the provider.
* It's also a Container to load single instances of requires classes.
*
* @since 1.4.7
*/
abstract class Core {
/**
* Unique provider slug.
*
* @since 1.4.7
*
* @var string
*/
public $slug;
/**
* Translatable provider name.
*
* @since 1.4.7
*
* @var string
*/
public $name;
/**
* Custom provider icon (logo).
*
* @since 1.4.7
*
* @var string
*/
public $icon;
/**
* Custom priority for a provider, that will affect loading/placement order.
*
* @since 1.4.8
*
* @var int
*/
const PRIORITY = 10;
/**
* Get the instance of the class.
*
* @since 1.4.7
* @since 1.7.3 Compatibility with PHP 8.1
*
* @return Core
*/
public static function get_instance() {
static $instance;
$class = static::class;
if ( empty( $instance[ $class ] ) ) {
// Same as new static(), but allows avoiding "abstract class init" error.
$instance[ $class ] = new $class();
}
return $instance[ $class ];
}
/**
* Core constructor.
*
* @since 1.4.7
*
* @param array $params Possible keys: slug*, name*, icon. * are required.
*
* @throws \UnexpectedValueException Provider class should define provider's "slug"/"name" params.
*/
public function __construct( array $params ) {
// Define required provider properties.
if ( ! empty( $params['slug'] ) ) {
$this->slug = \sanitize_key( $params['slug'] );
} else {
throw new \UnexpectedValueException( 'Provider class should define a provider "slug" param in its constructor.' );
}
if ( ! empty( $params['name'] ) ) {
$this->name = \sanitize_text_field( $params['name'] );
} else {
throw new \UnexpectedValueException( 'Provider class should define a provider "name" param in its constructor.' );
}
$this->icon = WPFORMS_PLUGIN_URL . 'assets/images/sullie.png';
if ( ! empty( $params['icon'] ) ) {
$this->icon = \esc_url_raw( $params['icon'] );
}
}
/**
* Add to list of registered providers.
*
* @since 1.4.7
*
* @param array $providers Array of all active providers.
*
* @return array
*/
public function register_provider( array $providers ) {
$providers[ $this->slug ] = $this->name;
return $providers;
}
/**
* Provide an instance of the object, that should process the submitted entry.
* It will use data from an already saved entry to pass it further to a Provider.
*
* @since 1.4.7
*
* @return null|\WPForms\Providers\Provider\Process
*/
abstract public function get_process();
/**
* Provide an instance of the object, that should display provider settings
* on Settings > Integrations page in admin area.
* If you don't want to display it (i.e. you don't need it), you can pass null here in your Core provider class.
*
* @since 1.4.7
*
* @return null|\WPForms\Providers\Provider\Settings\PageIntegrations
*/
abstract public function get_page_integrations();
/**
* Provide an instance of the object, that should display provider settings in the Form Builder.
* If you don't want to display it (i.e. you don't need it), you can pass null here in your Core provider class.
*
* @since 1.4.7
*
* @return null|\WPForms\Providers\Provider\Settings\FormBuilder
*/
abstract public function get_form_builder();
}
Providers/Provider/Settings/FormBuilder.php 0000644 00000037226 15133255232 0015051 0 ustar 00 <?php
namespace WPForms\Providers\Provider\Settings;
use WPForms\Providers\Provider\Core;
use WPForms\Providers\Provider\Status;
/**
* Class FormBuilder handles functionality inside the form builder.
*
* @since 1.4.7
*/
abstract class FormBuilder implements FormBuilderInterface {
/**
* Get the Core loader class of a provider.
*
* @since 1.4.7
*
* @var Core
*/
protected $core;
/**
* Most of Marketing providers will have 'connection' type.
* Payment providers may have (or not) something different.
*
* @since 1.4.7
*
* @var string
*/
protected $type = 'connection';
/**
* Form data and settings.
*
* @since 1.4.7
*
* @var array
*/
protected $form_data = array();
/**
* Integrations constructor.
*
* @since 1.4.7
*
* @param Core $core Core provider class.
*/
public function __construct( Core $core ) {
$this->core = $core;
if ( ! empty( $_GET['form_id'] ) ) { // phpcs:ignore
$this->form_data = \wpforms()->form->get(
\absint( $_GET['form_id'] ), // phpcs:ignore
array(
'content_only' => true,
)
);
}
$this->init_hooks();
}
/**
* Register all hooks (actions and filters) here.
*
* @since 1.4.7
*/
protected function init_hooks() {
// Register builder HTML template(s).
\add_action( 'wpforms_builder_print_footer_scripts', array( $this, 'builder_templates' ), 10 );
\add_action( 'wpforms_builder_print_footer_scripts', array( $this, 'builder_custom_templates' ), 11 );
// Process builder AJAX requests.
\add_action( "wp_ajax_wpforms_builder_provider_ajax_{$this->core->slug}", array( $this, 'process_ajax' ) );
/*
* Enqueue assets.
*/
if (
( ! empty( $_GET['page'] ) && $_GET['page'] === 'wpforms-builder' ) && // phpcs:ignore
! empty( $_GET['form_id'] ) && // phpcs:ignore
\is_admin()
) {
\add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ) );
}
}
/**
* Used to register generic templates for all providers inside form builder.
*
* @since 1.4.7
* @since 1.6.2 Added sub-templates for conditional logic based on provider.
*/
public function builder_templates() {
$cl_builder_block = wpforms_conditional_logic()->builder_block(
array(
'form' => $this->form_data,
'type' => 'panel',
'parent' => 'providers',
'panel' => esc_attr( $this->core->slug ),
'subsection' => '%connection_id%',
'reference' => esc_html__( 'Marketing provider connection', 'wpforms-lite' ),
),
false
);
?>
<!-- Single connection block sub-template: FIELDS -->
<script type="text/html" id="tmpl-wpforms-providers-builder-content-connection-fields">
<div class="wpforms-builder-provider-connection-block wpforms-builder-provider-connection-fields">
<table class="wpforms-builder-provider-connection-fields-table">
<thead>
<tr>
<th><?php \esc_html_e( 'Custom Field Name', 'wpforms-lite' ); ?></th>
<th colspan="3"><?php \esc_html_e( 'Form Field Value', 'wpforms-lite' ); ?></th>
</tr>
</thead>
<tbody>
<# if ( ! _.isEmpty( data.connection.fields_meta ) ) { #>
<# _.each( data.connection.fields_meta, function( item, meta_id ) { #>
<tr class="wpforms-builder-provider-connection-fields-table-row">
<td>
<# if ( ! _.isEmpty( data.provider.fields ) ) { #>
<select class="wpforms-builder-provider-connection-field-name"
name="providers[{{ data.provider.slug }}][{{ data.connection.id }}][fields_meta][{{ meta_id }}][name]">
<option value=""><# if ( ! _.isEmpty( data.provider.placeholder ) ) { #>{{ data.provider.placeholder }}<# } else { #><?php esc_html_e( '--- Select Field ---', 'wpforms-lite' ); ?><# } #></option>
<# _.each( data.provider.fields, function( field_name, field_id ) { #>
<option value="{{ field_id }}"
<# if ( field_id === item.name ) { #>selected="selected"<# } #>
>
{{ field_name }}
</option>
<# } ); #>
</select>
<# } else { #>
<input type="text" value="{{ item.name }}"
class="wpforms-builder-provider-connection-field-name"
name="providers[{{ data.provider.slug }}][{{ data.connection.id }}][fields_meta][{{ meta_id }}][name]"
placeholder="<?php \esc_attr_e( 'Field Name', 'wpforms-lite' ); ?>"
/>
<# } #>
</td>
<td>
<select class="wpforms-builder-provider-connection-field-value"
name="providers[{{ data.provider.slug }}][{{ data.connection.id }}][fields_meta][{{ meta_id }}][field_id]">
<option value=""><?php esc_html_e( '--- Select Form Field ---', 'wpforms-lite' ); ?></option>
<# _.each( data.fields, function( field, key ) { #>
<option value="{{ field.id }}"
<# if ( field.id === item.field_id ) { #>selected="selected"<# } #>
>
<# if ( ! _.isUndefined( field.label ) && field.label.toString().trim() !== '' ) { #>
{{ field.label.toString().trim() }}
<# } else { #>
{{ wpforms_builder.field + ' #' + key }}
<# } #>
</option>
<# } ); #>
</select>
</td>
<td class="add">
<button class="button-secondary js-wpforms-builder-provider-connection-fields-add"
title="<?php \esc_attr_e( 'Add Another', 'wpforms-lite' ); ?>">
<i class="fa fa-plus-circle"></i>
</button>
</td>
<td class="delete">
<button class="button js-wpforms-builder-provider-connection-fields-delete <# if ( meta_id === 0 ) { #>hidden<# } #>"
title="<?php \esc_attr_e( 'Remove', 'wpforms-lite' ); ?>">
<i class="fa fa-minus-circle"></i>
</button>
</td>
</tr>
<# } ); #>
<# } else { #>
<tr class="wpforms-builder-provider-connection-fields-table-row">
<td>
<# if ( ! _.isEmpty( data.provider.fields ) ) { #>
<select class="wpforms-builder-provider-connection-field-name"
name="providers[{{ data.provider.slug }}][{{ data.connection.id }}][fields_meta][0][name]">
<option value=""><# if ( ! _.isEmpty( data.provider.placeholder ) ) { #>{{ data.provider.placeholder }}<# } else { #><?php esc_html_e( '--- Select Field ---', 'wpforms-lite' ); ?><# } #></option>
<# _.each( data.provider.fields, function( field_name, field_id ) { #>
<option value="{{ field_id }}">
{{ field_name }}
</option>
<# } ); #>
</select>
<# } else { #>
<input type="text" value=""
class="wpforms-builder-provider-connection-field-name"
name="providers[{{ data.provider.slug }}][{{ data.connection.id }}][fields_meta][0][name]"
placeholder="<?php \esc_attr_e( 'Field Name', 'wpforms-lite' ); ?>"
/>
<# } #>
</td>
<td>
<select class="wpforms-builder-provider-connection-field-value"
name="providers[{{ data.provider.slug }}][{{ data.connection.id }}][fields_meta][0][field_id]">
<option value=""><?php esc_html_e( '--- Select Form Field ---', 'wpforms-lite' ); ?></option>
<# _.each( data.fields, function( field, key ) { #>
<option value="{{ field.id }}">
<# if ( ! _.isUndefined( field.label ) && field.label.toString().trim() !== '' ) { #>
{{ field.label.toString().trim() }}
<# } else { #>
{{ wpforms_builder.field + ' #' + key }}
<# } #>
</option>
<# } ); #>
</select>
</td>
<td class="add">
<button class="button-secondary js-wpforms-builder-provider-connection-fields-add"
title="<?php \esc_attr_e( 'Add Another', 'wpforms-lite' ); ?>">
<i class="fa fa-plus-circle"></i>
</button>
</td>
<td class="delete">
<button class="button js-wpforms-builder-provider-connection-fields-delete hidden"
title="<?php \esc_attr_e( 'Delete', 'wpforms-lite' ); ?>">
<i class="fa fa-minus-circle"></i>
</button>
</td>
</tr>
<# } #>
</tbody>
</table><!-- /.wpforms-builder-provider-connection-fields-table -->
<p class="description">
<?php \esc_html_e( 'Map custom fields (or properties) to form fields values.', 'wpforms-lite' ); ?>
</p>
</div><!-- /.wpforms-builder-provider-connection-fields -->
</script>
<!-- Single connection block sub-template: CONDITIONAL LOGIC -->
<script type="text/html" id="tmpl-wpforms-<?php echo esc_attr( $this->core->slug ); ?>-builder-content-connection-conditionals">
<?php echo $cl_builder_block; // phpcs:ignore ?>
</script>
<!-- DEPRECATED: Should be removed when we will make changes in our addons. -->
<script type="text/html" id="tmpl-wpforms-providers-builder-content-connection-conditionals">
<?php echo $cl_builder_block; // phpcs:ignore ?>
</script>
<?php
}
/**
* Enqueue JavaScript and CSS files if needed.
* When extending - include the `parent::enqueue_assets();` not to break things!
*
* @since 1.4.7
*/
public function enqueue_assets() {
$min = \wpforms_get_min_suffix();
\wp_enqueue_script(
'wpforms-admin-builder-templates',
WPFORMS_PLUGIN_URL . "assets/js/components/admin/builder/templates{$min}.js",
array( 'wp-util' ),
WPFORMS_VERSION,
true
);
\wp_enqueue_script(
'wpforms-admin-builder-providers',
WPFORMS_PLUGIN_URL . "assets/js/components/admin/builder/providers{$min}.js",
array( 'wpforms-utils', 'wpforms-builder', 'wpforms-admin-builder-templates' ),
WPFORMS_VERSION,
true
);
}
/**
* Process the Builder AJAX requests.
*
* @since 1.4.7
*/
public function process_ajax() {
// Run a security check.
\check_ajax_referer( 'wpforms-builder', 'nonce' );
// Check for permissions.
if ( ! \wpforms_current_user_can( 'edit_forms' ) ) {
\wp_send_json_error(
array(
'error' => \esc_html__( 'You do not have permission to perform this action.', 'wpforms-lite' ),
)
);
}
// Process required values.
$error = array( 'error' => \esc_html__( 'Something went wrong while performing an AJAX request.', 'wpforms-lite' ) );
if (
empty( $_POST['id'] ) ||
empty( $_POST['task'] )
) {
\wp_send_json_error( $error );
}
$form_id = (int) $_POST['id'];
$task = sanitize_key( $_POST['task'] );
$revisions = wpforms()->get( 'revisions' );
$revision = $revisions ? $revisions->get_revision() : null;
if ( $revision ) {
// Setup form data based on the revision_id, that we got from AJAX request.
$this->form_data = wpforms_decode( $revision->post_content );
} else {
// Setup form data based on the ID, that we got from AJAX request.
$form_handler = wpforms()->get( 'form' );
$this->form_data = $form_handler ? $form_handler->get( $form_id, [ 'content_only' => true ] ) : [];
}
// Do not allow to proceed further, as form_id may be incorrect.
if ( empty( $this->form_data ) ) {
\wp_send_json_error( $error );
}
$data = \apply_filters(
'wpforms_providers_settings_builder_ajax_' . $task . '_' . $this->core->slug,
null
);
if ( null !== $data ) {
\wp_send_json_success( $data );
}
\wp_send_json_error( $error );
}
/**
* Display content inside the panel sidebar area.
*
* @since 1.4.7
*/
public function display_sidebar() {
$configured = '';
if ( ! empty( $this->form_data['id'] ) && Status::init( $this->core->slug )->is_ready( $this->form_data['id'] ) ) {
$configured = 'configured';
}
$classes = array(
'wpforms-panel-sidebar-section',
'icon',
$configured,
'wpforms-panel-sidebar-section-' . $this->core->slug,
);
?>
<a href="#" class="<?php echo \esc_attr( \implode( ' ', $classes ) ); ?>"
data-section="<?php echo \esc_attr( $this->core->slug ); ?>">
<img src="<?php echo \esc_url( $this->core->icon ); ?>">
<?php echo \esc_html( $this->core->name ); ?>
<i class="fa fa-angle-right wpforms-toggle-arrow"></i>
<?php if ( ! empty( $configured ) ) : ?>
<i class="fa fa-check-circle-o"></i>
<?php endif; ?>
</a>
<?php
}
/**
* Wrap the builder section content with the required (for tabs switching) markup.
*
* @since 1.4.7
*/
public function display_content() {
?>
<div class="wpforms-panel-content-section wpforms-builder-provider wpforms-panel-content-section-<?php echo \esc_attr( $this->core->slug ); ?>" id="<?php echo \esc_attr( $this->core->slug ); ?>-provider" data-provider="<?php echo \esc_attr( $this->core->slug ); ?>">
<!-- Provider content goes here. -->
<?php
$this->display_content_header();
$form_id = ! empty( $this->form_data['id'] ) ? $this->form_data['id'] : '';
self::display_content_default_screen(
Status::init( $this->core->slug )->is_connected( $form_id ),
$this->core->slug,
$this->core->name,
$this->core->icon
);
?>
<div class="wpforms-builder-provider-body">
<div class="wpforms-provider-connections-wrap wpforms-clear">
<div class="wpforms-builder-provider-connections"></div>
</div>
</div>
</div>
<?php
}
/**
* Display provider default screen.
*
* @since 1.6.8
*
* @param bool $is_connected True if connections are configured.
* @param string $slug Provider slug.
* @param string $name Provider name.
* @param string $icon Provider icon.
*/
public static function display_content_default_screen( $is_connected, $slug, $name, $icon ) {
// Hide provider default settings screen when it's already connected.
$class = $is_connected ? ' wpforms-hidden' : '';
?>
<div class="wpforms-builder-provider-connections-default<?php echo esc_attr( $class ); ?>">
<img src="<?php echo esc_url( $icon ); ?>">
<div class="wpforms-builder-provider-settings-default-content">
<?php
/*
* Allows developers to change the default content of the provider's settings default screen.
*
* @since 1.6.8
*
* @param string $content Content of the provider's settings default screen.
*/
echo apply_filters( // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
"wpforms_providers_provider_settings_formbuilder_display_content_default_screen_{$slug}",
sprintf( /* translators: %s - Provider name. */
'<p>' . esc_html__( 'Get the most out of WPForms — use it with an active %s account.', 'wpforms-lite' ) . '</p>',
esc_html( $name )
)
);
?>
</div>
</div>
<?php
}
/**
* Section content header.
*
* @since 1.4.7
*/
protected function display_content_header() {
$is_configured = Status::init( $this->core->slug )->is_configured();
?>
<div class="wpforms-builder-provider-title wpforms-panel-content-section-title">
<?php echo \esc_html( $this->core->name ); ?>
<span class="wpforms-builder-provider-title-spinner">
<i class="wpforms-loading-spinner wpforms-loading-md wpforms-loading-inline"></i>
</span>
<button class="wpforms-builder-provider-title-add js-wpforms-builder-provider-connection-add <?php echo $is_configured ? '' : 'hidden'; ?>"
data-form_id="<?php echo \absint( $_GET['form_id'] ); ?>"
data-provider="<?php echo \esc_attr( $this->core->slug ); ?>">
<?php \esc_html_e( 'Add New Connection', 'wpforms-lite' ); ?>
</button>
<button class="wpforms-builder-provider-title-add js-wpforms-builder-provider-account-add <?php echo ! $is_configured ? '' : 'hidden'; ?>"
data-form_id="<?php echo \absint( $_GET['form_id'] ); ?>"
data-provider="<?php echo \esc_attr( $this->core->slug ); ?>">
<?php \esc_html_e( 'Add New Account', 'wpforms-lite' ); ?>
</button>
</div>
<?php
}
}
Providers/Provider/Settings/PageIntegrationsInterface.php 0000644 00000001103 15133255232 0017704 0 ustar 00 <?php
namespace WPForms\Providers\Provider\Settings;
/**
* Interface PageIntegrationsInterface defines methods that are common among all Integration page providers content.
*
* @since 1.4.7
*/
interface PageIntegrationsInterface {
/**
* Display the data for integrations tab.
* This is a default one, that can be easily overwritten inside the child class of a specific provider.
*
* @since 1.4.7
*
* @param array $active Array of activated providers addons.
* @param array $settings Providers options.
*/
public function display( $active, $settings );
}
Providers/Provider/Settings/FormBuilderInterface.php 0000644 00000001265 15133255232 0016664 0 ustar 00 <?php
namespace WPForms\Providers\Provider\Settings;
/**
* Interface FormBuilderInterface defines required method for builder to work properly.
*
* @since 1.4.7
*/
interface FormBuilderInterface {
/**
* Every provider should display a title in a Builder.
*
* @since 1.4.7
*/
public function display_sidebar();
/**
* Every provider should display a content of its settings in a Builder.
*
* @since 1.4.7
*/
public function display_content();
/**
* Use this method to register own templates for form builder.
* Make sure, that you have `tmpl-` in template name in `<script id="tmpl-*">`.
*
* @since 1.4.7
*/
public function builder_custom_templates();
}
Providers/Provider/Settings/PageIntegrations.php 0000644 00000020372 15133255232 0016074 0 ustar 00 <?php
namespace WPForms\Providers\Provider\Settings;
use WPForms\Providers\Provider\Core;
/**
* Class PageIntegrations handles the WPForms -> Settings -> Integrations page.
*
* @since 1.4.7
*/
abstract class PageIntegrations implements PageIntegrationsInterface {
/**
* Get the Core loader class of a provider.
*
* @since 1.4.7
*
* @var Core
*/
protected $core;
/**
* Integrations constructor.
*
* @since 1.4.7
*
* @param Core $core Core provider object.
*/
public function __construct( Core $core ) {
$this->core = $core;
$this->ajax();
}
/**
* Process the default ajax functionality.
*
* @since 1.4.7
*/
protected function ajax() {
// Remove provider from Settings Integrations tab.
\add_action( "wp_ajax_wpforms_settings_provider_disconnect_{$this->core->slug}", array( $this, 'ajax_disconnect' ) );
// Add new provider from Settings Integrations tab.
\add_action( "wp_ajax_wpforms_settings_provider_add_{$this->core->slug}", array( $this, 'ajax_connect' ) );
}
/**
* @inheritdoc
*/
public function display( $active, $settings ) {
$connected = ! empty( $active[ $this->core->slug ] );
$accounts = ! empty( $settings[ $this->core->slug ] ) ? $settings[ $this->core->slug ] : array();
$class = $connected && $accounts ? 'connected' : '';
$arrow = 'right';
// This lets us highlight a specific service by a special link.
if ( ! empty( $_GET['wpforms-integration'] ) ) { //phpcs:ignore
if ( $this->core->slug === $_GET['wpforms-integration'] ) { //phpcs:ignore
$class .= ' focus-in';
$arrow = 'down';
} else {
$class .= ' focus-out';
}
}
?>
<div id="wpforms-integration-<?php echo \esc_attr( $this->core->slug ); ?>"
class="wpforms-settings-provider wpforms-clear <?php echo \esc_attr( $this->core->slug ); ?> <?php echo \esc_attr( $class ); ?>">
<div class="wpforms-settings-provider-header wpforms-clear" data-provider="<?php echo \esc_attr( $this->core->slug ); ?>">
<div class="wpforms-settings-provider-logo">
<i title="<?php \esc_attr_e( 'Show Accounts', 'wpforms-lite' ); ?>" class="fa fa-chevron-<?php echo \esc_attr( $arrow ); ?>"></i>
<img src="<?php echo \esc_url( $this->core->icon ); ?>">
</div>
<div class="wpforms-settings-provider-info">
<h3><?php echo \esc_html( $this->core->name ); ?></h3>
<p>
<?php
/* translators: %s - provider name. */
\printf( \esc_html__( 'Integrate %s with WPForms', 'wpforms-lite' ), \esc_html( $this->core->name ) );
?>
</p>
<span class="connected-indicator green"><i class="fa fa-check-circle-o"></i> <?php \esc_html_e( 'Connected', 'wpforms-lite' ); ?></span>
</div>
</div>
<div class="wpforms-settings-provider-accounts" id="provider-<?php echo \esc_attr( $this->core->slug ); ?>">
<div class="wpforms-settings-provider-accounts-list">
<ul>
<?php
if ( ! empty( $accounts ) ) {
foreach ( $accounts as $account_id => $account ) {
if ( empty( $account_id ) ) {
continue;
}
$this->display_connected_account( $account_id, $account );
}
}
?>
</ul>
</div>
<?php $this->display_add_new(); ?>
</div>
</div>
<?php
}
/**
* Display connected account.
*
* @since 1.7.5
*
* @param string $account_id Account ID.
* @param array $account Account data.
*/
protected function display_connected_account( $account_id, $account ) {
$account_connected = ! empty( $account['date'] )
? wpforms_date_format( $account['date'] )
: esc_html__( 'N/A', 'wpforms-lite' );
echo '<li class="wpforms-clear">';
/**
* Allow adding markup before connected account item.
*
* @since 1.7.5
*
* @param string $account_id Account ID.
* @param array $account Account data.
*/
do_action( 'wpforms_providers_provider_settings_page_integrations_display_connected_account_item_before', $account_id, $account );
echo '<span class="label">';
echo ! empty( $account['label'] ) ? esc_html( $account['label'] ) : '<em>' . esc_html__( 'No Label', 'wpforms-lite' ) . '</em>';
echo '</span>';
/* translators: %s - Connection date. */
echo '<span class="date">' . sprintf( esc_html__( 'Connected on: %s', 'wpforms-lite' ), esc_html( $account_connected ) ) . '</span>';
echo '<span class="remove"><a href="#" data-provider="' . esc_attr( $this->core->slug ) . '" data-key="' . esc_attr( $account_id ) . '">' . esc_html__( 'Disconnect', 'wpforms-lite' ) . '</a></span>';
/**
* Allow adding markup after connected account item.
*
* @since 1.7.5
*
* @param string $account_id Account ID.
* @param array $account Account data.
*/
do_action( 'wpforms_providers_provider_settings_page_integrations_display_connected_account_item_after', $account_id, $account );
echo '</li>';
}
/**
* Any new connection should be added.
* So display the content of that.
*
* @since 1.4.7
*/
protected function display_add_new() {
?>
<p class="wpforms-settings-provider-accounts-toggle">
<a class="wpforms-btn wpforms-btn-md wpforms-btn-light-grey" href="#" data-provider="<?php echo esc_attr( $this->core->slug ); ?>">
<i class="fa fa-plus"></i> <?php esc_html_e( 'Add New Account', 'wpforms-lite' ); ?>
</a>
</p>
<div class="wpforms-settings-provider-accounts-connect">
<form>
<p><?php esc_html_e( 'Please fill out all of the fields below to add your new provider account.', 'wpforms-lite' ); ?></span></p>
<p class="wpforms-settings-provider-accounts-connect-fields">
<?php $this->display_add_new_connection_fields(); ?>
</p>
<?php $this->display_add_new_connection_submit_button(); ?>
</form>
</div>
<?php
}
/**
* Some providers may or may not have fields.
*
* @since 1.4.7
*/
protected function display_add_new_connection_fields() {
}
/**
* Some providers may modify the form button and add their form handler.
*
* @since 1.7.4
*/
protected function display_add_new_connection_submit_button() {
/* translators: %s - provider name. */
$title = sprintf( __( 'Connect to %s', 'wpforms-lite' ), $this->core->name );
?>
<button type="submit" class="wpforms-btn wpforms-btn-md wpforms-btn-orange wpforms-settings-provider-connect"
data-provider="<?php echo esc_attr( $this->core->slug ); ?>" title="<?php echo esc_attr( $title ); ?>">
<?php echo esc_html( $title ); ?>
</button>
<?php
}
/**
* AJAX to disconnect a provider from the settings integrations tab.
*
* @since 1.4.7
*/
public function ajax_disconnect() {
// Run a security check.
if ( ! \check_ajax_referer( 'wpforms-admin', 'nonce', false ) ) {
\wp_send_json_error(
array(
'error_msg' => \esc_html__( 'Your session expired. Please reload the page.', 'wpforms-lite' ),
)
);
}
// Check for permissions.
if ( ! \wpforms_current_user_can() ) {
\wp_send_json_error(
array(
'error_msg' => \esc_html__( 'You do not have permission.', 'wpforms-lite' ),
)
);
}
if ( empty( $_POST['provider'] ) || empty( $_POST['key'] ) ) {
\wp_send_json_error(
array(
'error_msg' => \esc_html__( 'Missing data.', 'wpforms-lite' ),
)
);
}
$providers = \wpforms_get_providers_options();
if ( ! empty( $providers[ $_POST['provider'] ][ $_POST['key'] ] ) ) {
unset( $providers[ $_POST['provider'] ][ $_POST['key'] ] );
\update_option( 'wpforms_providers', $providers );
\wp_send_json_success();
} else {
\wp_send_json_error(
array(
'error_msg' => \esc_html__( 'Connection missing.', 'wpforms-lite' ),
)
);
}
}
/**
* AJAX to add a provider from the settings integrations tab.
*
* @since 1.4.7
*/
public function ajax_connect() {
// Run a security check.
if ( ! \check_ajax_referer( 'wpforms-admin', 'nonce', false ) ) {
\wp_send_json_error(
array(
'error_msg' => \esc_html__( 'Your session expired. Please reload the page.', 'wpforms-lite' ),
)
);
}
// Check for permissions.
if ( ! \wpforms_current_user_can() ) {
\wp_send_json_error(
array(
'error_msg' => \esc_html__( 'You do not have permissions.', 'wpforms-lite' ),
)
);
}
if ( empty( $_POST['data'] ) ) {
\wp_send_json_error(
array(
'error_msg' => \esc_html__( 'Missing required data in payload.', 'wpforms-lite' ),
)
);
}
}
}
Providers/Provider/Process.php 0000644 00000004353 15133255232 0012450 0 ustar 00 <?php
namespace WPForms\Providers\Provider;
/**
* Class Process handles entries processing using the provider settings and configuration.
*
* @since 1.4.7
*/
abstract class Process {
/**
* Get the Core loader class of a provider.
*
* @since 1.4.7
*
* @var Core
*/
protected $core;
/**
* Array of form fields.
*
* @since 1.4.7
*
* @var array
*/
protected $fields = [];
/**
* Submitted form content.
*
* @since 1.4.7
*
* @var array
*/
protected $entry = [];
/**
* Form data and settings.
*
* @since 1.4.7
*
* @var array
*/
protected $form_data = [];
/**
* ID of a saved entry.
*
* @since 1.4.7
*
* @var int
*/
protected $entry_id;
/**
* Process constructor.
*
* @since 1.4.7
*
* @param Core $core Provider core class.
*/
public function __construct( Core $core ) {
$this->core = $core;
}
/**
* Receive all wpforms_process_complete params and do the actual processing.
*
* @since 1.4.7
*
* @param array $fields Array of form fields.
* @param array $entry Submitted form content.
* @param array $form_data Form data and settings.
* @param int $entry_id ID of a saved entry.
*/
abstract public function process( $fields, $entry, $form_data, $entry_id );
/**
* Process conditional logic for a connection.
*
* @since 1.4.7
*
* @param array $fields Array of form fields.
* @param array $form_data Form data and settings.
* @param array $connection All connection data.
*
* @return bool
*/
protected function process_conditionals( $fields, $form_data, $connection ) {
if (
empty( $connection['conditional_logic'] ) ||
empty( $connection['conditionals'] ) ||
! function_exists( 'wpforms_conditional_logic' )
) {
return true;
}
$process = wpforms_conditional_logic()->process( $fields, $form_data, $connection['conditionals'] );
if (
! empty( $connection['conditional_type'] ) &&
$connection['conditional_type'] === 'stop'
) {
$process = ! $process;
}
return $process;
}
/**
* Get provider options, saved on Settings > Integrations page.
*
* @since 1.4.7
*
* @return array
*/
protected function get_options() {
return wpforms_get_providers_options( $this->core->slug );
}
}
Admin/FlyoutMenu.php 0000644 00000007015 15133255232 0010420 0 ustar 00 <?php
namespace WPForms\Admin;
/**
* Admin Flyout Menu.
*
* @since 1.5.7
*/
class FlyoutMenu {
/**
* Constructor.
*
* @since 1.5.7
*/
public function __construct() {
if ( ! \wpforms_is_admin_page() || \wpforms_is_admin_page( 'builder' ) ) {
return;
}
if ( ! \apply_filters( 'wpforms_admin_flyoutmenu', true ) ) {
return;
}
// Check if WPForms Challenge can be displayed.
if ( wpforms()->get( 'challenge' )->challenge_can_start() ) {
return;
}
$this->hooks();
}
/**
* Hooks.
*
* @since 1.5.7
*/
public function hooks() {
\add_action( 'admin_footer', array( $this, 'output' ) );
}
/**
* Output menu.
*
* @since 1.5.7
*/
public function output() {
printf(
'<div id="wpforms-flyout">
<div id="wpforms-flyout-items">
%1$s
</div>
<a href="#" class="wpforms-flyout-button wpforms-flyout-head">
<div class="wpforms-flyout-label">%2$s</div>
<img src="%3$s" alt="%2$s" data-active="%4$s" />
</a>
</div>',
$this->get_items_html(), // phpcs:ignore
\esc_attr__( 'See Quick Links', 'wpforms-lite' ),
\esc_url( \WPFORMS_PLUGIN_URL . 'assets/images/admin-flyout-menu/sullie-default.svg' ),
\esc_url( \WPFORMS_PLUGIN_URL . 'assets/images/admin-flyout-menu/sullie-active.svg' )
);
}
/**
* Generate menu items HTML.
*
* @since 1.5.7
*
* @return string Menu items HTML.
*/
private function get_items_html() {
$items = array_reverse( $this->menu_items() );
$items_html = '';
foreach ( $items as $item_key => $item ) {
$items_html .= sprintf(
'<a href="%1$s" target="_blank" rel="noopener noreferrer" class="wpforms-flyout-button wpforms-flyout-item wpforms-flyout-item-%2$d"%5$s%6$s>
<div class="wpforms-flyout-label">%3$s</div>
<i class="fa %4$s"></i>
</a>',
\esc_url( $item['url'] ),
(int) $item_key,
\esc_html( $item['title'] ),
\sanitize_html_class( $item['icon'] ),
! empty( $item['bgcolor'] ) ? ' style="background-color: ' . \esc_attr( $item['bgcolor'] ) . '"' : '',
! empty( $item['hover_bgcolor'] ) ? ' onMouseOver="this.style.backgroundColor=\'' . \esc_attr( $item['hover_bgcolor'] ) . '\'" onMouseOut="this.style.backgroundColor=\'' . \esc_attr( $item['bgcolor'] ) . '\'"' : ''
);
}
return $items_html;
}
/**
* Menu items data.
*
* @since 1.5.7
*/
private function menu_items() {
$is_pro = wpforms()->is_pro();
$utm_campaign = $is_pro ? 'plugin' : 'liteplugin';
$items = array(
array(
'title' => \esc_html__( 'Upgrade to WPForms Pro', 'wpforms-lite' ),
'url' => \wpforms_admin_upgrade_link( 'flyout-menu' ),
'icon' => 'fa-star',
'bgcolor' => '#E1772F',
'hover_bgcolor' => '#ff8931',
),
array(
'title' => \esc_html__( 'Support & Docs', 'wpforms-lite' ),
'url' => 'https://wpforms.com/docs/?utm_source=WordPress&utm_medium=Flyout Menu&utm_campaign=' . $utm_campaign . '&utm_content=Support',
'icon' => 'fa-life-ring',
),
array(
'title' => \esc_html__( 'Join Our Community', 'wpforms-lite' ),
'url' => 'https://www.facebook.com/groups/wpformsvip/',
'icon' => 'fa-comments',
),
array(
'title' => \esc_html__( 'Suggest a Feature', 'wpforms-lite' ),
'url' => 'https://wpforms.com/features/suggest/?utm_source=WordPress&utm_medium=Flyout Menu&utm_campaign=' . $utm_campaign . '&utm_content=Feature',
'icon' => 'fa-lightbulb-o',
),
);
if ( $is_pro ) {
array_shift( $items );
}
return \apply_filters( 'wpforms_admin_flyout_menu_items', $items );
}
}
Admin/Tools/Importers.php 0000644 00000002477 15133255232 0011404 0 ustar 00 <?php
namespace WPForms\Admin\Tools;
use WPForms\Admin\Tools\Importers\ContactForm7;
use WPForms\Admin\Tools\Importers\NinjaForms;
use WPForms\Admin\Tools\Importers\PirateForms;
/**
* Load the different form importers.
*
* @since 1.6.6
*/
class Importers {
/**
* Available importers.
*
* @since 1.6.6
*
* @var array
*/
private $importers = [];
/**
* Load default form importers.
*
* @since 1.6.6
*/
public function load() {
if ( empty( $this->importers ) ) {
$this->importers = [
'contact-form-7' => new ContactForm7(),
'ninja-forms' => new NinjaForms(),
'pirate-forms' => new PirateForms(),
];
}
}
/**
* Load default form importers.
*
* @since 1.6.6
*
* @return array
*/
public function get_importers() {
$this->load();
$importers = [];
foreach ( $this->importers as $importer ) {
$importers = $importer->register( $importers );
}
return apply_filters( 'wpforms_importers', $importers );
}
/**
* Get a importer forms.
*
* @since 1.6.6
*
* @param string $provider Provider.
*
* @return array
*/
public function get_importer_forms( $provider ) {
if ( isset( $this->importers[ $provider ] ) ) {
return apply_filters( "wpforms_importer_forms_{$provider}", $this->importers[ $provider ]->get_forms() );
}
return [];
}
}
Admin/Tools/Views/Importer.php 0000644 00000023255 15133255232 0012313 0 ustar 00 <?php
namespace WPForms\Admin\Tools\Views;
use WPForms\Admin\Tools\Importers;
/**
* Class Importer.
*
* @since 1.6.6
*/
class Importer extends View {
/**
* View slug.
*
* @since 1.6.6
*
* @var string
*/
protected $slug = 'import';
/**
* Registered importers.
*
* @since 1.6.6
*
* @var array
*/
public $importers = [];
/**
* Available forms for a specific importer.
*
* @since 1.6.6
*
* @var array
*/
public $importer_forms = [];
/**
* Init view.
*
* @since 1.6.6
*/
public function init() {
$importers = new Importers();
$this->importers = $importers->get_importers();
if ( ! empty( $_GET['provider'] ) ) { //phpcs:ignore WordPress.Security.NonceVerification.Recommended
$this->importer_forms = $importers->get_importer_forms( sanitize_key( $_GET['provider'] ) );//phpcs:ignore WordPress.Security.NonceVerification.Recommended
}
// Load the Underscores templates for importers.
add_action( 'admin_print_scripts', [ $this, 'importer_templates' ] );
}
/**
* Get view label.
*
* @since 1.6.6
*
* @return string
*/
public function get_label() {
return '';
}
/**
* Checking user capability to view.
*
* @since 1.6.6
*
* @return bool
*/
public function check_capability() {
return wpforms_current_user_can( 'create_forms' );
}
/**
* Checking if needs display in navigation.
*
* @since 1.6.6
*
* @return bool
*/
public function hide_from_nav() {
return true;
}
/**
* Importer view content.
*
* @since 1.6.6
*/
public function display() {
$this->heading_block();
$this->forms_block();
$this->analyze_block();
$this->process_block();
}
/**
* Get provider.
*
* @since 1.6.6
*
* @return string
*/
private function get_provider_name() {
//phpcs:ignore WordPress.Security.NonceVerification.Recommended
$slug = ! empty( $_GET['provider'] ) ? sanitize_key( $_GET['provider'] ) : '';
return isset( $this->importers[ $slug ] ) ? $this->importers[ $slug ]['name'] : '';
}
/**
* Heading block.
*
* @since 1.6.6
*/
private function heading_block() {
?>
<div class="wpforms-setting-row tools wpforms-clear section-heading no-desc">
<div class="wpforms-setting-field">
<h4><?php esc_html_e( 'Form Import', 'wpforms-lite' ); ?></h4>
</div>
</div>
<?php
}
/**
* Forms block.
*
* @since 1.6.6
*/
private function forms_block() {
?>
<div id="wpforms-importer-forms">
<div class="wpforms-setting-row tools">
<p><?php esc_html_e( 'Select the forms you would like to import.', 'wpforms-lite' ); ?></p>
<div class="checkbox-multiselect-columns">
<div class="first-column">
<h5 class="header"><?php esc_html_e( 'Available Forms', 'wpforms-lite' ); ?></h5>
<ul>
<?php
if ( empty( $this->importer_forms ) ) {
echo '<li>' . esc_html__( 'No forms found.', 'wpforms-lite' ) . '</li>';
} else {
foreach ( $this->importer_forms as $id => $form ) {
printf(
'<li><label><input type="checkbox" name="forms[]" value="%s">%s</label></li>',
esc_attr( $id ),
esc_attr( sanitize_text_field( $form ) )
);
}
}
?>
</ul>
<?php if ( ! empty( $this->importer_forms ) ) : ?>
<a href="#" class="all"><?php esc_html_e( 'Select All', 'wpforms-lite' ); ?></a>
<?php endif; ?>
</div>
<div class="second-column">
<h5 class="header"><?php esc_html_e( 'Forms to Import', 'wpforms-lite' ); ?></h5>
<ul></ul>
</div>
</div>
</div>
<?php if ( ! empty( $this->importer_forms ) ) : ?>
<p class="submit">
<button class="wpforms-btn wpforms-btn-md wpforms-btn-orange"
id="wpforms-importer-forms-submit"><?php esc_html_e( 'Import', 'wpforms-lite' ); ?></button>
</p>
<?php endif; ?>
</div>
<?php
}
/**
* Analyze block.
*
* @since 1.6.6
*/
private function analyze_block() {
?>
<div id="wpforms-importer-analyze">
<p class="process-analyze">
<i class="fa fa-spinner fa-spin" aria-hidden="true"></i>
<?php
printf(
wp_kses( /* translators: %s - Provider name. */
__( 'Analyzing <span class="form-current">1</span> of <span class="form-total">0</span> forms from %s.', 'wpforms-lite' ),
[
'span' => [
'class' => [],
],
]
),
esc_attr( sanitize_text_field( $this->get_provider_name() ) )
);
?>
</p>
<div class="upgrade">
<h5><?php esc_html_e( 'Heads Up!', 'wpforms-lite' ); ?></h5>
<p><?php esc_html_e( 'One or more of your forms contain fields that are not available in WPForms Lite. To properly import these fields, we recommend upgrading to WPForms Pro.', 'wpforms-lite' ); ?></p>
<p><?php esc_html_e( 'You can continue with the import without upgrading, and we will do our best to match the fields. However, some of them will be omitted due to compatibility issues.', 'wpforms-lite' ); ?></p>
<p>
<a href="<?php echo esc_url( wpforms_admin_upgrade_link( 'tools-import' ) ); ?>" target="_blank"
rel="noopener noreferrer"
class="wpforms-btn wpforms-btn-md wpforms-btn-orange wpforms-upgrade-modal"><?php esc_html_e( 'Upgrade to WPForms Pro', 'wpforms-lite' ); ?></a>
<a href="#" class="wpforms-btn wpforms-btn-md wpforms-btn-light-grey"
id="wpforms-importer-continue-submit"><?php esc_html_e( 'Continue Import without Upgrading', 'wpforms-lite' ); ?></a>
</p>
<hr>
<p><?php esc_html_e( 'Below is the list of form fields that may be impacted:', 'wpforms-lite' ); ?></p>
</div>
</div>
<?php
}
/**
* Process block.
*
* @since 1.6.6
*/
private function process_block() {
?>
<div id="wpforms-importer-process">
<p class="process-count">
<i class="fa fa-spinner fa-spin" aria-hidden="true"></i>
<?php
printf(
wp_kses( /* translators: %s - Provider name. */
__( 'Importing <span class="form-current">1</span> of <span class="form-total">0</span> forms from %s.', 'wpforms-lite' ),
[
'span' => [
'class' => [],
],
]
),
esc_attr( sanitize_text_field( $this->get_provider_name() ) )
);
?>
</p>
<p class="process-completed">
<?php
echo wp_kses(
__( 'Congrats, the import process has finished! We have successfully imported <span class="forms-completed"></span> forms. You can review the results below.', 'wpforms-lite' ),
[
'span' => [
'class' => [],
],
]
);
?>
</p>
<div class="status"></div>
</div>
<?php
}
/**
* Various Underscores templates for form importing.
*
* @since 1.6.6
*/
public function importer_templates() {
?>
<script type="text/html" id="tmpl-wpforms-importer-upgrade">
<# _.each( data, function( item, key ) { #>
<ul>
<li class="form">{{ item.name }}</li>
<# _.each( item.fields, function( val, key ) { #>
<li>{{ val }}</li>
<# }) #>
</ul>
<# }) #>
</script>
<script type="text/html" id="tmpl-wpforms-importer-status-error">
<div class="item">
<div class="wpforms-clear">
<span class="name">
<i class="status-icon fa fa-times" aria-hidden="true"></i>
{{ data.name }}
</span>
</div>
<p>{{ data.msg }}</p>
</div>
</script>
<script type="text/html" id="tmpl-wpforms-importer-status-update">
<div class="item">
<div class="wpforms-clear">
<span class="name">
<# if ( ! _.isEmpty( data.upgrade_omit ) ) { #>
<i class="status-icon fa fa-exclamation-circle" aria-hidden="true"></i>
<# } else if ( ! _.isEmpty( data.upgrade_plain ) ) { #>
<i class="status-icon fa fa-exclamation-triangle" aria-hidden="true"></i>
<# } else if ( ! _.isEmpty( data.unsupported ) ) { #>
<i class="status-icon fa fa-info-circle" aria-hidden="true"></i>
<# } else { #>
<i class="status-icon fa fa-check" aria-hidden="true"></i>
<# } #>
{{ data.name }}
</span>
<span class="actions">
<a href="{{ data.edit }}" target="_blank"><?php esc_html_e( 'Edit', 'wpforms-lite' ); ?></a>
<span class="sep">|</span>
<a href="{{ data.preview }}" target="_blank"><?php esc_html_e( 'Preview', 'wpforms-lite' ); ?></a>
</span>
</div>
<# if ( ! _.isEmpty( data.upgrade_omit ) ) { #>
<p><?php esc_html_e( 'The following fields are available in PRO and were not imported:', 'wpforms-lite' ); ?></p>
<ul>
<# _.each( data.upgrade_omit, function( val, key ) { #>
<li>{{ val }}</li>
<# }) #>
</ul>
<# } #>
<# if ( ! _.isEmpty( data.upgrade_plain ) ) { #>
<p><?php esc_html_e( 'The following fields are available in PRO and were imported as text fields:', 'wpforms-lite' ); ?></p>
<ul>
<# _.each( data.upgrade_plain, function( val, key ) { #>
<li>{{ val }}</li>
<# }) #>
</ul>
<# } #>
<# if ( ! _.isEmpty( data.unsupported ) ) { #>
<p><?php esc_html_e( 'The following fields are not supported and were not imported:', 'wpforms-lite' ); ?></p>
<ul>
<# _.each( data.unsupported, function( val, key ) { #>
<li>{{ val }}</li>
<# }) #>
</ul>
<# } #>
<# if ( ! _.isEmpty( data.upgrade_plain ) || ! _.isEmpty( data.upgrade_omit ) ) { #>
<p>
<?php esc_html_e( 'Upgrade to the PRO plan to import these fields.', 'wpforms-lite' ); ?><br><br>
<a href="<?php echo esc_url( wpforms_admin_upgrade_link( 'tools-import' ) ); ?>" class="wpforms-btn wpforms-btn-orange wpforms-btn-md wpforms-upgrade-modal" target="_blank" rel="noopener noreferrer">
<?php esc_html_e( 'Upgrade Now', 'wpforms-lite' ); ?>
</a>
</p>
<# } #>
</div>
</script>
<?php
}
}
Admin/Tools/Views/Export.php 0000644 00000021202 15133255232 0011761 0 ustar 00 <?php
namespace WPForms\Admin\Tools\Views;
/**
* Class Export.
*
* @since 1.6.6
*/
class Export extends View {
/**
* View slug.
*
* @since 1.6.6
*
* @var string
*/
protected $slug = 'export';
/**
* Template code if generated.
*
* @since 1.6.6
*
* @var string
*/
private $template = '';
/**
* Existed forms.
*
* @since 1.6.6
*
* @var []
*/
private $forms = [];
/**
* Init view.
*
* @since 1.6.6
*/
public function init() {
add_action( 'wpforms_tools_init', [ $this, 'process' ] );
}
/**
* Get view label.
*
* @since 1.6.6
*
* @return string
*/
public function get_label() {
return esc_html__( 'Export', 'wpforms-lite' );
}
/**
* Export process.
*
* @since 1.6.6
*/
public function process() {
if (
empty( $_POST['action'] ) || //phpcs:ignore WordPress.Security.NonceVerification
! isset( $_POST['submit-export'] ) || //phpcs:ignore WordPress.Security.NonceVerification
! $this->verify_nonce()
) {
return;
}
if ( $_POST['action'] === 'export_form' && ! empty( $_POST['forms'] ) ) { //phpcs:ignore WordPress.Security.NonceVerification
$this->process_form();
}
if ( $_POST['action'] === 'export_template' && ! empty( $_POST['form'] ) ) { //phpcs:ignore WordPress.Security.NonceVerification
$this->process_template();
}
}
/**
* Checking user capability to view.
*
* @since 1.6.6
*
* @return bool
*/
public function check_capability() {
return wpforms_current_user_can( [ 'edit_forms', 'view_entries' ] );
}
/**
* Get available forms.
*
* @since 1.6.6
*
* @return array
*/
public function get_forms() {
$forms = wpforms()->form->get( '', [ 'orderby' => 'title' ] );
return ! empty( $forms ) ? $forms : [];
}
/**
* Export view content.
*
* @since 1.6.6
*/
public function display() {
$this->forms = $this->get_forms();
if ( empty( $this->forms ) ) {
echo wpforms_render( 'admin/empty-states/no-forms' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
return;
}
do_action( 'wpforms_admin_tools_export_top' );
$this->forms_export_block();
$this->form_template_export_block();
do_action( 'wpforms_admin_tools_export_bottom' );
}
/**
* Forms export block.
*
* @since 1.6.6
*/
private function forms_export_block() {
?>
<div class="wpforms-setting-row tools">
<h4 id="form-export"><?php esc_html_e( 'Form Export', 'wpforms-lite' ); ?></h4>
<p><?php esc_html_e( 'Form exports files can be used to create a backup of your forms or to import forms into another site.', 'wpforms-lite' ); ?></p>
<?php if ( ! empty( $this->forms ) ) { ?>
<form method="post" action="<?php echo esc_attr( $this->get_link() ); ?>">
<?php $this->forms_select_html( 'wpforms-tools-form-export', 'forms[]', esc_html__( 'Select Form(s)', 'wpforms-lite' ) ); ?>
<br>
<input type="hidden" name="action" value="export_form">
<?php $this->nonce_field(); ?>
<button name="submit-export" class="wpforms-btn wpforms-btn-md wpforms-btn-orange">
<?php esc_html_e( 'Export', 'wpforms-lite' ); ?>
</button>
</form>
<?php } else { ?>
<p><?php esc_html_e( 'You need to create a form before you can use form export.', 'wpforms-lite' ); ?></p>
<?php } ?>
</div>
<?php
}
/**
* Forms export block.
*
* @since 1.6.6
*/
private function form_template_export_block() {
?>
<div class="wpforms-setting-row tools">
<h4 id="template-export"><?php esc_html_e( 'Form Template Export', 'wpforms-lite' ); ?></h4>
<?php
if ( $this->template ) {
$doc_link = sprintf(
wp_kses( /* translators: %s - WPForms.com docs URL. */
__( 'For more information <a href="%s" target="_blank" rel="noopener noreferrer">see our documentation</a>.', 'wpforms-lite' ),
[
'a' => [
'href' => [],
'target' => [],
'rel' => [],
],
]
),
'https://wpforms.com/docs/how-to-create-a-custom-form-template/'
);
?>
<p><?php esc_html_e( 'The following code can be used to register your custom form template. Copy and paste the following code to your theme\'s functions.php file or include it within an external file.', 'wpforms-lite' ); ?><p>
<p><?php echo $doc_link; //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?><p>
<textarea class="info-area" readonly><?php echo esc_textarea( $this->template ); ?></textarea><br>
<?php
}
?>
<p><?php esc_html_e( 'Select a form to generate PHP code that can be used to register a custom form template.', 'wpforms-lite' ); ?></p>
<?php if ( ! empty( $this->forms ) ) { ?>
<form method="post" action="<?php echo esc_attr( $this->get_link() ); ?>">
<?php $this->forms_select_html( 'wpforms-tools-form-template', 'form', esc_html__( 'Select a Template', 'wpforms-lite' ), false ); ?>
<br>
<input type="hidden" name="action" value="export_template">
<?php $this->nonce_field(); ?>
<button name="submit-export" class="wpforms-btn wpforms-btn-md wpforms-btn-orange">
<?php esc_html_e( 'Export Template', 'wpforms-lite' ); ?>
</button>
</form>
<?php } else { ?>
<p><?php esc_html_e( 'You need to create a form before you can generate a template.', 'wpforms-lite' ); ?></p>
<?php } ?>
</div>
<?php
}
/**
* Forms selector.
*
* @since 1.6.6
*
* @param string $select_id Select id.
* @param string $select_name Select name.
* @param string $placeholder Placeholder.
* @param bool $multiple Is multiple select.
*/
private function forms_select_html( $select_id, $select_name, $placeholder, $multiple = true ) {
?>
<span class="choicesjs-select-wrap">
<select id="<?php echo esc_attr( $select_id ); ?>" class="choicesjs-select" name="<?php echo esc_attr( $select_name ); ?>" <?php if ( $multiple ) { //phpcs:ignore ?> multiple <?php } ?> data-search="true">
<option value=""><?php echo esc_attr( $placeholder ); ?></option>
<?php foreach ( $this->forms as $form ) { ?>
<option value="<?php echo absint( $form->ID ); ?>"><?php echo esc_html( $form->post_title ); ?></option>
<?php } ?>
</select>
</span>
<?php
}
/**
* Export processing.
*
* @since 1.6.6
*/
private function process_form() {
$export = [];
$forms = get_posts(
[
'post_type' => 'wpforms',
'nopaging' => true,
'post__in' => isset( $_POST['forms'] ) ? array_map( 'intval', $_POST['forms'] ) : [], //phpcs:ignore WordPress.Security.NonceVerification
]
);
foreach ( $forms as $form ) {
$export[] = wpforms_decode( $form->post_content );
}
ignore_user_abort( true );
wpforms_set_time_limit();
nocache_headers();
header( 'Content-Type: application/json; charset=utf-8' );
header( 'Content-Disposition: attachment; filename=wpforms-form-export-' . current_time( 'm-d-Y' ) . '.json' );
header( 'Expires: 0' );
echo wp_json_encode( $export );
exit;
}
/**
* Export template processing.
*
* @since 1.6.6
*/
private function process_template() {
$form_data = false;
if ( isset( $_POST['form'] ) ) { //phpcs:ignore WordPress.Security.NonceVerification
$form_data = wpforms()->form->get(
absint( $_POST['form'] ), //phpcs:ignore WordPress.Security.NonceVerification
[ 'content_only' => true ]
);
}
if ( ! $form_data ) {
return;
}
// Define basic data.
$name = sanitize_text_field( $form_data['settings']['form_title'] );
$desc = sanitize_text_field( $form_data['settings']['form_desc'] );
$slug = sanitize_key( str_replace( [ ' ', '-' ], '_', $form_data['settings']['form_title'] ) );
$class = 'WPForms_Template_' . $slug;
// Format template field and settings data.
$data = $form_data;
$data['meta']['template'] = $slug;
$data['fields'] = isset( $data['fields'] ) ? wpforms_array_remove_empty_strings( $data['fields'] ) : [];
$data['settings'] = wpforms_array_remove_empty_strings( $data['settings'] );
unset( $data['id'] );
$data = var_export( $data, true ); //phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_var_export
$data = str_replace( ' ', "\t", $data );
$data = preg_replace( '/([\t\r\n]+?)array/', 'array', $data );
// Build the final template string.
$this->template = <<<EOT
if ( class_exists( 'WPForms_Template', false ) ) :
/**
* {$name}
* Template for WPForms.
*/
class {$class} extends WPForms_Template {
/**
* Primary class constructor.
*
* @since 1.0.0
*/
public function init() {
// Template name
\$this->name = '{$name}';
// Template slug
\$this->slug = '{$slug}';
// Template description
\$this->description = '{$desc}';
// Template field and settings
\$this->data = {$data};
}
}
new {$class}();
endif;
EOT;
}
}
Admin/Tools/Views/View.php 0000644 00000003244 15133255232 0011420 0 ustar 00 <?php
namespace WPForms\Admin\Tools\Views;
use WPForms\Admin\Tools\Tools;
/**
* Single view class.
*
* @since 1.6.6
*/
abstract class View {
/**
* View slug.
*
* @since 1.6.6
*
* @var string
*/
protected $slug;
/**
* Init view.
*
* @since 1.6.6
*/
abstract public function init();
/**
* Get link to the view.
*
* @since 1.6.6
*
* @return string
*/
public function get_link() {
return add_query_arg(
[
'page' => Tools::SLUG,
'view' => $this->slug,
],
admin_url( 'admin.php' )
);
}
/**
* Get view label.
*
* @since 1.6.6
*
* @return string
*/
abstract public function get_label();
/**
* Checking user capability to view.
*
* @since 1.6.6
*
* @return bool
*/
abstract public function check_capability();
/**
* Checking if needs display in navigation.
*
* @since 1.6.6
*
* @return bool
*/
public function hide_from_nav() {
return false;
}
/**
* Checking if navigation needs display.
*
* @since 1.6.6
*
* @return bool
*/
public function show_nav() {
return true;
}
/**
* Display nonce field.
*
* @since 1.6.6
*/
public function nonce_field() {
wp_nonce_field( 'wpforms_' . $this->slug . '_nonce', 'wpforms-tools-' . $this->slug . '-nonce' );
}
/**
* Verify nonce field.
*
* @since 1.6.6
*/
public function verify_nonce() {
return ! empty( $_POST[ 'wpforms-tools-' . $this->slug . '-nonce' ] ) && wp_verify_nonce( sanitize_key( wp_unslash( $_POST[ 'wpforms-tools-' . $this->slug . '-nonce' ] ) ), 'wpforms_' . $this->slug . '_nonce' );
}
/**
* Display view content.
*
* @since 1.6.6
*/
abstract public function display();
}
Admin/Tools/Views/Logs.php 0000644 00000017170 15133255232 0011415 0 ustar 00 <?php
namespace WPForms\Admin\Tools\Views;
use WPForms\Logger\Log;
/**
* Class Logs.
*
* @since 1.6.6
*/
class Logs extends View {
/**
* View slug.
*
* @since 1.6.6
*
* @var string
*/
protected $slug = 'logs';
/**
* ListTable instance.
*
* @since 1.6.6
*
* @var \WPForms\Logger\ListTable
*/
private $list_table = [];
/**
* Init view.
*
* @since 1.6.6
*/
public function init() {
$this->logs_controller();
}
/**
* Get view label.
*
* @since 1.6.6
*
* @return string
*/
public function get_label() {
return esc_html__( 'Logs', 'wpforms-lite' );
}
/**
* Checking user capability to view.
*
* @since 1.6.6
*
* @return bool
*/
public function check_capability() {
return wpforms_current_user_can();
}
/**
* Get ListTable instance.
*
* @since 1.6.6
*
* @return \WPForms\Logger\ListTable
*/
private function get_list_table() {
if ( empty( $this->list_table ) ) {
$this->list_table = wpforms()->get( 'log' )->get_list_table();
}
return $this->list_table;
}
/**
* Display view content.
*
* @since 1.6.6
*/
public function display() {
?>
<form action="<?php echo esc_url( $this->get_link() ); ?>" method="POST">
<?php $this->nonce_field(); ?>
<div class="wpforms-setting-row tools">
<h3><?php esc_html_e( 'Logs', 'wpforms-lite' ); ?></h3>
<p><?php esc_html_e( 'On this page, you can enable and configure the logging functionality while debugging behavior of various parts of the plugin, including forms and entries processing.', 'wpforms-lite' ); ?></p>
</div>
<div class="wpforms-setting-row tools wpforms-setting-row-checkbox wpforms-clear"
id="wpforms-setting-row-logs-enable">
<div class="wpforms-setting-label">
<label
for="wpforms-setting-logs-enable"><?php esc_html_e( 'Enable Logs', 'wpforms-lite' ); ?></label>
</div>
<div class="wpforms-setting-field">
<input type="checkbox" id="wpforms-setting-logs-enable" name="logs-enable" value="1"
<?php checked( wpforms_setting( 'logs-enable' ) ); ?>>
<p class="desc">
<?php esc_html_e( 'Check this option to start logging WPForms-related events. This is recommended only while debugging.', 'wpforms-lite' ); ?>
</p>
</div>
</div>
<?php
if ( wpforms_setting( 'logs-enable' ) ) {
$this->types_block();
$this->user_roles_block();
$this->users_block();
}
?>
<p class="submit">
<button class="wpforms-btn wpforms-btn-md wpforms-btn-orange" name="wpforms-settings-submit">
<?php esc_html_e( 'Save Settings', 'wpforms-lite' ); ?>
</button>
</p>
</form>
<?php
$logs_list_table = $this->get_list_table();
if ( wpforms_setting( 'logs-enable' ) || $logs_list_table->get_total() ) {
$logs_list_table->display_page();
}
}
/**
* Types block.
*
* @since 1.6.6
*/
private function types_block() {
?>
<div class="wpforms-setting-row tools wpforms-setting-row-select wpforms-clear"
id="wpforms-setting-row-log-types">
<div class="wpforms-setting-label">
<label for="wpforms-setting-logs-types"><?php esc_html_e( 'Log Types', 'wpforms-lite' ); ?></label>
</div>
<div class="wpforms-setting-field">
<span class="choicesjs-select-wrap">
<select id="wpforms-setting-logs-types" class="choicesjs-select" name="logs-types[]" multiple>
<?php
$log_types = wpforms_setting( 'logs-types', [] );
foreach ( Log::get_log_types() as $slug => $name ) {
?>
<option value="<?php echo esc_attr( $slug ); ?>" <?php selected( in_array( $slug, $log_types, true ) ); ?> >
<?php echo esc_html( $name ); ?>
</option>
<?php } ?>
</select>
</span>
<p class="desc"><?php esc_html_e( 'Select the types of events you want to log. Everything is logged by default.', 'wpforms-lite' ); ?></p>
</div>
</div>
<?php
}
/**
* User roles block.
*
* @since 1.6.6
*/
private function user_roles_block() {
?>
<div class="wpforms-setting-row tools wpforms-setting-row-select wpforms-clear"
id="wpforms-setting-row-log-user-roles">
<div class="wpforms-setting-label">
<label for="wpforms-setting-logs-user-roles"><?php esc_html_e( 'User Roles', 'wpforms-lite' ); ?></label>
</div>
<div class="wpforms-setting-field">
<span class="choicesjs-select-wrap">
<?php
$logs_user_roles = wpforms_setting( 'logs-user-roles', [] );
$roles = wp_list_pluck( get_editable_roles(), 'name' );
?>
<select id="wpforms-setting-logs-user-roles" class="choicesjs-select" name="logs-user-roles[]" multiple>
<?php foreach ( $roles as $slug => $name ) { ?>
<option value="<?php echo esc_attr( $slug ); ?>" <?php selected( in_array( $slug, $logs_user_roles, true ) ); ?> >
<?php echo esc_html( $name ); ?>
</option>
<?php } ?>
</select>
<span class="hidden" id="wpforms-setting-logs-user-roles-selectform-spinner">
<i class="fa fa-cog fa-spin fa-lg"></i>
</span>
</span>
<p class="desc">
<?php esc_html_e( 'Select the user roles you want to log. All roles are logged by default.', 'wpforms-lite' ); ?>
</p>
</div>
</div>
<?php
}
/**
* Users block.
*
* @since 1.6.6
*/
private function users_block() {
?>
<div class="wpforms-setting-row tools wpforms-setting-row-select wpforms-clear"
id="wpforms-setting-row-log-users">
<div class="wpforms-setting-label">
<label for="wpforms-setting-logs-users"><?php esc_html_e( 'Users', 'wpforms-lite' ); ?></label>
</div>
<div class="wpforms-setting-field">
<span class="choicesjs-select-wrap">
<select id="wpforms-setting-logs-users" class="choicesjs-select" name="logs-users[]" multiple>
<?php
$users = get_users( [ 'fields' => [ 'ID', 'display_name' ] ] );
$users = wp_list_pluck( $users, 'display_name', 'ID' );
$logs_users = wpforms_setting( 'logs-users', [] );
foreach ( $users as $slug => $name ) {
?>
<option value="<?php echo esc_attr( $slug ); ?>" <?php selected( in_array( $slug, $logs_users, true ) ); ?> >
<?php echo esc_html( $name ); ?>
</option>
<?php } ?>
</select>
<span class="hidden" id="wpforms-setting-logs-users-selectform-spinner">
<i class="fa fa-cog fa-spin fa-lg"></i>
</span>
</span>
<p class="desc">
<?php esc_html_e( 'Log events for specific users only. All users are logged by default.', 'wpforms-lite' ); ?>
</p>
</div>
</div>
<?php
}
/**
* Controller.
*
* @since 1.6.6
*/
private function logs_controller() {
$log = wpforms()->get( 'log' );
$log->create_table();
if ( $this->verify_nonce() ) {
$settings = get_option( 'wpforms_settings' );
$was_enabled = ! empty( $settings['logs-enable'] ) ? $settings['logs-enable'] : 0;
$settings['logs-enable'] = filter_input( INPUT_POST, 'logs-enable', FILTER_VALIDATE_BOOLEAN );
$logs_types = filter_input( INPUT_POST, 'logs-types', FILTER_SANITIZE_FULL_SPECIAL_CHARS, FILTER_REQUIRE_ARRAY );
$logs_user_roles = filter_input( INPUT_POST, 'logs-user-roles', FILTER_SANITIZE_FULL_SPECIAL_CHARS, FILTER_REQUIRE_ARRAY );
$logs_users = filter_input( INPUT_POST, 'logs-users', FILTER_SANITIZE_NUMBER_INT, FILTER_REQUIRE_ARRAY );
if ( $was_enabled ) {
$settings['logs-types'] = $logs_types ? $logs_types : [];
$settings['logs-user-roles'] = $logs_user_roles ? $logs_user_roles : [];
$settings['logs-users'] = $logs_users ? array_map( 'absint', $logs_users ) : [];
}
wpforms_update_settings( $settings );
}
$logs_list_table = $this->get_list_table();
$logs_list_table->process_admin_ui();
}
}
Admin/Tools/Views/System.php 0000644 00000030121 15133255232 0011764 0 ustar 00 <?php
namespace WPForms\Admin\Tools\Views;
/**
* Class System.
*
* @since 1.6.6
*/
class System extends View {
/**
* View slug.
*
* @since 1.6.6
*
* @var string
*/
protected $slug = 'system';
/**
* Init view.
*
* @since 1.6.6
*/
public function init() {}
/**
* Get view label.
*
* @since 1.6.6
*
* @return string
*/
public function get_label() {
return esc_html__( 'System Info', 'wpforms-lite' );
}
/**
* Checking user capability to view.
*
* @since 1.6.6
*
* @return bool
*/
public function check_capability() {
return wpforms_current_user_can();
}
/**
* System view content.
*
* @since 1.6.6
*/
public function display() {
?>
<div class="wpforms-setting-row tools">
<h4 id="form-export"><?php esc_html_e( 'System Information', 'wpforms-lite' ); ?></h4>
<textarea class="info-area" readonly><?php echo esc_textarea( $this->get_system_info() ); ?></textarea>
</div>
<div class="wpforms-setting-row tools">
<h4 id="ssl-verify"><?php esc_html_e( 'Test SSL Connections', 'wpforms-lite' ); ?></h4>
<p><?php esc_html_e( 'Click the button below to verify your web server can perform SSL connections successfully.', 'wpforms-lite' ); ?></p>
<button type="button" id="wpforms-ssl-verify" class="wpforms-btn wpforms-btn-md wpforms-btn-orange">
<?php esc_html_e( 'Test Connection', 'wpforms-lite' ); ?>
</button>
</div>
<?php
}
/**
* Get system information.
*
* Based on a function from Easy Digital Downloads by Pippin Williamson.
*
* @link https://github.com/easydigitaldownloads/easy-digital-downloads/blob/master/includes/admin/tools.php#L470
*
* @since 1.6.6
*
* @return string
*/
public function get_system_info() {
$data = '### Begin System Info ###' . "\n\n";
$data .= $this->wpforms_info();
$data .= $this->site_info();
$data .= $this->wp_info();
$data .= $this->uploads_info();
$data .= $this->plugins_info();
$data .= $this->server_info();
$data .= "\n" . '### End System Info ###';
return $data;
}
/**
* Get WPForms info.
*
* @since 1.6.6
*
* @return string
*/
private function wpforms_info() {
$activated = get_option( 'wpforms_activated', [] );
$data = '-- WPForms Info' . "\n\n";
if ( ! empty( $activated['pro'] ) ) {
$date = $activated['pro'] + ( get_option( 'gmt_offset' ) * 3600 );
$data .= 'Pro: ' . date_i18n( 'M j, Y @ g:ia', $date ) . "\n";
}
if ( ! empty( $activated['lite'] ) ) {
$date = $activated['lite'] + ( get_option( 'gmt_offset' ) * 3600 );
$data .= 'Lite: ' . date_i18n( 'M j, Y @ g:ia', $date ) . "\n";
}
$data .= 'Lite Connect: ' . $this->get_lite_connect_info() . "\n";
return $data;
}
/**
* Get Site info.
*
* @since 1.6.6
*
* @return string
*/
private function site_info() {
$data = "\n" . '-- Site Info' . "\n\n";
$data .= 'Site URL: ' . site_url() . "\n";
$data .= 'Home URL: ' . home_url() . "\n";
$data .= 'Multisite: ' . ( is_multisite() ? 'Yes' : 'No' ) . "\n";
return $data;
}
/**
* Get WordPress Configuration info.
*
* @since 1.6.6
*
* @return string
*/
private function wp_info() {
global $wpdb;
$theme_data = wp_get_theme();
$theme = $theme_data->name . ' ' . $theme_data->version;
$data = "\n" . '-- WordPress Configuration' . "\n\n";
$data .= 'Version: ' . get_bloginfo( 'version' ) . "\n";
$data .= 'Language: ' . get_locale() . "\n";
$data .= 'User Language: ' . get_user_locale() . "\n";
$data .= 'Permalink Structure: ' . ( get_option( 'permalink_structure' ) ? get_option( 'permalink_structure' ) : 'Default' ) . "\n";
$data .= 'Active Theme: ' . $theme . "\n";
$data .= 'Show On Front: ' . get_option( 'show_on_front' ) . "\n";
// Only show page specs if front page is set to 'page'.
if ( get_option( 'show_on_front' ) === 'page' ) {
$front_page_id = get_option( 'page_on_front' );
$blog_page_id = get_option( 'page_for_posts' );
$data .= 'Page On Front: ' . ( $front_page_id ? get_the_title( $front_page_id ) . ' (#' . $front_page_id . ')' : 'Unset' ) . "\n";
$data .= 'Page For Posts: ' . ( $blog_page_id ? get_the_title( $blog_page_id ) . ' (#' . $blog_page_id . ')' : 'Unset' ) . "\n";
}
$data .= 'ABSPATH: ' . ABSPATH . "\n";
$data .= 'Table Prefix: ' . 'Length: ' . strlen( $wpdb->prefix ) . ' Status: ' . ( strlen( $wpdb->prefix ) > 16 ? 'ERROR: Too long' : 'Acceptable' ) . "\n"; //phpcs:ignore
$data .= 'WP_DEBUG: ' . ( defined( 'WP_DEBUG' ) ? WP_DEBUG ? 'Enabled' : 'Disabled' : 'Not set' ) . "\n";
$data .= 'WPFORMS_DEBUG: ' . ( defined( 'WPFORMS_DEBUG' ) ? WPFORMS_DEBUG ? 'Enabled' : 'Disabled' : 'Not set' ) . "\n";
$data .= 'Memory Limit: ' . WP_MEMORY_LIMIT . "\n";
$data .= 'Registered Post Stati: ' . implode( ', ', get_post_stati() ) . "\n";
$data .= 'Revisions: ' . ( WP_POST_REVISIONS ? WP_POST_REVISIONS > 1 ? 'Limited to ' . WP_POST_REVISIONS : 'Enabled' : 'Disabled' ) . "\n";
return $data;
}
/**
* Get Uploads/Constants info.
*
* @since 1.6.6
*
* @return string
*/
private function uploads_info() {
// @todo WPForms configuration/specific details.
$data = "\n" . '-- WordPress Uploads/Constants' . "\n\n";
$data .= 'WP_CONTENT_DIR: ' . ( defined( 'WP_CONTENT_DIR' ) ? WP_CONTENT_DIR ? WP_CONTENT_DIR : 'Disabled' : 'Not set' ) . "\n";
$data .= 'WP_CONTENT_URL: ' . ( defined( 'WP_CONTENT_URL' ) ? WP_CONTENT_URL ? WP_CONTENT_URL : 'Disabled' : 'Not set' ) . "\n";
$data .= 'UPLOADS: ' . ( defined( 'UPLOADS' ) ? UPLOADS ? UPLOADS : 'Disabled' : 'Not set' ) . "\n";
$uploads_dir = wp_upload_dir();
$data .= 'wp_uploads_dir() path: ' . $uploads_dir['path'] . "\n";
$data .= 'wp_uploads_dir() url: ' . $uploads_dir['url'] . "\n";
$data .= 'wp_uploads_dir() basedir: ' . $uploads_dir['basedir'] . "\n";
$data .= 'wp_uploads_dir() baseurl: ' . $uploads_dir['baseurl'] . "\n";
return $data;
}
/**
* Get Plugins info.
*
* @since 1.6.6
*
* @return string
*/
private function plugins_info() {
// Get plugins that have an update.
$data = $this->mu_plugins();
$data .= $this->installed_plugins();
$data .= $this->multisite_plugins();
return $data;
}
/**
* Get MU Plugins info.
*
* @since 1.6.6
*
* @return string
*/
private function mu_plugins() {
$data = '';
// Must-use plugins.
// NOTE: MU plugins can't show updates!
$muplugins = get_mu_plugins();
if ( ! empty( $muplugins ) && count( $muplugins ) > 0 ) {
$data = "\n" . '-- Must-Use Plugins' . "\n\n";
foreach ( $muplugins as $plugin => $plugin_data ) {
$data .= $plugin_data['Name'] . ': ' . $plugin_data['Version'] . "\n";
}
}
return $data;
}
/**
* Get Installed Plugins info.
*
* @since 1.6.6
*
* @return string
*/
private function installed_plugins() {
$updates = get_plugin_updates();
// WordPress active plugins.
$data = "\n" . '-- WordPress Active Plugins' . "\n\n";
$plugins = get_plugins();
$active_plugins = get_option( 'active_plugins', [] );
foreach ( $plugins as $plugin_path => $plugin ) {
if ( ! in_array( $plugin_path, $active_plugins, true ) ) {
continue;
}
$update = ( array_key_exists( $plugin_path, $updates ) ) ? ' (needs update - ' . $updates[ $plugin_path ]->update->new_version . ')' : '';
$data .= $plugin['Name'] . ': ' . $plugin['Version'] . $update . "\n";
}
// WordPress inactive plugins.
$data .= "\n" . '-- WordPress Inactive Plugins' . "\n\n";
foreach ( $plugins as $plugin_path => $plugin ) {
if ( in_array( $plugin_path, $active_plugins, true ) ) {
continue;
}
$update = ( array_key_exists( $plugin_path, $updates ) ) ? ' (needs update - ' . $updates[ $plugin_path ]->update->new_version . ')' : '';
$data .= $plugin['Name'] . ': ' . $plugin['Version'] . $update . "\n";
}
return $data;
}
/**
* Get Multisite Plugins info.
*
* @since 1.6.6
*
* @return string
*/
private function multisite_plugins() {
$data = '';
if ( ! is_multisite() ) {
return $data;
}
$updates = get_plugin_updates();
// WordPress Multisite active plugins.
$data = "\n" . '-- Network Active Plugins' . "\n\n";
$plugins = wp_get_active_network_plugins();
$active_plugins = get_site_option( 'active_sitewide_plugins', [] );
foreach ( $plugins as $plugin_path ) {
$plugin_base = plugin_basename( $plugin_path );
if ( ! array_key_exists( $plugin_base, $active_plugins ) ) {
continue;
}
$update = ( array_key_exists( $plugin_path, $updates ) ) ? ' (needs update - ' . $updates[ $plugin_path ]->update->new_version . ')' : '';
$plugin = get_plugin_data( $plugin_path );
$data .= $plugin['Name'] . ': ' . $plugin['Version'] . $update . "\n";
}
return $data;
}
/**
* Get Server info.
*
* @since 1.6.6
*
* @return string
*/
private function server_info() {
global $wpdb;
// Server configuration (really just versions).
$data = "\n" . '-- Webserver Configuration' . "\n\n";
$data .= 'PHP Version: ' . PHP_VERSION . "\n";
$data .= 'MySQL Version: ' . $wpdb->db_version() . "\n";
$data .= 'Webserver Info: ' . ( isset( $_SERVER['SERVER_SOFTWARE'] ) ? sanitize_text_field( wp_unslash( $_SERVER['SERVER_SOFTWARE'] ) ) : '' ) . "\n";
// PHP configs... now we're getting to the important stuff.
$data .= "\n" . '-- PHP Configuration' . "\n\n";
$data .= 'Memory Limit: ' . ini_get( 'memory_limit' ) . "\n";
$data .= 'Upload Max Size: ' . ini_get( 'upload_max_filesize' ) . "\n";
$data .= 'Post Max Size: ' . ini_get( 'post_max_size' ) . "\n";
$data .= 'Upload Max Filesize: ' . ini_get( 'upload_max_filesize' ) . "\n";
$data .= 'Time Limit: ' . ini_get( 'max_execution_time' ) . "\n";
$data .= 'Max Input Vars: ' . ini_get( 'max_input_vars' ) . "\n";
$data .= 'Display Errors: ' . ( ini_get( 'display_errors' ) ? 'On (' . ini_get( 'display_errors' ) . ')' : 'N/A' ) . "\n";
// PHP extensions and such.
$data .= "\n" . '-- PHP Extensions' . "\n\n";
$data .= 'cURL: ' . ( function_exists( 'curl_init' ) ? 'Supported' : 'Not Supported' ) . "\n";
$data .= 'fsockopen: ' . ( function_exists( 'fsockopen' ) ? 'Supported' : 'Not Supported' ) . "\n";
$data .= 'SOAP Client: ' . ( class_exists( 'SoapClient', false ) ? 'Installed' : 'Not Installed' ) . "\n";
$data .= 'Suhosin: ' . ( extension_loaded( 'suhosin' ) ? 'Installed' : 'Not Installed' ) . "\n";
// Session stuff.
$data .= "\n" . '-- Session Configuration' . "\n\n";
$data .= 'Session: ' . ( isset( $_SESSION ) ? 'Enabled' : 'Disabled' ) . "\n";
// The rest of this is only relevant if session is enabled.
if ( isset( $_SESSION ) ) {
$data .= 'Session Name: ' . esc_html( ini_get( 'session.name' ) ) . "\n";
$data .= 'Cookie Path: ' . esc_html( ini_get( 'session.cookie_path' ) ) . "\n";
$data .= 'Save Path: ' . esc_html( ini_get( 'session.save_path' ) ) . "\n";
$data .= 'Use Cookies: ' . ( ini_get( 'session.use_cookies' ) ? 'On' : 'Off' ) . "\n";
$data .= 'Use Only Cookies: ' . ( ini_get( 'session.use_only_cookies' ) ? 'On' : 'Off' ) . "\n";
}
return $data;
}
/**
* Get Lite Connect status info string.
*
* @since 1.7.5
*
* @return string
*/
private function get_lite_connect_info() {
$lc_enabled = wpforms_setting( 'lite-connect-enabled' );
$lc_enabled_since = wpforms_setting( 'lite-connect-enabled-since' );
$date = date_i18n( 'M j, Y @ g:ia', $lc_enabled_since + get_option( 'gmt_offset' ) * 3600 );
if ( $lc_enabled ) {
$string = $lc_enabled_since ? 'Backup is enabled since ' . $date : 'Backup is enabled';
} else {
$string = $lc_enabled_since ? 'Backup is not enabled. Previously was enabled since ' . $date : 'Backup is not enabled';
}
return $string;
}
}
Admin/Tools/Views/ActionScheduler.php 0000644 00000002571 15133255232 0013564 0 ustar 00 <?php
namespace WPForms\Admin\Tools\Views;
use ActionScheduler_AdminView;
/**
* Class ActionScheduler view.
*
* @since 1.6.6
*/
class ActionScheduler extends View {
/**
* View slug.
*
* @since 1.6.6
*
* @var string
*/
protected $slug = 'action-scheduler';
/**
* Init view.
*
* @since 1.6.6
*/
public function init() {
if ( $this->admin_view_exists() ) {
ActionScheduler_AdminView::instance()->process_admin_ui();
}
}
/**
* Get link to the view.
*
* @since 1.6.9
*
* @return string
*/
public function get_link() {
return add_query_arg(
[
's' => 'wpforms',
],
parent::get_link()
);
}
/**
* Get view label.
*
* @since 1.6.6
*
* @return string
*/
public function get_label() {
return esc_html__( 'Scheduled Actions', 'wpforms-lite' );
}
/**
* Checking user capability to view.
*
* @since 1.6.6
*
* @return bool
*/
public function check_capability() {
return wpforms_current_user_can();
}
/**
* Display view content.
*
* @since 1.6.6
*/
public function display() {
if ( ! $this->admin_view_exists() ) {
return;
}
( new ActionSchedulerList() )->display_page();
}
/**
* Check if ActionScheduler_AdminView class exists.
*
* @since 1.6.6
*
* @return bool
*/
private function admin_view_exists() {
return class_exists( 'ActionScheduler_AdminView' );
}
}
Admin/Tools/Views/Import.php 0000644 00000017311 15133255232 0011760 0 ustar 00 <?php
namespace WPForms\Admin\Tools\Views;
use WPForms\Helpers\File;
use WPForms\Admin\Tools\Importers;
use WPForms\Admin\Tools\Tools;
use WPForms_Form_Handler;
/**
* Class Import.
*
* @since 1.6.6
*/
class Import extends View {
/**
* View slug.
*
* @since 1.6.6
*
* @var string
*/
protected $slug = 'import';
/**
* Registered importers.
*
* @since 1.6.6
*
* @var array
*/
public $importers = [];
/**
* Checking user capability to view.
*
* @since 1.6.6
*
* @return bool
*/
public function check_capability() {
return wpforms_current_user_can( 'create_forms' );
}
/**
* Init view.
*
* @since 1.6.6
*/
public function init() {
add_action( 'wpforms_tools_init', [ $this, 'import_process' ] );
$this->importers = ( new Importers() )->get_importers();
}
/**
* Get view label.
*
* @since 1.6.6
*
* @return string
*/
public function get_label() {
return esc_html__( 'Import', 'wpforms-lite' );
}
/**
* Import process.
*
* @since 1.6.6
*/
public function import_process() {
if (
empty( $_POST['action'] ) || //phpcs:ignore WordPress.Security.NonceVerification
$_POST['action'] !== 'import_form' || //phpcs:ignore WordPress.Security.NonceVerification
empty( $_FILES['file']['tmp_name'] ) ||
! isset( $_POST['submit-import'] ) || //phpcs:ignore WordPress.Security.NonceVerification
! $this->verify_nonce()
) {
return;
}
$this->process();
}
/**
* Import view content.
*
* @since 1.6.6
*/
public function display() {
$this->success_import_message();
$this->wpforms_block();
$this->other_forms_block();
}
/**
* Success import message.
*
* @since 1.6.6
*/
private function success_import_message() {
if ( isset( $_GET['wpforms_notice'] ) && $_GET['wpforms_notice'] === 'forms-imported' ) { //phpcs:ignore WordPress.Security.NonceVerification.Recommended
?>
<div class="updated notice is-dismissible">
<p>
<?php esc_html_e( 'Import was successfully finished.', 'wpforms-lite' ); ?>
<?php
if ( wpforms_current_user_can( 'view_forms' ) ) {
printf(
wp_kses( /* translators: %s - Forms list page URL. */
__( 'You can go and <a href="%s">check your forms</a>.', 'wpforms-lite' ),
[ 'a' => [ 'href' => [] ] ]
),
esc_url( admin_url( 'admin.php?page=wpforms-overview' ) )
);
}
?>
</p>
</div>
<?php
}
}
/**
* WPForms section.
*
* @since 1.6.6
*/
private function wpforms_block() {
?>
<div class="wpforms-setting-row tools">
<h4><?php esc_html_e( 'WPForms Import', 'wpforms-lite' ); ?></h4>
<p><?php esc_html_e( 'Select a WPForms export file.', 'wpforms-lite' ); ?></p>
<form method="post" enctype="multipart/form-data" action="<?php echo esc_attr( $this->get_link() ); ?>">
<div class="wpforms-file-upload">
<input type="file" name="file" id="wpforms-tools-form-import" class="inputfile"
data-multiple-caption="{count} <?php esc_attr_e( 'files selected', 'wpforms-lite' ); ?>"
accept=".json" />
<label for="wpforms-tools-form-import">
<span class="fld"><span class="placeholder"><?php esc_html_e( 'No file chosen', 'wpforms-lite' ); ?></span></span>
<strong class="wpforms-btn wpforms-btn-md wpforms-btn-light-grey">
<i class="fa fa-upload"></i><?php esc_html_e( 'Choose a file…', 'wpforms-lite' ); ?>
</strong>
</label>
</div>
<br>
<input type="hidden" name="action" value="import_form">
<button name="submit-import" class="wpforms-btn wpforms-btn-md wpforms-btn-orange">
<?php esc_html_e( 'Import', 'wpforms-lite' ); ?>
</button>
<?php $this->nonce_field(); ?>
</form>
</div>
<?php
}
/**
* WPForms section.
*
* @since 1.6.6
*/
private function other_forms_block() {
?>
<div class="wpforms-setting-row tools" id="wpforms-importers">
<h4><?php esc_html_e( 'Import from Other Form Plugins', 'wpforms-lite' ); ?></h4>
<p><?php esc_html_e( 'Not happy with other WordPress contact form plugins?', 'wpforms-lite' ); ?></p>
<p><?php esc_html_e( 'WPForms makes it easy for you to switch by allowing you import your third-party forms with a single click.', 'wpforms-lite' ); ?></p>
<div class="wpforms-importers-wrap">
<?php if ( empty( $this->importers ) ) { ?>
<p><?php esc_html_e( 'No form importers are currently enabled.', 'wpforms-lite' ); ?> </p>
<?php } else { ?>
<form action="<?php echo esc_url( admin_url( 'admin.php' ) ); ?>">
<span class="choicesjs-select-wrap">
<select class="choicesjs-select" name="provider" required>
<option value=""><?php esc_html_e( 'Select previous contact form plugin...', 'wpforms-lite' ); ?></option>
<?php
foreach ( $this->importers as $importer ) {
$status = '';
if ( empty( $importer['installed'] ) ) {
$status = esc_html__( 'Not Installed', 'wpforms-lite' );
} elseif ( empty( $importer['active'] ) ) {
$status = esc_html__( 'Not Active', 'wpforms-lite' );
}
printf(
'<option value="%s" %s>%s %s</option>',
esc_attr( $importer['slug'] ),
! empty( $status ) ? 'disabled' : '',
esc_html( $importer['name'] ),
! empty( $status ) ? '(' . esc_html( $status ) . ')' : '' //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
);
}
?>
</select>
</span>
<br />
<input type="hidden" name="page" value="<?php echo esc_attr( Tools::SLUG ); ?>">
<input type="hidden" name="view" value="importer">
<button class="wpforms-btn wpforms-btn-md wpforms-btn-orange">
<?php esc_html_e( 'Import', 'wpforms-lite' ); ?>
</button>
</form>
<?php } ?>
</div>
</div>
<?php
}
/**
* Import processing.
*
* @since 1.6.6
*/
private function process() {
// Add filter of the link rel attr to avoid JSON damage.
add_filter( 'wp_targeted_link_rel', '__return_empty_string', 50, 1 );
$ext = '';
if ( isset( $_FILES['file']['name'] ) ) {
$ext = strtolower( pathinfo( sanitize_text_field( wp_unslash( $_FILES['file']['name'] ) ), PATHINFO_EXTENSION ) );
}
if ( $ext !== 'json' ) {
wp_die(
esc_html__( 'Please upload a valid .json form export file.', 'wpforms-lite' ),
esc_html__( 'Error', 'wpforms-lite' ),
[
'response' => 400,
]
);
}
$tmp_name = isset( $_FILES['file']['tmp_name'] ) ? sanitize_text_field( $_FILES['file']['tmp_name'] ) : ''; //phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash -- wp_unslash() breaks upload on Windows.
$forms = json_decode( File::remove_utf8_bom( file_get_contents( $tmp_name ) ), true );
if ( empty( $forms ) || ! is_array( $forms ) ) {
wp_die(
esc_html__( 'Form data cannot be imported.', 'wpforms-lite' ),
esc_html__( 'Error', 'wpforms-lite' ),
[
'response' => 400,
]
);
}
foreach ( $forms as $form ) {
$title = ! empty( $form['settings']['form_title'] ) ? $form['settings']['form_title'] : '';
$desc = ! empty( $form['settings']['form_desc'] ) ? $form['settings']['form_desc'] : '';
$new_id = wp_insert_post(
[
'post_title' => $title,
'post_status' => 'publish',
'post_type' => 'wpforms',
'post_excerpt' => $desc,
]
);
if ( $new_id ) {
$form['id'] = $new_id;
wp_update_post(
[
'ID' => $new_id,
'post_content' => wpforms_encode( $form ),
]
);
}
if ( ! empty( $form['settings']['form_tags'] ) ) {
wp_set_post_terms(
$new_id,
implode( ',', (array) $form['settings']['form_tags'] ),
WPForms_Form_Handler::TAGS_TAXONOMY
);
}
}
wp_safe_redirect( add_query_arg( [ 'wpforms_notice' => 'forms-imported' ] ) );
exit;
}
}
Admin/Tools/Views/ActionSchedulerList.php 0000644 00000004160 15133255232 0014414 0 ustar 00 <?php
namespace WPForms\Admin\Tools\Views;
use ActionScheduler as Scheduler;
use ActionScheduler_ListTable;
/**
* Action Scheduler list table.
*
* @since 1.7.6
*/
class ActionSchedulerList extends ActionScheduler_ListTable {
/**
* ActionSchedulerList constructor.
*
* @since 1.7.6
*/
public function __construct() {
parent::__construct(
Scheduler::store(),
Scheduler::logger(),
Scheduler::runner()
);
$this->process_actions();
}
/**
* Display the table heading.
*
* @since 1.7.6
*/
protected function display_header() {
?>
<h1><?php echo esc_html__( 'Scheduled Actions', 'wpforms-lite' ); ?></h1>
<p>
<?php
echo sprintf(
wp_kses( /* translators: %s - Action Scheduler website URL. */
__( 'WPForms is using the <a href="%s" target="_blank" rel="noopener noreferrer">Action Scheduler</a> library, which allows it to queue and process bigger tasks in the background without making your site slower for your visitors. Below you can see the list of all tasks and their status. This table can be very useful when debugging certain issues.', 'wpforms-lite' ),
[
'a' => [
'href' => [],
'rel' => [],
'target' => [],
],
]
),
'https://actionscheduler.org/'
);
?>
</p>
<p>
<?php echo esc_html__( 'Action Scheduler library is also used by other plugins, like WP Mail SMTP and WooCommerce, so you might see tasks that are not related to our plugin in the table below.', 'wpforms-lite' ); ?>
</p>
<?php
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
if ( ! empty( $_GET['s'] ) ) {
?>
<div id="wpforms-reset-filter">
<?php
echo wp_kses(
sprintf( /* translators: %s - search term. */
__( 'Search results for <strong>%s</strong>', 'wpforms-lite' ),
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
sanitize_text_field( wp_unslash( $_GET['s'] ) )
),
[
'strong' => [],
]
);
?>
<a href="<?php echo esc_url( remove_query_arg( 's' ) ); ?>">
<span class="reset fa fa-times-circle"></span>
</a>
</div>
<?php
}
}
}
Admin/Tools/Importers/ContactForm7.php 0000644 00000045331 15133255232 0013706 0 ustar 00 <?php
namespace WPForms\Admin\Tools\Importers;
/**
* Contact Form 7 Importer class.
*
* @since 1.6.6
*/
class ContactForm7 extends Base {
/**
* Define required properties.
*
* @since 1.6.6
*/
public function init() {
$this->name = 'Contact Form 7';
$this->slug = 'contact-form-7';
$this->path = 'contact-form-7/wp-contact-form-7.php';
}
/**
* Get ALL THE FORMS.
*
* @since 1.6.6
*/
public function get_forms() {
$forms_final = [];
if ( ! $this->is_active() ) {
return $forms_final;
}
$forms = \WPCF7_ContactForm::find( [ 'posts_per_page' => - 1 ] );
if ( empty( $forms ) ) {
return $forms_final;
}
foreach ( $forms as $form ) {
if ( ! empty( $form ) && ( $form instanceof \WPCF7_ContactForm ) ) {
$forms_final[ $form->id() ] = $form->title();
}
}
return $forms_final;
}
/**
* Get a single form.
*
* @since 1.6.6
*
* @param int $id Form ID.
*
* @return \WPCF7_ContactForm|bool
*/
public function get_form( $id ) {
$form = \WPCF7_ContactForm::find(
[
'posts_per_page' => 1,
'p' => $id,
]
);
if ( ! empty( $form[0] ) && ( $form[0] instanceof \WPCF7_ContactForm ) ) {
return $form[0];
}
return false;
}
/**
* Import a single form using AJAX.
*
* @since 1.6.6
*/
public function import_form() {
// Run a security check.
check_ajax_referer( 'wpforms-admin', 'nonce' );
// Check for permissions.
if ( ! wpforms_current_user_can( 'create_forms' ) ) {
wp_send_json_error();
}
// Define some basic information.
$analyze = isset( $_POST['analyze'] );
$cf7_id = ! empty( $_POST['form_id'] ) ? (int) $_POST['form_id'] : 0;
$cf7_form = $this->get_form( $cf7_id );
if ( ! $cf7_form ) {
wp_send_json_error(
[
'error' => true,
'name' => esc_html__( 'Unknown Form', 'wpforms-lite' ),
'msg' => esc_html__( 'The form you are trying to import does not exist.', 'wpforms-lite' ),
]
);
}
$cf7_form_name = $cf7_form->title();
$cf7_fields = $cf7_form->scan_form_tags();
$cf7_properties = $cf7_form->get_properties();
$cf7_recaptcha = false;
$fields_pro_plain = [ 'url', 'tel', 'date' ];
$fields_pro_omit = [ 'file' ];
$fields_unsupported = [ 'quiz', 'hidden' ];
$upgrade_plain = [];
$upgrade_omit = [];
$unsupported = [];
$form = [
'id' => '',
'field_id' => '',
'fields' => [],
'settings' => [
'form_title' => $cf7_form_name,
'form_desc' => '',
'submit_text' => esc_html__( 'Submit', 'wpforms-lite' ),
'submit_text_processing' => esc_html__( 'Sending', 'wpforms-lite' ),
'antispam' => '1',
'notification_enable' => '1',
'notifications' => [
1 => [
'notification_name' => esc_html__( 'Notification 1', 'wpforms-lite' ),
'email' => '{admin_email}',
/* translators: %s - form name. */
'subject' => sprintf( esc_html__( 'New Entry: %s', 'wpforms-lite' ), $cf7_form_name ),
'sender_name' => get_bloginfo( 'name' ),
'sender_address' => '{admin_email}',
'replyto' => '',
'message' => '{all_fields}',
],
],
'confirmations' => [
1 => [
'type' => 'message',
'message' => esc_html__( 'Thanks for contacting us! We will be in touch with you shortly.', 'wpforms-lite' ),
'message_scroll' => '1',
],
],
'import_form_id' => $cf7_id,
],
];
// If form does not contain fields, bail.
if ( empty( $cf7_fields ) ) {
wp_send_json_success(
[
'error' => true,
'name' => sanitize_text_field( $cf7_form_name ),
'msg' => esc_html__( 'No form fields found.', 'wpforms-lite' ),
]
);
}
// Convert fields.
foreach ( $cf7_fields as $cf7_field ) {
if ( ! $cf7_field instanceof \WPCF7_FormTag ) {
continue;
}
// Try to determine field label to use.
$label = $this->get_field_label( $cf7_properties['form'], $cf7_field->type, $cf7_field->name );
// Next, check if field is unsupported. If supported make note and
// then continue to the next field.
if ( in_array( $cf7_field->basetype, $fields_unsupported, true ) ) {
$unsupported[] = $label;
continue;
}
// Now check if this install is Lite. If it is Lite and it's a
// field type not included, make a note then continue to the next
// field.
if ( ! wpforms()->is_pro() && in_array( $cf7_field->basetype, $fields_pro_plain, true ) ) {
$upgrade_plain[] = $label;
}
if ( ! wpforms()->is_pro() && in_array( $cf7_field->basetype, $fields_pro_omit, true ) ) {
$upgrade_omit[] = $label;
continue;
}
// Determine next field ID to assign.
if ( empty( $form['fields'] ) ) {
$field_id = 1;
} else {
$field_id = (int) max( array_keys( $form['fields'] ) ) + 1;
}
switch ( $cf7_field->basetype ) {
// Plain text, email, URL, number, and textarea fields.
case 'text':
case 'email':
case 'url':
case 'number':
case 'textarea':
$type = $cf7_field->basetype;
if ( $type === 'url' && ! wpforms()->is_pro() ) {
$type = 'text';
}
$form['fields'][ $field_id ] = [
'id' => $field_id,
'type' => $type,
'label' => $label,
'size' => 'medium',
'required' => $cf7_field->is_required() ? '1' : '',
'placeholder' => $this->get_field_placeholder_default( $cf7_field ),
'default_value' => $this->get_field_placeholder_default( $cf7_field, 'default' ),
'cf7_name' => $cf7_field->name,
];
break;
// Phone number field.
case 'tel':
$form['fields'][ $field_id ] = [
'id' => $field_id,
'type' => 'phone',
'label' => $label,
'format' => 'international',
'size' => 'medium',
'required' => $cf7_field->is_required() ? '1' : '',
'placeholder' => $this->get_field_placeholder_default( $cf7_field ),
'default_value' => $this->get_field_placeholder_default( $cf7_field, 'default' ),
'cf7_name' => $cf7_field->name,
];
break;
// Date field.
case 'date':
$type = wpforms()->is_pro() ? 'date-time' : 'text';
$form['fields'][ $field_id ] = [
'id' => $field_id,
'type' => $type,
'label' => $label,
'format' => 'date',
'size' => 'medium',
'required' => $cf7_field->is_required() ? '1' : '',
'date_placeholder' => '',
'date_format' => 'm/d/Y',
'date_type' => 'datepicker',
'time_format' => 'g:i A',
'time_interval' => 30,
'cf7_name' => $cf7_field->name,
];
break;
// Select, radio, and checkbox fields.
case 'select':
case 'radio':
case 'checkbox':
$choices = [];
$options = (array) $cf7_field->labels;
foreach ( $options as $option ) {
$choices[] = [
'label' => $option,
'value' => '',
];
}
$form['fields'][ $field_id ] = [
'id' => $field_id,
'type' => $cf7_field->basetype,
'label' => $label,
'choices' => $choices,
'size' => 'medium',
'required' => $cf7_field->is_required() ? '1' : '',
'cf7_name' => $cf7_field->name,
];
if (
$cf7_field->basetype === 'select' &&
$cf7_field->has_option( 'include_blank' )
) {
$form['fields'][ $field_id ]['placeholder'] = '---';
}
break;
// File upload field.
case 'file':
$extensions = '';
$max_size = '';
$file_types = $cf7_field->get_option( 'filetypes' );
$limit = $cf7_field->get_option( 'limit' );
if ( ! empty( $file_types[0] ) ) {
$extensions = implode( ',', explode( '|', strtolower( preg_replace( '/[^A-Za-z0-9|]/', '', strtolower( $file_types[0] ) ) ) ) );
}
if ( ! empty( $limit[0] ) ) {
$limit = $limit[0];
$mb = ( strpos( $limit, 'm' ) !== false );
$kb = ( strpos( $limit, 'kb' ) !== false );
$limit = (int) preg_replace( '/[^0-9]/', '', $limit );
if ( $mb ) {
$max_size = $limit;
} elseif ( $kb ) {
$max_size = round( $limit / 1024, 1 );
} else {
$max_size = round( $limit / 1048576, 1 );
}
}
$form['fields'][ $field_id ] = [
'id' => $field_id,
'type' => 'file-upload',
'label' => $label,
'size' => 'medium',
'extensions' => $extensions,
'max_size' => $max_size,
'required' => $cf7_field->is_required() ? '1' : '',
'cf7_name' => $cf7_field->name,
];
break;
// Acceptance field.
case 'acceptance':
$form['fields'][ $field_id ] = [
'id' => $field_id,
'type' => 'checkbox',
'label' => esc_html__( 'Acceptance Field', 'wpforms-lite' ),
'choices' => [
1 => [
'label' => $label,
'value' => '',
],
],
'size' => 'medium',
'required' => '1',
'label_hide' => '1',
'cf7_name' => $cf7_field->name,
];
break;
// ReCAPTCHA field.
case 'recaptcha':
$cf7_recaptcha = true;
}
}
// If we are only analyzing the form, we can stop here and return the
// details about this form.
if ( $analyze ) {
wp_send_json_success(
[
'name' => $cf7_form_name,
'upgrade_plain' => $upgrade_plain,
'upgrade_omit' => $upgrade_omit,
]
);
}
// Settings.
// Confirmation message.
if ( ! empty( $cf7_properties['messages']['mail_sent_ok'] ) ) {
$form['settings']['confirmation_message'] = $cf7_properties['messages']['mail_sent_ok'];
}
// ReCAPTCHA.
if ( $cf7_recaptcha ) {
// If the user has already defined v2 reCAPTCHA keys in the WPForms
// settings, use those.
$site_key = wpforms_setting( 'recaptcha-site-key', '' );
$secret_key = wpforms_setting( 'recaptcha-secret-key', '' );
$type = wpforms_setting( 'recaptcha-type', 'v2' );
// Try to abstract keys from CF7.
if ( empty( $site_key ) || empty( $secret_key ) ) {
$cf7_settings = get_option( 'wpcf7' );
if (
! empty( $cf7_settings['recaptcha'] ) &&
is_array( $cf7_settings['recaptcha'] )
) {
foreach ( $cf7_settings['recaptcha'] as $key => $val ) {
if ( ! empty( $key ) && ! empty( $val ) ) {
$site_key = $key;
$secret_key = $val;
}
}
$wpforms_settings = get_option( 'wpforms_settings', [] );
$wpforms_settings['recaptcha-site-key'] = $site_key;
$wpforms_settings['recaptcha-secret-key'] = $secret_key;
update_option( 'wpforms_settings', $wpforms_settings );
}
}
// Don't enable reCAPTCHA if user had configured invisible reCAPTCHA.
if (
$type === 'v2' &&
! empty( $site_key ) &&
! empty( $secret_key )
) {
$form['settings']['recaptcha'] = '1';
}
}
// Setup email notifications.
if ( ! empty( $cf7_properties['mail']['subject'] ) ) {
$form['settings']['notifications'][1]['subject'] = $this->get_smarttags( $cf7_properties['mail']['subject'], $form['fields'] );
}
if ( ! empty( $cf7_properties['mail']['recipient'] ) ) {
$form['settings']['notifications'][1]['email'] = $this->get_smarttags( $cf7_properties['mail']['recipient'], $form['fields'] );
}
if ( ! empty( $cf7_properties['mail']['body'] ) ) {
$form['settings']['notifications'][1]['message'] = $this->get_smarttags( $cf7_properties['mail']['body'], $form['fields'] );
}
if ( ! empty( $cf7_properties['mail']['additional_headers'] ) ) {
$form['settings']['notifications'][1]['replyto'] = $this->get_replyto( $cf7_properties['mail']['additional_headers'], $form['fields'] );
}
if ( ! empty( $cf7_properties['mail']['sender'] ) ) {
$sender = $this->get_sender_details( $cf7_properties['mail']['sender'], $form['fields'] );
if ( $sender ) {
$form['settings']['notifications'][1]['sender_name'] = $sender['name'];
$form['settings']['notifications'][1]['sender_address'] = $sender['address'];
}
}
if ( ! empty( $cf7_properties['mail_2'] ) && (int) $cf7_properties['mail_2']['active'] === 1 ) {
// Check if a secondary notification is enabled, if so set defaults
// and set it up.
$form['settings']['notifications'][2] = [
'notification_name' => esc_html__( 'Notification 2', 'wpforms-lite' ),
'email' => '{admin_email}',
/* translators: %s - form name. */
'subject' => sprintf( esc_html__( 'New Entry: %s', 'wpforms-lite' ), $cf7_form_name ),
'sender_name' => get_bloginfo( 'name' ),
'sender_address' => '{admin_email}',
'replyto' => '',
'message' => '{all_fields}',
];
if ( ! empty( $cf7_properties['mail_2']['subject'] ) ) {
$form['settings']['notifications'][2]['subject'] = $this->get_smarttags( $cf7_properties['mail_2']['subject'], $form['fields'] );
}
if ( ! empty( $cf7_properties['mail_2']['recipient'] ) ) {
$form['settings']['notifications'][2]['email'] = $this->get_smarttags( $cf7_properties['mail_2']['recipient'], $form['fields'] );
}
if ( ! empty( $cf7_properties['mail_2']['body'] ) ) {
$form['settings']['notifications'][2]['message'] = $this->get_smarttags( $cf7_properties['mail_2']['body'], $form['fields'] );
}
if ( ! empty( $cf7_properties['mail_2']['additional_headers'] ) ) {
$form['settings']['notifications'][2]['replyto'] = $this->get_replyto( $cf7_properties['mail_2']['additional_headers'], $form['fields'] );
}
if ( ! empty( $cf7_properties['mail_2']['sender'] ) ) {
$sender = $this->get_sender_details( $cf7_properties['mail_2']['sender'], $form['fields'] );
if ( $sender ) {
$form['settings']['notifications'][2]['sender_name'] = $sender['name'];
$form['settings']['notifications'][2]['sender_address'] = $sender['address'];
}
}
}
$this->add_form( $form, $unsupported, $upgrade_plain, $upgrade_omit );
}
/**
* Lookup and return the placeholder or default value.
*
* @since 1.6.6
*
* @param object $field Field object.
* @param string $type Type of the field.
*
* @return string
*/
public function get_field_placeholder_default( $field, $type = 'placeholder' ) {
$placeholder = '';
$default_value = (string) reset( $field->values );
if ( $field->has_option( 'placeholder' ) || $field->has_option( 'watermark' ) ) {
$placeholder = $default_value;
$default_value = '';
}
if ( $type === 'placeholder' ) {
return $placeholder;
}
return $default_value;
}
/**
* Get the field label.
*
* @since 1.6.6
*
* @param string $form Form data and settings.
* @param string $type Field type.
* @param string $name Field name.
*
* @return string
*/
public function get_field_label( $form, $type, $name = '' ) {
preg_match_all( '/<label>([ \w\S\r\n\t]+?)<\/label>/', $form, $matches );
foreach ( $matches[1] as $match ) {
$match = trim( str_replace( "\n", '', $match ) );
preg_match( '/\[(?:' . preg_quote( $type ) . ') ' . $name . '(?:[ ](.*?))?(?:[\r\n\t ](\/))?\]/', $match, $input_match );
if ( ! empty( $input_match[0] ) ) {
return strip_shortcodes( sanitize_text_field( str_replace( $input_match[0], '', $match ) ) );
}
}
$label = sprintf( /* translators: %1$s - field type; %2$s - field name if available. */
esc_html__( '%1$s Field %2$s', 'wpforms-lite' ),
ucfirst( $type ),
! empty( $name ) ? "($name)" : ''
);
return trim( $label );
}
/**
* Replace 3rd-party form provider tags/shortcodes with our own Smart Tags.
*
* @since 1.6.6
*
* @param string $string Text to look for Smart Tags in.
* @param array $fields List of fields to process Smart Tags in.
*
* @return string
*/
public function get_smarttags( $string, $fields ) {
preg_match_all( '/\[(.+?)\]/', $string, $tags );
if ( empty( $tags[1] ) ) {
return $string;
}
// Process form-tags and mail-tags.
foreach ( $tags[1] as $tag ) {
foreach ( $fields as $field ) {
if ( ! empty( $field['cf7_name'] ) && $field['cf7_name'] === $tag ) {
$string = str_replace( '[' . $tag . ']', '{field_id="' . $field['id'] . '"}', $string );
}
}
}
// Process CF7 tags that we can map with WPForms alternatives.
$string = str_replace(
[
'[_remote_ip]',
'[_date]',
'[_serial_number]',
'[_post_id]',
'[_post_title]',
'[_post_url]',
'[_url]',
'[_post_author]',
'[_post_author_email]',
'[_site_admin_email]',
'[_user_login]',
'[_user_email]',
'[_user_first_name]',
'[_user_last_name]',
'[_user_nickname]',
'[_user_display_name]',
],
[
'{user_ip}',
'{date format="m/d/Y"}',
'{entry_id}',
'{page_id}',
'{page_title}',
'{page_url}',
'{page_url}',
'{author_display}',
'{author_email}',
'{admin_email}',
'{user_display}',
'{user_email}',
'{user_first_name}',
'{user_last_name}',
'{user_display}',
'{user_full_name}',
],
$string
);
// Replace those CF7 that are used in Notifications by default and that we can't leave empty.
$string = str_replace(
[
'[_site_title]',
'[_site_description]',
'[_site_url]',
],
[
get_bloginfo( 'name' ),
get_bloginfo( 'description' ),
get_bloginfo( 'url' ),
],
$string
);
/*
* We are not replacing certain special CF7 tags: [_user_url], [_post_name], [_time], [_user_agent].
* Without them some logic may be broken and for user it will be harder to stop missing strings.
* With them - they can see strange text and will be able to understand, based on the tag name, which value is expected there.
*/
return $string;
}
/**
* Find Reply-To in headers if provided.
*
* @since 1.6.6
*
* @param string $headers CF7 email headers.
* @param array $fields List of fields.
*
* @return string
*/
public function get_replyto( $headers, $fields ) {
if ( strpos( $headers, 'Reply-To:' ) !== false ) {
preg_match( '/Reply-To: \[(.+?)\]/', $headers, $tag );
if ( ! empty( $tag[1] ) ) {
foreach ( $fields as $field ) {
if ( ! empty( $field['cf7_name'] ) && $field['cf7_name'] === $tag[1] ) {
return '{field_id="' . $field['id'] . '"}';
}
}
}
}
return '';
}
/**
* Sender information.
*
* @since 1.6.6
*
* @param string $sender Sender strings in "Name <email@example.com>" format.
* @param array $fields List of fields.
*
* @return bool|array
*/
public function get_sender_details( $sender, $fields ) {
preg_match( '/(.+?)\<(.+?)\>/', $sender, $tag );
if ( ! empty( $tag[1] ) && ! empty( $tag[2] ) ) {
return [
'name' => $this->get_smarttags( $tag[1], $fields ),
'address' => $this->get_smarttags( $tag[2], $fields ),
];
}
return false;
}
}
Admin/Tools/Importers/Base.php 0000644 00000006713 15133255232 0012253 0 ustar 00 <?php
namespace WPForms\Admin\Tools\Importers;
/**
* Base Importer class.
*
* @since 1.6.6
*/
abstract class Base implements ImporterInterface {
/**
* Importer name.
*
* @since 1.6.6
*
* @var string
*/
public $name;
/**
* Importer name in slug format.
*
* @since 1.6.6
*
* @var string
*/
public $slug;
/**
* Importer plugin path.
*
* @since 1.6.6
*
* @var string
*/
public $path;
/**
* Primary class constructor.
*
* @since 1.6.6
*/
public function __construct() {
$this->init();
// Import a specific form with AJAX.
add_action( "wp_ajax_wpforms_import_form_{$this->slug}", [ $this, 'import_form' ] );
}
/**
* Add to list of registered importers.
*
* @since 1.6.6
*
* @param array $importers List of supported importers.
*
* @return array
*/
public function register( $importers = [] ) {
$importers[ $this->slug ] = [
'name' => $this->name,
'slug' => $this->slug,
'path' => $this->path,
'installed' => file_exists( trailingslashit( WP_PLUGIN_DIR ) . $this->path ),
'active' => $this->is_active(),
];
return $importers;
}
/**
* If the importer source is available.
*
* @since 1.6.6
*
* @return bool
*/
protected function is_active() {
return is_plugin_active( $this->path );
}
/**
* Add the new form to the database and return AJAX data.
*
* @since 1.6.6
*
* @param array $form Form to import.
* @param array $unsupported List of unsupported fields.
* @param array $upgrade_plain List of fields, that are supported inside the paid WPForms, but not in Lite.
* @param array $upgrade_omit No field alternative in WPForms.
*/
public function add_form( $form, $unsupported = [], $upgrade_plain = [], $upgrade_omit = [] ) {
// Create empty form so we have an ID to work with.
$form_id = wp_insert_post(
[
'post_status' => 'publish',
'post_type' => 'wpforms',
]
);
if ( empty( $form_id ) || is_wp_error( $form_id ) ) {
wp_send_json_success(
[
'error' => true,
'name' => sanitize_text_field( $form['settings']['form_title'] ),
'msg' => esc_html__( 'There was an error while creating a new form.', 'wpforms-lite' ),
]
);
}
$form['id'] = $form_id;
$form['field_id'] = count( $form['fields'] ) + 1;
// Update the form with all our compiled data.
wpforms()->form->update( $form_id, $form );
// Make note that this form has been imported.
$this->track_import( $form['settings']['import_form_id'], $form_id );
// Build and send final AJAX response!
wp_send_json_success(
[
'name' => $form['settings']['form_title'],
'edit' => esc_url_raw( admin_url( 'admin.php?page=wpforms-builder&view=fields&form_id=' . $form_id ) ),
'preview' => wpforms_get_form_preview_url( $form_id ),
'unsupported' => $unsupported,
'upgrade_plain' => $upgrade_plain,
'upgrade_omit' => $upgrade_omit,
]
);
}
/**
* After a form has been successfully imported we track it, so that in the
* future we can alert users if they try to import a form that has already
* been imported.
*
* @since 1.6.6
*
* @param int $source_id Imported plugin form ID.
* @param int $wpforms_id WPForms form ID.
*/
public function track_import( $source_id, $wpforms_id ) {
$imported = get_option( 'wpforms_imported', [] );
$imported[ $this->slug ][ $wpforms_id ] = $source_id;
update_option( 'wpforms_imported', $imported, false );
}
}
Admin/Tools/Importers/NinjaForms.php 0000644 00000036645 15133255232 0013456 0 ustar 00 <?php
namespace WPForms\Admin\Tools\Importers;
/**
* Ninja Forms Importer class.
*
* @since 1.6.6
*/
class NinjaForms extends Base {
/**
* Define required properties.
*
* @since 1.6.6
*/
public function init() {
$this->name = 'Ninja Forms';
$this->slug = 'ninja-forms';
$this->path = 'ninja-forms/ninja-forms.php';
}
/**
* Get ALL THE FORMS.
*
* @since 1.6.6
*
* @return \NF_Database_Models_Form[]
*/
public function get_forms() {
$forms_final = [];
if ( ! $this->is_active() ) {
return $forms_final;
}
$forms = \Ninja_Forms()->form()->get_forms();
if ( ! empty( $forms ) ) {
foreach ( $forms as $form ) {
if ( ! $form instanceof \NF_Database_Models_Form ) {
continue;
}
$forms_final[ $form->get_id() ] = $form->get_setting( 'title' );
}
}
return $forms_final;
}
/**
* Get a single form.
*
* @since 1.6.6
*
* @param int $id Form ID.
*
* @return array
*/
public function get_form( $id ) {
$form = [];
$form['settings'] = \Ninja_Forms()->form( $id )->get()->get_settings();
$fields = \Ninja_Forms()->form( $id )->get_fields();
$actions = \Ninja_Forms()->form( $id )->get_actions();
foreach ( $fields as $field ) {
if ( ! $field instanceof \NF_Database_Models_Field ) {
continue;
}
$form['fields'][] = array_merge(
[
'id' => $field->get_id(),
],
$field->get_settings()
);
}
foreach ( $actions as $action ) {
if ( ! $action instanceof \NF_Database_Models_Action ) {
continue;
}
$form['actions'][] = $action->get_settings();
}
return $form;
}
/**
* Import a single form using AJAX.
*
* @since 1.6.6
*/
public function import_form() {
// Run a security check.
check_ajax_referer( 'wpforms-admin', 'nonce' );
// Check for permissions.
if ( ! wpforms_current_user_can( 'create_forms' ) ) {
wp_send_json_error();
}
// Define some basic information.
$analyze = isset( $_POST['analyze'] );
$nf_id = ! empty( $_POST['form_id'] ) ? (int) $_POST['form_id'] : 0;
$nf_form = $this->get_form( $nf_id );
$nf_form_name = $nf_form['settings']['title'];
$nf_recaptcha = false;
$nf_recaptcha_type = 'v2';
$fields_pro_plain = [ 'phone', 'date' ];
$fields_pro_omit = [ 'html', 'divider' ];
$fields_unsupported = [ 'spam', 'starrating', 'listmultiselect', 'hidden', 'total', 'shipping', 'quantity', 'product' ];
$upgrade_plain = [];
$upgrade_omit = [];
$unsupported = [];
$form = [
'id' => '',
'field_id' => '',
'fields' => [],
'settings' => [
'form_title' => $nf_form_name,
'form_desc' => '',
'submit_text' => esc_html__( 'Submit', 'wpforms-lite' ),
'submit_text_processing' => esc_html__( 'Sending', 'wpforms-lite' ),
'antispam' => '1',
'notification_enable' => '1',
'notifications' => [
1 => [
'notification_name' => esc_html__( 'Notification 1', 'wpforms-lite' ),
'email' => '{admin_email}',
/* translators: %s - form name. */
'subject' => sprintf( esc_html__( 'New Entry: %s', 'wpforms-lite' ), $nf_form_name ),
'sender_name' => get_bloginfo( 'name' ),
'sender_address' => '{admin_email}',
'replyto' => '',
'message' => '{all_fields}',
],
],
'confirmations' => [
1 => [
'type' => 'message',
'message' => esc_html__( 'Thanks for contacting us! We will be in touch with you shortly.', 'wpforms-lite' ),
'message_scroll' => '1',
],
],
'import_form_id' => $nf_id,
],
];
// If form does not contain fields, bail.
if ( empty( $nf_form['fields'] ) ) {
wp_send_json_success(
[
'error' => true,
'name' => sanitize_text_field( $nf_form_name ),
'msg' => esc_html__( 'No form fields found.', 'wpforms-lite' ),
]
);
}
// Convert fields.
foreach ( $nf_form['fields'] as $nf_field ) {
// Try to determine field label to use.
$label = $this->get_field_label( $nf_field );
// Next, check if field is unsupported. If unsupported make note and
// then continue to the next field.
if ( in_array( $nf_field['type'], $fields_unsupported, true ) ) {
$unsupported[] = $label;
continue;
}
// Now check if this install is Lite. If it is Lite and it's a
// field type not included, make a note then continue to the next
// field.
if ( ! wpforms()->is_pro() && in_array( $nf_field['type'], $fields_pro_plain, true ) ) {
$upgrade_plain[] = $label;
}
if ( ! wpforms()->is_pro() && in_array( $nf_field['type'], $fields_pro_omit, true ) ) {
$upgrade_omit[] = $label;
continue;
}
// Determine next field ID to assign.
if ( empty( $form['fields'] ) ) {
$field_id = 1;
} else {
$field_id = (int) max( array_keys( $form['fields'] ) ) + 1;
}
switch ( $nf_field['type'] ) {
// Single line text, address, city, first name, last name,
// zipcode, email, number, textarea fields.
case 'textbox':
case 'address':
case 'city':
case 'firstname':
case 'lastname':
case 'zip':
case 'email':
case 'number':
case 'textarea':
$type = 'text';
if ( $nf_field['type'] === 'email' ) {
$type = 'email';
} elseif ( $nf_field['type'] === 'number' ) {
$type = 'number';
} elseif ( $nf_field['type'] === 'textarea' ) {
$type = 'textarea';
}
$form['fields'][ $field_id ] = [
'id' => $field_id,
'type' => $type,
'label' => $label,
'description' => ! empty( $nf_field['desc_text'] ) ? $nf_field['desc_text'] : '',
'size' => 'medium',
'required' => ! empty( $nf_field['required'] ) ? '1' : '',
'placeholder' => ! empty( $nf_field['placeholder'] ) ? $nf_field['placeholder'] : '',
'default_value' => ! empty( $nf_field['default'] ) ? $nf_field['default'] : '',
'nf_key' => $nf_field['key'],
];
break;
// Single checkbox field.
case 'checkbox':
$form['fields'][ $field_id ] = [
'id' => $field_id,
'type' => 'checkbox',
'label' => esc_html__( 'Single Checkbox Field', 'wpforms-lite' ),
'choices' => [
1 => [
'label' => $label,
'value' => '',
],
],
'description' => ! empty( $nf_field['desc_text'] ) ? $nf_field['desc_text'] : '',
'size' => 'medium',
'required' => ! empty( $nf_field['required'] ) ? '1' : '',
'label_hide' => '1',
'nf_key' => $nf_field['key'],
];
break;
// Multi-check field, radio, select, state, and country fields.
case 'listcheckbox':
case 'listradio':
case 'listselect':
case 'liststate':
case 'listcountry':
$type = 'select';
if ( $nf_field['type'] === 'listcheckbox' ) {
$type = 'checkbox';
} elseif ( $nf_field['type'] === 'listradio' ) {
$type = 'radio';
}
$choices = [];
if ( $nf_field['type'] === 'listcountry' ) {
$countries = wpforms_countries();
foreach ( $countries as $key => $country ) {
$choices[] = [
'label' => $country,
'value' => $key,
'default' => isset( $nf_field['default'] ) && $nf_field['default'] === $key ? '1' : '',
];
}
} else {
foreach ( $nf_field['options'] as $option ) {
$choices[] = [
'label' => $option['label'],
'value' => $option['value'],
];
}
}
$form['fields'][ $field_id ] = [
'id' => $field_id,
'type' => $type,
'label' => $label,
'choices' => $choices,
'description' => ! empty( $nf_field['desc_text'] ) ? $nf_field['desc_text'] : '',
'size' => 'medium',
'required' => ! empty( $nf_field['required'] ) ? '1' : '',
'nf_key' => $nf_field['key'],
];
break;
// HTML field.
case 'html':
$form['fields'][ $field_id ] = [
'id' => $field_id,
'type' => 'html',
'code' => ! empty( $nf_field['default'] ) ? $nf_field['default'] : '',
'label_disable' => '1',
'nf_key' => $nf_field['key'],
];
break;
// Divider field.
case 'hr':
$form['fields'][ $field_id ] = [
'id' => $field_id,
'type' => 'divider',
'label' => '',
'description' => '',
'label_disable' => '1',
'nf_key' => $nf_field['key'],
];
break;
// Phone number field.
case 'phone':
$type = wpforms()->is_pro() ? 'phone' : 'text';
$form['fields'][ $field_id ] = [
'id' => $field_id,
'type' => $type,
'label' => $label,
'format' => ! empty( $nf_field['mask'] ) && '(999) 999-9999' === $nf_field['mask'] ? 'us' : 'international',
'description' => ! empty( $nf_field['desc_text'] ) ? $nf_field['desc_text'] : '',
'size' => 'medium',
'required' => ! empty( $nf_field['required'] ) ? '1' : '',
'placeholder' => ! empty( $nf_field['placeholder'] ) ? $nf_field['placeholder'] : '',
'default_value' => ! empty( $nf_field['default'] ) ? $nf_field['default'] : '',
'nf_key' => $nf_field['key'],
];
break;
// Date field.
case 'date':
$type = wpforms()->is_pro() ? 'date-time' : 'text';
$form['fields'][ $field_id ] = [
'id' => $field_id,
'type' => $type,
'label' => $label,
'description' => ! empty( $nf_field['desc_text'] ) ? $nf_field['desc_text'] : '',
'format' => 'date',
'size' => 'medium',
'required' => ! empty( $nf_field['required'] ) ? '1' : '',
'date_placeholder' => '',
'date_format' => 'm/d/Y',
'date_type' => 'datepicker',
'time_format' => 'g:i A',
'time_interval' => 30,
'nf_key' => $nf_field['key'],
];
break;
// ReCAPTCHA field.
case 'recaptcha':
$nf_recaptcha = true;
if ( $nf_field['size'] === 'invisible' ) {
$nf_recaptcha_type = 'invisible';
}
}
}
// If we are only analyzing the form, we can stop here and return the
// details about this form.
if ( $analyze ) {
wp_send_json_success(
[
'name' => $nf_form_name,
'upgrade_plain' => $upgrade_plain,
'upgrade_omit' => $upgrade_omit,
]
);
}
// Settings.
// Confirmation message.
foreach ( $nf_form['actions'] as $action ) {
if ( $action['type'] === 'successmessage' ) {
$form['settings']['confirmations'][1]['message'] = $this->get_smarttags( $action['message'], $form['fields'] );
}
}
// ReCAPTCHA.
if ( $nf_recaptcha ) {
// If the user has already defined v2 reCAPTCHA keys in the WPForms
// settings, use those.
$site_key = wpforms_setting( 'recaptcha-site-key', '' );
$secret_key = wpforms_setting( 'recaptcha-secret-key', '' );
// Try to abstract keys from NF.
if ( empty( $site_key ) || empty( $secret_key ) ) {
$nf_settings = get_option( 'ninja_forms_settings' );
if ( ! empty( $nf_settings['recaptcha_site_key'] ) && ! empty( $nf_settings['recaptcha_secret_key'] ) ) {
$wpforms_settings = get_option( 'wpforms_settings', [] );
$wpforms_settings['recaptcha-site-key'] = $nf_settings['recaptcha_site_key'];
$wpforms_settings['recaptcha-secret-key'] = $nf_settings['recaptcha_secret_key'];
$wpforms_settings['recaptcha-type'] = $nf_recaptcha_type;
update_option( 'wpforms_settings', $wpforms_settings );
}
}
if ( ! empty( $site_key ) && ! empty( $secret_key ) ) {
$form['settings']['recaptcha'] = '1';
}
}
// Setup email notifications.
$action_count = 1;
$action_defaults = [
'notification_name' => esc_html__( 'Notification', 'wpforms-lite' ) . " $action_count",
'email' => '{admin_email}',
/* translators: %s - form name. */
'subject' => sprintf( esc_html__( 'New Entry: %s', 'wpforms-lite' ), $nf_form_name ),
'sender_name' => get_bloginfo( 'name' ),
'sender_address' => '{admin_email}',
'replyto' => '',
'message' => '{all_fields}',
];
foreach ( $nf_form['actions'] as $action ) {
if ( $action['type'] !== 'email' ) {
continue;
}
$action_defaults['notification_name'] = esc_html__( 'Notification', 'wpforms-lite' ) . " $action_count";
$form['settings']['notifications'][ $action_count ] = $action_defaults;
if ( ! empty( $action['label'] ) ) {
$form['settings']['notifications'][ $action_count ]['notification_name'] = $action['label'];
}
if ( ! empty( $action['to'] ) ) {
$form['settings']['notifications'][ $action_count ]['email'] = $this->get_smarttags( $action['to'], $form['fields'] );
}
if ( ! empty( $action['reply_to'] ) ) {
$form['settings']['notifications'][ $action_count ]['replyto'] = $this->get_smarttags( $action['reply_to'], $form['fields'] );
}
if ( ! empty( $action['email_subject'] ) ) {
$form['settings']['notifications'][ $action_count ]['subject'] = $this->get_smarttags( $action['email_subject'], $form['fields'] );
}
if ( ! empty( $action['email_message'] ) ) {
$form['settings']['notifications'][ $action_count ]['message'] = $this->get_smarttags( $action['email_message'], $form['fields'] );
}
if ( ! empty( $action['from_name'] ) ) {
$form['settings']['notifications'][ $action_count ]['sender_name'] = $this->get_smarttags( $action['from_name'], $form['fields'] );
}
if ( ! empty( $action['from_address'] ) ) {
$form['settings']['notifications'][ $action_count ]['sender_address'] = $this->get_smarttags( $action['from_address'], $form['fields'] );
}
$action_count ++;
}
$this->add_form( $form, $unsupported, $upgrade_plain, $upgrade_omit );
}
/**
* Get the field label.
*
* @since 1.6.6
*
* @param array $field Field data.
*
* @return string
*/
public function get_field_label( $field ) {
if ( ! empty( $field['label'] ) ) {
$label = sanitize_text_field( $field['label'] );
} else {
$label = sprintf( /* translators: %s - field type. */
esc_html__( '%s Field', 'wpforms-lite' ),
ucfirst( $field['type'] )
);
}
return trim( $label );
}
/**
* Replace 3rd-party form provider tags/shortcodes with our own Smart Tags.
*
* @since 1.6.6
*
* @param string $string Text to look for Smart Tags in.
* @param array $fields List of fields to process Smart Tags in.
*
* @return string
*/
public function get_smarttags( $string, $fields ) {
preg_match_all( '/\{(.+?)\}/', $string, $tags );
if ( empty( $tags[1] ) ) {
return $string;
}
foreach ( $tags[1] as $tag ) {
$tag_formatted = str_replace( 'field:', '', $tag );
foreach ( $fields as $field ) {
if ( ! empty( $field['nf_key'] ) && $field['nf_key'] === $tag_formatted ) {
$string = str_replace( '{' . $tag . '}', '{field_id="' . $field['id'] . '"}', $string );
}
}
if ( in_array( $tag, [ 'wp:admin_email', 'system:admin_email' ], true ) ) {
$string = str_replace( [ '{wp:admin_email}', '{system:admin_email}' ], '{admin_email}', $string );
}
if ( $tag === 'all_fields_table' || $tag === 'fields_table' ) {
$string = str_replace( '{' . $tag . '}', '{all_fields}', $string );
}
}
return $string;
}
}
Admin/Tools/Importers/ImporterInterface.php 0000644 00000001605 15133255232 0015016 0 ustar 00 <?php
namespace WPForms\Admin\Tools\Importers;
/**
* Interface WPForms_Importer_Interface to handle common methods for all importers.
*
* @since 1.6.6
*/
interface ImporterInterface {
/**
* Define required properties.
*
* @since 1.6.6
*/
public function init();
/**
* Get ALL THE FORMS.
*
* @since 1.6.6
*/
public function get_forms();
/**
* Get a single form.
*
* @since 1.6.6
*
* @param int $id Form ID.
*/
public function get_form( $id );
/**
* Import a single form using AJAX.
*
* @since 1.6.6
*/
public function import_form();
/**
* Replace 3rd-party form provider tags/shortcodes with our own Smart Tags.
*
* @since 1.6.6
*
* @param string $string Text to look for Smart Tags in.
* @param array $fields List of fields to process Smart Tags in.
*
* @return string
*/
public function get_smarttags( $string, $fields );
}
Admin/Tools/Importers/PirateForms.php 0000644 00000043461 15133255232 0013635 0 ustar 00 <?php
namespace WPForms\Admin\Tools\Importers;
use WPForms\Helpers\PluginSilentUpgrader;
use WPForms\Helpers\PluginSilentUpgraderSkin;
/**
* Pirate Forms Importer class.
*
* @since 1.6.6
*/
class PirateForms extends Base {
/**
* Direct URL to download the latest version of WP Mail SMTP plugin from WP.org repo.
*
* @since 1.6.6
*
* @var string
*/
const URL_SMTP_ZIP = 'https://downloads.wordpress.org/plugin/wp-mail-smtp.zip';
/**
* WP Mail SMTP plugin basename.
*
* @since 1.6.6
*
* @var string
*/
const SLUG_SMTP_PLUGIN = 'wp-mail-smtp/wp_mail_smtp.php';
/**
* Default PirateForms smart tags.
*
* @since 1.6.6
*
* @var array
*/
public static $tags = [
'[email]',
];
/**
* Define required properties.
*
* @since 1.6.6
*/
public function init() {
$this->name = 'Pirate Forms';
$this->slug = 'pirate-forms';
$this->path = 'pirate-forms/pirate-forms.php';
}
/**
* Get ALL THE FORMS.
* We need only ID's and names here.
*
* @since 1.6.6
*
* @return array
*/
public function get_forms() {
// Union those arrays, as array_merge() does keys reindexing.
$forms = $this->get_default_forms() + $this->get_pro_forms();
// Sort by IDs ASC.
ksort( $forms );
return $forms;
}
/**
* Pirate Forms has a default form, which doesn't have an ID.
*
* @since 1.6.6
*
* @return array
*/
protected function get_default_forms() {
$form = \PirateForms_Util::get_form_options();
// Just make sure that it's there and not broken.
if ( empty( $form ) ) {
return [];
}
return [ 0 => esc_html__( 'Default Form', 'wpforms-lite' ) ];
}
/**
* Copy-paste from Pro plugin code, it doesn't have API to get this data easily.
*
* @since 1.6.6
*
* @return array
*/
protected function get_pro_forms() {
$forms = [];
$query = new \WP_Query(
[
'post_type' => 'pf_form',
'post_status' => 'publish',
'posts_per_page' => - 1,
'update_post_meta_cache' => false,
'update_post_term_cache' => false,
]
);
if ( $query->have_posts() ) {
while ( $query->have_posts() ) {
$query->the_post();
$forms[ get_the_ID() ] = get_the_title();
}
}
return $forms;
}
/**
* Get a single form options.
*
* @since 1.6.6
*
* @param int $id Form ID.
*
* @return array
*/
public function get_form( $id ) {
return \PirateForms_Util::get_form_options( (int) $id );
}
/**
* Import a single form using AJAX.
*
* @since 1.6.6
*/
public function import_form() {
// Run a security check.
check_ajax_referer( 'wpforms-admin', 'nonce' );
// Check for permissions.
if ( ! wpforms_current_user_can( 'create_forms' ) ) {
wp_send_json_error();
}
$analyze = isset( $_POST['analyze'] );
$pf_form_id = isset( $_POST['form_id'] ) ? (int) $_POST['form_id'] : 0;
$pf_form = $this->get_form( $pf_form_id );
$pf_fields_custom = \PirateForms_Util::get_post_meta( $pf_form_id, 'custom' );
$pf_fields_default = [
'name',
'email',
'subject',
'message',
'attachment',
'checkbox',
'recaptcha',
];
$fields_pro_plain = [ 'tel' ]; // Convert them in Lite to the closest Standard alternatives.
$fields_pro_omit = [ 'label', 'file', 'attachment' ]; // Strict PRO fields with no Lite alternatives.
$upgrade_plain = [];
$upgrade_omit = [];
$unsupported = [];
$fields = [];
if ( ! empty( $pf_fields_custom[0] ) ) {
$pf_fields_custom = $pf_fields_custom[0];
} else {
$pf_fields_custom = [];
}
if ( empty( $pf_form_id ) ) {
$pf_form_name = esc_html__( 'Default Form', 'wpforms-lite' );
} else {
$pf_form_name = get_post_field( 'post_title', $pf_form_id );
}
$pf_form_name = wpforms_decode_string( apply_filters( 'the_title', $pf_form_name, $pf_form_id ) );
// Prepare all DEFAULT fields.
foreach ( $pf_fields_default as $field ) {
// Ignore fields that are not displayed or not added at all.
if ( empty( $pf_form[ 'pirateformsopt_' . $field . '_field' ] ) ) {
continue;
}
// Ignore certain fields as they are dealt with later.
if ( $field === 'recaptcha' ) {
continue;
}
$required = $pf_form[ 'pirateformsopt_' . $field . '_field' ] === 'req' ? '1' : '';
$label = ! empty( $pf_form[ 'pirateformsopt_label_' . $field ] ) ? $pf_form[ 'pirateformsopt_label_' . $field ] : ucwords( $field );
// If it is Lite and it's a field type not included, make a note then continue to the next field.
if ( ! wpforms()->is_pro() && in_array( $field, $fields_pro_plain, true ) ) {
$upgrade_plain[] = $label;
}
if ( ! wpforms()->is_pro() && in_array( $field, $fields_pro_omit, true ) ) {
$upgrade_omit[] = $label;
continue;
}
// Determine next field ID to assign.
if ( empty( $fields ) ) {
$field_id = 1;
} else {
$field_id = (int) max( array_keys( $fields ) ) + 1;
}
// Separately process certain fields.
switch ( $field ) {
case 'name':
case 'email':
case 'subject':
case 'message':
$type = $field;
if ( $field === 'subject' ) {
$type = 'text';
} elseif ( $field === 'message' ) {
$type = 'textarea';
}
$fields[ $field_id ] = [
'id' => $field_id,
'type' => $type,
'label' => $label,
'required' => $required,
'size' => 'medium',
];
if ( $field === 'name' ) {
$fields[ $field_id ]['format'] = 'simple';
}
break;
case 'checkbox':
$fields[ $field_id ] = [
'id' => $field_id,
'type' => 'checkbox',
'label' => esc_html__( 'Single Checkbox Field', 'wpforms-lite' ),
'choices' => [
1 => [
'label' => $label,
'value' => '',
],
],
'size' => 'medium',
'required' => $required,
'label_hide' => true,
];
break;
case 'attachment':
case 'file':
$fields[ $field_id ] = [
'id' => $field_id,
'type' => 'file-upload',
'label' => $label,
'required' => $required,
'label_hide' => true,
];
// If PF attachments were saved into FS, we need to save them in WP Media.
// That will allow admins to easily delete if needed.
if (
! empty( $pf_form['pirateformsopt_save_attachment'] ) &&
$pf_form['pirateformsopt_save_attachment'] === 'yes'
) {
$fields[ $field_id ]['media_library'] = true;
}
break;
}
}
// Prepare all CUSTOM fields.
foreach ( $pf_fields_custom as $id => $field ) {
// Ignore fields that are not displayed.
if ( empty( $field['display'] ) ) {
continue;
}
$required = $field['display'] === 'req' ? '1' : ''; // Possible values in PF: 'yes', 'req'.
$label = sanitize_text_field( $field['label'] );
// If it is Lite and it's a field type not included, make a note then continue to the next field.
if ( ! wpforms()->is_pro() && in_array( $field['type'], $fields_pro_plain, true ) ) {
$upgrade_plain[] = $label;
}
if ( ! wpforms()->is_pro() && in_array( $field['type'], $fields_pro_omit, true ) ) {
$upgrade_omit[] = $label;
continue;
}
// Determine next field ID to assign.
if ( empty( $fields ) ) {
$field_id = 1;
} else {
$field_id = (int) max( array_keys( $fields ) ) + 1;
}
switch ( $field['type'] ) {
case 'text':
case 'textarea':
case 'number':
case 'tel':
$type = $field['type'];
if ( $field['type'] === 'textarea' ) {
$type = 'textarea';
}
if ( $field['type'] === 'tel' ) {
$type = 'phone';
}
$fields[ $field_id ] = [
'id' => $field_id,
'type' => $type,
'label' => $label,
'required' => $required,
'size' => 'medium',
];
if ( $field['type'] === 'tel' ) {
$fields[ $field_id ]['format'] = 'international';
}
break;
case 'checkbox':
$fields[ $field_id ] = [
'id' => $field_id,
'type' => 'checkbox',
'label' => esc_html__( 'Single Checkbox Field', 'wpforms-lite' ),
'choices' => [
1 => [
'label' => $label,
'value' => '',
],
],
'size' => 'medium',
'required' => $required,
'label_hide' => true,
];
break;
case 'select':
case 'multiselect':
$options = [];
$i = 1;
$type = 'select';
if ( $field['type'] === 'multiselect' ) {
$type = 'checkbox';
}
foreach ( explode( PHP_EOL, $field['options'] ) as $option ) {
$options[ $i ] = [
'label' => $option,
'value' => '',
'image' => '',
];
$i ++;
}
$fields[ $field_id ] = [
'id' => $field_id,
'type' => $type,
'label' => $label,
'required' => $required,
'size' => 'medium',
'choices' => $options,
];
break;
case 'label':
$fields[ $field_id ] = [
'id' => $field_id,
'type' => 'html',
'code' => $field['label'],
'label_disable' => true,
];
break;
case 'file':
$fields[ $field_id ] = [
'id' => $field_id,
'type' => 'file-upload',
'label' => $label,
'required' => $required,
'label_hide' => true,
];
// If PF attachments were saved into FS, we need to save them in WP Media.
// That will allow admins to easily delete if needed.
if (
! empty( $pf_form['pirateformsopt_save_attachment'] ) &&
$pf_form['pirateformsopt_save_attachment'] === 'yes'
) {
$fields[ $field_id ]['media_library'] = true;
}
break;
}
}
// If we are analyzing the form (in Lite only),
// we can stop here and return the details about this form.
if ( $analyze ) {
wp_send_json_success(
[
'name' => $pf_form_name,
'upgrade_plain' => $upgrade_plain,
'upgrade_omit' => $upgrade_omit,
]
);
}
// Make sure we have imported some fields.
if ( empty( $fields ) ) {
wp_send_json_success(
[
'error' => true,
'name' => $pf_form_name,
'msg' => esc_html__( 'No form fields found.', 'wpforms-lite' ),
]
);
}
// Create a form array, that holds all the data.
$form = [
'id' => '',
'field_id' => '',
'fields' => $fields,
'settings' => [
'form_title' => $pf_form_name,
'form_desc' => '',
'submit_text' => stripslashes( $pf_form['pirateformsopt_label_submit_btn'] ),
'submit_text_processing' => esc_html__( 'Sending', 'wpforms-lite' ),
'notification_enable' => '1',
'notifications' => [
1 => [
'notification_name' => esc_html__( 'Default Notification', 'wpforms-lite' ),
'email' => $pf_form['pirateformsopt_email_recipients'],
/* translators: %s - form name. */
'subject' => sprintf( esc_html__( 'New Entry: %s', 'wpforms-lite' ), $pf_form_name ),
'sender_name' => get_bloginfo( 'name' ),
'sender_address' => $this->get_smarttags( $pf_form['pirateformsopt_email'], $fields ),
'replyto' => '',
'message' => '{all_fields}',
],
],
'confirmations' => [
1 => [
'type' => empty( $pf_form['pirateformsopt_thank_you_url'] ) ? 'message' : 'page',
'page' => (int) $pf_form['pirateformsopt_thank_you_url'],
'message' => ! empty( $pf_form['pirateformsopt_label_submit'] ) ? $pf_form['pirateformsopt_label_submit'] : esc_html__( 'Thanks for contacting us! We will be in touch with you shortly.', 'wpforms-lite' ),
'message_scroll' => '1',
],
],
'disable_entries' => $pf_form['pirateformsopt_store'] === 'yes' ? '0' : '1',
'import_form_id' => $pf_form_id,
],
];
// Do not save user IP address and UA.
if ( empty( $pf_form['pirateformsopt_store_ip'] ) || $pf_form['pirateformsopt_store_ip'] !== 'yes' ) {
$wpforms_settings = get_option( 'wpforms_settings', [] );
$wpforms_settings['gdpr'] = true;
update_option( 'wpforms_settings', $wpforms_settings );
$form['settings']['disable_ip'] = true;
}
// Save recaptcha keys.
if (
! empty( $pf_form['pirateformsopt_recaptcha_field'] ) &&
$pf_form['pirateformsopt_recaptcha_field'] === 'yes'
) {
// If the user has already defined v2 reCAPTCHA keys, use those.
$site_key = wpforms_setting( 'recaptcha-site-key', '' );
$secret_key = wpforms_setting( 'recaptcha-secret-key', '' );
// Try to abstract keys from PF.
if ( empty( $site_key ) || empty( $secret_key ) ) {
if ( ! empty( $pf_form['pirateformsopt_recaptcha_sitekey'] ) && ! empty( $pf_form['pirateformsopt_recaptcha_secretkey'] ) ) {
$wpforms_settings = get_option( 'wpforms_settings', [] );
$wpforms_settings['recaptcha-site-key'] = $pf_form['pirateformsopt_recaptcha_sitekey'];
$wpforms_settings['recaptcha-secret-key'] = $pf_form['pirateformsopt_recaptcha_secretkey'];
$wpforms_settings['recaptcha-type'] = 'v2';
update_option( 'wpforms_settings', $wpforms_settings );
}
}
if (
( ! empty( $site_key ) && ! empty( $secret_key ) ) ||
( ! empty( $wpforms_settings['recaptcha-site-key'] ) && ! empty( $wpforms_settings['recaptcha-secret-key'] ) )
) {
$form['settings']['recaptcha'] = '1';
}
}
$this->import_smtp( $pf_form_id, $form );
$this->add_form( $form, $unsupported, $upgrade_plain, $upgrade_omit );
}
/**
* Replace 3rd-party form provider tags/shortcodes with our own Smart Tags.
* See: PirateForms_Util::get_magic_tags() for all PF tags.
*
* @since 1.6.6
*
* @param string $string String to process the smart tag in.
* @param array $fields List of fields for the form.
*
* @return string
*/
public function get_smarttags( $string, $fields ) {
foreach ( self::$tags as $tag ) {
$wpf_tag = '';
if ( $tag === '[email]' ) {
foreach ( $fields as $field ) {
if ( $field['type'] === 'email' ) {
$wpf_tag = '{field_id="' . $field['id'] . '"}';
break;
}
}
}
$string = str_replace( $tag, $wpf_tag, $string );
}
return $string;
}
/**
* Import SMTP settings from Default form only.
*
* @since 1.6.6
*
* @param int $pf_form_id PirateForms form ID.
* @param array $form WPForms form array.
*/
protected function import_smtp( $pf_form_id, $form ) {
// At this point we import only default form SMTP settings.
if ( $pf_form_id !== 0 ) {
return;
}
$pf_form = $this->get_form( 0 );
// Use only if enabled.
if ( empty( $pf_form['pirateformsopt_use_smtp'] ) || $pf_form['pirateformsopt_use_smtp'] !== 'yes' ) {
return;
}
// If user has WP Mail SMTP already activated - do nothing as it's most likely already configured.
if ( is_plugin_active( self::SLUG_SMTP_PLUGIN ) ) {
return;
}
// Check that we successfully installed and activated the plugin.
if ( ! $this->install_activate_smtp() ) {
return;
}
/*
* Finally, start the settings importing.
*/
// WP Mail SMTP 1.x and PHP 5.3+ are allowed. Older WPMS versions are ignored.
if ( ! function_exists( 'wp_mail_smtp' ) ) {
return;
}
// TODO: change to \WPMailSMTP\Options in future.
$options = get_option( 'wp_mail_smtp', [] );
$options['mail']['from_email'] = $this->get_smarttags( $pf_form['pirateformsopt_email'], $form['fields'] );
$options['mail']['mailer'] = 'smtp';
$options['smtp']['host'] = $pf_form['pirateformsopt_smtp_host'];
$options['smtp']['port'] = $pf_form['pirateformsopt_smtp_port'];
$options['smtp']['encryption'] = empty( $pf_form['pirateformsopt_use_secure'] ) ? 'none' : $pf_form['pirateformsopt_use_secure'];
$options['smtp']['auth'] = ! empty( $pf_form['pirateformsopt_use_smtp_authentication'] ) && $pf_form['pirateformsopt_use_smtp_authentication'] === 'yes';
$options['smtp']['user'] = $pf_form['pirateformsopt_smtp_username'];
$options['smtp']['pass'] = $pf_form['pirateformsopt_smtp_password'];
update_option( 'wp_mail_smtp', $options );
}
/**
* Do all the voodoo to install and activate the WP Mail SMTP plugin behind the scene.
* No user interaction is needed.
*
* @since 1.6.6
*
* @return bool
*/
protected function install_activate_smtp() {
/*
* Check installation.
* If installed but not activated - bail.
* We don't want to break current site email deliverability.
*/
if ( ! function_exists( 'get_plugins' ) ) {
require_once ABSPATH . 'wp-admin/includes/plugin.php';
}
// FALSE will bail the import.
if ( array_key_exists( self::SLUG_SMTP_PLUGIN, get_plugins() ) ) {
return false;
}
/*
* Let's try to install.
*/
$url = add_query_arg(
[
'provider' => $this->slug,
'page' => 'wpforms-tools',
'view' => 'importer',
],
admin_url( 'admin.php' )
);
$creds = request_filesystem_credentials( esc_url_raw( $url ), '', false, false, null );
// Check for file system permissions.
if ( $creds === false ) {
return false;
}
if ( ! WP_Filesystem( $creds ) ) {
return false;
}
// Do not allow WordPress to search/download translations, as this will break JS output.
remove_action( 'upgrader_process_complete', [ 'Language_Pack_Upgrader', 'async_upgrade' ], 20 );
// Create the plugin upgrader with our custom skin.
$installer = new PluginSilentUpgrader( new PluginSilentUpgraderSkin() );
// Error check.
if ( ! method_exists( $installer, 'install' ) ) {
return false;
}
$installer->install( self::URL_SMTP_ZIP );
// Flush the cache and return the newly installed plugin basename.
wp_cache_flush();
if ( $installer->plugin_info() ) {
$plugin_basename = $installer->plugin_info();
// Activate, do not redirect, run the plugin activation routine.
$activated = activate_plugin( $plugin_basename );
if ( ! is_wp_error( $activated ) ) {
return true;
}
}
return false;
}
}
Admin/Tools/Tools.php 0000644 00000007263 15133255232 0010516 0 ustar 00 <?php
namespace WPForms\Admin\Tools;
/**
* Main Tools class.
*
* @since 1.6.6
*/
class Tools {
/**
* Tools page slug.
*
* @since 1.6.6
*/
const SLUG = 'wpforms-tools';
/**
* Available pages.
*
* @since 1.6.6
*
* @var array
*/
private $views = [];
/**
* The current view.
*
* @since 1.6.6
*
* @var null|\WPForms\Admin\Tools\Views\View
*/
private $view = null;
/**
* The active view slug.
*
* @since 1.6.6
*
* @var string
*/
private $active_view_slug;
/**
* Initialize class.
*
* @since 1.6.6
*/
public function init() {
if ( $this->is_tools_page() ) {
$this->init_view();
$this->hooks();
}
}
/**
* Check if we're on tools page.
*
* @since 1.6.6
*
* @return bool
*/
private function is_tools_page() {
$page = isset( $_GET['page'] ) ? sanitize_key( $_GET['page'] ) : ''; //phpcs:ignore WordPress.Security
// Only load if we are actually on the settings page.
return $page === self::SLUG;
}
/**
* Init current view.
*
* @since 1.6.6
*/
private function init_view() {
$view_ids = array_keys( $this->get_views() );
// Determine the current active settings tab.
$this->active_view_slug = ! empty( $_GET['view'] ) ? sanitize_key( $_GET['view'] ) : 'import'; //phpcs:ignore WordPress.Security.NonceVerification.Recommended
// If the user tries to load an invalid view - fallback to the first available.
if (
! in_array( $this->active_view_slug, $view_ids, true ) &&
! has_action( 'wpforms_tools_display_tab_' . $this->active_view_slug )
) {
$this->active_view_slug = reset( $view_ids );
}
if ( isset( $this->views[ $this->active_view_slug ] ) ) {
$this->view = $this->views[ $this->active_view_slug ];
$this->view->init();
}
}
/**
* Get Views.
*
* @since 1.6.6
*
* @return array
*/
public function get_views() {
if ( empty( $this->views ) ) {
$this->views = [
'import' => new Views\Import(),
'importer' => new Views\Importer(),
'export' => new Views\Export(),
'system' => new Views\System(),
'action-scheduler' => new Views\ActionScheduler(),
'logs' => new Views\Logs(),
];
}
$this->views = apply_filters( 'wpforms_tools_views', $this->views );
return array_filter(
$this->views,
static function ( $view ) {
return $view->check_capability();
}
);
}
/**
* Register hooks.
*
* @since 1.6.6
*/
public function hooks() {
add_action( 'wpforms_admin_page', [ $this, 'output' ] );
// Hook for addons.
do_action( 'wpforms_tools_init' );
}
/**
* Build the output for the Tools admin page.
*
* @since 1.6.6
*/
public function output() {
?>
<div id="wpforms-tools" class="wrap wpforms-admin-wrap wpforms-tools-tab-<?php echo esc_attr( $this->active_view_slug ); ?>">
<?php
if ( $this->view && $this->view->show_nav() ) {
echo '<ul class="wpforms-admin-tabs">';
foreach ( $this->views as $slug => $view ) {
if ( $view->hide_from_nav() || ! $view->check_capability() ) {
continue;
}
echo '<li>';
printf(
'<a href="%1$s" class="%2$s">%3$s</a>',
esc_url( $view->get_link() ),
sanitize_html_class( $this->active_view_slug === $slug ? 'active' : '' ),
$view->get_label() //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
);
echo '</li>';
}
echo '</ul>';
}
?>
<h1 class="wpforms-h1-placeholder"></h1>
<div class="wpforms-admin-content wpforms-admin-settings">
<?php
if ( $this->view ) {
$this->view->display();
} else {
do_action( 'wpforms_tools_display_tab_' . $this->active_view_slug );
}
?>
</div>
</div>
<?php
}
}
Admin/Notice.php 0000644 00000022224 15133255232 0007531 0 ustar 00 <?php
namespace WPForms\Admin;
/**
* Dismissible admin notices.
*
* @since 1.6.7.1
*
* @example Dismissible - global:
* \WPForms\Admin\Notice::error(
* 'Fatal error!',
* [
* 'dismiss' => \WPForms\Admin\Notice::DISMISS_GLOBAL,
* 'slug' => 'fatal_error_3678975',
* ]
* );
*
* @example Dismissible - per user:
* \WPForms\Admin\Notice::warning(
* 'Do something please.',
* [
* 'dismiss' => \WPForms\Admin\Notice::DISMISS_USER,
* 'slug' => 'do_something_1238943',
* ]
* );
*
* @example Dismissible - global, add custom class to output and disable auto paragraph in text:
* \WPForms\Admin\Notice::error(
* 'Fatal error!',
* [
* 'dismiss' => \WPForms\Admin\Notice::DISMISS_GLOBAL,
* 'slug' => 'fatal_error_348975',
* 'autop' => false,
* 'class' => 'some-additional-class',
* ]
* );
*
* @example Not dismissible:
* \WPForms\Admin\Notice::success( 'Everything is good!' );
*/
class Notice {
/**
* Not dismissible.
*
* Constant attended to use as the value of the $args['dismiss'] argument.
* DISMISS_NONE means that the notice is not dismissible.
*
* @since 1.6.7.1
*/
const DISMISS_NONE = 0;
/**
* Dismissible global.
*
* Constant attended to use as the value of the $args['dismiss'] argument.
* DISMISS_GLOBAL means that the notice will have the dismiss button, and after clicking this button, the notice will be dismissed for all users.
*
* @since 1.6.7.1
*/
const DISMISS_GLOBAL = 1;
/**
* Dismissible per user.
*
* Constant attended to use as the value of the $args['dismiss'] argument.
* DISMISS_USER means that the notice will have the dismiss button, and after clicking this button, the notice will be dismissed only for the current user..
*
* @since 1.6.7.1
*/
const DISMISS_USER = 2;
/**
* Added notices.
*
* @since 1.6.7.1
*
* @var array
*/
private $notices = [];
/**
* Init.
*
* @since 1.6.7.1
*/
public function init() {
$this->hooks();
}
/**
* Hooks.
*
* @since 1.6.7.1
*/
public function hooks() {
add_action( 'admin_notices', [ $this, 'display' ], PHP_INT_MAX );
add_action( 'wp_ajax_wpforms_notice_dismiss', [ $this, 'dismiss_ajax' ] );
}
/**
* Enqueue assets.
*
* @since 1.6.7.1
*/
private function enqueues() {
$min = wpforms_get_min_suffix();
wp_enqueue_script(
'wpforms-admin-notices',
WPFORMS_PLUGIN_URL . "assets/js/components/admin/notices{$min}.js",
[ 'jquery' ],
WPFORMS_VERSION,
true
);
wp_localize_script(
'wpforms-admin-notices',
'wpforms_admin_notices',
[
'ajax_url' => admin_url( 'admin-ajax.php' ),
'nonce' => wp_create_nonce( 'wpforms-admin' ),
]
);
}
/**
* Display the notices.
*
* @since 1.6.7.1
*/
public function display() {
$dismissed_notices = get_user_meta( get_current_user_id(), 'wpforms_admin_notices', true );
$dismissed_notices = is_array( $dismissed_notices ) ? $dismissed_notices : [];
$dismissed_notices = array_merge( $dismissed_notices, (array) get_option( 'wpforms_admin_notices', [] ) );
foreach ( $this->notices as $slug => $notice ) {
if ( isset( $dismissed_notices[ $slug ] ) && ! empty( $dismissed_notices[ $slug ]['dismissed'] ) ) {
unset( $this->notices[ $slug ] );
}
}
$output = implode( '', $this->notices );
echo $output; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
// Enqueue script only when it's needed.
if ( strpos( $output, 'is-dismissible' ) !== false ) {
$this->enqueues();
}
}
/**
* Add notice to the registry.
*
* @since 1.6.7.1
*
* @param string $message Message to display.
* @param string $type Type of the notice. Can be [ '' (default) | 'info' | 'error' | 'success' | 'warning' ].
* @param array $args The array of additional arguments. Please see the $defaults array below.
*/
public static function add( $message, $type = '', $args = [] ) {
static $uniq_id = 0;
$defaults = [
'dismiss' => self::DISMISS_NONE, // Dismissible level: one of the self::DISMISS_* const. By default notice is not dismissible.
'slug' => '', // Slug. Should be unique if dismissible is not equal self::DISMISS_NONE.
'autop' => true, // `false` if not needed to pass message through wpautop().
'class' => '', // Additional CSS class.
];
$args = wp_parse_args( $args, $defaults );
$dismissible = (int) $args['dismiss'];
$dismissible = $dismissible > self::DISMISS_USER ? self::DISMISS_USER : $dismissible;
$class = $dismissible > self::DISMISS_NONE ? ' is-dismissible' : '';
$global = ( $dismissible === self::DISMISS_GLOBAL ) ? 'global-' : '';
$slug = sanitize_key( $args['slug'] );
++$uniq_id;
$uniq_id += ( $uniq_id === (int) $slug ) ? 1 : 0;
$id = 'wpforms-notice-' . $global;
$id .= empty( $slug ) ? $uniq_id : $slug;
$type = ! empty( $type ) ? 'notice-' . esc_attr( sanitize_key( $type ) ) : '';
$class = empty( $args['class'] ) ? $class : $class . ' ' . esc_attr( sanitize_key( $args['class'] ) );
$message = $args['autop'] ? wpautop( $message ) : $message;
$notice = sprintf(
'<div class="notice wpforms-notice %s%s" id="%s">%s</div>',
esc_attr( $type ),
esc_attr( $class ),
esc_attr( $id ),
$message
);
if ( empty( $slug ) ) {
wpforms()->get( 'notice' )->notices[] = $notice;
} else {
wpforms()->get( 'notice' )->notices[ $slug ] = $notice;
}
}
/**
* Add info notice.
*
* @since 1.6.7.1
*
* @param string $message Message to display.
* @param array $args Array of additional arguments. Details in the self::add() method.
*/
public static function info( $message, $args = [] ) {
self::add( $message, 'info', $args );
}
/**
* Add error notice.
*
* @since 1.6.7.1
*
* @param string $message Message to display.
* @param array $args Array of additional arguments. Details in the self::add() method.
*/
public static function error( $message, $args = [] ) {
self::add( $message, 'error', $args );
}
/**
* Add success notice.
*
* @since 1.6.7.1
*
* @param string $message Message to display.
* @param array $args Array of additional arguments. Details in the self::add() method.
*/
public static function success( $message, $args = [] ) {
self::add( $message, 'success', $args );
}
/**
* Add warning notice.
*
* @since 1.6.7.1
*
* @param string $message Message to display.
* @param array $args Array of additional arguments. Details in the self::add() method.
*/
public static function warning( $message, $args = [] ) {
self::add( $message, 'warning', $args );
}
/**
* AJAX routine that updates dismissed notices meta data.
*
* @since 1.6.7.1
*/
public function dismiss_ajax() {
// Run a security check.
check_ajax_referer( 'wpforms-admin', 'nonce' );
// Sanitize POST data.
$post = array_map( 'sanitize_key', wp_unslash( $_POST ) );
// Update notices meta data.
if ( strpos( $post['id'], 'global-' ) !== false ) {
// Check for permissions.
if ( ! wpforms_current_user_can() ) {
wp_send_json_error();
}
$notices = $this->dismiss_global( $post['id'] );
$level = self::DISMISS_GLOBAL;
} else {
$notices = $this->dismiss_user( $post['id'] );
$level = self::DISMISS_USER;
}
/**
* Allows developers to apply additional logic to the dismissing notice process.
* Executes after updating option or user meta (according to the notice level).
*
* @since 1.6.7.1
*
* @param string $notice_id Notice ID (slug).
* @param integer $level Notice level.
* @param array $notices Dismissed notices.
*/
do_action( 'wpforms_admin_notice_dismiss_ajax', $post['id'], $level, $notices );
if ( ! wpforms_debug() ) {
wp_send_json_success();
}
wp_send_json_success(
[
'id' => $post['id'],
'time' => time(),
'level' => $level,
'notices' => $notices,
]
);
}
/**
* AJAX sub-routine that updates dismissed notices option.
*
* @since 1.6.7.1
*
* @param string $id Notice Id.
*
* @return array Notices.
*/
private function dismiss_global( $id ) {
$id = str_replace( 'global-', '', $id );
$notices = get_option( 'wpforms_admin_notices', [] );
$notices[ $id ] = [
'time' => time(),
'dismissed' => true,
];
update_option( 'wpforms_admin_notices', $notices, true );
return $notices;
}
/**
* AJAX sub-routine that updates dismissed notices user meta.
*
* @since 1.6.7.1
*
* @param string $id Notice Id.
*
* @return array Notices.
*/
private function dismiss_user( $id ) {
$user_id = get_current_user_id();
$notices = get_user_meta( $user_id, 'wpforms_admin_notices', true );
$notices = ! is_array( $notices ) ? [] : $notices;
$notices[ $id ] = [
'time' => time(),
'dismissed' => true,
];
update_user_meta( $user_id, 'wpforms_admin_notices', $notices );
return $notices;
}
}
Admin/AdminBarMenu.php 0000644 00000022017 15133255232 0010612 0 ustar 00 <?php
namespace WPForms\Admin;
use WP_Admin_Bar;
/**
* WPForms admin bar menu.
*
* @since 1.6.0
*/
class AdminBarMenu {
/**
* Initialize class.
*
* @since 1.6.0
*/
public function init() {
if ( ! $this->has_access() ) {
return;
}
$this->hooks();
}
/**
* Register hooks.
*
* @since 1.6.0
*/
public function hooks() {
add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_css' ] );
add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_css' ] );
add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_js' ] );
add_action( 'admin_bar_menu', [ $this, 'register' ], 999 );
add_action( 'wpforms_wp_footer_end', [ $this, 'menu_forms_data_html' ] );
}
/**
* Determine whether the current user has access to see admin bar menu.
*
* @since 1.6.0
*
* @return bool
*/
public function has_access() {
$access = false;
if (
is_admin_bar_showing() &&
wpforms_current_user_can() &&
! wpforms_setting( 'hide-admin-bar', false )
) {
$access = true;
}
return (bool) apply_filters( 'wpforms_admin_adminbarmenu_has_access', $access );
}
/**
* Determine whether new notifications are available.
*
* @since 1.6.0
*
* @return bool
*/
public function has_notifications() {
return wpforms()->get( 'notifications' )->get_count();
}
/**
* Enqueue CSS styles.
*
* @since 1.6.0
*/
public function enqueue_css() {
$min = wpforms_get_min_suffix();
wp_enqueue_style(
'wpforms-admin-bar',
WPFORMS_PLUGIN_URL . "assets/css/admin-bar{$min}.css",
[],
WPFORMS_VERSION
);
// Apply WordPress pre/post 5.7 accent color, only when admin bar is displayed on the frontend or we're
// inside the Form Builder - it does not load some WP core admin styles, including themes.
if ( wpforms_is_admin_page( 'builder' ) || ! is_admin() ) {
wp_add_inline_style(
'wpforms-admin-bar',
sprintf(
'#wpadminbar .wpforms-menu-notification-counter, #wpadminbar .wpforms-menu-notification-indicator {
background-color: %s !important;
color: #ffffff !important;
}',
version_compare( get_bloginfo( 'version' ), '5.7', '<' ) ? '#ca4a1f' : '#d63638'
)
);
}
}
/**
* Enqueue JavaScript files.
*
* @since 1.6.5
*/
public function enqueue_js() {
// In WordPress 5.3.1 the `admin-bar.js` file was rewritten and removed all jQuery dependencies.
$is_wp_531_plus = version_compare( get_bloginfo( 'version' ), '5.3.1', '>=' );
$inline_script = sprintf(
"( function() {
function wpforms_admin_bar_menu_init() {
var template = document.getElementById( 'tmpl-wpforms-admin-menubar-data' ),
notifications = document.getElementById( 'wp-admin-bar-wpforms-notifications' );
if ( ! template ) {
return;
}
if ( ! notifications ) {
var menu = document.getElementById( 'wp-admin-bar-wpforms-menu-default' );
if ( ! menu ) {
return;
}
menu.insertAdjacentHTML( 'afterBegin', template.innerHTML );
} else {
notifications.insertAdjacentHTML( 'afterend', template.innerHTML );
}
};
%s
}() );",
$is_wp_531_plus ? "document.addEventListener( 'DOMContentLoaded', wpforms_admin_bar_menu_init );" : "if ( typeof( jQuery ) != 'undefined' ) { jQuery( wpforms_admin_bar_menu_init ); } else { document.addEventListener( 'DOMContentLoaded', wpforms_admin_bar_menu_init ); }"
);
wp_add_inline_script( 'admin-bar', $inline_script, 'before' );
}
/**
* Register and render admin bar menu items.
*
* @since 1.6.0
*
* @param WP_Admin_Bar $wp_admin_bar WordPress Admin Bar object.
*/
public function register( WP_Admin_Bar $wp_admin_bar ) {
$items = (array) apply_filters(
'wpforms_admin_adminbarmenu_register',
[
'main_menu',
'notification_menu',
'all_forms_menu',
'add_new_menu',
'community_menu',
'support_menu',
],
$wp_admin_bar
);
foreach ( $items as $item ) {
$this->{ $item }( $wp_admin_bar );
do_action( "wpforms_admin_adminbarmenu_register_{$item}_after", $wp_admin_bar );
}
}
/**
* Render primary top-level admin bar menu item.
*
* @since 1.6.0
*
* @param WP_Admin_Bar $wp_admin_bar WordPress Admin Bar object.
*/
public function main_menu( WP_Admin_Bar $wp_admin_bar ) {
$indicator = '';
$notifications = $this->has_notifications();
if ( $notifications ) {
$count = $notifications < 10 ? $notifications : '!';
$indicator = ' <div class="wp-core-ui wp-ui-notification wpforms-menu-notification-counter">' . $count . '</div>';
}
$wp_admin_bar->add_menu(
[
'id' => 'wpforms-menu',
'title' => 'WPForms' . $indicator,
'href' => admin_url( 'admin.php?page=wpforms-overview' ),
]
);
}
/**
* Render Notifications admin bar menu item.
*
* @since 1.6.0
*
* @param WP_Admin_Bar $wp_admin_bar WordPress Admin Bar object.
*/
public function notification_menu( WP_Admin_Bar $wp_admin_bar ) {
if ( ! $this->has_notifications() ) {
return;
}
$wp_admin_bar->add_menu(
[
'parent' => 'wpforms-menu',
'id' => 'wpforms-notifications',
'title' => esc_html__( 'Notifications', 'wpforms-lite' ) . ' <div class="wp-core-ui wp-ui-notification wpforms-menu-notification-indicator"></div>',
'href' => admin_url( 'admin.php?page=wpforms-overview' ),
]
);
}
/**
* Render All Forms admin bar menu item.
*
* @since 1.6.0
*
* @param WP_Admin_Bar $wp_admin_bar WordPress Admin Bar object.
*/
public function all_forms_menu( WP_Admin_Bar $wp_admin_bar ) {
$wp_admin_bar->add_menu(
[
'parent' => 'wpforms-menu',
'id' => 'wpforms-forms',
'title' => esc_html__( 'All Forms', 'wpforms-lite' ),
'href' => admin_url( 'admin.php?page=wpforms-overview' ),
]
);
}
/**
* Render Add New admin bar menu item.
*
* @since 1.6.0
*
* @param WP_Admin_Bar $wp_admin_bar WordPress Admin Bar object.
*/
public function add_new_menu( WP_Admin_Bar $wp_admin_bar ) {
$wp_admin_bar->add_menu(
[
'parent' => 'wpforms-menu',
'id' => 'wpforms-add-new',
'title' => esc_html__( 'Add New', 'wpforms-lite' ),
'href' => admin_url( 'admin.php?page=wpforms-builder' ),
]
);
}
/**
* Render Community admin bar menu item.
*
* @since 1.6.0
*
* @param WP_Admin_Bar $wp_admin_bar WordPress Admin Bar object.
*/
public function community_menu( WP_Admin_Bar $wp_admin_bar ) {
$wp_admin_bar->add_menu(
[
'parent' => 'wpforms-menu',
'id' => 'wpforms-community',
'title' => esc_html__( 'Community', 'wpforms-lite' ),
'href' => 'https://www.facebook.com/groups/wpformsvip/',
'meta' => [
'target' => '_blank',
'rel' => 'noopener noreferrer',
],
]
);
}
/**
* Render Support admin bar menu item.
*
* @since 1.6.0
* @since 1.7.4 Update the `Support` item title to `Help Docs`.
*
* @param WP_Admin_Bar $wp_admin_bar WordPress Admin Bar object.
*/
public function support_menu( WP_Admin_Bar $wp_admin_bar ) {
$href = add_query_arg(
[
'utm_campaign' => wpforms()->is_pro() ? 'plugin' : 'liteplugin',
'utm_medium' => 'admin-bar',
'utm_source' => 'WordPress',
'utm_content' => 'Documentation',
],
'https://wpforms.com/docs/'
);
$wp_admin_bar->add_menu(
[
'parent' => 'wpforms-menu',
'id' => 'wpforms-help-docs',
'title' => esc_html__( 'Help Docs', 'wpforms-lite' ),
'href' => $href,
'meta' => [
'target' => '_blank',
'rel' => 'noopener noreferrer',
],
]
);
}
/**
* Get form data for JS to modify the admin bar menu.
*
* @since 1.6.5
*
* @param array $forms Forms array.
*
* @return array
*/
protected function get_forms_data( $forms ) {
$data = [
'has_notifications' => $this->has_notifications(),
'edit_text' => esc_html__( 'Edit Form', 'wpforms-lite' ),
'entry_text' => esc_html__( 'View Entries', 'wpforms-lite' ),
'survey_text' => esc_html__( 'Survey Results', 'wpforms-lite' ),
'forms' => [],
];
foreach ( $forms as $form ) {
$form_id = absint( $form['id'] );
if ( empty( $form_id ) ) {
continue;
}
/* translators: %d - Form ID */
$form_title = sprintf( esc_html__( 'Form ID: %d', 'wpforms-lite' ), $form_id );
if ( ! empty( $form['settings'] ) && ! empty( $form['settings']['form_title'] ) ) {
$form_title = wp_html_excerpt(
sanitize_text_field( $form['settings']['form_title'] ),
99,
'…'
);
}
$data['forms'][] = apply_filters(
'wpforms_admin_adminbarmenu_get_form_data',
[
'form_id' => $form_id,
'title' => $form_title,
'edit_url' => admin_url( 'admin.php?page=wpforms-builder&view=fields&form_id=' . $form_id ),
]
);
}
return $data;
}
/**
* Add form(s) data to the page.
*
* @since 1.6.5
*
* @param array $forms Forms array.
*/
public function menu_forms_data_html( $forms ) {
if ( empty( $forms ) ) {
return;
}
echo wpforms_render( // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
'admin-bar-menu',
[
'forms_data' => $this->get_forms_data( $forms ),
],
true
);
}
}
Admin/Notifications/Notifications.php 0000644 00000037313 15133255232 0013737 0 ustar 00 <?php
namespace WPForms\Admin\Notifications;
/**
* Notifications.
*
* @since 1.7.5
*/
class Notifications {
/**
* Source of notifications content.
*
* @since 1.7.5
*
* @var string
*/
const SOURCE_URL = 'https://plugin.wpforms.com/wp-content/notifications.json';
/**
* Array of license types, that are considered being Elite level.
*
* @since 1.7.5
*
* @var array
*/
const LICENSES_ELITE = [ 'agency', 'ultimate', 'elite' ];
/**
* Option value.
*
* @since 1.7.5
*
* @var bool|array
*/
public $option = false;
/**
* Current license type.
*
* @since 1.7.5
*
* @var string
*/
private $license_type;
/**
* Initialize class.
*
* @since 1.7.5
*/
public function init() {
$this->hooks();
}
/**
* Register hooks.
*
* @since 1.7.5
*/
public function hooks() {
add_action( 'wpforms_overview_enqueue', [ $this, 'enqueues' ] );
add_action( 'wpforms_admin_overview_before_table', [ $this, 'output' ] );
add_action( 'wpforms_admin_notifications_update', [ $this, 'update' ] );
add_action( 'deactivate_plugin', [ $this, 'delete' ], 10, 2 );
add_action( 'wp_ajax_wpforms_notification_dismiss', [ $this, 'dismiss' ] );
}
/**
* Check if user has access and is enabled.
*
* @since 1.7.5
*
* @return bool
*/
public function has_access() {
$access = wpforms_current_user_can( 'view_forms' ) && ! wpforms_setting( 'hide-announcements' );
// phpcs:disable WPForms.PHP.ValidateHooks.InvalidHookName
/**
* Allow modifying state if a user has access.
*
* @since 1.6.0
*
* @param bool $access True if user has access.
*/
return (bool) apply_filters( 'wpforms_admin_notifications_has_access', $access );
// phpcs:enable WPForms.PHP.ValidateHooks.InvalidHookName
}
/**
* Get option value.
*
* @since 1.7.5
*
* @param bool $cache Reference property cache if available.
*
* @return array
*/
public function get_option( $cache = true ) {
if ( $this->option && $cache ) {
return $this->option;
}
$option = (array) get_option( 'wpforms_notifications', [] );
$this->option = [
'update' => ! empty( $option['update'] ) ? (int) $option['update'] : 0,
'feed' => ! empty( $option['feed'] ) ? (array) $option['feed'] : [],
'events' => ! empty( $option['events'] ) ? (array) $option['events'] : [],
'dismissed' => ! empty( $option['dismissed'] ) ? (array) $option['dismissed'] : [],
];
return $this->option;
}
/**
* Fetch notifications from feed.
*
* @since 1.7.5
*
* @return array
*/
public function fetch_feed() {
$response = wp_remote_get(
self::SOURCE_URL,
[
'timeout' => 10,
'user-agent' => wpforms_get_default_user_agent(),
]
);
if ( is_wp_error( $response ) ) {
return [];
}
$body = wp_remote_retrieve_body( $response );
if ( empty( $body ) ) {
return [];
}
return $this->verify( json_decode( $body, true ) );
}
/**
* Verify notification data before it is saved.
*
* @since 1.7.5
*
* @param array $notifications Array of notifications items to verify.
*
* @return array
*/
public function verify( $notifications ) {
$data = [];
if ( ! is_array( $notifications ) || empty( $notifications ) ) {
return $data;
}
foreach ( $notifications as $notification ) {
// Ignore if one of the conditional checks is true:
//
// 1. notification message is empty.
// 2. license type does not match.
// 3. notification is expired.
// 4. notification has already been dismissed.
// 5. notification existed before installing WPForms.
// (Prevents bombarding the user with notifications after activation).
if (
empty( $notification['content'] ) ||
! $this->is_license_type_match( $notification ) ||
$this->is_expired( $notification ) ||
$this->is_dismissed( $notification ) ||
$this->is_existed( $notification )
) {
continue;
}
$data[] = $notification;
}
return $data;
}
/**
* Verify saved notification data for active notifications.
*
* @since 1.7.5
*
* @param array $notifications Array of notifications items to verify.
*
* @return array
*/
public function verify_active( $notifications ) {
if ( ! is_array( $notifications ) || empty( $notifications ) ) {
return [];
}
$current_timestamp = time();
// Remove notifications that are not active.
foreach ( $notifications as $key => $notification ) {
if (
( ! empty( $notification['start'] ) && $current_timestamp < strtotime( $notification['start'] ) ) ||
( ! empty( $notification['end'] ) && $current_timestamp > strtotime( $notification['end'] ) )
) {
unset( $notifications[ $key ] );
}
}
return $notifications;
}
/**
* Get notification data.
*
* @since 1.7.5
*
* @return array
*/
public function get() {
if ( ! $this->has_access() ) {
return [];
}
$option = $this->get_option();
// Update notifications using async task.
if ( empty( $option['update'] ) || time() > $option['update'] + DAY_IN_SECONDS ) {
$tasks = wpforms()->get( 'tasks' );
if ( ! $tasks->is_scheduled( 'wpforms_admin_notifications_update' ) !== false ) {
$tasks
->create( 'wpforms_admin_notifications_update' )
->async()
->params()
->register();
}
}
$feed = ! empty( $option['feed'] ) ? $this->verify_active( $option['feed'] ) : [];
$events = ! empty( $option['events'] ) ? $this->verify_active( $option['events'] ) : [];
return array_merge( $feed, $events );
}
/**
* Get notification count.
*
* @since 1.7.5
*
* @return int
*/
public function get_count() {
return count( $this->get() );
}
/**
* Add a new Event Driven notification.
*
* @since 1.7.5
*
* @param array $notification Notification data.
*/
public function add( $notification ) {
if ( ! $this->is_valid( $notification ) ) {
return;
}
$option = $this->get_option();
// Notification ID already exists.
if ( ! empty( $option['events'][ $notification['id'] ] ) ) {
return;
}
update_option(
'wpforms_notifications',
[
'update' => $option['update'],
'feed' => $option['feed'],
'events' => array_merge( $notification, $option['events'] ),
'dismissed' => $option['dismissed'],
]
);
}
/**
* Determine if notification data is valid.
*
* @since 1.7.5
*
* @param array $notification Notification data.
*
* @return bool
*/
public function is_valid( $notification ) {
if ( empty( $notification['id'] ) ) {
return false;
}
return ! empty( $this->verify( [ $notification ] ) );
}
/**
* Determine if notification has already been dismissed.
*
* @since 1.7.5
*
* @param array $notification Notification data.
*
* @return bool
*/
private function is_dismissed( $notification ) {
$option = $this->get_option();
// phpcs:ignore WordPress.PHP.StrictInArray.MissingTrueStrict
return ! empty( $option['dismissed'] ) && in_array( $notification['id'], $option['dismissed'] );
}
/**
* Determine if license type is match.
*
* @since 1.7.5
*
* @param array $notification Notification data.
*
* @return bool
*/
private function is_license_type_match( $notification ) {
// A specific license type is not required.
if ( empty( $notification['type'] ) ) {
return true;
}
return in_array( $this->get_license_type(), (array) $notification['type'], true );
}
/**
* Determine if notification is expired.
*
* @since 1.7.5
*
* @param array $notification Notification data.
*
* @return bool
*/
private function is_expired( $notification ) {
return ! empty( $notification['end'] ) && time() > strtotime( $notification['end'] );
}
/**
* Determine if notification existed before installing WPForms.
*
* @since 1.7.5
*
* @param array $notification Notification data.
*
* @return bool
*/
private function is_existed( $notification ) {
$activated = wpforms_get_activated_timestamp();
return ! empty( $activated ) &&
! empty( $notification['start'] ) &&
$activated > strtotime( $notification['start'] );
}
/**
* Update notification data from feed.
*
* @since 1.7.5
*/
public function update() {
$option = $this->get_option();
$data = [
'update' => time(),
'feed' => $this->fetch_feed(),
'events' => $option['events'],
'dismissed' => $option['dismissed'],
];
// phpcs:disable WPForms.PHP.ValidateHooks.InvalidHookName
/**
* Allow changing notification data before it will be updated in database.
*
* @since 1.7.5
*
* @param array $data New notification data.
*/
$data = (array) apply_filters( 'wpforms_admin_notifications_update_data', $data );
// phpcs:enable WPForms.PHP.ValidateHooks.InvalidHookName
update_option( 'wpforms_notifications', $data );
}
/**
* Remove notification data from database before a plugin is deactivated.
*
* @since 1.7.5
*
* @param string $plugin Path to the plugin file relative to the plugins directory.
* @param bool $network_deactivating Whether the plugin is deactivated for all sites in the network
* or just the current site. Multisite only. Default false.
*/
public function delete( $plugin, $network_deactivating ) {
$wpforms_plugins = [
'wpforms-lite/wpforms.php',
'wpforms/wpforms.php',
];
if ( ! in_array( $plugin, $wpforms_plugins, true ) ) {
return;
}
delete_option( 'wpforms_notifications' );
}
/**
* Enqueue assets on Form Overview admin page.
*
* @since 1.7.5
*/
public function enqueues() {
if ( ! $this->get_count() ) {
return;
}
$min = wpforms_get_min_suffix();
wp_enqueue_style(
'wpforms-admin-notifications',
WPFORMS_PLUGIN_URL . "assets/css/admin-notifications{$min}.css",
[ 'wpforms-lity' ],
WPFORMS_VERSION
);
wp_enqueue_script(
'wpforms-admin-notifications',
WPFORMS_PLUGIN_URL . "assets/js/admin-notifications{$min}.js",
[ 'jquery', 'wpforms-lity' ],
WPFORMS_VERSION,
true
);
// Lity.
wp_enqueue_style(
'wpforms-lity',
WPFORMS_PLUGIN_URL . 'assets/lib/lity/lity.min.css',
[],
WPFORMS_VERSION
);
wp_enqueue_script(
'wpforms-lity',
WPFORMS_PLUGIN_URL . 'assets/lib/lity/lity.min.js',
[ 'jquery' ],
WPFORMS_VERSION,
true
);
}
/**
* Output notifications on Form Overview admin area.
*
* @since 1.7.5
*/
public function output() {
$notifications = $this->get();
if ( empty( $notifications ) ) {
return;
}
$notifications_html = '';
$current_class = ' current';
$content_allowed_tags = [
'em' => [],
'strong' => [],
'span' => [
'style' => [],
],
'a' => [
'href' => [],
'target' => [],
'rel' => [],
],
];
foreach ( $notifications as $notification ) {
// Prepare required arguments.
$notification = wp_parse_args(
$notification,
[
'id' => 0,
'title' => '',
'content' => '',
'video' => '',
]
);
$title = $this->get_component_data( $notification['title'] );
$content = $this->get_component_data( $notification['content'] );
if ( ! $title && ! $content ) {
continue;
}
// Notification HTML.
$notifications_html .= sprintf(
'<div class="wpforms-notifications-message%5$s" data-message-id="%4$s">
<h3 class="wpforms-notifications-title">%1$s%6$s</h3>
<p class="wpforms-notifications-content">%2$s</p>
%3$s
</div>',
esc_html( $title ),
wp_kses( $content, $content_allowed_tags ),
$this->get_notification_buttons_html( $notification ),
esc_attr( $notification['id'] ),
esc_attr( $current_class ),
$this->get_video_badge_html( $this->get_component_data( $notification['video'] ) )
);
// Only first notification is current.
$current_class = '';
}
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo wpforms_render(
'admin/notifications',
[
'notifications' => [
'count' => count( $notifications ),
'html' => $notifications_html,
],
],
true
);
}
/**
* Retrieve notification's buttons HTML.
*
* @since 1.7.5
*
* @param array $notification Notification data.
*
* @return string
*/
private function get_notification_buttons_html( $notification ) {
$html = '';
if ( empty( $notification['btns'] ) || ! is_array( $notification['btns'] ) ) {
return $html;
}
foreach ( $notification['btns'] as $btn_type => $btn ) {
$btn = $this->get_component_data( $btn );
if ( ! $btn ) {
continue;
}
$url = $this->prepare_btn_url( $btn );
$target = ! empty( $btn['target'] ) ? $btn['target'] : '_blank';
$target = ! empty( $url ) && strpos( $url, home_url() ) === 0 ? '_self' : $target;
$html .= sprintf(
'<a href="%1$s" class="button button-%2$s"%3$s>%4$s</a>',
esc_url( $url ),
$btn_type === 'main' ? 'primary' : 'secondary',
$target === '_blank' ? ' target="_blank" rel="noopener noreferrer"' : '',
! empty( $btn['text'] ) ? esc_html( $btn['text'] ) : ''
);
}
return ! empty( $html ) ? sprintf( '<div class="wpforms-notifications-buttons">%s</div>', $html ) : '';
}
/**
* Retrieve notification's component data by a license type.
*
* @since 1.7.5
*
* @param mixed $data Component data.
*
* @return false|mixed
*/
private function get_component_data( $data ) {
if ( empty( $data['license'] ) ) {
return $data;
}
$license_type = $this->get_license_type();
if ( in_array( $license_type, self::LICENSES_ELITE, true ) ) {
$license_type = 'elite';
}
return ! empty( $data['license'][ $license_type ] ) ? $data['license'][ $license_type ] : false;
}
/**
* Retrieve the current installation license type (always lowercase).
*
* @since 1.7.5
*
* @return string
*/
private function get_license_type() {
if ( $this->license_type ) {
return $this->license_type;
}
$this->license_type = wpforms_get_license_type();
if ( ! $this->license_type ) {
$this->license_type = 'lite';
}
return $this->license_type;
}
/**
* Dismiss notification via AJAX.
*
* @since 1.7.5
*/
public function dismiss() {
// Check for required param, security and access.
if (
empty( $_POST['id'] ) ||
! check_ajax_referer( 'wpforms-admin', 'nonce', false ) ||
! $this->has_access()
) {
wp_send_json_error();
}
$id = sanitize_key( $_POST['id'] );
$type = is_numeric( $id ) ? 'feed' : 'events';
$option = $this->get_option();
$option['dismissed'][] = $id;
$option['dismissed'] = array_unique( $option['dismissed'] );
// Remove notification.
if ( is_array( $option[ $type ] ) && ! empty( $option[ $type ] ) ) {
foreach ( $option[ $type ] as $key => $notification ) {
if ( (string) $notification['id'] === (string) $id ) {
unset( $option[ $type ][ $key ] );
break;
}
}
}
update_option( 'wpforms_notifications', $option );
wp_send_json_success();
}
/**
* Prepare button URL.
*
* @since 1.7.5
*
* @param array $btn Button data.
*
* @return string
*/
private function prepare_btn_url( $btn ) {
if ( empty( $btn['url'] ) ) {
return '';
}
$replace_tags = [
'{admin_url}' => admin_url(),
];
return str_replace( array_keys( $replace_tags ), array_values( $replace_tags ), $btn['url'] );
}
/**
* Get the notification's video badge HTML.
*
* @since 1.7.5
*
* @param string $video_url Valid video URL.
*
* @return string
*/
private function get_video_badge_html( $video_url ) {
$video_url = wp_http_validate_url( $video_url );
if ( empty( $video_url ) ) {
return '';
}
$data_attr_lity = wp_is_mobile() ? '' : 'data-lity';
return sprintf(
'<a class="wpforms-notifications-badge" href="%1$s" %2$s><i class="fa fa-youtube-play" aria-hidden="true"></i>%3$s</a>',
esc_url( $video_url ),
esc_attr( $data_attr_lity ),
esc_html__( 'Watch Video', 'wpforms-lite' )
);
}
}
Admin/Notifications/EventDriven.php 0000644 00000053131 15133255232 0013353 0 ustar 00 <?php
namespace WPForms\Admin\Notifications;
/**
* Class EventDriven.
*
* @since 1.7.5
*/
class EventDriven {
/**
* WPForms version when the Event Driven feature has been introduced.
*
* @since 1.7.5
*
* @var string
*/
const FEATURE_INTRODUCED = '1.7.5';
/**
* Expected date format for notifications.
*
* @since 1.7.5
*
* @var string
*/
const DATE_FORMAT = 'Y-m-d H:i:s';
/**
* Common UTM parameters.
*
* @since 1.7.5
*
* @var array
*/
const UTM_PARAMS = [
'utm_source' => 'WordPress',
'utm_medium' => 'Event Notification',
];
/**
* Common targets for date logic.
*
* Available items:
* - upgraded (upgraded to a latest version)
* - activated
* - forms_first_created
* - X.X.X.X (upgraded to a specific version)
* - pro (activated/installed)
* - lite (activated/installed)
*
* @since 1.7.5
*
* @var array
*/
const DATE_LOGIC = [ 'upgraded', 'activated', 'forms_first_created' ];
/**
* Timestamps.
*
* @since 1.7.5
*
* @var array
*/
private $timestamps = [];
/**
* Initialize class.
*
* @since 1.7.5
*/
public function init() {
if ( ! $this->allow_load() ) {
return;
}
$this->hooks();
}
/**
* Indicate if this is allowed to load.
*
* @since 1.7.5
*
* @return bool
*/
private function allow_load() {
return wpforms()->get( 'notifications' )->has_access() || wp_doing_cron();
}
/**
* Hooks.
*
* @since 1.7.5
*/
private function hooks() {
add_filter( 'wpforms_admin_notifications_update_data', [ $this, 'update_events' ] );
}
/**
* Add Event Driven notifications before saving them in database.
*
* @since 1.7.5
*
* @param array $data Notification data.
*
* @return array
*/
public function update_events( $data ) {
$updated = [];
/**
* Allow developers to turn on debug mode: store all notifications and then show all of them.
*
* @since 1.7.5
*
* @param bool $is_debug True if it's a debug mode. Default: false.
*/
$is_debug = (bool) apply_filters( 'wpforms_admin_notifications_event_driven_update_events_debug', false );
$wpforms_notifications = wpforms()->get( 'notifications' );
foreach ( $this->get_notifications() as $slug => $notification ) {
$is_processed = ! empty( $data['events'][ $slug ]['start'] );
$is_conditional_ok = ! ( isset( $notification['condition'] ) && $notification['condition'] === false );
// If it's a debug mode OR valid notification has been already processed - skip running logic checks and save it.
if (
$is_debug ||
( $is_processed && $is_conditional_ok && $wpforms_notifications->is_valid( $data['events'][ $slug ] ) )
) {
unset( $notification['date_logic'], $notification['offset'], $notification['condition'] );
// phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
$notification['start'] = $is_debug ? date( self::DATE_FORMAT ) : $data['events'][ $slug ]['start'];
$updated[ $slug ] = $notification;
continue;
}
// Ignore if a condition is not passed conditional checks.
if ( ! $is_conditional_ok ) {
continue;
}
$timestamp = $this->get_timestamp_by_date_logic(
$this->prepare_date_logic( $notification )
);
if ( empty( $timestamp ) ) {
continue;
}
// Probably, notification should be visible after some time.
$offset = empty( $notification['offset'] ) ? 0 : absint( $notification['offset'] );
// Set a start date when notification will be shown.
// phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
$notification['start'] = date( self::DATE_FORMAT, $timestamp + $offset );
// Ignore if notification data is not valid.
if ( ! $wpforms_notifications->is_valid( $notification ) ) {
continue;
}
// Remove unnecessary values, mark notification as active, and save it.
unset( $notification['date_logic'], $notification['offset'], $notification['condition'] );
$updated[ $slug ] = $notification;
}
$data['events'] = $updated;
return $data;
}
/**
* Prepare and retrieve date logic.
*
* @since 1.7.5
*
* @param array $notification Notification data.
*
* @return array
*/
private function prepare_date_logic( $notification ) {
$date_logic = empty( $notification['date_logic'] ) || ! is_array( $notification['date_logic'] ) ? self::DATE_LOGIC : $notification['date_logic'];
return array_filter( array_filter( $date_logic, 'is_string' ) );
}
/**
* Retrieve a notification timestamp based on date logic.
*
* @since 1.7.5
*
* @param array $args Date logic.
*
* @return int
*/
private function get_timestamp_by_date_logic( $args ) {
foreach ( $args as $target ) {
if ( ! empty( $this->timestamps[ $target ] ) ) {
return $this->timestamps[ $target ];
}
$timestamp = call_user_func(
$this->get_timestamp_callback( $target ),
$target
);
if ( ! empty( $timestamp ) ) {
$this->timestamps[ $target ] = $timestamp;
return $timestamp;
}
}
return 0;
}
/**
* Retrieve a callback that determines needed timestamp.
*
* @since 1.7.5
*
* @param string $target Date logic target.
*
* @return callable
*/
private function get_timestamp_callback( $target ) {
$raw_target = $target;
// As $target should be a part of name for callback method,
// this regular expression allow lowercase characters, numbers, and underscore.
$target = strtolower( preg_replace( '/[^a-z0-9_]/', '', $target ) );
// Basic callback.
$callback = [ $this, 'get_timestamp_' . $target ];
// Determine if a special version number is passed.
// Uses the regular expression to check a SemVer string.
// @link https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string.
if ( preg_match( '/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:\.([1-9\d*]))?(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/', $raw_target ) ) {
$callback = [ $this, 'get_timestamp_upgraded' ];
}
// If callback is callable, return it. Otherwise, return fallback.
return is_callable( $callback ) ? $callback : '__return_zero';
}
/**
* Retrieve a timestamp when WPForms was upgraded.
*
* @since 1.7.5
*
* @param string $version WPForms version.
*
* @return int|false Unix timestamp. False on failure.
*/
private function get_timestamp_upgraded( $version ) {
if ( $version === 'upgraded' ) {
$version = WPFORMS_VERSION;
}
$timestamp = wpforms_get_upgraded_timestamp( $version );
if ( $timestamp === false ) {
return false;
}
// Return a current timestamp if no luck to return a migration's timestamp.
return $timestamp <= 0 ? time() : $timestamp;
}
/**
* Retrieve a timestamp when WPForms was first installed/activated.
*
* @since 1.7.5
*
* @return int|false Unix timestamp. False on failure.
*/
private function get_timestamp_activated() {
return wpforms_get_activated_timestamp();
}
/**
* Retrieve a timestamp when Lite was first installed.
*
* @since 1.7.5
*
* @return int|false Unix timestamp. False on failure.
*/
private function get_timestamp_lite() {
$activated = (array) get_option( 'wpforms_activated', [] );
return ! empty( $activated['lite'] ) ? absint( $activated['lite'] ) : false;
}
/**
* Retrieve a timestamp when Pro was first installed.
*
* @since 1.7.5
*
* @return int|false Unix timestamp. False on failure.
*/
private function get_timestamp_pro() {
$activated = (array) get_option( 'wpforms_activated', [] );
return ! empty( $activated['pro'] ) ? absint( $activated['pro'] ) : false;
}
/**
* Retrieve a timestamp when a first form was created.
*
* @since 1.7.5
*
* @return int|false Unix timestamp. False on failure.
*/
private function get_timestamp_forms_first_created() {
$timestamp = get_option( 'wpforms_forms_first_created' );
return ! empty( $timestamp ) ? absint( $timestamp ) : false;
}
/**
* Retrieve a number of entries.
*
* @since 1.7.5
*
* @return int
*/
private function get_entry_count() {
static $count;
if ( is_int( $count ) ) {
return $count;
}
global $wpdb;
$count = 0;
$entry_handler = wpforms()->get( 'entry' );
$entry_meta_handler = wpforms()->get( 'entry_meta' );
if ( ! $entry_handler || ! $entry_meta_handler ) {
return $count;
}
$query = "SELECT COUNT({$entry_handler->primary_key})
FROM {$entry_handler->table_name}
WHERE {$entry_handler->primary_key}
NOT IN (
SELECT entry_id
FROM {$entry_meta_handler->table_name}
WHERE type = 'backup_id'
);";
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared
$count = (int) $wpdb->get_var( $query );
return $count;
}
/**
* Retrieve forms.
*
* @since 1.7.5
*
* @param int $posts_per_page Number of form to return.
*
* @return array
*/
private function get_forms( $posts_per_page ) {
$forms = wpforms()->get( 'form' )->get(
'',
[
'posts_per_page' => (int) $posts_per_page,
'nopaging' => false,
'update_post_meta_cache' => false,
'update_post_term_cache' => false,
'cap' => false,
]
);
return ! empty( $forms ) ? (array) $forms : [];
}
/**
* Determine if the user has at least 1 form.
*
* @since 1.7.5
*
* @return bool
*/
private function has_form() {
return ! empty( $this->get_forms( 1 ) );
}
/**
* Determine if it is a new user.
*
* @since 1.7.5
*
* @return bool
*/
private function is_new_user() {
// Check if this is an update or first install.
return ! get_option( 'wpforms_version_upgraded_from' );
}
/**
* Determine if it's an English site.
*
* @since 1.7.5
*
* @return bool
*/
private function is_english_site() {
static $result;
if ( is_bool( $result ) ) {
return $result;
}
$locales = array_unique(
array_map(
[ $this, 'language_to_iso' ],
[ get_locale(), get_user_locale() ]
)
);
$result = count( $locales ) === 1 && $locales[0] === 'en';
return $result;
}
/**
* Convert language to ISO.
*
* @since 1.7.5
*
* @param string $lang Language value.
*
* @return string
*/
private function language_to_iso( $lang ) {
return $lang === '' ? $lang : explode( '_', $lang )[0];
}
/**
* Retrieve a modified URL query string.
*
* @since 1.7.5
*
* @param array $args An associative array of query variables.
* @param string $url A URL to act upon.
*
* @return string
*/
private function add_query_arg( $args, $url ) {
return add_query_arg(
array_merge( $this->get_utm_params(), array_map( 'rawurlencode', $args ) ),
$url
);
}
/**
* Retrieve UTM parameters for Event Driven notifications links.
*
* @since 1.7.5
*
* @return array
*/
private function get_utm_params() {
static $utm_params;
if ( ! $utm_params ) {
$utm_params = [
'utm_source' => self::UTM_PARAMS['utm_source'],
'utm_medium' => rawurlencode( self::UTM_PARAMS['utm_medium'] ),
'utm_campaign' => wpforms()->is_pro() ? 'plugin' : 'liteplugin',
];
}
return $utm_params;
}
/**
* Retrieve Event Driven notifications.
*
* @since 1.7.5
*
* @return array
*/
private function get_notifications() {
return [
'welcome-message' => [
'id' => 'welcome-message',
'title' => esc_html__( 'Welcome to WPForms!', 'wpforms-lite' ),
'content' => esc_html__( 'We’re grateful that you chose WPForms for your website! Now that you’ve installed the plugin, you’re less than 5 minutes away from publishing your first form. To make it easy, we’ve got 400+ form templates to get you started!', 'wpforms-lite' ),
'btns' => [
'main' => [
'url' => admin_url( 'admin.php?page=wpforms-builder' ),
'text' => esc_html__( 'Start Building', 'wpforms-lite' ),
],
'alt' => [
'url' => $this->add_query_arg(
[ 'utm_content' => 'Welcome Read the Guide' ],
'https://wpforms.com/docs/creating-first-form/'
),
'text' => esc_html__( 'Read the Guide', 'wpforms-lite' ),
],
],
'type' => [
'lite',
'basic',
'plus',
'pro',
'agency',
'elite',
'ultimate',
],
// Immediately after activation (new users only, not upgrades).
'condition' => $this->is_new_user(),
],
'wp-mail-smtp-education' => [
'id' => 'wp-mail-smtp-education',
'title' => esc_html__( 'Don’t Miss Your Form Notification Emails!', 'wpforms-lite' ),
'content' => esc_html__( 'Did you know that many WordPress sites are not properly configured to send emails? With the free WP Mail SMTP plugin, you can easily optimize your site to send emails, avoid the spam folder, and make sure your emails land in the recipient’s inbox every time.', 'wpforms-lite' ),
'btns' => [
'main' => [
'url' => admin_url( 'admin.php?page=wpforms-smtp' ),
'text' => esc_html__( 'Install Now', 'wpforms-lite' ),
],
'alt' => [
'url' => $this->add_query_arg(
[ 'utm_content' => 'WP Mail SMTP Learn More' ],
'https://wpforms.com/docs/how-to-set-up-smtp-using-the-wp-mail-smtp-plugin/'
),
'text' => esc_html__( 'Learn More', 'wpforms-lite' ),
],
],
// 3 days after activation/upgrade.
'offset' => 3 * DAY_IN_SECONDS,
'condition' => ! function_exists( 'wp_mail_smtp' ),
],
'join-vip-circle' => [
'id' => 'join-vip-circle',
'title' => esc_html__( 'Want to Be a VIP? Join Now!', 'wpforms-lite' ),
'content' => esc_html__( 'Running a WordPress site can be challenging. But help is just around the corner! Our Facebook group contains tons of tips and help to get your business growing! When you join our VIP Circle, you’ll get instant access to tips, tricks, and answers from a community of loyal WPForms users. Best of all, membership is 100% free!', 'wpforms-lite' ),
'btns' => [
'main' => [
'url' => 'https://www.facebook.com/groups/wpformsvip/',
'text' => esc_html__( 'Join Now', 'wpforms-lite' ),
],
],
// 30 days after activation/upgrade.
'offset' => 30 * DAY_IN_SECONDS,
],
'survey-reports' => [
'id' => 'survey-reports',
'title' => esc_html__( 'Want to Know What Your Customers Really Think?', 'wpforms-lite' ),
'content' => esc_html__( 'Nothing beats real feedback from your customers and visitors. That’s why many small businesses love our awesome Surveys and Polls addon. Instantly unlock full survey reporting right in your WordPress dashboard. And don’t forget: building a survey is easy with our pre-made templates, so you could get started within a few minutes!', 'wpforms-lite' ),
'btns' => [
'main' => [
'license' => [
'lite' => [
'url' => $this->add_query_arg(
[ 'utm_content' => 'Surveys and Polls Upgrade Lite' ],
'https://wpforms.com/lite-upgrade/'
),
'text' => esc_html__( 'Upgrade Now', 'wpforms-lite' ),
],
'basic' => [
'url' => $this->add_query_arg(
[ 'utm_content' => 'Surveys and Polls Upgrade Basic' ],
'https://wpforms.com/account/licenses/'
),
'text' => esc_html__( 'Upgrade Now', 'wpforms-lite' ),
],
'plus' => [
'url' => $this->add_query_arg(
[ 'utm_content' => 'Surveys and Polls Upgrade Basic' ],
'https://wpforms.com/account/licenses/'
),
'text' => esc_html__( 'Upgrade Now', 'wpforms-lite' ),
],
'pro' => [
'url' => admin_url( 'admin.php?page=wpforms-addons' ),
'text' => esc_html__( 'Install Now', 'wpforms-lite' ),
],
'elite' => [
'url' => admin_url( 'admin.php?page=wpforms-addons' ),
'text' => esc_html__( 'Install Now', 'wpforms-lite' ),
],
],
],
'alt' => [
'url' => $this->add_query_arg(
[ 'utm_content' => 'Surveys and Polls Learn More' ],
'https://wpforms.com/docs/how-to-install-and-use-the-surveys-and-polls-addon/'
),
'text' => esc_html__( 'Learn More', 'wpforms-lite' ),
],
],
// 45 days after activation/upgrade.
'offset' => 45 * DAY_IN_SECONDS,
'condition' => ! defined( 'WPFORMS_SURVEYS_POLLS_VERSION' ),
],
'form-abandonment' => [
'id' => 'form-abandonment',
'title' => esc_html__( 'Get More Leads From Your Forms!', 'wpforms-lite' ),
'content' => esc_html__( 'Are your forms converting fewer visitors than you hoped? Often, visitors quit forms partway through. That can prevent you from getting all the leads you deserve to capture. With our Form Abandonment addon, you can capture partial entries even if your visitor didn’t hit Submit! From there, it’s easy to follow up with leads and turn them into loyal customers.', 'wpforms-lite' ),
'btns' => [
'main' => [
'license' => [
'lite' => [
'url' => $this->add_query_arg(
[ 'utm_content' => 'Form Abandonment Upgrade Lite' ],
'https://wpforms.com/lite-upgrade/'
),
'text' => esc_html__( 'Upgrade Now', 'wpforms-lite' ),
],
'basic' => [
'url' => $this->add_query_arg(
[ 'utm_content' => 'Form Abandonment Upgrade Basic' ],
'https://wpforms.com/account/licenses/'
),
'text' => esc_html__( 'Upgrade Now', 'wpforms-lite' ),
],
'plus' => [
'url' => $this->add_query_arg(
[ 'utm_content' => 'Form Abandonment Upgrade Basic' ],
'https://wpforms.com/account/licenses/'
),
'text' => esc_html__( 'Upgrade Now', 'wpforms-lite' ),
],
'pro' => [
'url' => admin_url( 'admin.php?page=wpforms-addons' ),
'text' => esc_html__( 'Install Now', 'wpforms-lite' ),
],
'elite' => [
'url' => admin_url( 'admin.php?page=wpforms-addons' ),
'text' => esc_html__( 'Install Now', 'wpforms-lite' ),
],
],
],
'alt' => [
'url' => $this->add_query_arg(
[ 'utm_content' => 'Form Abandonment Learn More' ],
'https://wpforms.com/docs/how-to-install-and-use-form-abandonment-with-wpforms/'
),
'text' => esc_html__( 'Learn More', 'wpforms-lite' ),
],
],
// 60 days after activation/upgrade.
'offset' => 60 * DAY_IN_SECONDS,
'condition' => ! defined( 'WPFORMS_FORM_ABANDONMENT_VERSION' ),
],
'translation-community' => [
'id' => 'translation-community',
'title' => esc_html__( 'WPForms Needs Your Translation Skills!', 'wpforms-lite' ),
'content' => esc_html__( 'Did you know that WPForms has its own Translation Community? We’re on the lookout for skilled translators to help make WPForms truly accessible to our millions of users around the world. You can earn WPForms swag and a free Pro license. Want to get involved?', 'wpforms-lite' ),
'btns' => [
'main' => [
'url' => 'https://wpforms.com/translate/',
'text' => esc_html__( 'Join Now', 'wpforms-lite' ),
],
],
// 90 days after activation/upgrade.
'offset' => 90 * DAY_IN_SECONDS,
'condition' => $this->has_form() && ! $this->is_english_site(),
],
'ideas' => [
'id' => 'ideas',
'title' => esc_html__( 'What’s Your Dream WPForms Feature?', 'wpforms-lite' ),
'content' => esc_html__( 'If you could add just one feature to WPForms, what would it be? We want to know! Our team is busy surveying valued customers like you as we plan the year ahead. We’d love to know which features would take your business to the next level! Do you have a second to share your idea with us?', 'wpforms-lite' ),
'btns' => [
'main' => [
'url' => 'https://wpforms.com/share-your-idea/',
'text' => esc_html__( 'Share Your Idea', 'wpforms-lite' ),
],
],
// 120 days after activation/upgrade.
'offset' => 120 * DAY_IN_SECONDS,
'condition' => $this->has_form(),
],
'user-insights' => [
'id' => 'user-insights',
'title' => esc_html__( 'Congratulations! You Just Got Your 100th Form Entry!', 'wpforms-lite' ),
'content' => esc_html__( 'You just hit 100 entries… and this is just the beginning! Now it’s time to dig into the data and figure out what makes your visitors tick. The User Journey addon shows you what your visitors looked at before submitting your form. Now you can easily find which areas of your site are triggering form conversions.', 'wpforms-lite' ),
'btns' => [
'main' => [
'license' => [
'lite' => [
'url' => $this->add_query_arg(
[ 'utm_content' => 'User Journey Upgrade Lite' ],
'https://wpforms.com/lite-upgrade/'
),
'text' => esc_html__( 'Upgrade Now', 'wpforms-lite' ),
],
'basic' => [
'url' => $this->add_query_arg(
[ 'utm_content' => 'User Journey Upgrade Basic' ],
'https://wpforms.com/account/licenses/'
),
'text' => esc_html__( 'Upgrade Now', 'wpforms-lite' ),
],
'plus' => [
'url' => $this->add_query_arg(
[ 'utm_content' => 'User Journey Upgrade Basic' ],
'https://wpforms.com/account/licenses/'
),
'text' => esc_html__( 'Upgrade Now', 'wpforms-lite' ),
],
'pro' => [
'url' => admin_url( 'admin.php?page=wpforms-addons' ),
'text' => esc_html__( 'Install Now', 'wpforms-lite' ),
],
'elite' => [
'url' => admin_url( 'admin.php?page=wpforms-addons' ),
'text' => esc_html__( 'Install Now', 'wpforms-lite' ),
],
],
],
],
'condition' => ! defined( 'WPFORMS_USER_JOURNEY_VERSION' ) && $this->get_entry_count() >= 100,
],
];
}
}
Admin/Forms/Tags.php 0000644 00000036276 15133255232 0010310 0 ustar 00 <?php
namespace WPForms\Admin\Forms;
use WP_Post;
use WPForms_Form_Handler;
use WPForms_Overview_Table;
/**
* Tags on All Forms page.
*
* @since 1.7.5
*/
class Tags {
/**
* Current tags filter.
*
* @since 1.7.5
*
* @var array
*/
private $tags_filter;
/**
* Current view slug.
*
* @since 1.7.5
*
* @var string
*/
private $current_view;
/**
* Base URL.
*
* @since 1.7.5
*
* @var string
*/
private $base_url;
/**
* Determine if the class is allowed to load.
*
* @since 1.7.5
*
* @return bool
*/
private function allow_load() {
// Load only on the `All Forms` admin page.
return wpforms_is_admin_page( 'overview' );
}
/**
* Initialize class.
*
* @since 1.7.5
*/
public function init() {
// In case of AJAX call we need to initialize base URL only.
if ( wp_doing_ajax() ) {
$this->base_url = admin_url( 'admin.php?page=wpforms-overview' );
}
if ( ! $this->allow_load() ) {
return;
}
$this->update_view_vars();
$this->hooks();
}
/**
* Hooks.
*
* @since 1.7.5
*/
private function hooks() {
add_action( 'init', [ $this, 'update_tags_filter' ] );
add_action( 'admin_enqueue_scripts', [ $this, 'enqueues' ] );
add_action( 'wpforms_admin_overview_before_rows', [ $this, 'bulk_edit_tags' ] );
add_filter( 'wpforms_get_multiple_forms_args', [ $this, 'get_forms_args' ] );
add_filter( 'wpforms_admin_forms_bulk_actions_get_dropdown_items', [ $this, 'add_bulk_action' ], 10, 2 );
add_filter( 'wpforms_overview_table_columns', [ $this, 'filter_columns' ] );
}
/**
* Init view-related variables.
*
* @since 1.7.5
*/
private function update_view_vars() {
$views_object = wpforms()->get( 'forms_views' );
$this->current_view = $views_object->get_current_view();
$view_config = $views_object->get_view_by_slug( $this->current_view );
$this->base_url = remove_query_arg(
[ 'tags', 'search', 'action', 'action2', '_wpnonce', 'form_id', 'paged', '_wp_http_referer' ],
$views_object->get_base_url()
);
// Base URL should contain variable according to the current view.
if (
isset( $view_config['get_var'], $view_config['get_var_value'] ) && $this->current_view !== 'all'
) {
$this->base_url = add_query_arg( $view_config['get_var'], $view_config['get_var_value'], $this->base_url );
}
// Base URL fallback.
$this->base_url = empty( $this->base_url ) ? admin_url( 'admin.php?page=wpforms-overview' ) : $this->base_url;
}
/**
* Update tags filter value.
*
* @since 1.7.5
*/
public function update_tags_filter() {
// Do not need to update this property while doing AJAX.
if ( wp_doing_ajax() ) {
return;
}
// phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.MissingUnslash
$tags = isset( $_GET['tags'] ) ? sanitize_text_field( wp_unslash( rawurldecode( $_GET['tags'] ) ) ) : '';
$tags_slugs = explode( ',', $tags );
$tags_filter = array_filter(
self::get_all_tags_choices(),
static function( $tag ) use ( $tags_slugs ) {
return in_array( trim( rawurldecode( $tag['slug'] ) ), $tags_slugs, true );
}
);
$this->tags_filter = array_map( 'absint', wp_list_pluck( $tags_filter, 'value' ) );
}
/**
* Enqueue assets.
*
* @since 1.7.5
*/
public function enqueues() {
wp_enqueue_script(
'wpforms-admin-forms-overview-choicesjs',
WPFORMS_PLUGIN_URL . 'assets/lib/choices.min.js',
[],
'9.0.1',
true
);
wp_localize_script(
'wpforms-admin-forms-overview-choicesjs',
'wpforms_admin_forms_overview',
[
'choicesjs_config' => self::get_choicesjs_config(),
'edit_tags_form' => $this->get_column_tags_form(),
'all_tags_choices' => self::get_all_tags_choices(),
'strings' => $this->get_localize_strings(),
]
);
}
/**
* Get Choices.js configuration.
*
* @since 1.7.5
*/
public static function get_choicesjs_config() {
return [
'removeItemButton' => true,
'shouldSort' => false,
'loadingText' => esc_html__( 'Loading...', 'wpforms-lite' ),
'noResultsText' => esc_html__( 'No results found', 'wpforms-lite' ),
'noChoicesText' => esc_html__( 'No tags to choose from', 'wpforms-lite' ),
'itemSelectText' => '',
'searchEnabled' => true,
'searchChoices' => true,
'searchFloor' => 1,
'searchResultLimit' => 100,
'searchFields' => [ 'label' ],
// These `fuseOptions` options enable the search of chars not only from the beginning of the tags.
'fuseOptions' => [
'threshold' => 0.1,
'distance' => 1000,
'location' => 2,
],
];
}
/**
* Get all tags (terms) as items for Choices.js.
*
* @since 1.7.5
*
* @return array
*/
public static function get_all_tags_choices() {
static $choices = null;
if ( is_array( $choices ) ) {
return $choices;
}
$choices = [];
$tags = get_terms(
[
'taxonomy' => WPForms_Form_Handler::TAGS_TAXONOMY,
'hide_empty' => false,
]
);
foreach ( $tags as $tag ) {
$choices[] = [
'value' => (string) $tag->term_id,
'slug' => $tag->slug,
'label' => sanitize_term_field( 'name', $tag->name, $tag->term_id, WPForms_Form_Handler::TAGS_TAXONOMY, 'display' ),
'count' => (int) $tag->count,
];
}
return $choices;
}
/**
* Determine if the Tags column is hidden.
*
* @since 1.7.5
*
* @return bool
*/
private function is_tags_column_hidden() {
$overview_table = WPForms_Overview_Table::get_instance();
$columns = $overview_table->__call( 'get_column_info', [] );
return isset( $columns[1] ) && in_array( 'tags', $columns[1], true );
}
/**
* Get localize strings.
*
* @since 1.7.5
*/
private function get_localize_strings() {
return [
'nonce' => wp_create_nonce( 'wpforms-admin-forms-overview-nonce' ),
'is_tags_column_hidden' => $this->is_tags_column_hidden(),
'base_url' => admin_url( 'admin.php?' . wp_parse_url( $this->base_url, PHP_URL_QUERY ) ),
'add_new_tag' => esc_html__( 'Press Enter or "," key to add new tag', 'wpforms-lite' ),
'error' => esc_html__( 'Something wrong. Please try again later.', 'wpforms-lite' ),
'all_tags' => esc_html__( 'All Tags', 'wpforms-lite' ),
'bulk_edit_one_form' => wp_kses(
__( '<strong>1 form</strong> selected for Bulk Edit.', 'wpforms-lite' ),
[ 'strong' => [] ]
),
'bulk_edit_n_forms' => wp_kses( /* translators: %d - Number of forms selected for Bulk Edit. */
__( '<strong>%d forms</strong> selected for Bulk Edit.', 'wpforms-lite' ),
[ 'strong' => [] ]
),
'manage_tags_title' => esc_html__( 'Manage Tags', 'wpforms-lite' ),
'manage_tags_desc' => esc_html__( 'Delete tags that you\'re no longer using. Deleting a tag will remove it from a form, but will not delete the form itself.', 'wpforms-lite' ),
'manage_tags_save' => esc_html__( 'Delete Tags', 'wpforms-lite' ),
'manage_tags_one_tag' => wp_kses(
__( 'You have <strong>1 tag</strong> selected for deletion.', 'wpforms-lite' ),
[ 'strong' => [] ]
),
'manage_tags_n_tags' => wp_kses( /* translators: %d - Number of forms selected for Bulk Edit. */
__( 'You have <strong>%d tags</strong> selected for deletion.', 'wpforms-lite' ),
[ 'strong' => [] ]
),
'manage_tags_no_tags' => wp_kses(
__( 'There are no tags to delete.<br>Please create at least one by adding it to any form.', 'wpforms-lite' ),
[ 'br' => [] ]
),
'manage_tags_one_deleted' => esc_html__( '1 tag was successfully deleted.', 'wpforms-lite' ),
/* translators: %d - Number of deleted tags. */
'manage_tags_n_deleted' => esc_html__( '%d tags were successfully deleted.', 'wpforms-lite' ),
'manage_tags_result_title' => esc_html__( 'Almost done!', 'wpforms-lite' ),
'manage_tags_result_text' => esc_html__( 'In order to update the tags in the forms list, please refresh the page.', 'wpforms-lite' ),
'manage_tags_btn_refresh' => esc_html__( 'Refresh', 'wpforms-lite' ),
];
}
/**
* Determine if tags are editable.
*
* @since 1.7.5
*
* @param int|null $form_id Form ID.
*
* @return bool
*/
private function is_editable( $form_id = null ) {
if ( $this->current_view === 'trash' ) {
return false;
}
if ( ! empty( $form_id ) && ! wpforms_current_user_can( 'edit_form_single', $form_id ) ) {
return false;
}
if ( empty( $form_id ) && ! wpforms_current_user_can( 'edit_forms' ) ) {
return false;
}
return true;
}
/**
* Generate Tags column markup.
*
* @since 1.7.5
*
* @param WP_Post $form Form.
*
* @return string
*/
public function column_tags( $form ) {
$terms = get_the_terms( $form->ID, WPForms_Form_Handler::TAGS_TAXONOMY );
$data = $this->get_tags_data( $terms );
return $this->get_column_tags_links( $data['tags_links'], $data['tags_ids'], $form->ID ) . $this->get_column_tags_form( $data['tags_options'] );
}
/**
* Generate tags data.
*
* @since 1.7.5
*
* @param array $terms Tags terms.
*
* @return array
*/
public function get_tags_data( $terms ) {
if ( ! is_array( $terms ) ) {
$taxonomy_object = get_taxonomy( WPForms_Form_Handler::TAGS_TAXONOMY );
return [
'tags_links' => sprintf(
'<span aria-hidden="true">—</span><span class="screen-reader-text">%s</span>',
esc_html( isset( $taxonomy_object->labels->no_terms ) ? $taxonomy_object->labels->no_terms : '—' )
),
'tags_ids' => '',
'tags_options' => '',
];
}
$tags_links = [];
$tags_ids = [];
$tags_options = [];
$terms = empty( $terms ) ? [] : (array) $terms;
foreach ( $terms as $tag ) {
$filter_url = add_query_arg(
'tags',
rawurlencode( $tag->slug ),
$this->base_url
);
$tags_links[] = sprintf(
'<a href="%1$s">%2$s</a>',
esc_url( $filter_url ),
esc_html( $tag->name )
);
$tags_ids[] = $tag->term_id;
$tags_options[] = sprintf(
'<option value="%1$s" selected>%2$s</option>',
esc_attr( $tag->term_id ),
esc_html( $tag->name )
);
}
return [
/* translators: Used between list items, there is a space after the comma. */
'tags_links' => implode( __( ', ', 'wpforms-lite' ), $tags_links ),
'tags_ids' => implode( ',', array_filter( $tags_ids ) ),
'tags_options' => implode( '', $tags_options ),
];
}
/**
* Get form tags links list markup.
*
* @since 1.7.5
*
* @param string $tags_links Tags links.
* @param string $tags_ids Tags IDs.
* @param int $form_id Form ID.
*
* @return string
*/
private function get_column_tags_links( $tags_links = '', $tags_ids = '', $form_id = 0 ) {
$edit_link = '';
if ( $this->is_editable( $form_id ) ) {
$edit_link = sprintf(
'<a href="#" class="wpforms-column-tags-edit">%s</a>',
esc_html__( 'Edit', 'wpforms-lite' )
);
}
return sprintf(
'<div class="wpforms-column-tags-links" data-form-id="%1$d" data-is-editable="%2$s" data-tags="%3$s">
<div class="wpforms-column-tags-links-list">%4$s</div>
%5$s
</div>',
absint( $form_id ),
$this->is_editable( $form_id ) ? '1' : '0',
esc_attr( $tags_ids ),
$tags_links,
$edit_link
);
}
/**
* Get edit tags form markup in the Tags column.
*
* @since 1.7.5
*
* @param string $tags_options Tags options.
*
* @return string
*/
private function get_column_tags_form( $tags_options = '' ) {
return sprintf(
'<div class="wpforms-column-tags-form wpforms-hidden">
<select multiple>%1$s</select>
<i class="dashicons dashicons-dismiss wpforms-column-tags-edit-cancel" title="%2$s"></i>
<i class="dashicons dashicons-yes-alt wpforms-column-tags-edit-save" title="%3$s"></i>
<i class="wpforms-spinner spinner wpforms-hidden"></i>
</div>',
$tags_options,
esc_attr__( 'Cancel', 'wpforms-lite' ),
esc_attr__( 'Save changes', 'wpforms-lite' )
);
}
/**
* Extra controls to be displayed between bulk actions and pagination.
*
* @since 1.7.5
*
* @param string $which The location of the table navigation: 'top' or 'bottom'.
* @param WPForms_Overview_Table $overview_table Instance of the WPForms_Overview_Table class.
*/
public function extra_tablenav( $which, $overview_table ) {
if ( $this->current_view === 'trash' ) {
return;
}
$all_tags = self::get_all_tags_choices();
$is_column_hidden = $this->is_tags_column_hidden();
$is_hidden = $is_column_hidden || empty( $all_tags );
$tags_options = '';
if ( $this->is_filtered() ) {
$tags = get_terms(
[
'taxonomy' => WPForms_Form_Handler::TAGS_TAXONOMY,
'hide_empty' => false,
'include' => $this->tags_filter,
]
);
foreach ( $tags as $tag ) {
$tags_options .= sprintf(
'<option value="%1$s" selected>%2$s</option>',
esc_attr( $tag->term_id ),
esc_html( $tag->name )
);
}
}
printf(
'<div class="wpforms-tags-filter %1$s">
<select multiple size="1" data-tags-filter="1">
<option placeholder>%2$s</option>
%3$s
</select>
<button type="button" class="button">%4$s</button>
</div>
<button type="button" class="button wpforms-manage-tags %1$s">%5$s</button>',
esc_attr( $is_hidden ? 'wpforms-hidden' : '' ),
esc_html( empty( $tags_options ) ? __( 'All Tags', 'wpforms-lite' ) : '' ),
wp_kses(
$tags_options,
[
'option' => [
'value' => [],
'selected' => [],
],
]
),
esc_attr__( 'Filter', 'wpforms-lite' ),
esc_attr__( 'Manage Tags', 'wpforms-lite' )
);
}
/**
* Render and display Bulk Edit Tags form.
*
* @since 1.7.5
*
* @param WPForms_Overview_Table $list_table Overview lit table object.
*/
public function bulk_edit_tags( $list_table ) {
$columns = $list_table->get_columns();
echo wpforms_render( // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
'admin/forms/bulk-edit-tags',
[
'columns' => count( $columns ),
],
true
);
}
/**
* Determine whether a filtering is performing.
*
* @since 1.7.5
*
* @return bool
*/
private function is_filtered() {
return ! empty( $this->tags_filter );
}
/**
* Pass the tags to the arguments array.
*
* @since 1.7.5
*
* @param array $args Get posts arguments.
*
* @return array
*/
public function get_forms_args( $args ) {
if ( $args['post_status'] === 'trash' || ! $this->is_filtered() ) {
return $args;
}
// phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query
$args['tax_query'] = [
[
'taxonomy' => WPForms_Form_Handler::TAGS_TAXONOMY,
'field' => 'term_id',
'terms' => $this->tags_filter,
],
];
return $args;
}
/**
* Add item to Bulk Actions dropdown.
*
* @since 1.7.5
*
* @param array $items Dropdown items.
*
* @return array
*/
public function add_bulk_action( $items ) {
if ( $this->is_editable() ) {
$items['edit_tags'] = esc_html__( 'Edit Tags', 'wpforms-lite' );
}
return $items;
}
/**
* Filter list table columns.
*
* @since 1.7.5
*
* @param string[] $columns Array of columns.
*
* @return array
*/
public function filter_columns( $columns ) {
if ( $this->current_view === 'trash' ) {
unset( $columns['tags'] );
}
return $columns;
}
}
Admin/Forms/Ajax/Tags.php 0000644 00000015205 15133255232 0011160 0 ustar 00 <?php
namespace WPForms\Admin\Forms\Ajax;
use WPForms_Form_Handler;
/**
* Tags AJAX actions on All Forms page.
*
* @since 1.7.5
*/
class Tags {
/**
* Determine if the new tag was added during processing submitted tags.
*
* @since 1.7.5
*
* @var bool
*/
private $is_new_tag_added;
/**
* Determine if the class is allowed to load.
*
* @since 1.7.5
*
* @return bool
*/
private function allow_load() {
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
$action = isset( $_REQUEST['action'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['action'] ) ) : '';
// Load only in the case of AJAX calls on Forms Overview page.
return wp_doing_ajax() && strpos( $action, 'wpforms_admin_forms_overview_' ) === 0;
}
/**
* Initialize class.
*
* @since 1.7.5
*/
public function init() {
if ( ! $this->allow_load() ) {
return;
}
$this->hooks();
}
/**
* Hooks.
*
* @since 1.7.5
*/
private function hooks() {
add_action( 'wp_ajax_wpforms_admin_forms_overview_save_tags', [ $this, 'save_tags' ] );
add_action( 'wp_ajax_wpforms_admin_forms_overview_delete_tags', [ $this, 'delete_tags' ] );
}
/**
* Save tags.
*
* @since 1.7.5
*/
public function save_tags() {
$data = $this->get_prepared_data( 'save' );
$tags_ids = $this->get_processed_tags( $data['tags'] );
$tags_labels = wp_list_pluck( $data['tags'], 'label' );
// Set tags to each form.
$this->set_tags_to_forms( $data['forms'], $tags_ids, $tags_labels );
$tags_obj = wpforms()->get( 'forms_tags' );
$terms = get_the_terms( array_pop( $data['forms'] ), WPForms_Form_Handler::TAGS_TAXONOMY );
$tags_data = $tags_obj->get_tags_data( $terms );
if ( ! empty( $this->is_new_tag_added ) ) {
$tags_data['all_tags_choices'] = $tags_obj->get_all_tags_choices();
}
wp_send_json_success( $tags_data );
}
/**
* Delete tags.
*
* @since 1.7.5
*/
public function delete_tags() {
$form_obj = wpforms()->get( 'form' );
$data = $this->get_prepared_data( 'delete' );
$deleted = 0;
$labels = [];
// Get forms marked by the tags.
$args = [
'fields' => 'ids',
// phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query
'tax_query' => [
[
'taxonomy' => WPForms_Form_Handler::TAGS_TAXONOMY,
'field' => 'term_id',
'terms' => array_map( 'absint', $data['tags'] ),
],
],
];
$forms = $form_obj->get( 0, $args );
foreach ( $data['tags'] as $tag_id ) {
$term = get_term_by( 'term_id', $tag_id, WPForms_Form_Handler::TAGS_TAXONOMY, ARRAY_A );
$labels[] = $term['name'];
// Delete tag (term).
if ( wp_delete_term( $tag_id, WPForms_Form_Handler::TAGS_TAXONOMY ) === true ) {
$deleted++;
}
}
// Remove tags from the settings of the forms.
foreach ( $forms as $form_id ) {
$form_data = $form_obj->get( $form_id, [ 'content_only' => true ] );
if (
empty( $form_data['settings']['form_tags'] ) ||
! is_array( $form_data['settings']['form_tags'] )
) {
continue;
}
$form_data['settings']['form_tags'] = array_diff( $form_data['settings']['form_tags'], $labels );
$form_obj->update( $form_id, $form_data );
}
wp_send_json_success(
[
'deleted' => $deleted,
]
);
}
/**
* Get processed tags.
*
* @since 1.7.5
*
* @param array $tags_data Submitted tags data.
*
* @return array Tags IDs list.
*/
public function get_processed_tags( $tags_data ) {
if ( ! is_array( $tags_data ) ) {
return [];
}
$tags_ids = [];
// Process the tags' data.
foreach ( $tags_data as $tag ) {
$term = get_term( $tag['value'], WPForms_Form_Handler::TAGS_TAXONOMY );
// In the case when the term is not found, we should create the new term.
if ( empty( $term ) || is_wp_error( $term ) ) {
$new_term = wp_insert_term( sanitize_text_field( $tag['label'] ), WPForms_Form_Handler::TAGS_TAXONOMY );
$tag['value'] = ! is_wp_error( $new_term ) && isset( $new_term['term_id'] ) ? $new_term['term_id'] : 0;
$this->is_new_tag_added = $this->is_new_tag_added || $tag['value'] > 0;
}
if ( ! empty( $tag['value'] ) ) {
$tags_ids[] = absint( $tag['value'] );
}
}
return $tags_ids;
}
/**
* Get prepared data before perform ajax action.
*
* @since 1.7.5
*
* @param string $action Action: `save` OR `delete`.
*
* @return array
*/
private function get_prepared_data( $action ) {
// Run a security check.
if ( ! check_ajax_referer( 'wpforms-admin-forms-overview-nonce', 'nonce', false ) ) {
wp_send_json_error( esc_html__( 'Most likely, your session expired. Please reload the page.', 'wpforms-lite' ) );
}
// Check for permissions.
if ( ! wpforms_current_user_can( 'edit_forms' ) ) {
wp_send_json_error( esc_html__( 'You are not allowed to perform this action.', 'wpforms-lite' ) );
}
$data = [
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
'tags' => ! empty( $_POST['tags'] ) ? map_deep( (array) wp_unslash( $_POST['tags'] ), 'sanitize_text_field' ) : [],
];
if ( $action === 'save' ) {
$data['forms'] = $this->get_allowed_forms();
}
return $data;
}
/**
* Get allowed forms.
*
* @since 1.7.5
*
* @return array Allowed form IDs.
*/
private function get_allowed_forms() {
// phpcs:disable WordPress.Security.NonceVerification.Missing
if ( empty( $_POST['forms'] ) ) {
wp_send_json_error( esc_html__( 'No forms selected when trying to add a tag to them.', 'wpforms-lite' ) );
}
$forms_all = array_filter( array_map( 'absint', (array) $_POST['forms'] ) );
$forms_allowed = [];
// phpcs:enable WordPress.Security.NonceVerification.Missing
foreach ( $forms_all as $form_id ) {
if ( wpforms_current_user_can( 'edit_form_single', $form_id ) ) {
$forms_allowed[] = $form_id;
}
}
if ( empty( $forms_allowed ) ) {
wp_send_json_error( esc_html__( 'You are not allowed to perform this action.', 'wpforms-lite' ) );
}
return $forms_allowed;
}
/**
* Set tags to each form in the list.
*
* @since 1.7.5
*
* @param array $forms_ids Forms IDs list.
* @param array $tags_ids Tags IDs list.
* @param array $tags_labels Tags labels list.
*/
private function set_tags_to_forms( $forms_ids, $tags_ids, $tags_labels ) {
$form_obj = wpforms()->get( 'form' );
foreach ( $forms_ids as $form_id ) {
wp_set_post_terms(
$form_id,
$tags_ids,
WPForms_Form_Handler::TAGS_TAXONOMY
);
// Store tags labels in the form settings.
$form_data = $form_obj->get( $form_id, [ 'content_only' => true ] );
$form_data['settings']['form_tags'] = $tags_labels;
$form_obj->update( $form_id, $form_data );
}
}
}
Admin/Forms/BulkActions.php 0000644 00000021747 15133255232 0011625 0 ustar 00 <?php
namespace WPForms\Admin\Forms;
use WPForms\Admin\Notice;
/**
* Bulk actions on All Forms page.
*
* @since 1.7.3
*/
class BulkActions {
/**
* Allowed actions.
*
* @since 1.7.3
*
* @const array
*/
const ALLOWED_ACTIONS = [
'trash',
'restore',
'delete',
'duplicate',
'empty_trash',
];
/**
* Forms ids.
*
* @since 1.7.3
*
* @var array
*/
private $ids;
/**
* Current action.
*
* @since 1.7.3
*
* @var string
*/
private $action;
/**
* Current view.
*
* @since 1.7.3
*
* @var string
*/
private $view;
/**
* Determine if the class is allowed to load.
*
* @since 1.7.3
*
* @return bool
*/
private function allow_load() {
// Load only on the `All Forms` admin page.
return wpforms_is_admin_page( 'overview' );
}
/**
* Initialize class.
*
* @since 1.7.3
*/
public function init() {
if ( ! $this->allow_load() ) {
return;
}
$this->view = wpforms()->get( 'forms_views' )->get_current_view();
$this->hooks();
}
/**
* Hooks.
*
* @since 1.7.3
*/
private function hooks() {
add_action( 'load-toplevel_page_wpforms-overview', [ $this, 'notices' ] );
add_action( 'load-toplevel_page_wpforms-overview', [ $this, 'process' ] );
add_filter( 'removable_query_args', [ $this, 'removable_query_args' ] );
}
/**
* Process the bulk actions.
*
* @since 1.7.3
*/
public function process() {
// phpcs:disable WordPress.Security.NonceVerification.Recommended
$this->ids = isset( $_GET['form_id'] ) ? array_map( 'absint', (array) $_GET['form_id'] ) : [];
$this->action = isset( $_REQUEST['action'] ) ? sanitize_key( $_REQUEST['action'] ) : false;
if ( $this->action === '-1' ) {
$this->action = ! empty( $_REQUEST['action2'] ) ? sanitize_key( $_REQUEST['action2'] ) : false;
}
// phpcs:enable WordPress.Security.NonceVerification.Recommended
if ( empty( $this->ids ) || empty( $this->action ) ) {
return;
}
// Check exact action values.
if ( ! in_array( $this->action, self::ALLOWED_ACTIONS, true ) ) {
return;
}
if ( empty( $_GET['_wpnonce'] ) ) {
return;
}
// Check the nonce.
if (
! wp_verify_nonce( sanitize_key( $_GET['_wpnonce'] ), 'bulk-forms' ) &&
! wp_verify_nonce( sanitize_key( $_GET['_wpnonce'] ), 'wpforms_' . $this->action . '_form_nonce' )
) {
return;
}
// Finally, we can process the action.
$this->process_action();
}
/**
* Process action.
*
* @since 1.7.3
*
* @uses process_action_trash
* @uses process_action_restore
* @uses process_action_delete
* @uses process_action_duplicate
* @uses process_action_empty_trash
*/
private function process_action() {
$method = "process_action_{$this->action}";
// Check that we have a method for this action.
if ( ! method_exists( $this, $method ) ) {
return;
}
if ( empty( $this->ids ) || ! is_array( $this->ids ) ) {
return;
}
$result = [];
foreach ( $this->ids as $id ) {
$result[ $id ] = $this->$method( $id );
}
$count_result = count( array_keys( array_filter( $result ) ) );
// Empty trash action returns count of deleted forms.
if ( $method === 'process_action_empty_trash' ) {
$count_result = isset( $result[1] ) ? $result[1] : 0;
}
// Unset get vars and perform redirect to avoid action reuse.
wp_safe_redirect(
add_query_arg(
rtrim( $this->action, 'e' ) . 'ed',
$count_result,
remove_query_arg( [ 'action', 'action2', '_wpnonce', 'form_id', 'paged', '_wp_http_referer' ] )
)
);
exit;
}
/**
* Trash the form.
*
* @since 1.7.3
*
* @param int $id Form ID to trash.
*
* @return bool
*/
private function process_action_trash( $id ) {
return wpforms()->get( 'form' )->update_status( $id, 'trash' );
}
/**
* Restore the form.
*
* @since 1.7.3
*
* @param int $id Form ID to restore from trash.
*
* @return bool
*/
private function process_action_restore( $id ) {
return wpforms()->get( 'form' )->update_status( $id, 'publish' );
}
/**
* Delete the form.
*
* @since 1.7.3
*
* @param int $id Form ID to delete.
*
* @return bool
*/
private function process_action_delete( $id ) {
return wpforms()->get( 'form' )->delete( $id );
}
/**
* Duplicate the form.
*
* @since 1.7.3
*
* @param int $id Form ID to duplicate.
*
* @return bool
*/
private function process_action_duplicate( $id ) {
if ( ! wpforms_current_user_can( 'create_forms' ) ) {
return false;
}
if ( ! wpforms_current_user_can( 'view_form_single', $id ) ) {
return false;
}
return wpforms()->get( 'form' )->duplicate( $id );
}
/**
* Empty trash.
*
* @since 1.7.3
*
* @param int $id Form ID. This parameter is not used in this method,
* but we need to keep it here because all the `process_action_*` methods
* should be called with the $id parameter.
*
* @return bool
*/
private function process_action_empty_trash( $id ) {
// Empty trash is actually the "delete all forms in trash" action.
// So, after the execution we should display the same notice as for the `delete` action.
$this->action = 'delete';
return wpforms()->get( 'form' )->empty_trash();
}
/**
* Define bulk actions available for forms overview table.
*
* @since 1.7.3
*
* @return array
*/
public function get_dropdown_items() {
$items = [];
if ( wpforms_current_user_can( 'delete_forms' ) ) {
if ( $this->view === 'trash' ) {
$items = [
'restore' => esc_html__( 'Restore', 'wpforms-lite' ),
'delete' => esc_html__( 'Delete Permanently', 'wpforms-lite' ),
];
} else {
$items = [
'trash' => esc_html__( 'Trash', 'wpforms-lite' ),
];
}
}
// phpcs:disable WPForms.Comments.ParamTagHooks.InvalidParamTagsQuantity
/**
* Filters the Bulk Actions dropdown items.
*
* @since 1.7.5
*
* @param array $items Dropdown items.
*/
$items = apply_filters( 'wpforms_admin_forms_bulk_actions_get_dropdown_items', $items );
// phpcs:enable WPForms.Comments.ParamTagHooks.InvalidParamTagsQuantity
if ( empty( $items ) ) {
// We should have dummy item, otherwise, WP will hide the Bulk Actions Dropdown,
// which is not good from a design point of view.
return [
'' => '—',
];
}
return $items;
}
/**
* Admin notices.
*
* @since 1.7.3
*/
public function notices() {
// phpcs:disable WordPress.Security.NonceVerification
$results = [
'trashed' => ! empty( $_REQUEST['trashed'] ) ? sanitize_key( $_REQUEST['trashed'] ) : false,
'restored' => ! empty( $_REQUEST['restored'] ) ? sanitize_key( $_REQUEST['restored'] ) : false,
'deleted' => ! empty( $_REQUEST['deleted'] ) ? sanitize_key( $_REQUEST['deleted'] ) : false,
'duplicated' => ! empty( $_REQUEST['duplicated'] ) ? sanitize_key( $_REQUEST['duplicated'] ) : false,
];
// phpcs:enable WordPress.Security.NonceVerification
// Display notice in case of error.
if ( in_array( 'error', $results, true ) ) {
Notice::add(
esc_html__( 'Security check failed. Please try again.', 'wpforms-lite' ),
'error'
);
return;
}
$this->notices_success( $results );
}
/**
* Admin success notices.
*
* @since 1.7.3
*
* @param array $results Action results data.
*/
private function notices_success( $results ) {
if ( ! empty( $results['trashed'] ) ) {
$notice = sprintf( /* translators: %d - Trashed forms count. */
_n( '%d form was successfully moved to Trash.', '%d forms were successfully moved to Trash.', (int) $results['trashed'], 'wpforms-lite' ),
(int) $results['trashed']
);
}
if ( ! empty( $results['restored'] ) ) {
$notice = sprintf( /* translators: %d - Restored forms count. */
_n( '%d form was successfully restored.', '%d forms were successfully restored.', (int) $results['restored'], 'wpforms-lite' ),
(int) $results['restored']
);
}
if ( ! empty( $results['deleted'] ) ) {
$notice = sprintf( /* translators: %d - Deleted forms count. */
_n( '%d form was successfully permanently deleted.', '%d forms were successfully permanently deleted.', (int) $results['deleted'], 'wpforms-lite' ),
(int) $results['deleted']
);
}
if ( ! empty( $results['duplicated'] ) ) {
$notice = sprintf( /* translators: %d - Duplicated forms count. */
_n( '%d form was successfully duplicated.', '%d forms were successfully duplicated.', (int) $results['duplicated'], 'wpforms-lite' ),
(int) $results['duplicated']
);
}
if ( ! empty( $notice ) ) {
Notice::add( $notice, 'info' );
}
}
/**
* Remove certain arguments from a query string that WordPress should always hide for users.
*
* @since 1.7.3
*
* @param array $removable_query_args An array of parameters to remove from the URL.
*
* @return array Extended/filtered array of parameters to remove from the URL.
*/
public function removable_query_args( $removable_query_args ) {
$removable_query_args[] = 'trashed';
$removable_query_args[] = 'restored';
$removable_query_args[] = 'deleted';
$removable_query_args[] = 'duplicated';
return $removable_query_args;
}
}
Admin/Forms/Search.php 0000644 00000015433 15133255232 0010607 0 ustar 00 <?php
namespace WPForms\Admin\Forms;
/**
* Search Forms feature.
*
* @since 1.7.2
*/
class Search {
/**
* Current search term.
*
* @since 1.7.2
*
* @var string
*/
private $term;
/**
* Current search term escaped.
*
* @since 1.7.2
*
* @var string
*/
private $term_escaped;
/**
* Determine if the class is allowed to load.
*
* @since 1.7.2
*
* @return bool
*/
private function allow_load() {
// Load only on the `All Forms` admin page and only if the search should be performed.
return wpforms_is_admin_page( 'overview' ) && $this->is_search();
}
/**
* Initialize class.
*
* @since 1.7.2
*/
public function init() {
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
$this->term = isset( $_GET['search']['term'] ) ? sanitize_text_field( wp_unslash( $_GET['search']['term'] ) ) : '';
// phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
$this->term_escaped = isset( $_GET['search']['term'] ) ? esc_html( wp_unslash( $_GET['search']['term'] ) ) : '';
if ( ! $this->allow_load() ) {
return;
}
$this->hooks();
}
/**
* Hooks.
*
* @since 1.7.2
*/
private function hooks() {
// Use filter to add the search term to the get forms arguments.
add_filter( 'wpforms_get_multiple_forms_args', [ $this, 'get_forms_args' ] );
// Encapsulate search into posts_where.
add_action( 'wpforms_form_handler_get_multiple_before_get_posts', [ $this, 'before_get_posts' ] );
add_action( 'wpforms_form_handler_get_multiple_after_get_posts', [ $this, 'after_get_posts' ], 10, 2 );
}
/**
* Determine whether a search is performing.
*
* @since 1.7.2
*
* @return bool
*/
private function is_search() {
return ! wpforms_is_empty_string( $this->term_escaped );
}
/**
* Count search results.
*
* @since 1.7.2
* @deprecated 1.7.5
*
* @param array $count Number of forms in different views.
* @param array $args Get forms arguments.
*
* @return array
*/
public function update_count( $count, $args ) {
_deprecated_function( __METHOD__, '1.7.5 of the WPForms plugin', "wpforms()->get( 'forms_views' )->update_count()" );
return wpforms()->get( 'forms_views' )->update_count();
}
/**
* Pass the search term to the arguments array.
*
* @since 1.7.2
*
* @param array $args Get posts arguments.
*
* @return array
*/
public function get_forms_args( $args ) {
$args['search']['term'] = $this->term;
$args['search']['term_escaped'] = $this->term_escaped;
return $args;
}
/**
* Before get_posts() call routine.
*
* @since 1.7.2
*
* @param array $args Arguments of the `get_posts()`.
*/
public function before_get_posts( $args ) {
// The `posts_where` hook is very general and has broad usage across the WP core and tons of plugins.
// Therefore, in order to do not break something,
// we should add this hook right before the call of `get_posts()` inside \WPForms_Form_Handler::get_multiple().
add_filter( 'posts_where', [ $this, 'search_by_term_where' ], 10, 2 );
}
/**
* After get_posts() call routine.
*
* @since 1.7.2
*
* @param array $args Arguments of the get_posts().
* @param array $forms Forms data. Result of getting multiple forms.
*/
public function after_get_posts( $args, $forms ) {
// The `posts_where` hook is very general and has broad usage across the WP core and tons of plugins.
// Therefore, in order to do not break something,
// we should remove this hook right after the call of `get_posts()` inside \WPForms_Form_Handler::get_multiple().
remove_filter( 'posts_where', [ $this, 'search_by_term_where' ] );
}
/**
* Modify the WHERE clause of the SQL query in order to search forms by given term.
*
* @since 1.7.2
*
* @param string $where WHERE clause.
* @param \WP_Query $wp_query The WP_Query instance.
*
* @return string
*/
public function search_by_term_where( $where, $wp_query ) {
global $wpdb;
// When user types only HTML tag (<section> for example), the sanitized term we will be empty.
// In this case, it's better to return an empty result set than all the forms. It's not the same as the empty search term.
if ( wpforms_is_empty_string( $this->term ) && ! wpforms_is_empty_string( $this->term_escaped ) ) {
$where .= ' AND 1<>1';
}
if ( wpforms_is_empty_string( $this->term ) ) {
return $where;
}
// Prepare the WHERE clause to search form title and description.
$where .= $wpdb->prepare(
" AND (
{$wpdb->posts}.post_title LIKE %s OR
{$wpdb->posts}.post_excerpt LIKE %s
)",
'%' . $wpdb->esc_like( esc_html( $this->term ) ) . '%',
'%' . $wpdb->esc_like( $this->term ) . '%'
);
return $where;
}
/**
* Forms search markup.
*
* @since 1.7.2
*
* @param string $text The 'submit' button label.
* @param string $input_id ID attribute value for the search input field.
*/
public function search_box( $text, $input_id ) {
$search_term = wpforms_is_empty_string( $this->term ) ? $this->term_escaped : $this->term;
// Display search reset block.
$this->search_reset_block( $search_term );
// Display search box.
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo wpforms_render(
'admin/forms/search-box',
[
'term_input_id' => $input_id . '-term',
'text' => $text,
'search_term' => $search_term,
],
true
);
}
/**
* Forms search reset block.
*
* @since 1.7.2
*
* @param string $search_term Current search term.
*/
private function search_reset_block( $search_term ) {
if ( wpforms_is_empty_string( $search_term ) ) {
return;
}
$views = wpforms()->get( 'forms_views' );
$count = $views->get_count();
$view = $views->get_current_view();
$count['all'] = ! empty( $count['all'] ) ? $count['all'] : 0;
$message = sprintf(
wp_kses( /* translators: %1$d - number of forms found, %2$s - search term. */
_n(
'Found <strong>%1$d form</strong> containing <em>"%2$s"</em>',
'Found <strong>%1$d forms</strong> containing <em>"%2$s"</em>',
(int) $count['all'],
'wpforms-lite'
),
[
'strong' => [],
'em' => [],
]
),
(int) $count['all'],
esc_html( $search_term )
);
/**
* Filters the message in the search reset block.
*
* @since 1.7.3
*
* @param string $message Message text.
* @param string $search_term Search term.
* @param array $count Count forms in different views.
* @param string $view Current view.
*/
$message = apply_filters( 'wpforms_admin_forms_search_search_reset_block_message', $message, $search_term, $count, $view );
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo wpforms_render(
'admin/forms/search-reset',
[
'message' => $message,
],
true
);
}
}
Admin/Forms/Views.php 0000644 00000031735 15133255232 0010502 0 ustar 00 <?php
namespace WPForms\Admin\Forms;
use WP_Post;
/**
* List table views.
*
* @since 1.7.3
*/
class Views {
/**
* Current view slug.
*
* @since 1.7.3
*
* @var string
*/
private $current_view;
/**
* Views settings.
*
* @since 1.7.3
*
* @var array
*/
private $views;
/**
* Count forms in different views.
*
* @since 1.7.3
*
* @var array
*/
private $count;
/**
* Base URL.
*
* @since 1.7.3
*
* @var string
*/
private $base_url;
/**
* Views configuration.
*
* @since 1.7.3
*/
private function configuration() {
if ( ! empty( $this->views ) ) {
return;
}
// Define views.
$views = [
'all' => [
'title' => __( 'All', 'wpforms-lite' ),
'get_var' => '',
'get_var_value' => '',
],
'trash' => [
'title' => __( 'Trash', 'wpforms-lite' ),
'get_var' => 'status',
'get_var_value' => 'trash',
],
];
// phpcs:disable WPForms.Comments.ParamTagHooks.InvalidParamTagsQuantity
/**
* Filters configuration of the Forms Overview table views.
*
* @since 1.7.3
*
* @param array $views {
* Views array.
*
* @param array $view {
* Each view is the array with three elements:
*
* @param string $title View title.
* @param string $get_var URL query variable name.
* @param string $get_var_value URL query variable value.
* }
* ...
* }
*/
$this->views = apply_filters( 'wpforms_admin_forms_views_configuration', $views );
// phpcs:enable WPForms.Comments.ParamTagHooks.InvalidParamTagsQuantity
}
/**
* Determine if the class is allowed to load.
*
* @since 1.7.3
*
* @return bool
*/
private function allow_load() {
// Load only on the `All Forms` admin page.
return wpforms_is_admin_page( 'overview' );
}
/**
* Initialize class.
*
* @since 1.7.3
*/
public function init() {
if ( ! $this->allow_load() ) {
return;
}
$this->configuration();
$this->update_current_view();
$this->update_base_url();
$this->hooks();
}
/**
* Hooks.
*
* @since 1.7.3
*/
private function hooks() {
add_filter( 'wpforms_overview_table_update_count', [ $this, 'update_count' ], 10, 2 );
add_filter( 'wpforms_overview_table_update_count_all', [ $this, 'update_count' ], 10, 2 );
add_filter( 'wpforms_overview_table_prepare_items_args', [ $this, 'prepare_items_trash' ], 100 );
add_filter( 'wpforms_overview_row_actions', [ $this, 'row_actions_all' ], 9, 2 );
add_filter( 'wpforms_overview_row_actions', [ $this, 'row_actions_trash' ], PHP_INT_MAX, 2 );
add_filter( 'wpforms_admin_forms_search_search_reset_block_message', [ $this, 'search_reset_message' ], 10, 4 );
}
/**
* Determine and save current view slug.
*
* @since 1.7.3
*/
private function update_current_view() {
if ( ! is_array( $this->views ) ) {
return;
}
$this->current_view = 'all';
foreach ( $this->views as $slug => $view ) {
if (
// phpcs:disable WordPress.Security.NonceVerification.Recommended
isset( $_GET[ $view['get_var'] ] ) &&
$view['get_var_value'] === sanitize_key( $_GET[ $view['get_var'] ] )
// phpcs:enable WordPress.Security.NonceVerification.Recommended
) {
$this->current_view = $slug;
return;
}
}
}
/**
* Update Base URL.
*
* @since 1.7.3
*/
private function update_base_url() {
if ( ! is_array( $this->views ) ) {
return;
}
$get_vars = wp_list_pluck( $this->views, 'get_var' );
$get_vars = array_merge(
$get_vars,
[
'paged',
'trashed',
'restored',
'deleted',
'duplicated',
]
);
$this->base_url = remove_query_arg( $get_vars );
}
/**
* Get current view slug.
*
* @since 1.7.3
*
* @return string
*/
public function get_current_view() {
return $this->current_view;
}
/**
* Get base URL.
*
* @since 1.7.5
*
* @return string
*/
public function get_base_url() {
return $this->base_url;
}
/**
* Get view configuration by slug.
*
* @since 1.7.5
*
* @param string $slug View slug.
*
* @return array
*/
public function get_view_by_slug( $slug ) {
return isset( $this->views[ $slug ] ) ? $this->views[ $slug ] : [];
}
/**
* Update count.
*
* @since 1.7.3
*
* @param array $count Number of forms in different views.
* @param array $args Get forms arguments.
*
* @return array
*/
public function update_count( $count, $args ) {
// Count forms in Trash.
// We should perform the search of the trashed forms without paging and then count the results.
$args = array_merge(
$args,
[
'nopaging' => true,
'no_found_rows' => true,
'update_post_meta_cache' => false,
'update_post_term_cache' => false,
'fields' => 'ids',
'post_status' => 'trash',
]
);
$forms = wpforms()->get( 'form' )->get( '', $args );
$count['trash'] = is_array( $forms ) ? count( $forms ) : 0;
// Count all published forms.
$args['post_status'] = 'publish';
$forms = wpforms()->get( 'form' )->get( '', $args );
$count['all'] = is_array( $forms ) ? count( $forms ) : 0;
// Store in class property for further use.
$this->count = $count;
return $count;
}
/**
* Get counts of forms in different views.
*
* @since 1.7.3
*
* @return array
*/
public function get_count() {
return $this->count;
}
/**
* Get forms from Trash when preparing items for list table.
*
* @since 1.7.3
*
* @param array $args Get multiple forms arguments.
*
* @return array
*/
public function prepare_items_trash( $args ) {
// Update arguments of \WPForms_Form_Handler::get_multiple() in order to get forms in Trash.
if ( $this->current_view === 'trash' ) {
$args['post_status'] = 'trash';
}
return $args;
}
/**
* Generate views items.
*
* @since 1.7.3
*
* @return array
*/
public function get_views() {
if ( ! is_array( $this->views ) ) {
return [];
}
$views = [];
foreach ( $this->views as $slug => $view ) {
if (
$slug === 'trash' &&
$this->current_view !== 'trash' &&
empty( $this->count[ $slug ] )
) {
continue;
}
$views[ $slug ] = $this->get_view_markup( $slug );
}
/**
* Filters the Forms Overview table views links.
*
* @since 1.7.3
*
* @param array $views Views links.
* @param array $count Count forms in different views.
*/
return apply_filters( 'wpforms_admin_forms_views_get_views', $views, $this->count );
}
/**
* Generate single view item.
*
* @since 1.7.3
*
* @param string $slug View slug.
*
* @return string
*/
private function get_view_markup( $slug ) {
if ( empty( $this->views[ $slug ] ) ) {
return '';
}
$view = $this->views[ $slug ];
return sprintf(
'<a href="%1$s"%2$s>%3$s <span class="count">(%4$d)</span></a>',
$slug === 'all' ? esc_url( $this->base_url ) : esc_url( add_query_arg( $view['get_var'], $view['get_var_value'], $this->base_url ) ),
$this->current_view === $slug ? ' class="current"' : '',
esc_html( $view['title'] ),
empty( $this->count[ $slug ] ) ? 0 : absint( $this->count[ $slug ] )
);
}
/**
* Row actions for view "All".
*
* @since 1.7.3
*
* @param array $row_actions Row actions.
* @param WP_Post $form Form object.
*
* @return array
*/
public function row_actions_all( $row_actions, $form ) { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh
if ( $this->current_view !== 'all' ) {
return $row_actions;
}
$row_actions = [];
// Edit.
if ( wpforms_current_user_can( 'edit_form_single', $form->ID ) ) {
$row_actions['edit'] = sprintf(
'<a href="%s" title="%s">%s</a>',
esc_url(
add_query_arg(
[
'view' => 'fields',
'form_id' => $form->ID,
],
admin_url( 'admin.php?page=wpforms-builder' )
)
),
esc_attr__( 'Edit This Form', 'wpforms-lite' ),
esc_html__( 'Edit', 'wpforms-lite' )
);
}
// Entries.
if ( wpforms_current_user_can( 'view_entries_form_single', $form->ID ) ) {
$row_actions['entries'] = sprintf(
'<a href="%s" title="%s">%s</a>',
esc_url(
add_query_arg(
[
'view' => 'list',
'form_id' => $form->ID,
],
admin_url( 'admin.php?page=wpforms-entries' )
)
),
esc_attr__( 'View entries', 'wpforms-lite' ),
esc_html__( 'Entries', 'wpforms-lite' )
);
}
// Preview.
if ( wpforms_current_user_can( 'view_form_single', $form->ID ) ) {
$row_actions['preview_'] = sprintf(
'<a href="%s" title="%s" target="_blank" rel="noopener noreferrer">%s</a>',
esc_url( wpforms_get_form_preview_url( $form->ID ) ),
esc_attr__( 'View preview', 'wpforms-lite' ),
esc_html__( 'Preview', 'wpforms-lite' )
);
}
// Duplicate.
if ( wpforms_current_user_can( 'create_forms' ) && wpforms_current_user_can( 'view_form_single', $form->ID ) ) {
$row_actions['duplicate'] = sprintf(
'<a href="%s" title="%s">%s</a>',
esc_url(
wp_nonce_url(
add_query_arg(
[
'action' => 'duplicate',
'form_id' => $form->ID,
],
$this->base_url
),
'wpforms_duplicate_form_nonce'
)
),
esc_attr__( 'Duplicate this form', 'wpforms-lite' ),
esc_html__( 'Duplicate', 'wpforms-lite' )
);
}
// Trash.
if ( wpforms_current_user_can( 'delete_form_single', $form->ID ) ) {
$row_actions['trash'] = sprintf(
'<a href="%s" title="%s">%s</a>',
esc_url(
wp_nonce_url(
add_query_arg(
[
'action' => 'trash',
'form_id' => $form->ID,
],
$this->base_url
),
'wpforms_trash_form_nonce'
)
),
esc_attr__( 'Move this form to trash', 'wpforms-lite' ),
esc_html__( 'Trash', 'wpforms-lite' )
);
}
return $row_actions;
}
/**
* Row actions for view "Trash".
*
* @since 1.7.3
*
* @param array $row_actions Row actions.
* @param WP_Post $form Form object.
*
* @return array
*/
public function row_actions_trash( $row_actions, $form ) {
if (
$this->current_view !== 'trash' ||
! wpforms_current_user_can( 'delete_form_single', $form->ID )
) {
return $row_actions;
}
$row_actions = [];
// Restore form.
$row_actions['restore'] = sprintf(
'<a href="%s" title="%s">%s</a>',
esc_url(
wp_nonce_url(
add_query_arg(
[
'action' => 'restore',
'form_id' => $form->ID,
'status' => 'trash',
],
$this->base_url
),
'wpforms_restore_form_nonce'
)
),
esc_attr__( 'Restore this form', 'wpforms-lite' ),
esc_html__( 'Restore', 'wpforms-lite' )
);
// Delete permanently.
$row_actions['delete'] = sprintf(
'<a href="%s" title="%s">%s</a>',
esc_url(
wp_nonce_url(
add_query_arg(
[
'action' => 'delete',
'form_id' => $form->ID,
'status' => 'trash',
],
$this->base_url
),
'wpforms_delete_form_nonce'
)
),
esc_attr__( 'Delete this form permanently', 'wpforms-lite' ),
esc_html__( 'Delete Permanently', 'wpforms-lite' )
);
return $row_actions;
}
/**
* Search reset message.
*
* @since 1.7.3
*
* @param string $message Search reset block message.
* @param string $search_term Search term.
* @param array $count Count forms in different views.
* @param string $current_view Current view.
*
* @return string
*/
public function search_reset_message( $message, $search_term, $count, $current_view ) {
if ( $current_view !== 'trash' ) {
return $message;
}
$count['trash'] = ! empty( $count['trash'] ) ? $count['trash'] : 0;
return sprintf(
wp_kses( /* translators: %1$d - number of forms found in the trash, %2$s - search term. */
_n(
'Found <strong>%1$d form</strong> in <em>the trash</em> containing <em>"%2$s"</em>',
'Found <strong>%1$d forms</strong> in <em>the trash</em> containing <em>"%2$s"</em>',
(int) $count['trash'],
'wpforms-lite'
),
[
'strong' => [],
'em' => [],
]
),
(int) $count['trash'],
esc_html( $search_term )
);
}
/**
* Extra controls to be displayed between bulk actions and pagination.
*
* @since 1.7.3
*
* @param string $which The location of the table navigation: 'top' or 'bottom'.
*/
public function extra_tablenav( $which ) {
if ( ! wpforms_current_user_can( 'delete_form_single' ) ) {
return;
}
if ( $this->current_view !== 'trash' ) {
return;
}
// Preserve current view after applying bulk action.
echo '<input type="hidden" name="status" value="trash">';
// Display Empty Trash button.
printf(
'<a href="%1$s" class="button delete-all">%2$s</a>',
esc_url(
wp_nonce_url(
add_query_arg(
[
'action' => 'empty_trash',
'form_id' => 1, // Technically, `empty_trash` is one of the bulk actions, therefore we need to provide fake form_id to proceed.
'status' => 'trash',
],
$this->base_url
),
'wpforms_empty_trash_form_nonce'
)
),
esc_html__( 'Empty Trash', 'wpforms-lite' )
);
}
}
Admin/Pages/SMTP.php 0000644 00000037575 15133255232 0010151 0 ustar 00 <?php
namespace WPForms\Admin\Pages;
/**
* SMTP Sub-page.
*
* @since 1.5.7
*/
class SMTP {
/**
* Admin menu page slug.
*
* @since 1.5.7
*
* @var string
*/
const SLUG = 'wpforms-smtp';
/**
* Configuration.
*
* @since 1.5.7
*
* @var array
*/
private $config = [
'lite_plugin' => 'wp-mail-smtp/wp_mail_smtp.php',
'lite_wporg_url' => 'https://wordpress.org/plugins/wp-mail-smtp/',
'lite_download_url' => 'https://downloads.wordpress.org/plugin/wp-mail-smtp.zip',
'pro_plugin' => 'wp-mail-smtp-pro/wp_mail_smtp.php',
'smtp_settings_url' => 'admin.php?page=wp-mail-smtp',
'smtp_wizard_url' => 'admin.php?page=wp-mail-smtp-setup-wizard',
];
/**
* Runtime data used for generating page HTML.
*
* @since 1.5.7
*
* @var array
*/
private $output_data = [];
/**
* Constructor.
*
* @since 1.5.7
*/
public function __construct() {
if ( ! wpforms_current_user_can() ) {
return;
}
$this->hooks();
}
/**
* Hooks.
*
* @since 1.5.7
*/
public function hooks() {
if ( wp_doing_ajax() ) {
add_action( 'wp_ajax_wpforms_smtp_page_check_plugin_status', [ $this, 'ajax_check_plugin_status' ] );
}
// Check what page we are on.
$page = isset( $_GET['page'] ) ? sanitize_key( wp_unslash( $_GET['page'] ) ) : ''; // phpcs:ignore WordPress.CSRF.NonceVerification
// Only load if we are actually on the SMTP page.
if ( $page !== self::SLUG ) {
return;
}
add_action( 'admin_init', [ $this, 'redirect_to_smtp_settings' ] );
add_filter( 'wpforms_admin_header', '__return_false' );
add_action( 'wpforms_admin_page', [ $this, 'output' ] );
add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_assets' ] );
// Hook for addons.
do_action( 'wpforms_admin_pages_smtp_hooks' );
}
/**
* Enqueue JS and CSS files.
*
* @since 1.5.7
*/
public function enqueue_assets() {
$min = wpforms_get_min_suffix();
// Lity.
wp_enqueue_style(
'wpforms-lity',
WPFORMS_PLUGIN_URL . 'assets/lib/lity/lity.min.css',
null,
'3.0.0'
);
wp_enqueue_script(
'wpforms-lity',
WPFORMS_PLUGIN_URL . 'assets/lib/lity/lity.min.js',
[ 'jquery' ],
'3.0.0',
true
);
wp_enqueue_script(
'wpforms-admin-page-smtp',
WPFORMS_PLUGIN_URL . "assets/js/components/admin/pages/smtp{$min}.js",
[ 'jquery' ],
WPFORMS_VERSION,
true
);
wp_localize_script(
'wpforms-admin-page-smtp',
'wpforms_pluginlanding',
$this->get_js_strings()
);
}
/**
* JS Strings.
*
* @since 1.5.7
*
* @return array Array of strings.
*/
protected function get_js_strings() {
$error_could_not_install = sprintf(
wp_kses( /* translators: %s - Lite plugin download URL. */
__( 'Could not install the plugin automatically. Please <a href="%s">download</a> it and install it manually.', 'wpforms-lite' ),
[
'a' => [
'href' => true,
],
]
),
esc_url( $this->config['lite_download_url'] )
);
$error_could_not_activate = sprintf(
wp_kses( /* translators: %s - Lite plugin download URL. */
__( 'Could not activate the plugin. Please activate it on the <a href="%s">Plugins page</a>.', 'wpforms-lite' ),
[
'a' => [
'href' => true,
],
]
),
esc_url( admin_url( 'plugins.php' ) )
);
return [
'installing' => esc_html__( 'Installing...', 'wpforms-lite' ),
'activating' => esc_html__( 'Activating...', 'wpforms-lite' ),
'activated' => esc_html__( 'WP Mail SMTP Installed & Activated', 'wpforms-lite' ),
'install_now' => esc_html__( 'Install Now', 'wpforms-lite' ),
'activate_now' => esc_html__( 'Activate Now', 'wpforms-lite' ),
'download_now' => esc_html__( 'Download Now', 'wpforms-lite' ),
'plugins_page' => esc_html__( 'Go to Plugins page', 'wpforms-lite' ),
'error_could_not_install' => $error_could_not_install,
'error_could_not_activate' => $error_could_not_activate,
'manual_install_url' => $this->config['lite_download_url'],
'manual_activate_url' => admin_url( 'plugins.php' ),
'smtp_settings' => esc_html__( 'Go to SMTP settings', 'wpforms-lite' ),
'smtp_wizard' => esc_html__( 'Open Setup Wizard', 'wpforms-lite' ),
'smtp_settings_url' => esc_url( $this->config['smtp_settings_url'] ),
'smtp_wizard_url' => esc_url( $this->config['smtp_wizard_url'] ),
];
}
/**
* Generate and output page HTML.
*
* @since 1.5.7
*/
public function output() {
echo '<div id="wpforms-admin-smtp" class="wrap wpforms-admin-wrap wpforms-admin-plugin-landing">';
$this->output_section_heading();
$this->output_section_screenshot();
$this->output_section_step_install();
$this->output_section_step_setup();
echo '</div>';
}
/**
* Generate and output heading section HTML.
*
* @since 1.5.7
*/
protected function output_section_heading() {
// Heading section.
printf(
'<section class="top">
<img class="img-top" src="%1$s" srcset="%2$s 2x" alt="%3$s"/>
<h1>%4$s</h1>
<p>%5$s</p>
</section>',
esc_url( WPFORMS_PLUGIN_URL . 'assets/images/smtp/wpforms-wpmailsmtp.png' ),
esc_url( WPFORMS_PLUGIN_URL . 'assets/images/smtp/wpforms-wpmailsmtp@2x.png' ),
esc_attr__( 'WPForms ♥ WP Mail SMTP', 'wpforms-lite' ),
esc_html__( 'Making Email Deliverability Easy for WordPress', 'wpforms-lite' ),
esc_html__( 'WP Mail SMTP fixes deliverability problems with your WordPress emails and form notifications. It\'s built by the same folks behind WPForms.', 'wpforms-lite' )
);
}
/**
* Generate and output screenshot section HTML.
*
* @since 1.5.7
*/
protected function output_section_screenshot() {
// Screenshot section.
printf(
'<section class="screenshot">
<div class="cont">
<img src="%1$s" alt="%2$s"/>
<a href="%3$s" class="hover" data-lity></a>
</div>
<ul>
<li>%4$s</li>
<li>%5$s</li>
<li>%6$s</li>
<li>%7$s</li>
</ul>
</section>',
esc_url( WPFORMS_PLUGIN_URL . 'assets/images/smtp/screenshot-tnail.png?ver=' . WPFORMS_VERSION ),
esc_attr__( 'WP Mail SMTP screenshot', 'wpforms-lite' ),
esc_url( WPFORMS_PLUGIN_URL . 'assets/images/smtp/screenshot-full.png?ver=' . WPFORMS_VERSION ),
esc_html__( 'Improves email deliverability in WordPress.', 'wpforms-lite' ),
esc_html__( 'Used by 2+ million websites.', 'wpforms-lite' ),
esc_html__( 'Free mailers: SendLayer, SMTP.com, Sendinblue, Google Workspace / Gmail, Mailgun, Postmark, SendGrid.', 'wpforms-lite' ),
esc_html__( 'Pro mailers: Amazon SES, Microsoft 365 / Outlook.com, Zoho Mail.', 'wpforms-lite' )
);
}
/**
* Generate and output step 'Install' section HTML.
*
* @since 1.5.7
*/
protected function output_section_step_install() {
$step = $this->get_data_step_install();
if ( empty( $step ) ) {
return;
}
$button_format = '<button class="button %3$s" data-plugin="%1$s" data-action="%4$s">%2$s</button>';
$button_allowed_html = [
'button' => [
'class' => true,
'data-plugin' => true,
'data-action' => true,
],
];
if (
! $this->output_data['plugin_installed'] &&
! $this->output_data['pro_plugin_installed'] &&
! wpforms_can_install( 'plugin' )
) {
$button_format = '<a class="link" href="%1$s" target="_blank" rel="nofollow noopener">%2$s <span aria-hidden="true" class="dashicons dashicons-external"></span></a>';
$button_allowed_html = [
'a' => [
'class' => true,
'href' => true,
'target' => true,
'rel' => true,
],
'span' => [
'class' => true,
'aria-hidden' => true,
],
];
}
$button = sprintf( $button_format, esc_attr( $step['plugin'] ), esc_html( $step['button_text'] ), esc_attr( $step['button_class'] ), esc_attr( $step['button_action'] ) );
printf(
'<section class="step step-install">
<aside class="num">
<img src="%1$s" alt="%2$s" />
<i class="loader hidden"></i>
</aside>
<div>
<h2>%3$s</h2>
<p>%4$s</p>
%5$s
</div>
</section>',
esc_url( WPFORMS_PLUGIN_URL . 'assets/images/' . $step['icon'] ),
esc_attr__( 'Step 1', 'wpforms-lite' ),
esc_html( $step['heading'] ),
esc_html( $step['description'] ),
wp_kses( $button, $button_allowed_html )
);
}
/**
* Generate and output step 'Setup' section HTML.
*
* @since 1.5.7
*/
protected function output_section_step_setup() {
$step = $this->get_data_step_setup();
if ( empty( $step ) ) {
return;
}
printf(
'<section class="step step-setup %1$s">
<aside class="num">
<img src="%2$s" alt="%3$s" />
<i class="loader hidden"></i>
</aside>
<div>
<h2>%4$s</h2>
<p>%5$s</p>
<button class="button %6$s" data-url="%7$s">%8$s</button>
</div>
</section>',
esc_attr( $step['section_class'] ),
esc_url( WPFORMS_PLUGIN_URL . 'assets/images/' . $step['icon'] ),
esc_attr__( 'Step 2', 'wpforms-lite' ),
esc_html__( 'Set Up WP Mail SMTP', 'wpforms-lite' ),
esc_html__( 'Select and configure your mailer.', 'wpforms-lite' ),
esc_attr( $step['button_class'] ),
esc_url( admin_url( $this->config['smtp_wizard_url'] ) ),
esc_html( $step['button_text'] )
);
}
/**
* Step 'Install' data.
*
* @since 1.5.7
*
* @return array Step data.
*/
protected function get_data_step_install() {
$step = [];
$step['heading'] = esc_html__( 'Install and Activate WP Mail SMTP', 'wpforms-lite' );
$step['description'] = esc_html__( 'Install WP Mail SMTP from the WordPress.org plugin repository.', 'wpforms-lite' );
$this->output_data['all_plugins'] = get_plugins();
$this->output_data['plugin_installed'] = array_key_exists( $this->config['lite_plugin'], $this->output_data['all_plugins'] );
$this->output_data['pro_plugin_installed'] = array_key_exists( $this->config['pro_plugin'], $this->output_data['all_plugins'] );
$this->output_data['plugin_activated'] = false;
$this->output_data['plugin_setup'] = false;
if ( ! $this->output_data['plugin_installed'] && ! $this->output_data['pro_plugin_installed'] ) {
$step['icon'] = 'step-1.svg';
$step['button_text'] = esc_html__( 'Install WP Mail SMTP', 'wpforms-lite' );
$step['button_class'] = 'button-primary';
$step['button_action'] = 'install';
$step['plugin'] = $this->config['lite_download_url'];
if ( ! wpforms_can_install( 'plugin' ) ) {
$step['heading'] = esc_html__( 'WP Mail SMTP', 'wpforms-lite' );
$step['description'] = '';
$step['button_text'] = esc_html__( 'WP Mail SMTP on WordPress.org', 'wpforms-lite' );
$step['plugin'] = $this->config['lite_wporg_url'];
}
} else {
$this->output_data['plugin_activated'] = $this->is_smtp_activated();
$this->output_data['plugin_setup'] = $this->is_smtp_configured();
$step['icon'] = $this->output_data['plugin_activated'] ? 'step-complete.svg' : 'step-1.svg';
$step['button_text'] = $this->output_data['plugin_activated'] ? esc_html__( 'WP Mail SMTP Installed & Activated', 'wpforms-lite' ) : esc_html__( 'Activate WP Mail SMTP', 'wpforms-lite' );
$step['button_class'] = $this->output_data['plugin_activated'] ? 'grey disabled' : 'button-primary';
$step['button_action'] = $this->output_data['plugin_activated'] ? '' : 'activate';
$step['plugin'] = $this->output_data['pro_plugin_installed'] ? $this->config['pro_plugin'] : $this->config['lite_plugin'];
}
return $step;
}
/**
* Step 'Setup' data.
*
* @since 1.5.7
*
* @return array Step data.
*/
protected function get_data_step_setup() {
$step = [
'icon' => 'step-2.svg',
];
if ( $this->output_data['plugin_activated'] ) {
$step['section_class'] = '';
$step['button_class'] = 'button-primary';
$step['button_text'] = esc_html__( 'Open Setup Wizard', 'wpforms-lite' );
} else {
$step['section_class'] = 'grey';
$step['button_class'] = 'grey disabled';
$step['button_text'] = esc_html__( 'Start Setup', 'wpforms-lite' );
}
if ( $this->output_data['plugin_setup'] ) {
$step['icon'] = 'step-complete.svg';
$step['button_text'] = esc_html__( 'Go to SMTP settings', 'wpforms-lite' );
}
return $step;
}
/**
* Ajax endpoint. Check plugin setup status.
* Used to properly init step 'Setup' section after completing step 'Install'.
*
* @since 1.5.7
*/
public function ajax_check_plugin_status() {
// Security checks.
if (
! check_ajax_referer( 'wpforms-admin', 'nonce', false ) ||
! wpforms_current_user_can()
) {
wp_send_json_error(
[
'error' => esc_html__( 'You do not have permission.', 'wpforms-lite' ),
]
);
}
$result = [];
if ( ! $this->is_smtp_activated() ) {
wp_send_json_error(
[
'error' => esc_html__( 'Plugin unavailable.', 'wpforms-lite' ),
]
);
}
$result['setup_status'] = (int) $this->is_smtp_configured();
$result['license_level'] = wp_mail_smtp()->get_license_type();
// Prevent redirect to the WP Mail SMTP Setup Wizard on the fresh installs.
// We need this workaround since WP Mail SMTP doesn't check whether the mailer is already configured when redirecting to the Setup Wizard on the first run.
if ( $result['setup_status'] > 0 ) {
update_option( 'wp_mail_smtp_activation_prevent_redirect', true );
}
wp_send_json_success( $result );
}
/**
* Get $phpmailer instance.
*
* @since 1.5.7
* @since 1.6.1.2 Conditionally returns $phpmailer v5 or v6.
*
* @return \PHPMailer|\PHPMailer\PHPMailer\PHPMailer Instance of PHPMailer.
*/
protected function get_phpmailer() {
if ( version_compare( get_bloginfo( 'version' ), '5.5-alpha', '<' ) ) {
$phpmailer = $this->get_phpmailer_v5();
} else {
$phpmailer = $this->get_phpmailer_v6();
}
return $phpmailer;
}
/**
* Get $phpmailer v5 instance.
*
* @since 1.6.1.2
*
* @return \PHPMailer Instance of PHPMailer.
*/
private function get_phpmailer_v5() {
global $phpmailer;
if ( ! ( $phpmailer instanceof \PHPMailer ) ) {
require_once ABSPATH . WPINC . '/class-phpmailer.php';
require_once ABSPATH . WPINC . '/class-smtp.php';
$phpmailer = new \PHPMailer( true ); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
}
return $phpmailer;
}
/**
* Get $phpmailer v6 instance.
*
* @since 1.6.1.2
*
* @return \PHPMailer\PHPMailer\PHPMailer Instance of PHPMailer.
*/
private function get_phpmailer_v6() {
global $phpmailer;
if ( ! ( $phpmailer instanceof \PHPMailer\PHPMailer\PHPMailer ) ) {
require_once ABSPATH . WPINC . '/PHPMailer/PHPMailer.php';
require_once ABSPATH . WPINC . '/PHPMailer/SMTP.php';
require_once ABSPATH . WPINC . '/PHPMailer/Exception.php';
$phpmailer = new \PHPMailer\PHPMailer\PHPMailer( true ); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
}
return $phpmailer;
}
/**
* Whether WP Mail SMTP plugin configured or not.
*
* @since 1.5.7
*
* @return bool True if some mailer is selected and configured properly.
*/
protected function is_smtp_configured() {
if ( ! $this->is_smtp_activated() ) {
return false;
}
$phpmailer = $this->get_phpmailer();
$mailer = \WPMailSMTP\Options::init()->get( 'mail', 'mailer' );
return ! empty( $mailer ) &&
$mailer !== 'mail' &&
wp_mail_smtp()->get_providers()->get_mailer( $mailer, $phpmailer )->is_mailer_complete();
}
/**
* Whether WP Mail SMTP plugin active or not.
*
* @since 1.5.7
*
* @return bool True if SMTP plugin is active.
*/
protected function is_smtp_activated() {
return function_exists( 'wp_mail_smtp' ) && ( is_plugin_active( $this->config['lite_plugin'] ) || is_plugin_active( $this->config['pro_plugin'] ) );
}
/**
* Redirect to SMTP settings page.
*
* @since 1.5.7
*/
public function redirect_to_smtp_settings() {
// Redirect to SMTP plugin if it is activated.
if ( $this->is_smtp_configured() ) {
wp_safe_redirect( admin_url( $this->config['smtp_settings_url'] ) );
exit;
}
}
}
Admin/Pages/Analytics.php 0000644 00000041457 15133255232 0011307 0 ustar 00 <?php
namespace WPForms\Admin\Pages;
/**
* Analytics Sub-page.
*
* @since 1.5.7
*/
class Analytics {
/**
* Admin menu page slug.
*
* @since 1.5.7
*
* @var string
*/
const SLUG = 'wpforms-analytics';
/**
* Configuration.
*
* @since 1.5.7
*
* @var array
*/
private $config = array(
'lite_plugin' => 'google-analytics-for-wordpress/googleanalytics.php',
'lite_wporg_url' => 'https://wordpress.org/plugins/google-analytics-for-wordpress/',
'lite_download_url' => 'https://downloads.wordpress.org/plugin/google-analytics-for-wordpress.zip',
'pro_plugin' => 'google-analytics-premium/googleanalytics-premium.php',
'forms_addon' => 'monsterinsights-forms/monsterinsights-forms.php',
'mi_forms_addon_page' => 'https://www.monsterinsights.com/addon/forms/?utm_source=wpformsplugin&utm_medium=link&utm_campaign=analytics-page',
'mi_onboarding' => 'admin.php?page=monsterinsights-onboarding',
'mi_addons' => 'admin.php?page=monsterinsights_settings#/addons',
'mi_forms' => 'admin.php?page=monsterinsights_reports#/forms',
);
/**
* Runtime data used for generating page HTML.
*
* @since 1.5.7
*
* @var array
*/
private $output_data = array();
/**
* Constructor.
*
* @since 1.5.7
*/
public function __construct() {
if ( ! \wpforms_current_user_can() ) {
return;
}
$this->hooks();
}
/**
* Hooks.
*
* @since 1.5.7
*/
public function hooks() {
if ( wp_doing_ajax() ) {
add_action( 'wp_ajax_wpforms_analytics_page_check_plugin_status', array( $this, 'ajax_check_plugin_status' ) );
}
// Check what page we are on.
$page = isset( $_GET['page'] ) ? \sanitize_key( \wp_unslash( $_GET['page'] ) ) : ''; // phpcs:ignore WordPress.CSRF.NonceVerification
// Only load if we are actually on the Analytics page.
if ( self::SLUG !== $page ) {
return;
}
add_action( 'admin_init', array( $this, 'redirect_to_mi_forms' ) );
add_filter( 'wpforms_admin_header', '__return_false' );
add_action( 'wpforms_admin_page', array( $this, 'output' ) );
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ) );
// Hook for addons.
do_action( 'wpforms_admin_pages_analytics_hooks' );
}
/**
* Enqueue JS and CSS files.
*
* @since 1.5.7
*/
public function enqueue_assets() {
$min = \wpforms_get_min_suffix();
// Lity.
wp_enqueue_style(
'wpforms-lity',
WPFORMS_PLUGIN_URL . 'assets/lib/lity/lity.min.css',
null,
'3.0.0'
);
wp_enqueue_script(
'wpforms-lity',
WPFORMS_PLUGIN_URL . 'assets/lib/lity/lity.min.js',
array( 'jquery' ),
'3.0.0',
true
);
wp_enqueue_script(
'wpforms-admin-page-analytics',
WPFORMS_PLUGIN_URL . "assets/js/components/admin/pages/mi-analytics{$min}.js",
array( 'jquery' ),
WPFORMS_VERSION,
true
);
\wp_localize_script(
'wpforms-admin-page-analytics',
'wpforms_pluginlanding',
$this->get_js_strings()
);
}
/**
* JS Strings.
*
* @since 1.5.7
*
* @return array Array of strings.
*/
protected function get_js_strings() {
$error_could_not_install = sprintf(
wp_kses( /* translators: %s - Lite plugin download URL. */
__( 'Could not install the plugin automatically. Please <a href="%s">download</a> it and install it manually.', 'wpforms-lite' ),
[
'a' => [
'href' => true,
],
]
),
esc_url( $this->config['lite_download_url'] )
);
$error_could_not_activate = sprintf(
wp_kses( /* translators: %s - Lite plugin download URL. */
__( 'Could not activate the plugin. Please activate it on the <a href="%s">Plugins page</a>.', 'wpforms-lite' ),
[
'a' => [
'href' => true,
],
]
),
esc_url( admin_url( 'plugins.php' ) )
);
return [
'installing' => esc_html__( 'Installing...', 'wpforms-lite' ),
'activating' => esc_html__( 'Activating...', 'wpforms-lite' ),
'activated' => esc_html__( 'MonsterInsights Installed & Activated', 'wpforms-lite' ),
'install_now' => esc_html__( 'Install Now', 'wpforms-lite' ),
'activate_now' => esc_html__( 'Activate Now', 'wpforms-lite' ),
'download_now' => esc_html__( 'Download Now', 'wpforms-lite' ),
'plugins_page' => esc_html__( 'Go to Plugins page', 'wpforms-lite' ),
'error_could_not_install' => $error_could_not_install,
'error_could_not_activate' => $error_could_not_activate,
'mi_manual_install_url' => $this->config['lite_download_url'],
'mi_manual_activate_url' => admin_url( 'plugins.php' ),
];
}
/**
* Generate and output page HTML.
*
* @since 1.5.7
*/
public function output() {
echo '<div id="wpforms-admin-analytics" class="wrap wpforms-admin-wrap wpforms-admin-plugin-landing">';
$this->output_section_heading();
$this->output_section_screenshot();
$this->output_section_step_install();
$this->output_section_step_setup();
$this->output_section_step_addon();
echo '</div>';
}
/**
* Generate and output heading section HTML.
*
* @since 1.5.7
*/
public function output_section_heading() {
// Heading section.
printf(
'<section class="top">
<img class="img-top" src="%1$s" srcset="%2$s 2x" alt="%3$s"/>
<h1>%4$s</h1>
<p>%5$s</p>
</section>',
esc_url( WPFORMS_PLUGIN_URL . 'assets/images/analytics/wpforms-monsterinsights.png' ),
esc_url( WPFORMS_PLUGIN_URL . 'assets/images/analytics/wpforms-monsterinsights@2x.png' ),
esc_attr__( 'WPForms ♥ MonsterInsights', 'wpforms-lite' ),
esc_html__( 'The Best Google Analytics Plugin for WordPress', 'wpforms-lite' ),
esc_html__( 'MonsterInsights connects WPForms to Google Analytics, providing a powerful integration with their Forms addon. MonsterInsights is a sister company of WPForms.', 'wpforms-lite' )
);
}
/**
* Generate and output heading section HTML.
*
* @since 1.5.7
*/
protected function output_section_screenshot() {
// Screenshot section.
printf(
'<section class="screenshot">
<div class="cont">
<img src="%1$s" alt="%2$s"/>
<a href="%3$s" class="hover" data-lity></a>
</div>
<ul>
<li>%4$s</li>
<li>%5$s</li>
<li>%6$s</li>
<li>%7$s</li>
</ul>
</section>',
esc_url( WPFORMS_PLUGIN_URL . 'assets/images/analytics/screenshot-tnail.jpg' ),
esc_attr__( 'Analytics screenshot', 'wpforms-lite' ),
esc_url( WPFORMS_PLUGIN_URL . 'assets/images/analytics/screenshot-full.jpg' ),
esc_html__( 'Track form impressions and conversions.', 'wpforms-lite' ),
esc_html__( 'View form conversion rates from WordPress.', 'wpforms-lite' ),
esc_html__( 'Complete UTM tracking with form entries.', 'wpforms-lite' ),
esc_html__( 'Automatic integration with WPForms.', 'wpforms-lite' )
);
}
/**
* Generate and output step 'Install' section HTML.
*
* @since 1.5.7
*/
protected function output_section_step_install() {
$step = $this->get_data_step_install();
if ( empty( $step ) ) {
return;
}
$button_format = '<button class="button %3$s" data-plugin="%1$s" data-action="%4$s">%2$s</button>';
$button_allowed_html = [
'button' => [
'class' => true,
'data-plugin' => true,
'data-action' => true,
],
];
if (
! $this->output_data['plugin_installed'] &&
! $this->output_data['pro_plugin_installed'] &&
! wpforms_can_install( 'plugin' )
) {
$button_format = '<a class="link" href="%1$s" target="_blank" rel="nofollow noopener">%2$s <span aria-hidden="true" class="dashicons dashicons-external"></span></a>';
$button_allowed_html = [
'a' => [
'class' => true,
'href' => true,
'target' => true,
'rel' => true,
],
'span' => [
'class' => true,
'aria-hidden' => true,
],
];
}
$button = sprintf( $button_format, esc_attr( $step['plugin'] ), esc_html( $step['button_text'] ), esc_attr( $step['button_class'] ), esc_attr( $step['button_action'] ) );
printf(
'<section class="step step-install">
<aside class="num">
<img src="%1$s" alt="%2$s" />
<i class="loader hidden"></i>
</aside>
<div>
<h2>%3$s</h2>
<p>%4$s</p>
%5$s
</div>
</section>',
esc_url( WPFORMS_PLUGIN_URL . 'assets/images/' . $step['icon'] ),
esc_attr__( 'Step 1', 'wpforms-lite' ),
esc_html( $step['heading'] ),
esc_html( $step['description'] ),
wp_kses( $button, $button_allowed_html )
);
}
/**
* Generate and output step 'Setup' section HTML.
*
* @since 1.5.7
*/
protected function output_section_step_setup() {
$step = $this->get_data_step_setup();
if ( empty( $step ) ) {
return;
}
printf(
'<section class="step step-setup %1$s">
<aside class="num">
<img src="%2$s" alt="%3$s" />
<i class="loader hidden"></i>
</aside>
<div>
<h2>%4$s</h2>
<p>%5$s</p>
<button class="button %6$s" data-url="%7$s">%8$s</button>
</div>
</section>',
esc_attr( $step['section_class'] ),
esc_url( WPFORMS_PLUGIN_URL . 'assets/images/' . $step['icon'] ),
esc_attr__( 'Step 2', 'wpforms-lite' ),
esc_html__( 'Setup MonsterInsights', 'wpforms-lite' ),
esc_html__( 'MonsterInsights has an intuitive setup wizard to guide you through the setup process.', 'wpforms-lite' ),
esc_attr( $step['button_class'] ),
esc_url( admin_url( $this->config['mi_onboarding'] ) ),
esc_html( $step['button_text'] )
);
}
/**
* Generate and output step 'Addon' section HTML.
*
* @since 1.5.7
*/
protected function output_section_step_addon() {
$step = $this->get_data_step_addon();
if ( empty( $step ) ) {
return;
}
printf(
'<section class="step step-addon %1$s">
<aside class="num">
<img src="%2$s" alt="%3$s" />
<i class="loader hidden"></i>
</aside>
<div>
<h2>%4$s</h2>
<p>%5$s</p>
<button class="button %6$s" data-url="%7$s">%8$s</button>
</div>
</section>',
esc_attr( $step['section_class'] ),
esc_url( WPFORMS_PLUGIN_URL . 'assets/images/step-3.svg' ),
esc_attr__( 'Step 3', 'wpforms-lite' ),
esc_html__( 'Get Form Conversion Tracking', 'wpforms-lite' ),
esc_html__( 'With the MonsterInsights Form addon you can easily track your form views, entries, conversion rates, and more.', 'wpforms-lite' ),
esc_attr( $step['button_class'] ),
esc_url( $step['button_url'] ),
esc_html( $step['button_text'] )
);
}
/**
* Step 'Install' data.
*
* @since 1.5.7
*
* @return array Step data.
*/
protected function get_data_step_install() {
$step = array();
$step['heading'] = esc_html__( 'Install & Activate MonsterInsights', 'wpforms-lite' );
$step['description'] = esc_html__( 'Track form impressions and conversions.', 'wpforms-lite' );
$this->output_data['all_plugins'] = get_plugins();
$this->output_data['plugin_installed'] = array_key_exists( $this->config['lite_plugin'], $this->output_data['all_plugins'] );
$this->output_data['plugin_activated'] = false;
$this->output_data['pro_plugin_installed'] = array_key_exists( $this->config['pro_plugin'], $this->output_data['all_plugins'] );
$this->output_data['pro_plugin_activated'] = false;
if ( ! $this->output_data['plugin_installed'] && ! $this->output_data['pro_plugin_installed'] ) {
$step['icon'] = 'step-1.svg';
$step['button_text'] = esc_html__( 'Install MonsterInsights', 'wpforms-lite' );
$step['button_class'] = 'button-primary';
$step['button_action'] = 'install';
$step['plugin'] = $this->config['lite_download_url'];
if ( ! wpforms_can_install( 'plugin' ) ) {
$step['heading'] = esc_html__( 'MonsterInsights', 'wpforms-lite' );
$step['description'] = '';
$step['button_text'] = esc_html__( 'MonsterInsights on WordPress.org', 'wpforms-lite' );
$step['plugin'] = $this->config['lite_wporg_url'];
}
} else {
$this->output_data['plugin_activated'] = is_plugin_active( $this->config['lite_plugin'] ) || is_plugin_active( $this->config['pro_plugin'] );
$step['icon'] = $this->output_data['plugin_activated'] ? 'step-complete.svg' : 'step-1.svg';
$step['button_text'] = $this->output_data['plugin_activated'] ? esc_html__( 'MonsterInsights Installed & Activated', 'wpforms-lite' ) : esc_html__( 'Activate MonsterInsights', 'wpforms-lite' );
$step['button_class'] = $this->output_data['plugin_activated'] ? 'grey disabled' : 'button-primary';
$step['button_action'] = $this->output_data['plugin_activated'] ? '' : 'activate';
$step['plugin'] = $this->output_data['pro_plugin_installed'] ? $this->config['pro_plugin'] : $this->config['lite_plugin'];
}
return $step;
}
/**
* Step 'Setup' data.
*
* @since 1.5.7
*
* @return array Step data.
*/
protected function get_data_step_setup() {
$step = array();
$this->output_data['plugin_setup'] = false;
if ( $this->output_data['plugin_activated'] ) {
$this->output_data['plugin_setup'] = '' !== (string) \monsterinsights_get_ua();
}
$step['icon'] = 'step-2.svg';
$step['section_class'] = $this->output_data['plugin_activated'] ? '' : 'grey';
$step['button_text'] = esc_html__( 'Run Setup Wizard', 'wpforms-lite' );
$step['button_class'] = 'grey disabled';
if ( $this->output_data['plugin_setup'] ) {
$step['icon'] = 'step-complete.svg';
$step['section_class'] = '';
$step['button_text'] = esc_html__( 'Setup Complete', 'wpforms-lite' );
} else {
$step['button_class'] = $this->output_data['plugin_activated'] ? 'button-primary' : 'grey disabled';
}
return $step;
}
/**
* Step 'Addon' data.
*
* @since 1.5.7
*
* @return array Step data.
*/
protected function get_data_step_addon() {
$step = array();
$step['icon'] = 'step-3.svg';
$step['section_class'] = $this->output_data['plugin_setup'] ? '' : 'grey';
$step['button_text'] = esc_html__( 'Learn More', 'wpforms-lite' );
$step['button_class'] = 'grey disabled';
$step['button_url'] = '';
$plugin_license_level = false;
if ( $this->output_data['plugin_activated'] ) {
$mi = \MonsterInsights();
$plugin_license_level = 'lite';
if ( is_object( $mi->license ) && method_exists( $mi->license, 'license_can' ) ) {
$plugin_license_level = $mi->license->license_can( 'plus' ) ? 'lite' : $plugin_license_level;
$plugin_license_level = $mi->license->license_can( 'pro' ) || $mi->license->license_can( 'agency' ) ? 'pro' : $plugin_license_level;
}
}
switch ( $plugin_license_level ) {
case 'lite':
$step['button_url'] = $this->config['mi_forms_addon_page'];
$step['button_class'] = $this->output_data['plugin_setup'] ? 'button-primary' : 'grey';
break;
case 'pro':
$addon_installed = array_key_exists( $this->config['forms_addon'], $this->output_data['all_plugins'] );
$step['button_text'] = $addon_installed ? esc_html__( 'Activate Now', 'wpforms-lite' ) : esc_html__( 'Install Now', 'wpforms-lite' );
$step['button_url'] = admin_url( $this->config['mi_addons'] );
$step['button_class'] = $this->output_data['plugin_setup'] ? 'button-primary' : 'grey';
break;
}
return $step;
}
/**
* Ajax endpoint. Check plugin setup status.
* Used to properly init step 2 section after completing step 1.
*
* @since 1.5.7
*/
public function ajax_check_plugin_status() {
// Security checks.
if (
! check_ajax_referer( 'wpforms-admin', 'nonce', false ) ||
! wpforms_current_user_can()
) {
wp_send_json_error(
array(
'error' => esc_html__( 'You do not have permission.', 'wpforms-lite' ),
)
);
}
$result = array();
if ( ! function_exists( 'MonsterInsights' ) || ! function_exists( 'monsterinsights_get_ua' ) ) {
wp_send_json_error(
array(
'error' => esc_html__( 'Plugin unavailable.', 'wpforms-lite' ),
)
);
}
$result['setup_status'] = (int) ( '' !== (string) \monsterinsights_get_ua() );
$mi = \MonsterInsights();
$result['license_level'] = 'lite';
$result['step3_button_url'] = $this->config['mi_forms_addon_page'];
if ( is_object( $mi->license ) && method_exists( $mi->license, 'license_can' ) ) {
$result['license_level'] = $mi->license->license_can( 'pro' ) || $mi->license->license_can( 'agency' ) ? 'pro' : $result['license_level'];
$result['step3_button_url'] = admin_url( $this->config['mi_addons'] );
}
$result['addon_installed'] = (int) array_key_exists( $this->config['forms_addon'], get_plugins() );
wp_send_json_success( $result );
}
/**
* Redirect to MI forms reporting page.
* We need this function because `is_plugin_active()` available only after `admin_init` action.
*
* @since 1.5.7
*/
public function redirect_to_mi_forms() {
require_once ABSPATH . 'wp-admin/includes/plugin.php';
// Redirect to MI Forms addon if it is activated.
if ( is_plugin_active( $this->config['forms_addon'] ) ) {
wp_safe_redirect( admin_url( $this->config['mi_forms'] ) );
exit;
}
}
}
Admin/Pages/ConstantContact.php 0000644 00000003115 15133255232 0012452 0 ustar 00 <?php
namespace WPForms\Admin\Pages;
/**
* Constant Contact Sub-page.
*
* @since 1.7.3
*/
class ConstantContact {
/**
* Determine if the class is allowed to be loaded.
*
* @since 1.7.3
*/
private function allow_load() {
return wpforms_is_admin_page( 'page', 'constant-contact' );
}
/**
* Initialize class.
*
* @since 1.7.3
*/
public function init() {
if ( ! $this->allow_load() ) {
return;
}
$this->hooks();
}
/**
* Hooks.
*
* @since 1.7.3
*/
private function hooks() {
add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_assets' ] );
add_action( 'wpforms_admin_page', [ $this, 'view' ] );
}
/**
* Enqueue JS and CSS files.
*
* @since 1.7.3
*/
public function enqueue_assets() {
// Lity.
wp_enqueue_style(
'wpforms-lity',
WPFORMS_PLUGIN_URL . 'assets/lib/lity/lity.min.css',
null,
'3.0.0'
);
wp_enqueue_script(
'wpforms-lity',
WPFORMS_PLUGIN_URL . 'assets/lib/lity/lity.min.js',
[ 'jquery' ],
'3.0.0',
true
);
}
/**
* Page view.
*
* @since 1.7.3
*/
public function view() {
$sign_up_link = get_option( 'wpforms_constant_contact_signup', 'https://constant-contact.evyy.net/c/11535/341874/3411?sharedid=wpforms' );
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo wpforms_render(
'admin/pages/constant-contact',
[
'sign_up_link' => is_string( $sign_up_link ) ? $sign_up_link : '',
'wpbeginners_guide_link' => 'https://www.wpbeginner.com/beginners-guide/why-you-should-start-building-your-email-list-right-away',
],
true
);
}
}
Admin/Pages/Community.php 0000644 00000014222 15133255232 0011332 0 ustar 00 <?php
namespace WPForms\Admin\Pages;
/**
* Community Sub-page.
*
* @since 1.5.6
*/
class Community {
/**
* Admin menu page slug.
*
* @since 1.5.6
*
* @var string
*/
const SLUG = 'wpforms-community';
/**
* Constructor.
*
* @since 1.5.6
*/
public function __construct() {
if ( \wpforms_current_user_can() ) {
$this->hooks();
}
}
/**
* Hooks.
*
* @since 1.5.6
*/
public function hooks() {
// Check what page we are on.
$page = isset( $_GET['page'] ) ? sanitize_key( wp_unslash( $_GET['page'] ) ) : ''; // phpcs:ignore WordPress.CSRF.NonceVerification
// Only load if we are actually on the Community page.
if ( self::SLUG !== $page ) {
return;
}
add_action( 'wpforms_admin_page', [ $this, 'output' ] );
// Hook for addons.
do_action( 'wpforms_admin_community_init' );
}
/**
* Page data.
*
* @since 1.5.6
*/
public function get_blocks_data() {
$type = wpforms()->is_pro() ? 'plugin' : 'liteplugin';
$data = [];
$data['vip_circle'] = [
'title' => esc_html__( 'WPForms VIP Circle Facebook Group', 'wpforms-lite' ),
'description' => esc_html__( 'Powered by the community, for the community. Anything and everything WPForms: Discussions. Questions. Tutorials. Insights and sneak peaks. Also, exclusive giveaways!', 'wpforms-lite' ),
'button_text' => esc_html__( 'Join WPForms VIP Circle', 'wpforms-lite' ),
'button_link' => 'https://www.facebook.com/groups/wpformsvip/',
'cover_bg_color' => '#E4F0F6',
'cover_img' => 'vip-circle.png',
'cover_img2x' => 'vip-circle@2x.png',
];
$data['youtube'] = [
'title' => esc_html__( 'WPForms YouTube Channel', 'wpforms-lite' ),
'description' => esc_html__( 'Take a visual dive into everything WPForms has to offer. From simple contact forms to advanced payment forms and email marketing integrations, our extensive video collection covers it all.', 'wpforms-lite' ),
'button_text' => esc_html__( 'Visit WPForms YouTube Channel', 'wpforms-lite' ),
'button_link' => 'https://www.youtube.com/c/wpformsplugin',
'cover_bg_color' => '#FFE6E6',
'cover_img' => 'youtube.png',
'cover_img2x' => 'youtube@2x.png',
];
$data['dev_docs'] = [
'title' => esc_html__( 'WPForms Developer Documentation', 'wpforms-lite' ),
'description' => esc_html__( 'Customize and extend WPForms with code. Our comprehensive developer resources include tutorials, snippets, and documentation on core actions, filters, functions, and more.', 'wpforms-lite' ),
'button_text' => esc_html__( 'View WPForms Dev Docs', 'wpforms-lite' ),
'button_link' => 'https://wpforms.com/developers/?utm_source=WordPress&utm_medium=Community&utm_campaign=' . esc_attr( $type ) . '&utm_content=Developers',
'cover_bg_color' => '#EBEBEB',
'cover_img' => 'dev-docs.png',
'cover_img2x' => 'dev-docs@2x.png',
];
$data['wpbeginner'] = [
'title' => esc_html__( 'WPBeginner Engage Facebook Group', 'wpforms-lite' ),
'description' => esc_html__( 'Hang out with other WordPress experts and like minded website owners such as yourself! Hosted by WPBeginner, the largest free WordPress site for beginners.', 'wpforms-lite' ),
'button_text' => esc_html__( 'Join WPBeginner Engage', 'wpforms-lite' ),
'button_link' => 'https://www.facebook.com/groups/wpbeginner/',
'cover_bg_color' => '#FCEBDF',
'cover_img' => 'wpbeginner.png',
'cover_img2x' => 'wpbeginner@2x.png',
];
$data['translators'] = [
'title' => esc_html__( 'WPForms Translators Community', 'wpforms-lite' ),
'description' => esc_html__( 'We\'re building a community of translators and i18n experts to translate WPForms. Sign up to our translator community newsletter to learn more and get information on how you can contribute!', 'wpforms-lite' ),
'button_text' => esc_html__( 'Join Translators Community', 'wpforms-lite' ),
'button_link' => 'https://wpforms.com/translator-community-signup/?utm_source=WordPress&utm_medium=Community&utm_campaign=' . esc_attr( $type ) . '&utm_content=Translators',
'cover_bg_color' => '#F2FAED',
'cover_img' => 'translators.png',
'cover_img2x' => 'translators@2x.png',
];
$data['suggest'] = [
'title' => esc_html__( 'Suggest a Feature', 'wpforms-lite' ),
'description' => esc_html__( 'Do you have an idea or suggestion for WPForms? If you have thoughts on features, integrations, addons, or improvements - we want to hear it! We appreciate all feedback and insight from our users.', 'wpforms-lite' ),
'button_text' => esc_html__( 'Suggest a Feature', 'wpforms-lite' ),
'button_link' => 'https://wpforms.com/features/suggest/?utm_source=WordPress&utm_medium=Community&utm_campaign=' . esc_attr( $type ) . '&utm_content=Feature',
'cover_bg_color' => '#FFF9EF',
'cover_img' => 'suggest.png',
'cover_img2x' => 'suggest@2x.png',
];
return $data;
}
/**
* Generate and output page HTML.
*
* @since 1.5.6
*/
public function output() {
?>
<div id="wpforms-admin-community" class="wrap wpforms-admin-wrap">
<h1 class="page-title"><?php esc_html_e( 'Community', 'wpforms-lite' ); ?></h1>
<div class="items">
<?php
$data = $this->get_blocks_data();
foreach ( $data as $item ) {
printf(
'<div class="item">
<a href="%6$s" target="_blank" rel="noopener noreferrer" class="item-cover" style="background-color: %s;" title="%4$s"><img class="item-img" src="%s" srcset="%s 2x" alt="%4$s"/></a>
<h3 class="item-title">%s</h3>
<p class="item-description">%s</p>
<div class="item-footer">
<a class="button-primary" href="%s" target="_blank" rel="noopener noreferrer">%s</a>
</div>
</div>',
esc_attr( $item['cover_bg_color'] ),
esc_url( WPFORMS_PLUGIN_URL . 'assets/images/community/' . $item['cover_img'] ),
esc_url( WPFORMS_PLUGIN_URL . 'assets/images/community/' . $item['cover_img2x'] ),
esc_html( $item['title'] ),
esc_html( $item['description'] ),
esc_url( $item['button_link'] ),
esc_html( $item['button_text'] )
);
}
?>
</div>
</div>
<?php
}
}
Admin/FormEmbedWizard.php 0000644 00000021173 15133255232 0011333 0 ustar 00 <?php
namespace WPForms\Admin;
/**
* Embed Form in a Page wizard.
*
* @since 1.6.2
*/
class FormEmbedWizard {
/**
* Initialize class.
*
* @since 1.6.2
*/
public function init() {
// Form Embed Wizard should load only in the Form Builder and on the Edit/Add Page screen.
if (
! wpforms_is_admin_page( 'builder' ) &&
! wp_doing_ajax() &&
! $this->is_form_embed_page()
) {
return;
}
$this->hooks();
}
/**
* Register hooks.
*
* @since 1.6.2
*/
public function hooks() {
add_action( 'admin_enqueue_scripts', [ $this, 'enqueues' ] );
add_action( 'admin_footer', [ $this, 'output' ] );
add_filter( 'default_title', [ $this, 'embed_page_title' ], 10, 2 );
add_filter( 'default_content', [ $this, 'embed_page_content' ], 10, 2 );
add_action( 'wp_ajax_wpforms_admin_form_embed_wizard_embed_page_url', [ $this, 'get_embed_page_url_ajax' ] );
}
/**
* Enqueue assets.
*
* @since 1.6.2
*/
public function enqueues() {
$min = wpforms_get_min_suffix();
if ( $this->is_form_embed_page() && ! $this->is_challenge_active() ) {
wp_enqueue_style(
'wpforms-admin-form-embed-wizard',
WPFORMS_PLUGIN_URL . "assets/css/form-embed-wizard{$min}.css",
[],
WPFORMS_VERSION
);
wp_enqueue_style(
'tooltipster',
WPFORMS_PLUGIN_URL . 'assets/lib/jquery.tooltipster/jquery.tooltipster.min.css',
null,
'4.2.6'
);
wp_enqueue_script(
'tooltipster',
WPFORMS_PLUGIN_URL . 'assets/lib/jquery.tooltipster/jquery.tooltipster.min.js',
[ 'jquery' ],
'4.2.6',
true
);
}
wp_enqueue_script(
'wpforms-admin-form-embed-wizard',
WPFORMS_PLUGIN_URL . "assets/js/components/admin/form-embed-wizard{$min}.js",
[ 'jquery' ],
WPFORMS_VERSION
);
wp_localize_script(
'wpforms-admin-form-embed-wizard',
'wpforms_admin_form_embed_wizard',
[
'nonce' => wp_create_nonce( 'wpforms_admin_form_embed_wizard_nonce' ),
'is_edit_page' => (int) $this->is_form_embed_page( 'edit' ),
'video_url' => esc_url(
sprintf(
'https://youtube.com/embed/%s?rel=0&showinfo=0',
wpforms_is_gutenberg_active() ? '_29nTiDvmLw' : 'IxGVz3AjEe0'
)
),
]
);
}
/**
* Output HTML.
*
* @since 1.6.2
*/
public function output() {
// We do not need to output tooltip if Challenge is active.
if ( $this->is_form_embed_page() && $this->is_challenge_active() ) {
$this->delete_meta();
return;
}
$template = $this->is_form_embed_page() ? 'admin/form-embed-wizard/tooltip' : 'admin/form-embed-wizard/popup';
$args = [];
if ( ! $this->is_form_embed_page() ) {
$args['user_can_edit_pages'] = current_user_can( 'edit_pages' );
$args['dropdown_pages'] = $this->get_dropdown_pages();
}
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo wpforms_render( $template, $args );
$this->delete_meta();
}
/**
* Check if Challenge is active.
*
* @since 1.6.4
*
* @return boolean
*/
public function is_challenge_active() {
static $challenge_active = null;
if ( is_null( $challenge_active ) ) {
$challenge = wpforms()->get( 'challenge' );
$challenge_active = method_exists( $challenge, 'challenge_active' ) ? $challenge->challenge_active() : false;
}
return $challenge_active;
}
/**
* Check if the current page is a form embed page.
*
* @since 1.6.2
*
* @param string $type Type of the embed page to check. Can be '', 'add' or 'edit'. By default is empty string.
*
* @return boolean
*/
public function is_form_embed_page( $type = '' ) {
global $pagenow;
$type = $type === 'add' || $type === 'edit' ? $type : '';
if (
$pagenow !== 'post.php' &&
$pagenow !== 'post-new.php'
) {
return false;
}
// phpcs:disable WordPress.Security.NonceVerification.Recommended
$post_id = empty( $_GET['post'] ) ? 0 : (int) $_GET['post'];
$post_type = empty( $_GET['post_type'] ) ? '' : sanitize_key( $_GET['post_type'] );
$action = empty( $_GET['action'] ) ? 'add' : sanitize_key( $_GET['action'] );
// phpcs:enable
if ( $pagenow === 'post-new.php' &&
( empty( $post_type ) || $post_type !== 'page' )
) {
return false;
}
if (
$pagenow === 'post.php' &&
( empty( $post_id ) || get_post_type( $post_id ) !== 'page' )
) {
return false;
}
$meta = $this->get_meta();
$embed_page = ! empty( $meta['embed_page'] ) ? (int) $meta['embed_page'] : 0;
if ( 'add' === $action && 0 === $embed_page && $type !== 'edit' ) {
return true;
}
if ( ! empty( $post_id ) && $embed_page === $post_id && $type !== 'add' ) {
return true;
}
return false;
}
/**
* Set user's embed meta data.
*
* @since 1.6.2
*
* @param array $data Data array to set.
*/
public function set_meta( $data ) {
update_user_meta( get_current_user_id(), 'wpforms_admin_form_embed_wizard', $data );
}
/**
* Get user's embed meta data.
*
* @since 1.6.2
*
* @return array User's embed meta data.
*/
public function get_meta() {
return get_user_meta( get_current_user_id(), 'wpforms_admin_form_embed_wizard', true );
}
/**
* Delete user's embed meta data.
*
* @since 1.6.2
*/
public function delete_meta() {
delete_user_meta( get_current_user_id(), 'wpforms_admin_form_embed_wizard' );
}
/**
* Get embed page URL via AJAX.
*
* @since 1.6.2
*/
public function get_embed_page_url_ajax() {
check_admin_referer( 'wpforms_admin_form_embed_wizard_nonce' );
$page_id = ! empty( $_POST['pageId'] ) ? absint( $_POST['pageId'] ) : 0;
if ( ! empty( $page_id ) ) {
$url = get_edit_post_link( $page_id, '' );
$meta = [
'embed_page' => $page_id,
];
} else {
$url = add_query_arg( 'post_type', 'page', admin_url( 'post-new.php' ) );
$meta = [
'embed_page' => 0,
'embed_page_title' => ! empty( $_POST['pageTitle'] ) ? sanitize_text_field( wp_unslash( $_POST['pageTitle'] ) ) : '',
];
}
$meta['form_id'] = ! empty( $_POST['formId'] ) ? absint( $_POST['formId'] ) : 0;
$this->set_meta( $meta );
// Update challenge option to properly continue challenge on the embed page.
if ( $this->is_challenge_active() ) {
$challenge = wpforms()->get( 'challenge' );
if ( method_exists( $challenge, 'set_challenge_option' ) ) {
$challenge->set_challenge_option( [ 'embed_page' => $meta['embed_page'] ] );
}
}
wp_send_json_success( $url );
}
/**
* Set default title for the new page.
*
* @since 1.6.2
*
* @param string $post_title Default post title.
* @param \WP_Post $post Post object.
*
* @return string New default post title.
*/
public function embed_page_title( $post_title, $post ) {
$meta = $this->get_meta();
$this->delete_meta();
return empty( $meta['embed_page_title'] ) ? $post_title : $meta['embed_page_title'];
}
/**
* Embed the form to the new page.
*
* @since 1.6.2
*
* @param string $post_content Default post content.
* @param \WP_Post $post Post object.
*
* @return string Embedding string (shortcode or GB component code).
*/
public function embed_page_content( $post_content, $post ) {
$meta = $this->get_meta();
$form_id = ! empty( $meta['form_id'] ) ? $meta['form_id'] : 0;
$page_id = ! empty( $meta['embed_page'] ) ? $meta['embed_page'] : 0;
if ( ! empty( $page_id ) || empty( $form_id ) ) {
return $post_content;
}
if ( wpforms_is_gutenberg_active() ) {
$pattern = '<!-- wp:wpforms/form-selector {"formId":"%d"} /-->';
} else {
$pattern = '[wpforms id="%d" title="false" description="false"]';
}
return sprintf( $pattern, absint( $form_id ) );
}
/**
* Generate select with pages which are available to edit for current user.
*
* @since 1.6.6
*
* @return string HTML dropdown list of pages.
*/
private function get_dropdown_pages() {
add_filter( 'get_pages', [ $this, 'remove_inaccessible_pages' ], 20 );
$dropdown = wp_dropdown_pages(
[
'show_option_none' => esc_html__( 'Select a Page', 'wpforms-lite' ),
'id' => 'wpforms-admin-form-embed-wizard-select-page',
'name' => '',
'post_status' => [ 'publish', 'pending' ],
'echo' => false,
]
);
remove_filter( 'get_pages', [ $this, 'remove_inaccessible_pages' ], 20 );
return $dropdown;
}
/**
* Excludes pages from dropdown which user can't edit.
*
* @since 1.6.6
*
* @param \WP_Post[] $pages Array of page objects.
*
* @return \WP_Post[]|false Array of filtered pages or false.
*/
public function remove_inaccessible_pages( $pages ) {
if ( ! $pages ) {
return $pages;
}
foreach ( $pages as $key => $page ) {
if ( ! current_user_can( 'edit_page', $page->ID ) ) {
unset( $pages[ $key ] );
}
}
return $pages;
}
}
Admin/SiteHealth.php 0000644 00000006176 15133255232 0010352 0 ustar 00 <?php
namespace WPForms\Admin;
/**
* Site Health WPForms Info.
*
* @since 1.5.5
*/
class SiteHealth {
/**
* Indicate if Site Health is allowed to load.
*
* @since 1.5.5
*
* @return bool
*/
private function allow_load() {
global $wp_version;
return version_compare( $wp_version, '5.2', '>=' );
}
/**
* Init Site Health.
*
* @since 1.5.5
*/
final public function init() {
if ( ! $this->allow_load() ) {
return;
}
$this->hooks();
}
/**
* Integration hooks.
*
* @since 1.5.5
*/
protected function hooks() {
add_filter( 'debug_information', [ $this, 'add_info_section' ] );
}
/**
* Add WPForms section to Info tab.
*
* @since 1.5.5
*
* @param array $debug_info Array of all information.
*
* @return array Array with added WPForms info section.
*/
public function add_info_section( $debug_info ) {
$wpforms = [
'label' => 'WPForms',
'fields' => [
'version' => [
'label' => esc_html__( 'Version', 'wpforms-lite' ),
'value' => WPFORMS_VERSION,
],
],
];
// Install date.
$activated = get_option( 'wpforms_activated', [] );
if ( ! empty( $activated['lite'] ) ) {
$date = $activated['lite'] + ( get_option( 'gmt_offset' ) * 3600 );
$wpforms['fields']['lite'] = [
'label' => esc_html__( 'Lite install date', 'wpforms-lite' ),
'value' => date_i18n( esc_html__( 'M j, Y @ g:ia' ), $date ),
];
}
if ( ! empty( $activated['pro'] ) ) {
$date = $activated['pro'] + ( get_option( 'gmt_offset' ) * 3600 );
$wpforms['fields']['pro'] = [
'label' => esc_html__( 'Pro install date', 'wpforms-lite' ),
'value' => date_i18n( esc_html__( 'M j, Y @ g:ia' ), $date ),
];
}
// Permissions for the upload directory.
$upload_dir = wpforms_upload_dir();
$wpforms['fields']['upload_dir'] = [
'label' => esc_html__( 'Uploads directory', 'wpforms-lite' ),
'value' => empty( $upload_dir['error'] ) && ! empty( $upload_dir['path'] ) && wp_is_writable( $upload_dir['path'] ) ? esc_html__( 'Writable', 'wpforms-lite' ) : esc_html__( 'Not writable', 'wpforms-lite' ),
];
// DB tables.
$db_tables = wpforms()->get_existing_custom_tables();
if ( $db_tables ) {
$db_tables_str = empty( $db_tables ) ? esc_html__( 'Not found', 'wpforms-lite' ) : implode( ', ', $db_tables );
$wpforms['fields']['db_tables'] = [
'label' => esc_html__( 'DB tables', 'wpforms-lite' ),
'value' => $db_tables_str,
];
}
// Total forms.
$wpforms['fields']['total_forms'] = [
'label' => esc_html__( 'Total forms', 'wpforms-lite' ),
'value' => wp_count_posts( 'wpforms' )->publish,
];
if ( ! wpforms()->is_pro() ) {
$forms = wpforms()->form->get( '', array( 'fields' => 'ids' ) );
if ( empty( $forms ) || ! is_array( $forms ) ) {
$forms = [];
}
$count = 0;
foreach ( $forms as $form_id ) {
$count += (int) get_post_meta( $form_id, 'wpforms_entries_count', true );
}
$wpforms['fields']['total_submissions'] = [
'label' => esc_html__( 'Total submissions (since v1.5.0)', 'wpforms-lite' ),
'value' => $count,
];
}
$debug_info['wpforms'] = $wpforms;
return $debug_info;
}
}
Admin/Builder/Templates.php 0000644 00000037723 15133255232 0011646 0 ustar 00 <?php
namespace WPForms\Admin\Builder;
/**
* Templates class.
*
* @since 1.6.8
*/
class Templates {
/**
* All templates data from API.
*
* @since 1.6.8
*
* @var array
*/
private $api_templates;
/**
* Template categories data.
*
* @since 1.6.8
*
* @var array
*/
private $categories;
/**
* License data.
*
* @since 1.6.8
*
* @var array
*/
private $license;
/**
* All licenses list.
*
* @since 1.6.8
*
* @var array
*/
private $all_licenses;
/**
* Determine if the class is allowed to load.
*
* @since 1.6.8
*
* @return bool
*/
private function allow_load() {
// Load only in the Form Builder.
$allow = wp_doing_ajax() || wpforms_is_admin_page( 'builder' );
/**
* Whether to allow the form templates functionality to load.
*
* @since 1.7.2
*
* @param bool $allow True or false.
*/
return (bool) apply_filters( 'wpforms_admin_builder_templates_allow_load', $allow );
}
/**
* Initialize class.
*
* @since 1.6.8
*/
public function init() {
if ( ! $this->allow_load() ) {
return;
}
$this->init_license_data();
$this->init_templates_data();
$this->hooks();
}
/**
* Hooks.
*
* @since 1.6.8
*/
protected function hooks() {
add_action( 'admin_init', [ $this, 'create_form_on_request' ], 100 );
add_filter( 'wpforms_form_templates_core', [ $this, 'add_templates_to_setup_panel' ], 20 );
add_filter( 'wpforms_create_form_args', [ $this, 'apply_to_new_form' ], 10, 2 );
add_filter( 'wpforms_save_form_args', [ $this, 'apply_to_existing_form' ], 10, 3 );
}
/**
* Init license data.
*
* @since 1.6.8
*/
private function init_license_data() {
$this->all_licenses = [ 'lite', 'basic', 'plus', 'pro', 'elite', 'agency', 'ultimate' ];
// User license data.
$this->license['key'] = wpforms_get_license_key();
$this->license['type'] = wpforms_get_license_type();
$this->license['type'] = in_array( $this->license['type'], [ 'agency', 'ultimate' ], true ) ? 'elite' : $this->license['type'];
$this->license['type'] = empty( $this->license['type'] ) ? 'lite' : $this->license['type'];
$this->license['index'] = array_search( $this->license['type'], $this->all_licenses, true );
}
/**
* Init templates and categories data.
*
* @since 1.6.8
*/
private function init_templates_data() {
// Get cached templates data.
$cache_data = wpforms()->get( 'builder_templates_cache' )->get_cached();
$templates_all = ! empty( $cache_data['templates'] ) ? $cache_data['templates'] : [];
$this->categories = ! empty( $cache_data['categories'] ) ? $cache_data['categories'] : [];
// Higher priority templates slugs.
// These remote templates are the replication of the default templates,
// which were previously included with the WPForms plugin.
$higher_templates_slugs = [
'simple-contact-form-template',
'request-a-quote-form-template',
'donation-form-template',
'billing-order-form-template',
'newsletter-signup-form-template',
'suggestion-form-template',
];
$templates_higher = [];
$templates_access = [];
$templates_denied = [];
/**
* The form template was moved to wpforms/includes/templates/class-simple-contact-form.php file.
*
* @since 1.7.5.3
*/
unset( $templates_all['simple-contact-form-template'] );
foreach ( $templates_all as $i => $template ) {
$template['has_access'] = $this->has_access( $template );
$template['license'] = $this->get_license_level( $template );
$template['source'] = 'wpforms-api';
$template['categories'] = ! empty( $template['categories'] ) ? array_keys( $template['categories'] ) : [];
$is_higher = in_array( $i, $higher_templates_slugs, true );
if ( $template['has_access'] ) {
if ( $is_higher ) {
$templates_higher[ $i ] = $template;
} else {
$templates_access[ $i ] = $template;
}
} else {
if ( $is_higher ) {
$templates_denied = array_merge( [ $i => $template ], $templates_denied );
} else {
$templates_denied[ $i ] = $template;
}
}
}
// Sort higher priority templates according to the slugs order.
$templates_higher = array_replace( array_flip( $higher_templates_slugs ), $templates_higher );
$templates_higher = array_filter( $templates_higher, 'is_array' );
// Finally, merge templates from API.
$this->api_templates = array_merge( $templates_higher, $templates_access, $templates_denied );
}
/**
* Determine if user's license level has access to the template.
*
* @since 1.6.8
*
* @param array $template Template data.
*
* @return bool
*/
private function has_access( $template ) {
if ( ! empty( $template['has_access'] ) ) {
return true;
}
$template_licenses = empty( $template['license'] ) ? [] : array_map( 'strtolower', (array) $template['license'] );
$has_access = true;
foreach ( $template_licenses as $template_license ) {
$has_access = $this->license['index'] >= array_search( $template_license, $this->all_licenses, true );
if ( $has_access ) {
break;
}
}
return $has_access;
}
/**
* Determine if the template exists and the customer has access to it.
*
* @since 1.7.5.3
*
* @param string $slug Template slug or ID.
*
* @return bool
*/
public function is_valid_template( $slug ) {
$template = $this->get_template_by_id( $slug );
if ( ! $template ) {
return ! empty( $this->get_template_by_slug( $slug ) );
}
$has_cache = wpforms()->get( 'builder_template_single' )->instance( $template['id'], $this->license )->get_cached();
return $this->has_access( $template ) && $has_cache;
}
/**
* Determine license level of the template.
*
* @since 1.6.8
*
* @param array $template Template data.
*
* @return string
*/
private function get_license_level( $template ) {
$licenses_pro = [ 'basic', 'plus', 'pro' ];
$licenses_template = (array) $template['license'];
if (
empty( $template['license'] ) ||
in_array( 'lite', $licenses_template, true )
) {
return '';
}
foreach ( $licenses_pro as $license ) {
if ( in_array( $license, $licenses_template, true ) ) {
return 'pro';
}
}
return 'elite';
}
/**
* Get categories data.
*
* @since 1.6.8
*
* @return array
*/
public function get_categories() {
return $this->categories;
}
/**
* Get templates data.
*
* @since 1.6.8
*
* @return array
*/
public function get_templates() {
static $templates = [];
if ( ! empty( $templates ) ) {
return $templates;
}
// phpcs:disable WPForms.PHP.ValidateHooks.InvalidHookName
/**
* Form templates available in the WPForms core plugin.
*
* @since 1.4.0
*
* @param array $templates Core templates data.
*/
$core_templates = (array) apply_filters( 'wpforms_form_templates_core', [] );
/**
* Form templates available with the WPForms addons.
* Allows developers to provide additional templates with an addons.
*
* @since 1.4.0
*
* @param array $templates Addons templates data.
*/
$additional_templates = (array) apply_filters( 'wpforms_form_templates', [] );
// phpcs:enable WPForms.PHP.ValidateHooks.InvalidHookName
$templates = array_merge( $core_templates, $additional_templates );
return $templates;
}
/**
* Get single template data.
*
* @since 1.6.8
*
* @param string $slug Template slug OR Id.
*
* @return array
*/
private function get_template( $slug ) {
$template = $this->get_template_by_slug( $slug );
if ( ! $template ) {
$template = $this->get_template_by_id( $slug );
}
if ( empty( $template ) ) {
return [];
}
if ( empty( $template['id'] ) ) {
return $template;
}
// Attempt to get template with form data (if available).
$full_template = wpforms()
->get( 'builder_template_single' )
->instance( $template['id'], $this->license )
->get_cached();
if ( ! empty( $full_template['data'] ) ) {
return $full_template;
}
return $template;
}
/**
* Get template data by slug.
*
* @since 1.7.5.3
*
* @param string $slug Template slug.
*
* @return array
*/
private function get_template_by_slug( $slug ) {
foreach ( $this->get_templates() as $template ) {
if ( ! empty( $template['slug'] ) && $template['slug'] === $slug ) {
return $template;
}
}
return [];
}
/**
* Get template data by Id.
*
* @since 1.6.8
*
* @param string $id Template id.
*
* @return array
*/
private function get_template_by_id( $id ) {
foreach ( $this->api_templates as $template ) {
if ( ! empty( $template['id'] ) && $template['id'] === $id ) {
return $template;
}
}
return [];
}
/**
* Add templates to the list on the Setup panel.
*
* @since 1.6.8
*
* @param array $templates Templates list.
*
* @return array
*/
public function add_templates_to_setup_panel( $templates ) {
return array_merge( $templates, $this->api_templates );
}
/**
* Add template data when form is created.
*
* @since 1.6.8
*
* @param array $args Create form arguments.
* @param array $data Template data.
*
* @return array
*/
public function apply_to_new_form( $args, $data ) {
if ( empty( $data ) || empty( $data['template'] ) ) {
return $args;
}
$template = $this->get_template( $data['template'] );
if (
empty( $template['data'] ) ||
! $this->has_access( $template )
) {
return $args;
}
$template['data']['meta']['template'] = $template['id'];
// Enable Notifications by default.
$template['data']['settings']['notification_enable'] = isset( $template['data']['settings']['notification_enable'] )
? $template['data']['settings']['notification_enable']
: 1;
// Unset settings that should be defined locally.
unset(
$template['data']['settings']['form_title'],
$template['data']['settings']['conversational_forms_title'],
$template['data']['settings']['form_pages_title']
);
// Unset certain values for each Notification, since:
// - Email Subject Line field (subject) depends on the form name that is generated from the template name and form_id.
// - From Name field (sender_name) depends on the blog name and can be replaced by WP Mail SMTP plugin.
// - From Email field (sender_address) depends on the internal logic and can be replaced by WP Mail SMTP plugin.
if ( ! empty( $template['data']['settings']['notifications'] ) ) {
foreach ( (array) $template['data']['settings']['notifications'] as $key => $notification ) {
unset(
$template['data']['settings']['notifications'][ $key ]['subject'],
$template['data']['settings']['notifications'][ $key ]['sender_name'],
$template['data']['settings']['notifications'][ $key ]['sender_address']
);
}
}
// Encode template data to post content.
$args['post_content'] = wpforms_encode( $template['data'] );
return $args;
}
/**
* Add template data when form is updated.
*
* @since 1.6.8
*
* @param array $form Form post data.
* @param array $data Form data.
* @param array $args Update form arguments.
*
* @return array
*/
public function apply_to_existing_form( $form, $data, $args ) {
if ( empty( $args ) || empty( $args['template'] ) ) {
return $form;
}
$template = $this->get_template( $args['template'] );
if (
empty( $template['data'] ) ||
! $this->has_access( $template )
) {
return $form;
}
$form_data = wpforms_decode( wp_unslash( $form['post_content'] ) );
// Something is wrong with the form data.
if ( empty( $form_data ) ) {
return $form;
}
// Compile the new form data preserving needed data from the existing form.
$new = $template['data'];
$new['id'] = isset( $form['ID'] ) ? $form['ID'] : 0;
$new['field_id'] = isset( $form_data['field_id'] ) ? $form_data['field_id'] : 0;
$new['settings'] = isset( $form_data['settings'] ) ? $form_data['settings'] : [];
$new['payments'] = isset( $form_data['payments'] ) ? $form_data['payments'] : [];
$new['meta'] = isset( $form_data['meta'] ) ? $form_data['meta'] : [];
$new['meta']['template'] = $template['id'];
// Update the form with new data.
$form['post_content'] = wpforms_encode( $new );
return $form;
}
/**
* Create a form on request.
*
* @since 1.6.8
*/
public function create_form_on_request() {
$template = $this->get_template_on_request();
// Just return if template not found OR user doesn't have access.
if ( empty( $template['has_access'] ) ) {
return;
}
// Check if the template requires some addons.
if ( $this->check_template_required_addons( $template ) ) {
return;
}
// Set form title equal to the template's name.
$form_title = ! empty( $template['name'] ) ? $template['name'] : esc_html__( 'New form', 'wpforms-lite' );
$title_exists = get_page_by_title( $form_title, 'OBJECT', 'wpforms' );
$form_id = wpforms()->form->add(
$form_title,
[],
[
'template' => $template['id'],
]
);
// Return if something wrong.
if ( ! $form_id ) {
return;
}
// Update form title if duplicated.
if ( ! empty( $title_exists ) ) {
wpforms()->form->update(
$form_id,
[
'settings' => [
'form_title' => $form_title . ' (ID #' . $form_id . ')',
],
]
);
}
$this->create_form_on_request_redirect( $form_id );
}
/**
* Get template data before creating a new form on request.
*
* @since 1.6.8
*
* @return array|bool Template OR false.
*/
private function get_template_on_request() {
if ( ! wpforms_is_admin_page( 'builder' ) ) {
return false;
}
if ( ! wpforms_current_user_can( 'create_forms' ) ) {
return false;
}
$form_id = isset( $_GET['form_id'] ) ? (int) $_GET['form_id'] : false; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
if ( ! empty( $form_id ) ) {
return false;
}
$view = isset( $_GET['view'] ) ? sanitize_key( $_GET['view'] ) : 'setup'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
if ( $view !== 'setup' ) {
return false;
}
$template_id = isset( $_GET['template_id'] ) ? sanitize_key( $_GET['template_id'] ) : false; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
// Attempt to get the template.
$template = $this->get_template( $template_id );
// Just return if template is not found.
if ( empty( $template ) ) {
return false;
}
return $template;
}
/**
* Redirect after creating the form.
*
* @since 1.6.8
*
* @param integer $form_id Form ID.
*/
private function create_form_on_request_redirect( $form_id ) {
// Redirect to the builder if possible.
if ( wpforms_current_user_can( 'edit_form_single', $form_id ) ) {
wp_safe_redirect(
add_query_arg(
[
'view' => 'fields',
'form_id' => $form_id,
'newform' => '1',
],
admin_url( 'admin.php?page=wpforms-builder' )
)
);
exit;
}
// Redirect to the forms overview admin page if possible.
if ( wpforms_current_user_can( 'view_forms' ) ) {
wp_safe_redirect(
admin_url( 'admin.php?page=wpforms-overview' )
);
exit;
}
// Finally, redirect to the admin dashboard.
wp_safe_redirect( admin_url() );
exit;
}
/**
* Check if the template requires some addons and then redirect to the builder for further interaction if needed.
*
* @since 1.6.8
*
* @param array $template Template data.
*
* @return bool True if template requires some addons that are not yet installed and/or activated.
*/
private function check_template_required_addons( $template ) {
// Return false if none addons required.
if ( empty( $template['addons'] ) ) {
return false;
}
$required_addons = wpforms()->get( 'addons' )->get_by_slugs( $template['addons'] );
foreach ( $required_addons as $i => $addon ) {
if ( empty( $addon['action'] ) || ! in_array( $addon['action'], [ 'install', 'activate' ], true ) ) {
unset( $required_addons[ $i ] );
}
}
// Return false if not need to install or activate any addons.
// We can proceed with creating the form directly in this process.
if ( empty( $required_addons ) ) {
return false;
}
// Otherwise return true.
return true;
}
}
Admin/Builder/Help.php 0000644 00000143272 15133255232 0010575 0 ustar 00 <?php
namespace WPForms\Admin\Builder;
/**
* Form Builder Help Screen.
*
* @since 1.6.3
*/
class Help {
/**
* Settings.
*
* @since 1.6.3
*
* @var array
*/
private $settings;
/**
* Docs data.
*
* @since 1.6.4
*
* @var array
*/
private $docs;
/**
* Initialize class.
*
* @since 1.6.3
*/
public function init() {
// This should be here, otherwise scheduled task doesn't executes.
add_action( 'wpforms_builder_help_cache_update', [ $this, 'update_docs' ] );
// Terminate initialization if not in builder.
if ( ! wpforms_is_admin_page( 'builder' ) ) {
return;
}
$this->setup();
$this->hooks();
}
/**
* Setup settings and other things.
*
* @since 1.6.3
*/
private function setup() {
$upload_dir = wpforms_upload_dir();
$upload_path = ! empty( $upload_dir['path'] )
? trailingslashit( wp_normalize_path( $upload_dir['path'] ) )
: trailingslashit( WP_CONTENT_DIR ) . 'uploads/wpforms/';
$this->settings = [
// Remote source URL.
'docs_remote_source' => 'https://wpforms.com/wp-content/docs.json',
// Docs cache file (full path).
'cache_file' => $upload_path . 'cache/docs.json',
/**
* Allow modifying Help Docs cache TTL (time to live).
*
* @since 1.6.3
*
* @param int $cache_ttl Cache TTL in seconds. Defaults to 1 week.
*/
'cache_ttl' => (int) apply_filters( 'wpforms_admin_builder_help_cache_ttl', WEEK_IN_SECONDS ),
// Static URLs.
'docs_url' => 'https://wpforms.com/docs/',
'support_ticket_url' => 'https://wpforms.com/account/support/',
'upgrade_url' => 'https://wpforms.com/pricing/',
];
}
/**
* Hooks.
*
* @since 1.6.3
*/
private function hooks() {
add_action( 'wpforms_builder_enqueues', [ $this, 'enqueues' ] );
add_action( 'wpforms_admin_page', [ $this, 'output' ], 20 );
}
/**
* Enqueue assets.
*
* @since 1.6.3
*/
public function enqueues() {
$min = wpforms_get_min_suffix();
wp_enqueue_script(
'wpforms-builder-help',
WPFORMS_PLUGIN_URL . "assets/js/components/admin/builder/help{$min}.js",
[ 'wpforms-builder' ],
WPFORMS_VERSION,
true
);
wp_localize_script(
'wpforms-builder-help',
'wpforms_builder_help',
$this->get_localized_data()
);
}
/**
* Get localized data.
*
* @since 1.6.3
*
* @return array Localized data.
*/
public function get_localized_data() {
return [
'docs' => $this->get_docs(),
'categories' => $this->get_categories(),
'context' => [
'terms' => $this->get_context_terms(),
'docs' => $this->get_context_docs(),
],
];
}
/**
* Get docs from the cache.
*
* @since 1.6.3
*
* @return array Docs data.
*/
public function get_docs() {
if ( is_file( $this->settings['cache_file'] ) && is_readable( $this->settings['cache_file'] ) ) {
$docs = json_decode( file_get_contents( $this->settings['cache_file'] ), true );
}
clearstatcache();
if (
empty( $docs ) ||
(int) filemtime( $this->settings['cache_file'] ) + $this->settings['cache_ttl'] < time()
) {
// This code should execute once when the method called the first time,
// Next update_docs() should be executed by schedule.
$docs = $this->update_docs();
}
// Store in class private variable for further use.
$this->docs = ! empty( $docs ) ? $docs : [];
return $this->docs;
}
/**
* Update docs cache with actual data retrieved from the remote source.
*
* @since 1.6.3
*
* @return array|boolean Updated docs data. Or false on error.
*/
public function update_docs() {
// Unfortunately, we need to call setup() here for properly scheduled execution.
$this->setup();
$wpforms_key = 'lite';
if ( wpforms()->is_pro() ) {
$wpforms_key = wpforms_get_license_key();
}
$request = wp_remote_get(
add_query_arg( 'tgm-updater-key', $wpforms_key, $this->settings['docs_remote_source'] ),
[
// Limit the processing time to half of the default PHP max execution time,
// so users will have a chance to see the Form Builder even without the docs data.
'timeout' => 15,
'user-agent' => wpforms_get_default_user_agent(),
]
);
if ( is_wp_error( $request ) ) {
return false;
}
$content = wp_remote_retrieve_body( $request );
$cache_dir = dirname( $this->settings['cache_file'] );
// Check cache directory and create it if needed.
if ( ! file_exists( $cache_dir ) || ! wp_is_writable( $cache_dir ) ) {
wp_mkdir_p( $cache_dir );
wpforms_create_upload_dir_htaccess_file();
wpforms_create_index_html_file( $cache_dir );
}
// Attempt to decode the json data.
$docs = json_decode( $content, true );
// If the data successfully decoded to array we caching the content.
if ( is_array( $docs ) ) {
// phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_file_put_contents
file_put_contents( $this->settings['cache_file'], $content );
} else {
$docs = [];
}
// Schedule recurring updates.
$this->schedule_update_docs();
return $docs;
}
/**
* Schedule docs updates.
*
* @since 1.6.3
*/
public function schedule_update_docs() {
$tasks = wpforms()->get( 'tasks' );
if ( $tasks->is_scheduled( 'wpforms_builder_help_cache_update' ) !== false ) {
return;
}
$tasks->create( 'wpforms_builder_help_cache_update' )
->recurring( time() + $this->settings['cache_ttl'], $this->settings['cache_ttl'] )
->params()
->register();
}
/**
* Get categories.
*
* @since 1.6.3
*
* @return array Categories data.
*/
public function get_categories() {
return [
'getting-started' => esc_html__( 'Getting Started', 'wpforms-lite' ),
'functionality' => esc_html__( 'Functionality', 'wpforms-lite' ),
'fields' => esc_html__( 'Fields', 'wpforms-lite' ),
'addons' => esc_html__( 'Addons', 'wpforms-lite' ),
'payments' => esc_html__( 'Payments', 'wpforms-lite' ),
'entries' => esc_html__( 'Entries', 'wpforms-lite' ),
'styling' => esc_html__( 'Styling', 'wpforms-lite' ),
'extending' => esc_html__( 'Extending', 'wpforms-lite' ),
];
}
/**
* Get context search terms.
*
* @since 1.6.3
*
* @return array Search terms by context.
*/
public function get_context_terms() {
return [
'new_form' => 'add form',
'setup' => 'form template',
'fields/add_fields' => 'add fields',
'fields/field_options' => 'field options',
'fields/field_options/text' => 'single line text',
'fields/field_options/textarea' => 'paragraph text',
'fields/field_options/number-slider' => 'number slider',
'fields/field_options/select' => 'dropdown',
'fields/field_options/radio' => 'multiple choice',
'fields/field_options/checkbox' => 'checkboxes',
'fields/field_options/gdpr-checkbox' => 'gdpr agreement',
'fields/field_options/email' => 'email',
'fields/field_options/address' => 'address',
'fields/field_options/url' => 'website/url',
'fields/field_options/name' => 'name',
'fields/field_options/hidden' => 'hidden',
'fields/field_options/html' => 'html',
'fields/field_options/pagebreak' => 'page break',
'fields/field_options/entry-preview' => 'entry preview',
'fields/field_options/password' => 'password',
'fields/field_options/date-time' => 'date time',
'fields/field_options/divider' => 'section divider',
'fields/field_options/phone' => 'phone',
'fields/field_options/number' => 'numbers',
'fields/field_options/file-upload' => 'file upload',
'fields/field_options/captcha' => 'custom captcha',
'fields/field_options/rating' => 'rating',
'fields/field_options/richtext' => 'rich text',
'fields/field_options/likert_scale' => 'likert scale',
'fields/field_options/payment-single' => 'single item',
'fields/field_options/payment-multiple' => 'multiple items',
'fields/field_options/payment-checkbox' => 'checkbox items',
'fields/field_options/payment-select' => 'dropdown items',
'fields/field_options/payment-total' => 'total',
'fields/field_options/paypal-commerce' => 'paypal checkout',
'fields/field_options/stripe-credit-card' => 'stripe credit card',
'fields/field_options/authorize_net' => 'authorize.net credit card',
'fields/field_options/square' => 'square credit card',
'fields/field_options/signature' => 'signature',
'fields/field_options/net_promoter_score' => 'net promoter score',
'settings/general' => 'settings',
'settings/notifications' => 'notification emails',
'settings/confirmation' => 'confirmation message',
'settings/form_abandonment' => 'form abandonment',
'settings/post_submissions' => 'post submissions',
'settings/user_registration' => 'user registration',
'settings/surveys_polls' => 'surveys and polls',
'settings/conversational_forms' => 'conversational forms',
'settings/form_locker' => 'form locker',
'settings/form_pages' => 'form pages',
'settings/save_resume' => 'save and resume',
'settings/webhooks' => 'webhooks',
'providers' => '',
'providers/aweber' => 'aweber',
'providers/activecampaign' => 'activecampaign',
'providers/campaign_monitor' => 'campaign monitor',
'providers/constant_contact' => 'constant contact',
'providers/drip' => 'drip',
'providers/getresponse' => 'getresponse',
'providers/getresponse_v3' => 'getresponse',
'providers/mailchimp' => 'mailchimp',
'providers/mailchimpv3' => 'mailchimp',
'providers/mailerlite' => 'mailerlite',
'providers/zapier' => 'zapier',
'providers/salesforce' => 'salesforce',
'providers/sendinblue' => 'sendinblue',
'providers/hubspot' => 'hubspot',
'payments' => '',
'payments/paypal_commerce' => 'paypal commerce',
'payments/paypal_standard' => 'paypal standard',
'payments/stripe' => 'stripe',
'payments/authorize_net' => 'authorize.net',
'payments/square' => 'square',
'revisions' => 'revisions',
];
}
/**
* Get context (recommended) docs links.
*
* @since 1.6.3
*
* @return array Docs links by search terms.
*/
public function get_context_docs_links() {
return [
'add form' => [
'/docs/creating-first-form/',
'/docs/how-to-choose-the-right-form-field-for-your-forms/',
'/docs/how-to-customize-the-submit-button/',
],
'new form' => [
'/docs/creating-first-form/',
'/docs/how-to-choose-the-right-form-field-for-your-forms/',
'/docs/how-to-customize-the-submit-button/',
],
'create form' => [
'/docs/creating-first-form/',
'/docs/how-to-choose-the-right-form-field-for-your-forms/',
'/docs/how-to-customize-the-submit-button/',
],
'form template' => [
'/docs/how-to-create-a-custom-form-template/',
],
'add fields' => [
'/docs/how-to-choose-the-right-form-field-for-your-forms/',
],
'recaptcha' => [
'/docs/setup-captcha-wpforms/',
],
'spam' => [
'/docs/setup-captcha-wpforms/',
'/docs/how-to-install-and-use-custom-captcha-addon-in-wpforms/',
'/docs/setting-up-akismet-anti-spam-protection/',
],
'fields' => [
'/docs/how-to-choose-the-right-form-field-for-your-forms/',
],
'field options' => [
'/docs/how-to-customize-form-field-options/',
],
'field settings' => [
'/docs/how-to-customize-form-field-options/',
],
'conditional logic' => [
'/docs/how-to-use-conditional-logic-with-wpforms/',
'/docs/setup-form-notification-wpforms/',
'/docs/setup-form-confirmation-wpforms/',
],
'single line text' => [
'/docs/how-to-limit-words-or-characters-in-a-form-field/',
'/docs/how-to-use-custom-input-masks/',
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
],
'paragraph' => [
'/docs/how-to-limit-words-or-characters-in-a-form-field/',
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
],
'paragraph text' => [
'/docs/how-to-limit-words-or-characters-in-a-form-field/',
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
],
'textarea' => [
'/docs/how-to-limit-words-or-characters-in-a-form-field/',
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
],
'input mask' => [
'/docs/how-to-use-custom-input-masks/',
],
'limit words' => [
'/docs/how-to-limit-words-or-characters-in-a-form-field/',
],
'limit characters' => [
'/docs/how-to-limit-words-or-characters-in-a-form-field/',
],
'style' => [
'/docs/how-to-style-wpforms-with-custom-css-beginners-guide/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
'/docs/how-to-add-custom-css-to-your-wpforms/',
],
'custom css' => [
'/docs/how-to-style-wpforms-with-custom-css-beginners-guide/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
'/docs/how-to-add-custom-css-to-your-wpforms/',
],
'css' => [
'/docs/how-to-style-wpforms-with-custom-css-beginners-guide/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
'/docs/how-to-add-custom-css-to-your-wpforms/',
],
'dropdown' => [
'/docs/how-to-allow-multiple-selections-to-a-dropdown-field-in-wpforms/',
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
],
'select' => [
'/docs/how-to-allow-multiple-selections-to-a-dropdown-field-in-wpforms/',
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
],
'multiple options' => [
'/docs/how-to-allow-multiple-selections-to-a-dropdown-field-in-wpforms/',
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
],
'bulk add' => [
'/docs/how-to-bulk-add-choices-for-multiple-choice-checkbox-and-dropdown-fields/',
],
'multiple columns' => [
'/docs/how-to-create-a-multi-column-layout-for-radio-buttons-and-checkboxes/',
],
'columns' => [
'/docs/how-to-create-a-multi-column-layout-for-radio-buttons-and-checkboxes/',
],
'randomize' => [
'/docs/how-to-randomize-checkbox-and-multiple-choice-options/',
],
'image choices' => [
'/docs/how-to-add-image-choices-to-fields/',
],
'multiple choice' => [
'/docs/how-to-bulk-add-choices-for-multiple-choice-checkbox-and-dropdown-fields/',
'/docs/how-to-create-a-multi-column-layout-for-radio-buttons-and-checkboxes/',
'/docs/how-to-randomize-checkbox-and-multiple-choice-options/',
'/docs/how-to-add-image-choices-to-fields/',
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
],
'radio' => [
'/docs/how-to-bulk-add-choices-for-multiple-choice-checkbox-and-dropdown-fields/',
'/docs/how-to-create-a-multi-column-layout-for-radio-buttons-and-checkboxes/',
'/docs/how-to-randomize-checkbox-and-multiple-choice-options/',
'/docs/how-to-add-image-choices-to-fields/',
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
],
'checkboxes' => [
'/docs/how-to-bulk-add-choices-for-multiple-choice-checkbox-and-dropdown-fields/',
'/docs/how-to-add-a-terms-of-service-checkbox-to-a-form/',
'/docs/how-to-create-a-multi-column-layout-for-radio-buttons-and-checkboxes/',
'/docs/how-to-randomize-checkbox-and-multiple-choice-options/',
'/docs/how-to-add-image-choices-to-fields/',
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
],
'checkbox' => [
'/docs/how-to-bulk-add-choices-for-multiple-choice-checkbox-and-dropdown-fields/',
'/docs/how-to-add-a-terms-of-service-checkbox-to-a-form/',
'/docs/how-to-create-a-multi-column-layout-for-radio-buttons-and-checkboxes/',
'/docs/how-to-randomize-checkbox-and-multiple-choice-options/',
'/docs/how-to-add-image-choices-to-fields/',
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
],
'gdpr' => [
'/docs/how-to-create-gdpr-compliant-forms/',
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
],
'gdpr agreement' => [
'/docs/how-to-create-gdpr-compliant-forms/',
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
],
'number slider' => [
'/docs/how-to-add-a-number-slider-field-to-wpforms/',
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
],
'range' => [
'/docs/how-to-add-a-number-slider-field-to-wpforms/',
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
],
'email' => [
'/docs/setup-form-notification-wpforms/',
'/docs/how-to-create-conditional-form-notifications-in-wpforms/',
'/docs/troubleshooting-email-notifications/',
'/docs/how-to-fix-wordpress-contact-form-not-sending-email-with-smtp/',
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
],
'address' => [
'/docs/how-to-customize-the-address-field/',
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
],
'field' => [
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
],
'state' => [
'/docs/how-to-customize-the-address-field/',
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
],
'province' => [
'/docs/how-to-customize-the-address-field/',
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
],
'region' => [
'/docs/how-to-customize-the-address-field/',
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
],
'city' => [
'/docs/how-to-customize-the-address-field/',
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
],
'country' => [
'/docs/how-to-customize-the-address-field/',
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
],
'zip code' => [
'/docs/how-to-customize-the-address-field/',
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
],
'postal code' => [
'/docs/how-to-customize-the-address-field/',
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
],
'hidden' => [
'/docs/how-to-choose-the-right-form-field-for-your-forms/',
'/docs/how-to-use-smart-tags-in-wpforms/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
],
'rating' => [
'/docs/how-to-add-a-rating-field-to-wpforms/',
'/docs/how-to-install-and-use-the-surveys-and-polls-addon/',
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
],
'star' => [
'/docs/how-to-add-a-rating-field-to-wpforms/',
'/docs/how-to-install-and-use-the-surveys-and-polls-addon/',
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
],
'rich text' => [
'/docs/how-to-use-the-rich-text-field-in-wpforms/',
],
'wysiwyg' => [
'/docs/how-to-use-the-rich-text-field-in-wpforms/',
],
'editor' => [
'/docs/how-to-use-the-rich-text-field-in-wpforms/',
],
'rich editor' => [
'/docs/how-to-use-the-rich-text-field-in-wpforms/',
],
'page break' => [
'/docs/how-to-create-multi-page-forms-in-wpforms/',
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
],
'page' => [
'/docs/how-to-create-multi-page-forms-in-wpforms/',
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
],
'entry preview' => [
'/docs/how-to-show-entry-previews-in-wpforms/',
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
],
'break' => [
'/docs/how-to-create-multi-page-forms-in-wpforms/',
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
],
'multi-page' => [
'/docs/how-to-create-multi-page-forms-in-wpforms/',
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
],
'password' => [
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
],
'name' => [
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
],
'first' => [
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
],
'last' => [
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
],
'surname' => [
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
],
'custom captcha' => [
'/docs/how-to-install-and-use-custom-captcha-addon-in-wpforms/',
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
],
'numbers' => [
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
],
'website/url' => [
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
],
'website' => [
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
],
'url' => [
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
],
'html' => [
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
],
'code' => [
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
],
'date/time' => [
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
'/docs/how-to-customize-the-date-time-field-in-wpforms/',
],
'date' => [
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
'/docs/how-to-customize-the-date-time-field-in-wpforms/',
],
'time' => [
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
'/docs/how-to-customize-the-date-time-field-in-wpforms/',
],
'calendar' => [
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
'/docs/how-to-customize-the-date-time-field-in-wpforms/',
],
'section divider' => [
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
],
'section' => [
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
],
'divider' => [
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
],
'header' => [
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
],
'phone' => [
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
],
'telephone' => [
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
],
'mobile' => [
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
],
'file upload' => [
'/docs/a-complete-guide-to-the-file-upload-field/',
'/docs/how-to-allow-additional-file-upload-types/',
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
],
'file' => [
'/docs/a-complete-guide-to-the-file-upload-field/',
'/docs/how-to-allow-additional-file-upload-types/',
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
],
'upload' => [
'/docs/a-complete-guide-to-the-file-upload-field/',
'/docs/how-to-allow-additional-file-upload-types/',
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
],
'signature' => [
'/docs/how-to-install-and-use-the-signature-addon-in-wpforms/',
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
],
'likert scale' => [
'/docs/how-to-add-a-likert-scale-field-to-wpforms/',
'/docs/how-to-install-and-use-the-surveys-and-polls-addon/',
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
],
'likert' => [
'/docs/how-to-add-a-likert-scale-field-to-wpforms/',
'/docs/how-to-install-and-use-the-surveys-and-polls-addon/',
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
],
'scale' => [
'/docs/how-to-add-a-likert-scale-field-to-wpforms/',
'/docs/how-to-install-and-use-the-surveys-and-polls-addon/',
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
],
'net promoter score' => [
'/docs/how-to-add-a-net-promoter-score-field-to-wpforms/',
'/docs/how-to-install-and-use-the-surveys-and-polls-addon/',
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
],
'net' => [
'/docs/how-to-add-a-net-promoter-score-field-to-wpforms/',
'/docs/how-to-install-and-use-the-surveys-and-polls-addon/',
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
],
'promoter' => [
'/docs/how-to-add-a-net-promoter-score-field-to-wpforms/',
'/docs/how-to-install-and-use-the-surveys-and-polls-addon/',
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
],
'score' => [
'/docs/how-to-add-a-net-promoter-score-field-to-wpforms/',
'/docs/how-to-install-and-use-the-surveys-and-polls-addon/',
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
],
'nps' => [
'/docs/how-to-add-a-net-promoter-score-field-to-wpforms/',
'/docs/how-to-install-and-use-the-surveys-and-polls-addon/',
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
],
'payment' => [
'/docs/how-to-install-and-use-the-stripe-addon-with-wpforms/',
'/docs/paypal-commerce-addon/',
'/docs/install-use-paypal-addon-wpforms/',
'/docs/how-to-install-and-use-the-authorize-net-addon-with-wpforms/',
'/docs/how-to-create-a-donation-form-with-multiple-amounts/',
'/docs/how-to-allow-users-to-choose-a-payment-method-on-your-form/',
],
'price' => [
'/docs/how-to-install-and-use-the-stripe-addon-with-wpforms/',
'/docs/paypal-commerce-addon/',
'/docs/install-use-paypal-addon-wpforms/',
'/docs/how-to-install-and-use-the-authorize-net-addon-with-wpforms/',
'/docs/how-to-create-a-donation-form-with-multiple-amounts/',
'/docs/how-to-allow-users-to-choose-a-payment-method-on-your-form/',
],
'cost' => [
'/docs/how-to-install-and-use-the-stripe-addon-with-wpforms/',
'/docs/paypal-commerce-addon/',
'/docs/install-use-paypal-addon-wpforms/',
'/docs/how-to-install-and-use-the-authorize-net-addon-with-wpforms/',
'/docs/how-to-create-a-donation-form-with-multiple-amounts/',
'/docs/how-to-allow-users-to-choose-a-payment-method-on-your-form/',
],
'single item' => [
'/docs/how-to-install-and-use-the-stripe-addon-with-wpforms/',
'/docs/paypal-commerce-addon/',
'/docs/install-use-paypal-addon-wpforms/',
'/docs/how-to-install-and-use-the-authorize-net-addon-with-wpforms/',
'/docs/how-to-create-a-donation-form-with-multiple-amounts/',
'/docs/how-to-allow-users-to-choose-a-payment-method-on-your-form/',
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
],
'multiple items' => [
'/docs/how-to-install-and-use-the-stripe-addon-with-wpforms/',
'/docs/paypal-commerce-addon/',
'/docs/install-use-paypal-addon-wpforms/',
'/docs/how-to-install-and-use-the-authorize-net-addon-with-wpforms/',
'/docs/how-to-create-a-donation-form-with-multiple-amounts/',
'/docs/how-to-allow-users-to-choose-a-payment-method-on-your-form/',
'/docs/how-to-add-image-choices-to-fields/',
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
],
'checkbox items' => [
'/docs/how-to-install-and-use-the-stripe-addon-with-wpforms/',
'/docs/paypal-commerce-addon/',
'/docs/install-use-paypal-addon-wpforms/',
'/docs/how-to-install-and-use-the-authorize-net-addon-with-wpforms/',
'/docs/how-to-create-a-donation-form-with-multiple-amounts/',
'/docs/how-to-allow-users-to-choose-a-payment-method-on-your-form/',
'/docs/how-to-add-image-choices-to-fields/',
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
],
'dropdown items' => [
'/docs/how-to-install-and-use-the-stripe-addon-with-wpforms/',
'/docs/paypal-commerce-addon/',
'/docs/install-use-paypal-addon-wpforms/',
'/docs/how-to-install-and-use-the-authorize-net-addon-with-wpforms/',
'/docs/how-to-create-a-donation-form-with-multiple-amounts/',
'/docs/how-to-allow-users-to-choose-a-payment-method-on-your-form/',
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
],
'total' => [
'/docs/how-to-require-payment-total-with-a-wordpress-form/',
'/docs/how-to-install-and-use-the-stripe-addon-with-wpforms/',
'/docs/paypal-commerce-addon/',
'/docs/install-use-paypal-addon-wpforms/',
'/docs/how-to-install-and-use-the-authorize-net-addon-with-wpforms/',
'/docs/how-to-create-a-donation-form-with-multiple-amounts/',
'/docs/how-to-allow-users-to-choose-a-payment-method-on-your-form/',
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
'/docs/how-to-customize-the-style-of-individual-form-fields/',
],
'paypal checkout' => [
'/docs/paypal-commerce-addon/',
'/docs/testing-payments-with-the-paypal-commerce-addon/',
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
],
'stripe credit card' => [
'/docs/how-to-install-and-use-the-stripe-addon-with-wpforms/',
'/docs/how-to-test-stripe-payments-on-your-site/',
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
],
'authorize.net credit card' => [
'/docs/how-to-install-and-use-the-authorize-net-addon-with-wpforms/',
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
],
'square credit card' => [
'/docs/how-to-install-and-use-the-square-addon-with-wpforms/',
'/docs/how-to-test-square-payments-on-your-site/',
'/docs/how-to-customize-form-field-options/',
'/docs/how-to-use-conditional-logic-with-wpforms/',
],
'settings' => [
'/docs/creating-first-form/',
'/docs/setup-form-notification-wpforms/',
'/docs/setup-form-confirmation-wpforms/',
],
'submit' => [
'/docs/how-to-customize-the-submit-button/',
],
'button' => [
'/docs/how-to-customize-the-submit-button/',
],
'dynamic population' => [
'/developers/how-to-enable-dynamic-field-population/',
],
'offline' => [
'/docs/how-to-enable-ajax-form-submissions/',
],
'offline forms' => [
'/docs/how-to-enable-ajax-form-submissions/',
],
'notification' => [
'/docs/setup-form-notification-wpforms/',
'/docs/how-to-create-conditional-form-notifications-in-wpforms/',
'/docs/troubleshooting-email-notifications/',
'/docs/how-to-fix-wordpress-contact-form-not-sending-email-with-smtp/',
],
'notifications' => [
'/docs/setup-form-notification-wpforms/',
'/docs/how-to-create-conditional-form-notifications-in-wpforms/',
'/docs/troubleshooting-email-notifications/',
'/docs/how-to-fix-wordpress-contact-form-not-sending-email-with-smtp/',
],
'notification email' => [
'/docs/setup-form-notification-wpforms/',
'/docs/how-to-create-conditional-form-notifications-in-wpforms/',
'/docs/troubleshooting-email-notifications/',
'/docs/how-to-fix-wordpress-contact-form-not-sending-email-with-smtp/',
],
'notification emails' => [
'/docs/setup-form-notification-wpforms/',
'/docs/how-to-create-conditional-form-notifications-in-wpforms/',
'/docs/troubleshooting-email-notifications/',
'/docs/how-to-fix-wordpress-contact-form-not-sending-email-with-smtp/',
],
'confirmation' => [
'/docs/setup-form-confirmation-wpforms/',
'/docs/how-to-create-conditional-form-confirmations/',
],
'confirmation message' => [
'/docs/setup-form-confirmation-wpforms/',
'/docs/how-to-create-conditional-form-confirmations/',
],
'redirect' => [
'/docs/setup-form-confirmation-wpforms/',
'/docs/how-to-create-conditional-form-confirmations/',
],
'go to url (redirect)' => [
'/docs/setup-form-confirmation-wpforms/',
'/docs/how-to-create-conditional-form-confirmations/',
],
'confirmation page' => [
'/docs/setup-form-confirmation-wpforms/',
'/docs/how-to-create-conditional-form-confirmations/',
],
'conditional confirmation' => [
'/docs/setup-form-confirmation-wpforms/',
'/docs/how-to-create-conditional-form-confirmations/',
],
'form abandonment' => [
'/docs/how-to-install-and-use-form-abandonment-with-wpforms/',
],
'abandonment' => [
'/docs/how-to-install-and-use-form-abandonment-with-wpforms/',
],
'abandon' => [
'/docs/how-to-install-and-use-form-abandonment-with-wpforms/',
],
'lead capture' => [
'/docs/how-to-install-and-use-form-abandonment-with-wpforms/',
],
'post submissions' => [
'/docs/how-to-install-and-use-the-post-submissions-addon-in-wpforms/',
],
'guest post' => [
'/docs/how-to-install-and-use-the-post-submissions-addon-in-wpforms/',
],
'user submission' => [
'/docs/how-to-install-and-use-the-post-submissions-addon-in-wpforms/',
],
'blog' => [
'/docs/how-to-install-and-use-the-post-submissions-addon-in-wpforms/',
],
'post' => [
'/docs/how-to-install-and-use-the-post-submissions-addon-in-wpforms/',
],
'user registration' => [
'/docs/how-to-install-and-use-user-registration-addon-with-wpforms/',
'/docs/how-to-set-up-custom-user-meta-fields/',
],
'register' => [
'/docs/how-to-install-and-use-user-registration-addon-with-wpforms/',
'/docs/how-to-set-up-custom-user-meta-fields/',
],
'registration' => [
'/docs/how-to-install-and-use-user-registration-addon-with-wpforms/',
'/docs/how-to-set-up-custom-user-meta-fields/',
],
'user meta' => [
'/docs/how-to-install-and-use-user-registration-addon-with-wpforms/',
'/docs/how-to-set-up-custom-user-meta-fields/',
],
'user' => [
'/docs/how-to-install-and-use-user-registration-addon-with-wpforms/',
'/docs/how-to-set-up-custom-user-meta-fields/',
],
'surveys' => [
'/docs/how-to-install-and-use-the-surveys-and-polls-addon/',
],
'polls' => [
'/docs/how-to-install-and-use-the-surveys-and-polls-addon/',
],
'surveys and polls' => [
'/docs/how-to-install-and-use-the-surveys-and-polls-addon/',
],
'conversational forms' => [
'/docs/how-to-install-and-use-the-conversational-forms-addon/',
],
'conversational' => [
'/docs/how-to-install-and-use-the-conversational-forms-addon/',
],
'form locker' => [
'/docs/how-to-install-and-use-the-form-locker-addon-in-wpforms/',
'/developers/how-to-display-remaining-entry-limit-number/',
],
'password protection' => [
'/docs/how-to-install-and-use-the-form-locker-addon-in-wpforms/',
'/developers/how-to-display-remaining-entry-limit-number/',
],
'entry limit' => [
'/docs/how-to-install-and-use-the-form-locker-addon-in-wpforms/',
'/developers/how-to-display-remaining-entry-limit-number/',
],
'scheduling' => [
'/docs/how-to-install-and-use-the-form-locker-addon-in-wpforms/',
'/developers/how-to-display-remaining-entry-limit-number/',
],
'restrict access' => [
'/docs/how-to-install-and-use-the-form-locker-addon-in-wpforms/',
'/developers/how-to-display-remaining-entry-limit-number/',
],
'limit' => [
'/docs/how-to-install-and-use-the-form-locker-addon-in-wpforms/',
'/developers/how-to-display-remaining-entry-limit-number/',
],
'schedule' => [
'/docs/how-to-install-and-use-the-form-locker-addon-in-wpforms/',
'/developers/how-to-display-remaining-entry-limit-number/',
],
'restrict' => [
'/docs/how-to-install-and-use-the-form-locker-addon-in-wpforms/',
'/developers/how-to-display-remaining-entry-limit-number/',
],
'form pages' => [
'/docs/how-to-install-and-use-the-form-pages-addon/',
],
'save' => [
'/docs/how-to-install-and-use-the-save-and-resume-addon-with-wpforms/',
],
'resume' => [
'/docs/how-to-install-and-use-the-save-and-resume-addon-with-wpforms/',
],
'continue' => [
'/docs/how-to-install-and-use-the-save-and-resume-addon-with-wpforms/',
],
'save and resume' => [
'/docs/how-to-install-and-use-the-save-and-resume-addon-with-wpforms/',
],
'save and continue' => [
'/docs/how-to-install-and-use-the-save-and-resume-addon-with-wpforms/',
],
'webhooks' => [
'/docs/how-to-install-and-use-the-webhooks-addon-with-wpforms/',
],
'aweber' => [
'/docs/install-use-aweber-addon-wpforms/',
],
'campaign monitor' => [
'/docs/how-to-install-and-use-campaign-monitor-addon-with-wpforms/',
],
'constant contact' => [
'/docs/how-to-connect-constant-contact-with-wpforms/',
],
'drip' => [
'/docs/how-to-install-and-use-the-drip-addon-in-wpforms/',
],
'getresponse' => [
'/docs/how-to-install-and-use-getresponse-addon-with-wpforms/',
],
'mailchimp' => [
'/docs/install-use-mailchimp-addon-wpforms/',
],
'mailerlite' => [
'/docs/install-use-mailerlite-addon-wpforms/',
],
'zapier' => [
'/docs/how-to-install-and-use-zapier-addon-with-wpforms/',
],
'salesforce' => [
'/docs/how-to-install-and-use-the-salesforce-addon-with-wpforms/',
],
'sendinblue' => [
'/docs/how-to-install-and-use-the-sendinblue-addon-with-wpforms/',
],
'hubspot' => [
'/docs/how-to-install-and-use-the-hubspot-addon-in-wpforms/',
],
'integrate' => [
'/docs/how-to-install-and-use-zapier-addon-with-wpforms/',
'/docs/how-to-install-and-use-the-webhooks-addon-with-wpforms/',
],
'integration' => [
'/docs/how-to-install-and-use-zapier-addon-with-wpforms/',
'/docs/how-to-install-and-use-the-webhooks-addon-with-wpforms/',
],
'crm' => [
'/docs/how-to-install-and-use-zapier-addon-with-wpforms/',
'/docs/how-to-install-and-use-the-webhooks-addon-with-wpforms/',
],
'api' => [
'/docs/how-to-install-and-use-zapier-addon-with-wpforms/',
'/docs/how-to-install-and-use-the-webhooks-addon-with-wpforms/',
],
'paypal commerce' => [
'/docs/paypal-commerce-addon/',
'/docs/testing-payments-with-the-paypal-commerce-addon/',
],
'paypal standard' => [
'/docs/install-use-paypal-addon-wpforms/',
'/docs/how-to-test-paypal-payments-before-accepting-real-payments/',
'/docs/how-to-allow-users-to-choose-a-payment-method-on-your-form/',
],
'stripe' => [
'/docs/how-to-install-and-use-the-stripe-addon-with-wpforms/',
'/docs/how-to-test-stripe-payments-on-your-site/',
],
'authorize' => [
'/docs/how-to-install-and-use-the-authorize-net-addon-with-wpforms/',
],
'authorize.net' => [
'/docs/how-to-install-and-use-the-authorize-net-addon-with-wpforms/',
],
'square' => [
'/docs/how-to-install-and-use-the-square-addon-with-wpforms/',
'/docs/how-to-test-square-payments-on-your-site/',
],
'revisions' => [
'/docs/how-to-use-form-revisions-in-wpforms/',
],
];
}
/**
* Get context (recommended) docs.
*
* @since 1.6.3
*
* @return array Docs recommended by search terms.
*/
public function get_context_docs() {
if ( empty( $this->docs ) ) {
return [];
}
$docs_links = $this->get_context_docs_links();
$docs = [];
foreach ( $docs_links as $word => $links ) {
$docs[ $word ] = $this->get_doc_ids( $links );
}
return $docs;
}
/**
* Get doc id.
*
* @since 1.6.3
*
* @param string $link Absolute link to the doc without the domain part.
*
* @return array Array with doc id as element.
*/
public function get_doc_id( $link ) {
if ( empty( $this->docs ) ) {
return [];
}
$result = array_filter(
$this->docs,
static function( $doc ) use ( $link ) {
return ! empty( $doc['url'] ) && $doc['url'] === 'https://wpforms.com' . $link;
}
);
return ! empty( $result ) && is_array( $result ) ? array_keys( $result ) : [];
}
/**
* Get doc ids.
*
* @since 1.6.3
*
* @param array $links Array of the doc links.
*
* @return array Doc ids.
*/
public function get_doc_ids( $links ) {
if ( empty( $this->docs ) ) {
return [];
}
$ids = [];
foreach ( $links as $link ) {
$ids = array_merge( $ids, $this->get_doc_id( $link ) );
}
return $ids;
}
/**
* Output help modal markup.
*
* @since 1.6.3
*/
public function output() {
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo wpforms_render(
'builder/help',
[
'settings' => $this->settings,
],
true
);
}
}
Admin/Builder/TemplatesCache.php 0000644 00000004223 15133255232 0012557 0 ustar 00 <?php
namespace WPForms\Admin\Builder;
/**
* Form templates cache handler.
*
* @since 1.6.8
*/
class TemplatesCache extends \WPForms\Helpers\CacheBase {
/**
* Determine if the class is allowed to load.
*
* @since 1.6.8
*
* @return bool
*/
protected function allow_load() {
// Load only in the Form Builder.
$allow = wp_doing_ajax() || wpforms_is_admin_page( 'builder' );
/**
* Whether to load this class.
*
* @since 1.7.2
*
* @param bool $allow True or false.
*/
return (bool) apply_filters( 'wpforms_admin_builder_templatescache_allow_load', $allow );
}
/**
* Provide settings.
*
* @since 1.6.8
*
* @return array Settings array.
*/
protected function setup() {
return [
// Remote source URL.
'remote_source' => 'https://wpforms.com/templates/api/get/',
// Cache file.
'cache_file' => 'templates.json',
/**
* Time-to-live of the templates cache files in seconds.
*
* This applies to `uploads/wpforms/cache/templates.json`
* and all *.json files in `uploads/wpforms/cache/templates/` directory.
*
* @since 1.6.8
*
* @param integer $cache_ttl Cache time-to-live, in seconds.
* Default value: WEEK_IN_SECONDS.
*/
'cache_ttl' => (int) apply_filters( 'wpforms_admin_builder_templates_cache_ttl', WEEK_IN_SECONDS ),
// Scheduled update action.
'update_action' => 'wpforms_admin_builder_templates_cache_update',
];
}
/**
* Prepare data to store in a local cache.
*
* @since 1.6.8
*
* @param array $data Raw data received by the remote request.
*
* @return array Prepared data for caching.
*/
protected function prepare_cache_data( $data ) {
if (
empty( $data ) ||
! is_array( $data ) ||
empty( $data['status'] ) ||
$data['status'] !== 'success' ||
empty( $data['data'] )
) {
return [];
}
$cache_data = $data['data'];
// Strip the word "Template" from the end of each template name.
foreach ( $cache_data['templates'] as $slug => $template ) {
$cache_data['templates'][ $slug ]['name'] = preg_replace( '/\sTemplate$/', '', $template['name'] );
}
return $cache_data;
}
}
Admin/Builder/TemplateSingleCache.php 0000644 00000012010 15133255232 0013527 0 ustar 00 <?php
namespace WPForms\Admin\Builder;
use WPForms\Tasks\Actions\AsyncRequestTask;
/**
* Single template cache handler.
*
* @since 1.6.8
*/
class TemplateSingleCache extends \WPForms\Helpers\CacheBase {
/**
* Template Id (hash).
*
* @since 1.6.8
*
* @var string
*/
private $id;
/**
* License data (`key` and `type`).
*
* @since 1.6.8
*
* @var array
*/
private $license;
/**
* Determine if the class is allowed to load.
*
* @since 1.6.8
*
* @return bool
*/
protected function allow_load() {
// Load only in the Form Builder.
$allow = wp_doing_ajax() || wpforms_is_admin_page( 'builder' );
// phpcs:disable WPForms.PHP.ValidateHooks.InvalidHookName
/**
* Whether to allow to load this class.
*
* @since 1.7.2
*
* @param bool $allow True or false.
*/
return apply_filters( 'wpforms_admin_builder_templatesinglecache_allow_load', $allow );
// phpcs:enable WPForms.PHP.ValidateHooks.InvalidHookName
}
/**
* Re-initialize object with the particular template.
*
* @since 1.6.8
*
* @param string $template_id Template ID (hash).
* @param array $license License data.
*
* @return TemplateSingleCache
*/
public function instance( $template_id, $license ) {
$this->id = $template_id;
$this->license = $license;
$this->init();
return $this;
}
/**
* Provide settings.
*
* @since 1.6.8
*
* @return array Settings array.
*/
protected function setup() {
return [
// Remote source URL.
'remote_source' => $this->remote_source(),
// Cache file.
'cache_file' => $this->get_cache_file_name(),
// This filter is documented in wpforms/src/Admin/Builder/TemplatesCache.php.
// phpcs:ignore WPForms.PHP.ValidateHooks.InvalidHookName, WPForms.Comments.PHPDocHooks.RequiredHookDocumentation
'cache_ttl' => (int) apply_filters( 'wpforms_admin_builder_templates_cache_ttl', WEEK_IN_SECONDS ),
];
}
/**
* Generate single template remote URL.
*
* @since 1.6.8
*
* @param bool $cache True if the cache arg should be appended to the URL.
*
* @return string
*/
private function remote_source( $cache = false ) {
if ( ! isset( $this->license['key'] ) ) {
return '';
}
$args = [
'license' => $this->license['key'],
'site' => site_url(),
];
if ( $cache ) {
$args['cache'] = 1;
}
return add_query_arg(
$args,
'https://wpforms.com/templates/api/get/' . $this->id
);
}
/**
* Get cached data.
*
* @since 1.7.5
*
* @return array Cached data.
*/
public function get_cached() {
$data = parent::get_cached();
if ( parent::$updated === false ) {
$this->update_usage_tracking();
}
return $data;
}
/**
* Sends a request to update the form template usage tracking database.
*
* @since 1.7.5
*/
private function update_usage_tracking() {
$tasks = wpforms()->get( 'tasks' );
if ( ! $tasks ) {
return;
}
$url = $this->remote_source( true );
$args = [ 'blocking' => false ];
$tasks->create( AsyncRequestTask::ACTION )->async()->params( $url, $args )->register();
}
/**
* Get cache directory path.
*
* @since 1.6.8
*/
protected function get_cache_dir() {
return parent::get_cache_dir() . 'templates/';
}
/**
* Generate single template cache file name.
*
* @since 1.6.8
*
* @return string.
*/
private function get_cache_file_name() {
return sanitize_key( $this->id ) . '.json';
}
/**
* Prepare data to store in a local cache.
*
* @since 1.6.8
*
* @param array $data Raw data received by the remote request.
*
* @return array Prepared data for caching.
*/
protected function prepare_cache_data( $data ) {
if (
empty( $data ) ||
! is_array( $data ) ||
empty( $data['status'] ) ||
$data['status'] !== 'success' ||
empty( $data['data'] )
) {
return [];
}
$cache_data = $data['data'];
$cache_data['data'] = empty( $cache_data['data'] ) ? [] : $cache_data['data'];
$cache_data['data']['settings'] = empty( $cache_data['data']['settings'] ) ? [] : $cache_data['data']['settings'];
$cache_data['data']['settings']['ajax_submit'] = '1';
// Strip the word "Template" from the end of the template name and form title setting.
$cache_data['name'] = preg_replace( '/\sTemplate$/', '', $cache_data['name'] );
$cache_data['data']['settings']['form_title'] = $cache_data['name'];
// Unset `From Name` field of the notification settings.
// By default, the builder will use the `blogname` option value.
unset( $cache_data['data']['settings']['notifications'][1]['sender_name'] );
return $cache_data;
}
/**
* Wipe cache of an empty templates.
*
* @since 1.7.5
*/
public function wipe_empty_templates_cache() {
$cache_dir = $this->get_cache_dir();
$files = glob( $cache_dir . '*.json' );
foreach ( $files as $filename ) {
$content = file_get_contents( $filename );
if ( empty( $content ) || trim( $content ) === '[]' ) {
unlink( $filename );
}
}
}
}
Admin/Builder/Shortcuts.php 0000644 00000004274 15133255232 0011701 0 ustar 00 <?php
namespace WPForms\Admin\Builder;
/**
* Form Builder Keyboard Shortcuts modal content.
*
* @since 1.6.9
*/
class Shortcuts {
/**
* Initialize class.
*
* @since 1.6.9
*/
public function init() {
// Terminate initialization if not in builder.
if ( ! wpforms_is_admin_page( 'builder' ) ) {
return;
}
$this->hooks();
}
/**
* Hooks.
*
* @since 1.6.9
*/
private function hooks() {
add_filter( 'wpforms_builder_strings', [ $this, 'builder_strings' ], 10, 2 );
add_action( 'wpforms_admin_page', [ $this, 'output' ], 30 );
}
/**
* Get shortcuts list.
*
* @since 1.6.9
*
* @return array
*/
private function get_list() {
return [
'left' => [
'ctrl s' => __( 'Save Form', 'wpforms-lite' ),
'ctrl p' => __( 'Preview Form', 'wpforms-lite' ),
'ctrl b' => __( 'Embed Form', 'wpforms-lite' ),
],
'right' => [
'ctrl h' => __( 'Open Help', 'wpforms-lite' ),
'ctrl e' => __( 'View Entries', 'wpforms-lite' ),
'ctrl q' => __( 'Close Builder', 'wpforms-lite' ),
],
];
}
/**
* Add Form builder strings.
*
* @since 1.6.9
*
* @param array $strings Form Builder strings.
* @param \WP_Post|bool $form Form object.
*
* @return array
*/
public function builder_strings( $strings, $form ) {
$strings['shortcuts_modal_title'] = esc_html__( 'Keyboard Shortcuts', 'wpforms-lite' );
$strings['shortcuts_modal_msg'] = esc_html__( 'Handy shortcuts for common actions in the builder.', 'wpforms-lite' );
return $strings;
}
/**
* Generate and output shortcuts modal content as the wp.template.
*
* @since 1.6.9
*/
public function output() {
echo '
<script type="text/html" id="tmpl-wpforms-builder-keyboard-shortcuts">
<div class="wpforms-columns wpforms-columns-2">';
foreach ( $this->get_list() as $list ) {
echo "<ul class='wpforms-column'>";
foreach ( $list as $key => $label ) {
$key = explode( ' ', $key );
printf(
'<li>
%1$s
<span>
<i>%2$s</i><i>%3$s</i>
</span>
</li>',
esc_html( $label ),
esc_html( $key[0] ),
esc_html( $key[1] )
);
}
echo '</ul>';
}
echo '
</div>
</script>';
}
}
Admin/Settings/Captcha.php 0000644 00000032067 15133255232 0011461 0 ustar 00 <?php
namespace WPForms\Admin\Settings;
/**
* CAPTCHA setting page.
*
* @since 1.6.4
*/
class Captcha {
/**
* Slug identifier for admin page view.
*
* @since 1.6.4
*
* @var string
*/
const VIEW = 'captcha';
/**
* The hCaptcha javascript URL-resource.
*
* @since 1.6.4
*/
const HCAPTCHA_API_URL = 'https://hcaptcha.com/1/api.js';
/**
* The reCAPTCHA javascript URL-resource.
*
* @since 1.6.4
*/
const RECAPTCHA_API_URL = 'https://www.google.com/recaptcha/api.js';
/**
* Saved CAPTCHA settings.
*
* @since 1.6.4
*
* @var array
*/
private $settings;
/**
* Initialize class.
*
* @since 1.6.4
*/
public function init() {
// Only load if we are actually on the settings page.
if ( ! wpforms_is_admin_page( 'settings' ) ) {
return;
}
// Listen the previous reCAPTCHA page and safely redirect from it.
if ( wpforms_is_admin_page( 'settings', 'recaptcha' ) ) {
wp_safe_redirect( add_query_arg( 'view', self::VIEW, admin_url( 'admin.php?page=wpforms-settings' ) ) );
exit;
}
$this->init_settings();
$this->hooks();
}
/**
* Init CAPTCHA settings.
*
* @since 1.6.4
*/
public function init_settings() {
$this->settings = wp_parse_args( wpforms_get_captcha_settings(), [ 'provider' => 'none' ] );
}
/**
* Hooks.
*
* @since 1.6.4
*/
public function hooks() {
add_filter( 'wpforms_settings_tabs', [ $this, 'register_settings_tabs' ], 5, 1 );
add_filter( 'wpforms_settings_defaults', [ $this, 'register_settings_fields' ], 5, 1 );
add_action( 'wpforms_settings_updated', [ $this, 'updated' ] );
add_action( 'wpforms_settings_enqueue', [ $this, 'enqueues' ] );
add_action( 'admin_enqueue_scripts', [ $this, 'apply_noconflict' ], 9999 );
}
/**
* Register CAPTCHA settings tab.
*
* @since 1.6.4
*
* @param array $tabs Admin area tabs list.
*
* @return array
*/
public function register_settings_tabs( $tabs ) {
$captcha = [
self::VIEW => [
'name' => esc_html__( 'CAPTCHA', 'wpforms-lite' ),
'form' => true,
'submit' => esc_html__( 'Save Settings', 'wpforms-lite' ),
],
];
return wpforms_array_insert( $tabs, $captcha, 'email' );
}
/**
* Register CAPTCHA settings fields.
*
* @since 1.6.4
*
* @param array $settings Admin area settings list.
*
* @return array
*/
public function register_settings_fields( $settings ) {
$settings[ self::VIEW ] = [
self::VIEW . '-heading' => [
'id' => self::VIEW . '-heading',
'content' => '<h4>' . esc_html__( 'CAPTCHA', 'wpforms-lite' ) . '</h4><p>' . esc_html__( 'A CAPTCHA is an anti-spam technique which helps to protect your website from spam and abuse while letting real people pass through with ease. WPForms supports two popular services.', 'wpforms-lite' ) . '</p>',
'type' => 'content',
'no_label' => true,
'class' => [ 'wpforms-setting-captcha-heading', 'section-heading' ],
],
self::VIEW . '-provider' => [
'id' => self::VIEW . '-provider',
'type' => 'radio',
'default' => 'none',
'options' => [
'hcaptcha' => esc_html__( 'hCaptcha', 'wpforms-lite' ),
'recaptcha' => esc_html__( 'reCAPTCHA', 'wpforms-lite' ),
'none' => esc_html__( 'None', 'wpforms-lite' ),
],
'desc' => wp_kses(
/* translators: %s - WPForms.com CAPTCHA comparison page URL. */
__( 'Not sure which service is right for you? <a href="https://wpforms.com/docs/setup-captcha-wpforms/" target="_blank" rel="noopener noreferrer">Check out our comparison</a> for more details.', 'wpforms-lite' ),
[
'a' => [
'href' => [],
'target' => [],
'rel' => [],
],
]
),
],
'recaptcha-heading' => [
'id' => 'recaptcha-heading',
'content' => $this->get_recaptcha_field_desc(),
'type' => 'content',
'no_label' => true,
'class' => [ 'wpforms-setting-recaptcha', 'section-heading' ],
],
'hcaptcha-heading' => [
'id' => 'hcaptcha-heading',
'content' => $this->get_hcaptcha_field_desc(),
'type' => 'content',
'no_label' => true,
'class' => [ 'section-heading' ],
],
'recaptcha-type' => [
'id' => 'recaptcha-type',
'name' => esc_html__( 'Type', 'wpforms-lite' ),
'type' => 'radio',
'default' => 'v2',
'options' => [
'v2' => esc_html__( 'Checkbox reCAPTCHA v2', 'wpforms-lite' ),
'invisible' => esc_html__( 'Invisible reCAPTCHA v2', 'wpforms-lite' ),
'v3' => esc_html__( 'reCAPTCHA v3', 'wpforms-lite' ),
],
'class' => [ 'wpforms-setting-recaptcha' ],
],
'recaptcha-site-key' => [
'id' => 'recaptcha-site-key',
'name' => esc_html__( 'Site Key', 'wpforms-lite' ),
'type' => 'text',
],
'hcaptcha-site-key' => [
'id' => 'hcaptcha-site-key',
'name' => esc_html__( 'Site Key', 'wpforms-lite' ),
'type' => 'text',
],
'recaptcha-secret-key' => [
'id' => 'recaptcha-secret-key',
'name' => esc_html__( 'Secret Key', 'wpforms-lite' ),
'type' => 'text',
],
'hcaptcha-secret-key' => [
'id' => 'hcaptcha-secret-key',
'name' => esc_html__( 'Secret Key', 'wpforms-lite' ),
'type' => 'text',
],
'recaptcha-fail-msg' => [
'id' => 'recaptcha-fail-msg',
'name' => esc_html__( 'Fail Message', 'wpforms-lite' ),
'desc' => esc_html__( 'Displays to users who fail the verification process.', 'wpforms-lite' ),
'type' => 'text',
'default' => esc_html__( 'Google reCAPTCHA verification failed, please try again later.', 'wpforms-lite' ),
],
'hcaptcha-fail-msg' => [
'id' => 'hcaptcha-fail-msg',
'name' => esc_html__( 'Fail Message', 'wpforms-lite' ),
'desc' => esc_html__( 'Displays to users who fail the verification process.', 'wpforms-lite' ),
'type' => 'text',
'default' => esc_html__( 'hCaptcha verification failed, please try again later.', 'wpforms-lite' ),
],
'recaptcha-v3-threshold' => [
'id' => 'recaptcha-v3-threshold',
'name' => esc_html__( 'Score Threshold', 'wpforms-lite' ),
'desc' => esc_html__( 'reCAPTCHA v3 returns a score (1.0 is very likely a good interaction, 0.0 is very likely a bot). If the score less than or equal to this threshold, the form submission will be blocked and the message above will be displayed.', 'wpforms-lite' ),
'type' => 'number',
'attr' => [
'step' => '0.1',
'min' => '0.0',
'max' => '1.0',
],
'default' => esc_html__( '0.4', 'wpforms-lite' ),
'class' => 'recaptcha' === $this->settings['provider'] && 'v3' === $this->settings['recaptcha_type'] ? [ 'wpforms-setting-recaptcha' ] : [ 'wpforms-setting-recaptcha', 'wpforms-hidden' ],
],
'recaptcha-noconflict' => [
'id' => 'recaptcha-noconflict',
'name' => esc_html__( 'No-Conflict Mode', 'wpforms-lite' ),
'desc' => esc_html__( 'Check this option to forcefully remove other CAPTCHA occurrences in order to prevent conflicts. Only enable this option if your site is having compatibility issues or instructed by support.', 'wpforms-lite' ),
'type' => 'checkbox',
],
self::VIEW . '-preview' => [
'id' => self::VIEW . '-preview',
'name' => esc_html__( 'Preview', 'wpforms-lite' ),
'content' => '<p class="desc">' . esc_html__( 'Please save settings to generate a preview of your CAPTCHA here.', 'wpforms-lite' ) . '</p>',
'type' => 'content',
'class' => [ 'wpforms-hidden' ],
],
];
if (
'hcaptcha' === $this->settings['provider'] ||
( 'recaptcha' === $this->settings['provider'] && 'v2' === $this->settings['recaptcha_type'] )
) {
$data = apply_filters( 'wpforms_admin_pages_settings_captcha_data', [ 'sitekey' => $this->settings['site_key'] ] );
// Prepare HTML for CAPTCHA preview.
$placeholder_descr = $settings[ self::VIEW ][ self::VIEW . '-preview' ]['content'];
$captcha_descr = esc_html__( 'This CAPTCHA is generated using your site and secret keys. If an error is displayed, please double-check your keys.', 'wpforms-lite' );
$captcha_preview = sprintf( '<div class="wpforms-captcha-container" style="pointer-events:none!important;cursor:default!important;"><div %s></div><input type="text" name="wpforms-captcha-hidden" class="wpforms-recaptcha-hidden" style="position:absolute!important;clip:rect(0,0,0,0)!important;height:1px!important;width:1px!important;border:0!important;overflow:hidden!important;padding:0!important;margin:0!important;"></div>', wpforms_html_attributes( '', [ 'wpforms-captcha' ], $data ) );
$settings[ self::VIEW ][ self::VIEW . '-preview' ]['content'] = sprintf( '<div class="wpforms-captcha-preview">%1$s <p class="desc">%2$s</p></div><div class="wpforms-captcha-placeholder wpforms-hidden">%3$s</div>', $captcha_preview, $captcha_descr, $placeholder_descr );
$settings[ self::VIEW ][ self::VIEW . '-preview' ]['class'] = [];
}
return $settings;
}
/**
* Re-init CAPTCHA settings when plugin settings were updated.
*
* @since 1.6.4
*/
public function updated() {
$this->init_settings();
$this->notice();
}
/**
* Display notice about the CAPTCHA preview.
*
* @since 1.6.4
*/
protected function notice() {
if (
! wpforms_is_admin_page( 'settings', self::VIEW ) ||
! $this->is_captcha_preview_ready()
) {
return;
}
\WPForms\Admin\Notice::info( esc_html__( 'A preview of your CAPTCHA is displayed below. Please view to verify the CAPTCHA settings are correct.', 'wpforms-lite' ) );
}
/**
* Enqueue assets for the CAPTCHA settings page.
*
* @since 1.6.4
*/
public function enqueues() {
if (
! $this->is_captcha_preview_ready() ||
(bool) apply_filters( 'wpforms_admin_settings_captcha_enqueues_disable', false )
) {
return;
}
$api_url = $this->get_api_url();
$api_var = 'hcaptcha' === $this->settings['provider'] ? 'hcaptcha' : 'grecaptcha';
wp_enqueue_script( "wpforms-settings-{$this->settings['provider']}", $api_url, [ 'jquery' ], null, true );
wp_add_inline_script( "wpforms-settings-{$this->settings['provider']}", "var wpformsSettingsCaptchaLoad = function(){jQuery('.wpforms-captcha').each(function(index, el){var widgetID = {$api_var}.render(el);jQuery(el).attr('data-captcha-id', widgetID);});jQuery(document).trigger('wpformsSettingsCaptchaLoaded');};" );
}
/**
* Use the CAPTCHA no-conflict mode.
*
* When enabled in the WPForms settings, forcefully remove all other
* CAPTCHA enqueues to prevent conflicts. Filter can be used to target
* specific pages, etc.
*
* @since 1.6.4
*/
public function apply_noconflict() {
if (
! wpforms_is_admin_page( 'settings', self::VIEW ) ||
empty( wpforms_setting( 'recaptcha-noconflict' ) ) ||
! apply_filters( 'wpforms_admin_settings_captcha_apply_noconflict', true )
) {
return;
}
$scripts = wp_scripts();
$urls = [ 'google.com/recaptcha', 'gstatic.com/recaptcha', 'hcaptcha.com/1' ];
foreach ( $scripts->queue as $handle ) {
// Skip the WPForms JavaScript assets.
if (
! isset( $scripts->registered[ $handle ] ) ||
false !== strpos( $scripts->registered[ $handle ]->handle, 'wpforms' )
) {
return;
}
foreach ( $urls as $url ) {
if ( false !== strpos( $scripts->registered[ $handle ]->src, $url ) ) {
wp_dequeue_script( $handle );
wp_deregister_script( $handle );
break;
}
}
}
}
/**
* Check if CAPTCHA config is ready to display a preview.
*
* @since 1.6.4
*
* @return bool
*/
protected function is_captcha_preview_ready() {
return (
( 'hcaptcha' === $this->settings['provider'] || ( 'recaptcha' === $this->settings['provider'] && 'v2' === $this->settings['recaptcha_type'] ) ) &&
! empty( $this->settings['site_key'] ) &&
! empty( $this->settings['secret_key'] )
);
}
/**
* Retrieve the CAPTCHA provider API URL.
*
* @since 1.6.4
*
* @return string
*/
protected function get_api_url() {
$api_url = '';
if ( 'hcaptcha' === $this->settings['provider'] ) {
$api_url = self::HCAPTCHA_API_URL;
}
if ( 'recaptcha' === $this->settings['provider'] ) {
$api_url = self::RECAPTCHA_API_URL;
}
if ( ! empty( $api_url ) ) {
$api_url = add_query_arg( $this->get_api_url_query_arg(), $api_url );
}
return apply_filters( 'wpforms_admin_settings_captcha_get_api_url', $api_url, $this->settings );
}
/**
* Retrieve query arguments for the CAPTCHA API URL.
*
* @since 1.6.4
*
* @return array
*/
protected function get_api_url_query_arg() {
return (array) apply_filters(
'wpforms_admin_settings_captcha_get_api_url_query_arg',
[
'onload' => 'wpformsSettingsCaptchaLoad',
'render' => 'explicit',
],
$this->settings
);
}
/**
* Some heading descriptions, like for hCaptcha, are long so we define them separately.
*
* @since 1.6.4
*
* @return string
*/
private function get_hcaptcha_field_desc() {
return wpforms_render( 'admin/settings/hcaptcha-description' );
}
/**
* Some heading descriptions, like for reCAPTCHA, are long so we define them separately.
*
* @since 1.6.4
*
* @return string
*/
private function get_recaptcha_field_desc() {
return wpforms_render( 'admin/settings/recaptcha-description' );
}
}
Admin/Dashboard/Widget.php 0000644 00000017061 15133255232 0011425 0 ustar 00 <?php
namespace WPForms\Admin\Dashboard;
/**
* Class Widget.
*
* @since 1.7.3
*/
abstract class Widget {
/**
* Instance slug.
*
* @since 1.7.4
*
* @var string
*/
const SLUG = 'dash_widget';
/**
* Save a widget meta for a current user using AJAX.
*
* @since 1.7.4
*/
public function save_widget_meta_ajax() {
check_ajax_referer( 'wpforms_' . static::SLUG . '_nonce' );
$meta = ! empty( $_POST['meta'] ) ? sanitize_key( $_POST['meta'] ) : '';
$value = ! empty( $_POST['value'] ) ? absint( $_POST['value'] ) : 0;
$this->widget_meta( 'set', $meta, $value );
exit();
}
/**
* Get/set a widget meta.
*
* @since 1.7.4
*
* @param string $action Possible value: 'get' or 'set'.
* @param string $meta Meta name.
* @param int $value Value to set.
*
* @return mixed
*/
protected function widget_meta( $action, $meta, $value = 0 ) { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh
$allowed_actions = [ 'get', 'set' ];
if ( ! in_array( $action, $allowed_actions, true ) ) {
return false;
}
$defaults = [
'timespan' => $this->get_timespan_default(),
'active_form_id' => 0,
'hide_recommended_block' => 0,
'hide_graph' => 0,
'color_scheme' => 1, // 1 - wpforms, 2 - wp
'graph_style' => 2, // 1 - bar, 2 - line
];
if ( ! array_key_exists( $meta, $defaults ) ) {
return false;
}
$meta_key = 'wpforms_' . static::SLUG . '_' . $meta;
if ( $action === 'get' ) {
$meta_value = absint( get_user_meta( get_current_user_id(), $meta_key, true ) );
// Return a default value from $defaults if $meta_value is empty.
return empty( $meta_value ) ? $defaults[ $meta ] : $meta_value;
}
$value = absint( $value );
if ( $action === 'set' && ! empty( $value ) ) {
return update_user_meta( get_current_user_id(), $meta_key, $value );
}
if ( $action === 'set' && empty( $value ) ) {
return delete_user_meta( get_current_user_id(), $meta_key );
}
return false;
}
/**
* Get the default timespan option.
*
* @since 1.7.4
*
* @return int|null
*/
protected function get_timespan_default() {
$options = $this->get_timespan_options();
$default = reset( $options );
return is_numeric( $default ) ? $default : null;
}
/**
* Get timespan options (in days).
*
* @since 1.7.4
*
* @return array
*/
protected function get_timespan_options() {
$default = [ 7, 30 ];
$options = $default;
// Apply deprecated filters.
if ( function_exists( 'apply_filters_deprecated' ) ) {
// phpcs:disable WPForms.Comments.PHPDocHooks.RequiredHookDocumentation, WPForms.PHP.ValidateHooks.InvalidHookName
$options = apply_filters_deprecated( 'wpforms_dash_widget_chart_timespan_options', [ $options ], '5.0', 'wpforms_dash_widget_timespan_options' );
$options = apply_filters_deprecated( 'wpforms_dash_widget_forms_list_timespan_options', [ $options ], '5.0', 'wpforms_dash_widget_timespan_options' );
// phpcs:enable WPForms.Comments.PHPDocHooks.RequiredHookDocumentation, WPForms.PHP.ValidateHooks.InvalidHookName
} else {
// phpcs:disable WPForms.Comments.PHPDocHooks.RequiredHookDocumentation, WPForms.PHP.ValidateHooks.InvalidHookName
$options = apply_filters( 'wpforms_dash_widget_chart_timespan_options', $options );
$options = apply_filters( 'wpforms_dash_widget_forms_list_timespan_options', $options );
// phpcs:enable WPForms.Comments.PHPDocHooks.RequiredHookDocumentation, WPForms.PHP.ValidateHooks.InvalidHookName
}
if ( ! is_array( $options ) ) {
$options = $default;
}
$widget_slug = static::SLUG;
// phpcs:disable WPForms.Comments.PHPDocHooks.RequiredHookDocumentation, WPForms.PHP.ValidateHooks.InvalidHookName
$options = apply_filters( "wpforms_{$widget_slug}_timespan_options", $options );
// phpcs:enable WPForms.Comments.PHPDocHooks.RequiredHookDocumentation, WPForms.PHP.ValidateHooks.InvalidHookName
if ( ! is_array( $options ) ) {
return [];
}
$options = array_filter( $options, 'is_numeric' );
return empty( $options ) ? $default : $options;
}
/**
* Widget settings HTML.
*
* @since 1.7.4
*
* @param bool $enabled Is form fields should be enabled.
*/
protected function widget_settings_html( $enabled = true ) {
$graph_style = $this->widget_meta( 'get', 'graph_style' );
$color_scheme = $this->widget_meta( 'get', 'color_scheme' );
echo wpforms_render( // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
'admin/dashboard/widget/settings',
[
'graph_style' => $graph_style,
'color_scheme' => $color_scheme,
'enabled' => $enabled,
],
true
);
}
/**
* Return randomly chosen one of recommended plugins.
*
* @since 1.7.3
*
* @return array
*/
final protected function get_recommended_plugin() {
$plugins = [
'google-analytics-for-wordpress/googleanalytics.php' => [
'name' => __( 'MonsterInsights', 'wpforms-lite' ),
'slug' => 'google-analytics-for-wordpress',
'more' => 'https://www.monsterinsights.com/',
'pro' => [
'file' => 'google-analytics-premium/googleanalytics-premium.php',
],
],
'all-in-one-seo-pack/all_in_one_seo_pack.php' => [
'name' => __( 'AIOSEO', 'wpforms-lite' ),
'slug' => 'all-in-one-seo-pack',
'more' => 'https://aioseo.com/',
'pro' => [
'file' => 'all-in-one-seo-pack-pro/all_in_one_seo_pack.php',
],
],
'coming-soon/coming-soon.php' => [
'name' => __( 'SeedProd', 'wpforms-lite' ),
'slug' => 'coming-soon',
'more' => 'https://www.seedprod.com/',
'pro' => [
'file' => 'seedprod-coming-soon-pro-5/seedprod-coming-soon-pro-5.php',
],
],
'wp-mail-smtp/wp_mail_smtp.php' => [
'name' => __( 'WP Mail SMTP', 'wpforms-lite' ),
'slug' => 'wp-mail-smtp',
'more' => 'https://wpmailsmtp.com/',
'pro' => [
'file' => 'wp-mail-smtp-pro/wp_mail_smtp.php',
],
],
];
$installed = get_plugins();
foreach ( $plugins as $id => $plugin ) {
if ( isset( $installed[ $id ] ) ) {
unset( $plugins[ $id ] );
}
if ( isset( $plugin['pro']['file'], $installed[ $plugin['pro']['file'] ] ) ) {
unset( $plugins[ $id ] );
}
}
return $plugins ? $plugins[ array_rand( $plugins ) ] : [];
}
/**
* Timespan select HTML.
*
* @since 1.7.4
*
* @param int $active_form_id Currently preselected form ID.
* @param bool $enabled If the select menu items should be enabled.
*/
protected function timespan_select_html( $active_form_id, $enabled = true ) {
?>
<select id="wpforms-dash-widget-timespan" class="wpforms-dash-widget-select-timespan" title="<?php esc_attr_e( 'Select timespan', 'wpforms-lite' ); ?>"
<?php echo ! empty( $active_form_id ) ? 'data-active-form-id="' . absint( $active_form_id ) . '"' : ''; ?>>
<?php $this->timespan_options_html( $this->get_timespan_options(), $enabled ); ?>
</select>
<?php
}
/**
* Timespan select options HTML.
*
* @since 1.7.4
*
* @param array $options Timespan options (in days).
* @param bool $enabled If the select menu items should be enabled.
*/
protected function timespan_options_html( $options, $enabled = true ) {
$timespan = $this->widget_meta( 'get', 'timespan' );
foreach ( $options as $option ) :
?>
<option value="<?php echo absint( $option ); ?>" <?php selected( $timespan, absint( $option ) ); ?> <?php disabled( ! $enabled ); ?>>
<?php /* translators: %d - Number of days. */ ?>
<?php echo esc_html( sprintf( _n( 'Last %d day', 'Last %d days', absint( $option ), 'wpforms-lite' ), absint( $option ) ) ); ?>
</option>
<?php
endforeach;
}
}
Admin/Education/Fields.php 0000644 00000023641 15133255232 0011435 0 ustar 00 <?php
namespace WPForms\Admin\Education;
/**
* Fields data holder.
*
* @since 1.6.6
*/
class Fields {
/**
* All fields data.
*
* @since 1.6.6
*
* @var array
*/
protected $fields;
/**
* All fields data.
*
* @since 1.6.6
*
* @return array All possible fields.
*/
private function get_all() {
if ( ! empty( $this->fields ) ) {
return $this->fields;
}
$this->fields = [
[
'icon' => 'fa-pencil-square-o',
'name' => esc_html__( 'Rich Text', 'wpforms-lite' ),
'name_en' => 'Rich Text',
'type' => 'richtext',
'group' => 'fancy',
'order' => '300',
],
[
'icon' => 'fa-phone',
'name' => esc_html__( 'Phone', 'wpforms-lite' ),
'name_en' => 'Phone',
'type' => 'phone',
'group' => 'fancy',
'order' => '1',
],
[
'icon' => 'fa-map-marker',
'name' => esc_html__( 'Address', 'wpforms-lite' ),
'name_en' => 'Address',
'type' => 'address',
'group' => 'fancy',
'order' => '2',
],
[
'icon' => 'fa-calendar-o',
'name' => esc_html__( 'Date / Time', 'wpforms-lite' ),
'name_en' => 'Date / Time',
'type' => 'date-time',
'group' => 'fancy',
'order' => '3',
],
[
'icon' => 'fa-link',
'name' => esc_html__( 'Website / URL', 'wpforms-lite' ),
'name_en' => 'Website / URL',
'type' => 'url',
'group' => 'fancy',
'order' => '4',
],
[
'icon' => 'fa-upload',
'name' => esc_html__( 'File Upload', 'wpforms-lite' ),
'name_en' => 'File Upload',
'type' => 'file-upload',
'group' => 'fancy',
'order' => '5',
],
[
'icon' => 'fa-lock',
'name' => esc_html__( 'Password', 'wpforms-lite' ),
'name_en' => 'Password',
'type' => 'password',
'group' => 'fancy',
'order' => '6',
],
[
'icon' => 'fa-files-o',
'name' => esc_html__( 'Page Break', 'wpforms-lite' ),
'name_en' => 'Page Break',
'type' => 'pagebreak',
'group' => 'fancy',
'order' => '7',
],
[
'icon' => 'fa-arrows-h',
'name' => esc_html__( 'Section Divider', 'wpforms-lite' ),
'name_en' => 'Section Divider',
'type' => 'divider',
'group' => 'fancy',
'order' => '8',
],
[
'icon' => 'fa-file-text-o',
'name' => esc_html__( 'Entry Preview', 'wpforms-lite' ),
'name_en' => 'Entry Preview',
'type' => 'entry-preview',
'group' => 'fancy',
'order' => '9',
],
[
'icon' => 'fa-eye-slash',
'name' => esc_html__( 'Hidden Field', 'wpforms-lite' ),
'name_en' => 'Hidden Field',
'type' => 'hidden',
'group' => 'fancy',
'order' => '10',
],
[
'icon' => 'fa-code',
'name' => esc_html__( 'HTML', 'wpforms-lite' ),
'name_en' => 'HTML',
'type' => 'html',
'group' => 'fancy',
'order' => '11',
],
[
'icon' => 'fa-star',
'name' => esc_html__( 'Rating', 'wpforms-lite' ),
'name_en' => 'Rating',
'type' => 'rating',
'group' => 'fancy',
'order' => '12',
],
[
'icon' => 'fa-question-circle',
'name' => esc_html__( 'Custom Captcha', 'wpforms-lite' ),
'name_en' => 'Custom Captcha',
'type' => 'captcha',
'group' => 'fancy',
'addon' => 'wpforms-captcha',
'order' => '3000',
],
[
'icon' => 'fa-pencil',
'name' => esc_html__( 'Signature', 'wpforms-lite' ),
'name_en' => 'Signature',
'type' => 'signature',
'group' => 'fancy',
'addon' => 'wpforms-signatures',
'order' => '310',
],
[
'icon' => 'fa-ellipsis-h',
'name' => esc_html__( 'Likert Scale', 'wpforms-lite' ),
'name_en' => 'Likert Scale',
'type' => 'likert_scale',
'group' => 'fancy',
'addon' => 'wpforms-surveys-polls',
'order' => '4000',
],
[
'icon' => 'fa-tachometer',
'name' => esc_html__( 'Net Promoter Score', 'wpforms-lite' ),
'name_en' => 'Net Promoter Score',
'type' => 'net_promoter_score',
'group' => 'fancy',
'addon' => 'wpforms-surveys-polls',
'order' => '4100',
],
[
'icon' => 'fa-file-o',
'name' => esc_html__( 'Single Item', 'wpforms-lite' ),
'name_en' => 'Single Item',
'type' => 'payment-single',
'group' => 'payment',
'order' => '1',
],
[
'icon' => 'fa-list-ul',
'name' => esc_html__( 'Multiple Items', 'wpforms-lite' ),
'name_en' => 'Multiple Items',
'type' => 'payment-multiple',
'group' => 'payment',
'order' => '2',
],
[
'icon' => 'fa-check-square-o',
'name' => esc_html__( 'Checkbox Items', 'wpforms-lite' ),
'name_en' => 'Checkbox Items',
'type' => 'payment-checkbox',
'group' => 'payment',
'order' => '3',
],
[
'icon' => 'fa-caret-square-o-down',
'name' => esc_html__( 'Dropdown Items', 'wpforms-lite' ),
'name_en' => 'Dropdown Items',
'type' => 'payment-select',
'group' => 'payment',
'order' => '4',
],
[
'icon' => 'fa-credit-card',
'name' => esc_html__( 'PayPal Commerce', 'wpforms-lite' ),
'name_en' => 'PayPal Commerce',
'type' => 'paypal-commerce',
'group' => 'payment',
'addon' => 'wpforms-paypal-commerce',
'order' => '89',
],
[
'icon' => 'fa-credit-card',
'name' => esc_html__( 'Stripe Credit Card', 'wpforms-lite' ),
'name_en' => 'Stripe Credit Card',
'type' => 'stripe-credit-card',
'group' => 'payment',
'addon' => 'wpforms-stripe',
'order' => '90',
],
[
'icon' => 'fa-credit-card',
'name' => esc_html__( 'Square', 'wpforms-lite' ),
'name_en' => 'Square',
'type' => 'square',
'group' => 'payment',
'addon' => 'wpforms-square',
'order' => '92',
],
[
'icon' => 'fa-credit-card',
'name' => esc_html__( 'Authorize.Net', 'wpforms-lite' ),
'name_en' => 'Authorize.Net',
'type' => 'authorize_net',
'group' => 'payment',
'addon' => 'wpforms-authorize-net',
'order' => '95',
],
[
'icon' => 'fa-money',
'name' => esc_html__( 'Total', 'wpforms-lite' ),
'name_en' => 'Total',
'type' => 'payment-total',
'group' => 'payment',
'order' => '110',
],
];
$captcha = $this->get_captcha();
if ( ! empty( $captcha ) ) {
array_push( $this->fields, $captcha );
}
return $this->fields;
}
/**
* Get Captcha field data.
*
* @since 1.6.6
*
* @return array|false Captcha field data.
*/
private function get_captcha() {
$captcha_settings = wpforms_get_captcha_settings();
if ( empty( $captcha_settings['provider'] ) ) {
return false;
}
if ( ! empty( $captcha_settings['site_key'] ) || ! empty( $captcha_settings['secret_key'] ) ) {
$captcha_name = $captcha_settings['provider'] === 'hcaptcha' ? esc_html__( 'hCaptcha', 'wpforms-lite' ) : esc_html__( 'reCAPTCHA', 'wpforms-lite' );
$captcha_name_en = $captcha_settings['provider'] === 'hcaptcha' ? 'hCaptcha' : 'reCAPTCHA';
$captcha_icon = $captcha_settings['provider'] === 'hcaptcha' ? 'fa-question-circle-o' : 'fa-google';
} else {
$captcha_name = esc_html__( 'CAPTCHA', 'wpforms-lite' );
$captcha_name_en = 'CAPTCHA';
$captcha_icon = 'fa-question-circle-o';
}
return [
'icon' => $captcha_icon,
'name' => $captcha_name,
'name_en' => $captcha_name_en,
'type' => 'captcha_' . $captcha_settings['provider'],
'group' => 'standard',
'order' => 180,
'class' => 'not-draggable',
];
}
/**
* Get filtered fields data.
*
* Usage:
* get_filtered( [ 'group' => 'payment' ] ) - fields from the 'payment' group.
* get_filtered( [ 'addon' => 'surveys-polls' ] ) - fields of the addon 'surveys-polls'.
* get_filtered( [ 'type' => 'payment-total' ] ) - field 'payment-total'.
*
* @since 1.6.6
*
* @param array $args Arguments array.
*
* @return array Fields data filtered according to given arguments.
*/
private function get_filtered( $args = [] ) {
$default_args = [
'group' => '',
'addon' => '',
'type' => '',
];
$args = array_filter( wp_parse_args( $args, $default_args ) );
$fields = $this->get_all();
$filtered_fields = [];
foreach ( $args as $prop => $prop_val ) {
foreach ( $fields as $field ) {
if ( ! empty( $field[ $prop ] ) && $field[ $prop ] === $prop_val ) {
array_push( $filtered_fields, $field );
}
}
}
return $filtered_fields;
}
/**
* Get fields by group.
*
* @since 1.6.6
*
* @param string $group Fields group (standard, fancy or payment).
*
* @return array.
*/
public function get_by_group( $group ) {
return $this->get_filtered( [ 'group' => $group ] );
}
/**
* Get fields by addon.
*
* @since 1.6.6
*
* @param string $addon Addon slug.
*
* @return array.
*/
public function get_by_addon( $addon ) {
return $this->get_filtered( [ 'addon' => $addon ] );
}
/**
* Get field by type.
*
* @since 1.6.6
*
* @param string $type Field type.
*
* @return array Single field data. Empty array if field is not available.
*/
public function get_field( $type ) {
$fields = $this->get_filtered( [ 'type' => $type ] );
return ! empty( $fields[0] ) ? $fields[0] : [];
}
/**
* Set key value of each field (conditionally).
*
* @since 1.6.6
*
* @param array $fields Fields data.
* @param string $key Key.
* @param string $value Value.
* @param string $condition Condition.
*
* @return array Updated field data.
*/
public function set_values( $fields, $key, $value, $condition ) {
if ( empty( $fields ) || empty( $key ) ) {
return $fields;
}
foreach ( $fields as $f => $field ) {
switch ( $condition ) {
case 'empty':
$fields[ $f ][ $key ] = empty( $field[ $key ] ) ? $value : $field[ $key ];
break;
default:
$fields[ $f ][ $key ] = $value;
}
}
return $fields;
}
}
Admin/Education/Admin/Settings/Geolocation.php 0000644 00000003740 15133255232 0015320 0 ustar 00 <?php
namespace WPForms\Admin\Education\Admin\Settings;
use WPForms\Admin\Education\AddonsItemBase;
/**
* Admin/Settings/Geolocation Education feature for Lite and Pro.
*
* @since 1.6.6
*/
class Geolocation extends AddonsItemBase {
/**
* Slug.
*
* @since 1.6.6
*/
const SLUG = 'geolocation';
/**
* Hooks.
*
* @since 1.6.6
*/
public function hooks() {
add_action( 'admin_enqueue_scripts', [ $this, 'enqueues' ] );
add_filter( 'wpforms_settings_defaults', [ $this, 'add_sections' ] );
}
/**
* Indicate if current Education feature is allowed to load.
*
* @since 1.6.6
*
* @return bool
*/
public function allow_load() {
return wpforms_is_admin_page( 'settings', 'geolocation' );
}
/**
* Enqueues.
*
* @since 1.6.6
*/
public function enqueues() {
// Lity - lightbox for images.
wp_enqueue_style(
'wpforms-lity',
WPFORMS_PLUGIN_URL . 'assets/lib/lity/lity.min.css',
null,
'3.0.0'
);
wp_enqueue_script(
'wpforms-lity',
WPFORMS_PLUGIN_URL . 'assets/lib/lity/lity.min.js',
[ 'jquery' ],
'3.0.0',
true
);
}
/**
* Preview of education features for customers with not enough permissions.
*
* @since 1.6.6
*
* @param array $settings Settings sections.
*
* @return array
*/
public function add_sections( $settings ) {
$addon = $this->addons->get_addon( 'geolocation' );
if (
empty( $addon ) ||
empty( $addon['status'] ) ||
empty( $addon['action'] )
) {
return $settings;
}
$section_rows = [
'heading',
'screenshots',
'caps',
'submit',
];
foreach ( $section_rows as $section_row ) {
$settings[ self::SLUG ][ self::SLUG . '-' . $section_row ] = [
'id' => self::SLUG . '-' . $section_row,
'content' => wpforms_render( 'education/admin/settings/geolocation/' . $section_row, $addon, true ),
'type' => 'content',
'no_label' => true,
'class' => [ $section_row, 'wpforms-setting-row-education' ],
];
}
return $settings;
}
}
Admin/Education/Admin/Settings/Integrations.php 0000644 00000002471 15133255232 0015523 0 ustar 00 <?php
namespace WPForms\Admin\Education\Admin\Settings;
use \WPForms\Admin\Education\AddonsListBase;
/**
* Base class for Admin/Integrations feature for Lite and Pro.
*
* @since 1.6.6
*/
class Integrations extends AddonsListBase {
/**
* Template for rendering single addon item.
*
* @since 1.6.6
*
* @var string
*/
protected $single_addon_template = 'education/admin/settings/integrations-item';
/**
* Hooks.
*
* @since 1.6.6
*/
public function hooks() {
add_action( 'wpforms_settings_providers', [ $this, 'filter_addons' ], 1 );
add_action( 'wpforms_settings_providers', [ $this, 'display_addons' ], 500 );
}
/**
* Indicate if current Education feature is allowed to load.
*
* @since 1.6.6
*
* @return bool
*/
public function allow_load() {
return wpforms_is_admin_page( 'settings', 'integrations' );
}
/**
* Get addons for the Settings/Integrations tab.
*
* @since 1.6.6
*
* @return array Addons data.
*/
protected function get_addons() {
return $this->addons->get_by_category( 'providers' );
}
/**
* Ensure that we do not display activated addon items if those addons are not allowed according to the current license.
*
* @since 1.6.6
*/
public function filter_addons() {
$this->filter_not_allowed_addons( 'wpforms_settings_providers' );
}
}
Admin/Education/AddonsItemBase.php 0000644 00000003110 15133255232 0013036 0 ustar 00 <?php
namespace WPForms\Admin\Education;
/**
* Base class for all "addon item" type Education features.
*
* @since 1.6.6
*/
abstract class AddonsItemBase implements EducationInterface {
/**
* Instance of the Education\Core class.
*
* @since 1.6.6
*
* @var \WPForms\Admin\Education\Core
*/
protected $education;
/**
* Instance of the Education\Addons class.
*
* @since 1.6.6
*
* @var \WPForms\Admin\Addons\Addons
*/
protected $addons;
/**
* Template name for rendering single addon item.
*
* @since 1.6.6
*
* @var string
*/
protected $single_addon_template;
/**
* Indicate if current Education feature is allowed to load.
* Should be called from the child feature class.
*
* @since 1.6.6
*
* @return bool
*/
abstract public function allow_load();
/**
* Init.
*
* @since 1.6.6
*/
public function init() {
if ( ! $this->allow_load() ) {
return;
}
// Store the instance of the Education core class.
$this->education = wpforms()->get( 'education' );
// Store the instance of the Education\Addons class.
$this->addons = wpforms()->get( 'addons' );
// Define hooks.
$this->hooks();
}
/**
* Hooks.
*
* @since 1.6.6
*/
abstract public function hooks();
/**
* Display single addon item.
*
* @since 1.6.6
*
* @param array $addon Addon data.
*/
protected function display_single_addon( $addon ) {
if ( empty( $addon ) ) {
return;
}
echo wpforms_render( // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
$this->single_addon_template,
$addon,
true
);
}
}
Admin/Education/Builder/Captcha.php 0000644 00000012203 15133255232 0013150 0 ustar 00 <?php
namespace WPForms\Admin\Education\Builder;
use \WPForms\Admin\Education\EducationInterface;
/**
* Builder/ReCaptcha Education feature.
*
* @since 1.6.6
*/
class Captcha implements EducationInterface {
/**
* Indicate if current Education feature is allowed to load.
*
* @since 1.6.6
*/
public function allow_load() {
return wp_doing_ajax();
}
/**
* Init.
*
* @since 1.6.6
*/
public function init() {
if ( ! $this->allow_load() ) {
return;
}
// Define hooks.
$this->hooks();
}
/**
* Hooks.
*
* @since 1.6.6
*/
public function hooks() {
add_action( 'wp_ajax_wpforms_update_field_captcha', [ $this, 'captcha_field_callback' ] );
}
/**
* Targeting on hCaptcha/reCAPTCHA field button in the builder.
*
* @since 1.6.6
*/
public function captcha_field_callback() {
// Run a security check.
check_ajax_referer( 'wpforms-builder', 'nonce' );
// Check for permissions.
if ( ! wpforms_current_user_can() ) {
wp_send_json_error( esc_html__( 'You do not have permission.', 'wpforms-lite' ) );
}
// Check for form ID.
if ( ! isset( $_POST['id'] ) || empty( $_POST['id'] ) ) {
wp_send_json_error( esc_html__( 'No form ID found.', 'wpforms-lite' ) );
}
// Get an actual form data.
$form_id = absint( $_POST['id'] );
$form_data = wpforms()->form->get( $form_id, [ 'content_only' => true ] );
// Check that CAPTCHA is configured in the settings.
$captcha_settings = wpforms_get_captcha_settings();
$captcha_name = $this->get_captcha_name( $captcha_settings );
if ( empty( $form_data ) || empty( $captcha_name ) ) {
wp_send_json_error( esc_html__( 'Something wrong. Please try again later.', 'wpforms-lite' ) );
}
// Prepare a result array.
$data = $this->get_captcha_result_mockup( $captcha_settings );
if ( empty( $captcha_settings['site_key'] ) || empty( $captcha_settings['secret_key'] ) ) {
// If CAPTCHA is not configured in the WPForms plugin settings.
$data['current'] = 'not_configured';
} elseif ( ! isset( $form_data['settings']['recaptcha'] ) || $form_data['settings']['recaptcha'] !== '1' ) {
// If CAPTCHA is configured in WPForms plugin settings, but wasn't set in form settings.
$data['current'] = 'configured_not_enabled';
} else {
// If CAPTCHA is configured in WPForms plugin and form settings.
$data['current'] = 'configured_enabled';
}
wp_send_json_success( $data );
}
/**
* Retrieve the CAPTCHA name.
*
* @since 1.6.6
*
* @param array $settings The CAPTCHA settings.
*
* @return string
*/
private function get_captcha_name( $settings ) {
if ( empty( $settings['provider'] ) ) {
return '';
}
if ( empty( $settings['site_key'] ) && empty( $settings['secret_key'] ) ) {
return esc_html__( 'CAPTCHA', 'wpforms-lite' );
}
if ( $settings['provider'] === 'hcaptcha' ) {
return esc_html__( 'hCaptcha', 'wpforms-lite' );
}
$recaptcha_names = [
'v2' => esc_html__( 'Google Checkbox v2 reCAPTCHA', 'wpforms-lite' ),
'invisible' => esc_html__( 'Google Invisible v2 reCAPTCHA', 'wpforms-lite' ),
'v3' => esc_html__( 'Google v3 reCAPTCHA', 'wpforms-lite' ),
];
return isset( $recaptcha_names[ $settings['recaptcha_type'] ] ) ? $recaptcha_names[ $settings['recaptcha_type'] ] : '';
}
/**
* Get CAPTCHA callback result mockup.
*
* @since 1.6.6
*
* @param array $settings The CAPTCHA settings.
*
* @return array
*/
private function get_captcha_result_mockup( $settings ) {
$captcha_name = $this->get_captcha_name( $settings );
if ( empty( $captcha_name ) ) {
wp_send_json_error( esc_html__( 'Something wrong. Please, try again later.', 'wpforms-lite' ) );
}
return [
'current' => false,
'cases' => [
'not_configured' => [
'title' => esc_html__( 'Heads up!', 'wpforms-lite' ),
'content' => sprintf(
wp_kses( /* translators: %1$s - CAPTCHA settings page URL; %2$s - WPForms.com doc URL; %3$s - CAPTCHA name. */
__( 'The %3$s settings have not been configured yet. Please complete the setup in your <a href="%1$s" target="_blank">WPForms Settings</a>, and check out our <a href="%2$s" target="_blank" rel="noopener noreferrer">step by step tutorial</a> for full details.', 'wpforms-lite' ),
[
'a' => [
'href' => true,
'rel' => true,
'target' => true,
],
]
),
esc_url( admin_url( 'admin.php?page=wpforms-settings&view=captcha' ) ),
esc_url( wpforms_utm_link( 'https://wpforms.com/docs/setup-captcha-wpforms/', 'builder-modal', 'Captcha Documentation' ) ),
$captcha_name
),
],
'configured_not_enabled' => [
'title' => false,
/* translators: %s - CAPTCHA name. */
'content' => sprintf( esc_html__( '%s has been enabled for this form. Don\'t forget to save your form!', 'wpforms-lite' ), $captcha_name ),
],
'configured_enabled' => [
'title' => false,
/* translators: %s - CAPTCHA name. */
'content' => sprintf( esc_html__( 'Are you sure you want to disable %s for this form?', 'wpforms-lite' ), $captcha_name ),
'cancel' => true,
],
],
'provider' => $settings['provider'],
];
}
}
Admin/Education/Builder/Providers.php 0000644 00000001507 15133255232 0013567 0 ustar 00 <?php
namespace WPForms\Admin\Education\Builder;
use \WPForms\Admin\Education;
/**
* Builder/Providers Education feature.
*
* @since 1.6.6
*/
class Providers extends Education\Builder\Panel {
/**
* Panel slug.
*
* @since 1.6.6
*
* @return string
**/
protected function get_name() {
return 'providers';
}
/**
* Hooks.
*
* @since 1.6.6
*/
public function hooks() {
add_action( 'wpforms_providers_panel_sidebar', [ $this, 'filter_addons' ], 1 );
add_action( 'wpforms_providers_panel_sidebar', [ $this, 'display_addons' ], 500 );
}
/**
* Ensure that we do not display activated addon items if those addons are not allowed according to the current license.
*
* @since 1.6.6
*/
public function filter_addons() {
$this->filter_not_allowed_addons( 'wpforms_providers_panel_sidebar' );
}
}
Admin/Education/Builder/Geolocation.php 0000644 00000007376 15133255232 0014067 0 ustar 00 <?php
namespace WPForms\Admin\Education\Builder;
use WPForms\Admin\Education\AddonsItemBase;
/**
* Builder/Geolocation Education feature for Lite and Pro.
*
* @since 1.6.6
*/
class Geolocation extends AddonsItemBase {
/**
* Indicate if current Education feature is allowed to load.
*
* @since 1.6.6
*
* @return bool
*/
public function allow_load() {
return wpforms_is_admin_page( 'builder' ) || wp_doing_ajax();
}
/**
* Hooks.
*
* @since 1.6.6
*/
public function hooks() {
add_action( 'wpforms_field_options_bottom_advanced-options', [ $this, 'geolocation_options' ], 10, 2 );
}
/**
* Display geolocation options.
*
* @since 1.6.6
*
* @param array $field Field data.
* @param object $instance Builder instance.
*/
public function geolocation_options( $field, $instance ) {
if ( ! in_array( $field['type'], [ 'text', 'address' ], true ) ) {
return;
}
$addon = $this->addons->get_addon( 'geolocation' );
if (
empty( $addon ) ||
empty( $addon['action'] ) ||
empty( $addon['status'] ) || (
$addon['status'] === 'active' &&
$addon['action'] !== 'upgrade'
)
) {
return;
}
$row_args = $this->get_address_autocomplete_row_attributes( $addon );
$row_args['content'] = $instance->field_element(
'toggle',
$field,
$this->get_address_autocomplete_field_attributes( $field, $addon ),
false
);
$instance->field_element( 'row', $field, $row_args );
}
/**
* Get attributes for address autocomplete row.
*
* @since 1.6.6
*
* @param array $addon Current addon information.
*
* @return array
*/
private function get_address_autocomplete_row_attributes( $addon ) {
$default = [
'slug' => 'enable_address_autocomplete',
];
if ( $addon['plugin_allow'] && $addon['action'] === 'install' ) {
return wp_parse_args(
[
'data' => [
'action' => 'install',
'name' => $addon['modal_name'],
'url' => $addon['url'],
'nonce' => wp_create_nonce( 'wpforms-admin' ),
'license' => 'pro',
],
'class' => 'education-modal',
],
$default
);
}
if ( $addon['plugin_allow'] && $addon['action'] === 'activate' ) {
return wp_parse_args(
[
'data' => [
'action' => 'activate',
'name' => sprintf( /* translators: %s - Addon name. */
esc_html__( '%s addon', 'wpforms-lite' ),
$addon['title']
),
'path' => $addon['path'],
'nonce' => wp_create_nonce( 'wpforms-admin' ),
],
'class' => 'education-modal',
],
$default
);
}
return wp_parse_args(
[
'data' => [
'action' => 'upgrade',
'name' => esc_html__( 'Address Autocomplete', 'wpforms-lite' ),
'licence' => 'pro',
'message' => esc_html__( 'We\'re sorry, Address Autocomplete is part of the Geolocation Addon and not available on your plan. Please upgrade to the PRO plan to unlock all these awesome features.', 'wpforms-lite' ),
],
'class' => 'education-modal',
],
$default
);
}
/**
* Get attributes for address autocomplete field.
*
* @since 1.6.6
*
* @param array $field Field data.
* @param array $addon Current addon information.
*
* @return array
*/
private function get_address_autocomplete_field_attributes( $field, $addon ) {
$default = [
'slug' => 'enable_address_autocomplete',
'value' => '0',
'desc' => esc_html__( 'Enable Address Autocomplete', 'wpforms-lite' ),
];
if ( $addon['plugin_allow'] ) {
return $default;
}
return wp_parse_args(
[
'desc' => sprintf(
'%s<span class="wpforms-field-option-education-pro-badge">pro</span>',
esc_html__( 'Enable Address Autocomplete', 'wpforms-lite' )
),
'attrs' => [
'disabled' => 'disabled',
],
],
$default
);
}
}
Admin/Education/Builder/Payments.php 0000644 00000002016 15133255232 0013406 0 ustar 00 <?php
namespace WPForms\Admin\Education\Builder;
use \WPForms\Admin\Education;
/**
* Builder/Payments Education feature.
*
* @since 1.6.6
*/
class Payments extends Education\Builder\Panel {
/**
* Panel slug.
*
* @since 1.6.6
*
* @return string
**/
protected function get_name() {
return 'payments';
}
/**
* Hooks.
*
* @since 1.6.6
*/
public function hooks() {
add_action( 'wpforms_payments_panel_sidebar', [ $this, 'filter_addons' ], 1 );
add_action( 'wpforms_payments_panel_sidebar', [ $this, 'display_addons' ], 500 );
}
/**
* Template name for rendering single addon item.
*
* @since 1.6.6
*
* @return string
*/
protected function get_single_addon_template() {
return 'education/builder/providers-item';
}
/**
* Ensure that we do not display activated addon items if those addons are not allowed according to the current license.
*
* @since 1.6.6
*/
public function filter_addons() {
$this->filter_not_allowed_addons( 'wpforms_payments_panel_sidebar' );
}
}
Admin/Education/Builder/Settings.php 0000644 00000002322 15133255232 0013406 0 ustar 00 <?php
namespace WPForms\Admin\Education\Builder;
use \WPForms\Admin\Education;
/**
* Builder/Settings Education feature.
*
* @since 1.6.6
*/
class Settings extends Education\Builder\Panel {
/**
* Panel slug.
*
* @since 1.6.6
*
* @return string
**/
protected function get_name() {
return 'settings';
}
/**
* Hooks.
*
* @since 1.6.6
*/
public function hooks() {
add_filter( 'wpforms_builder_settings_sections', [ $this, 'filter_addons' ], 1 );
add_action( 'wpforms_builder_after_panel_sidebar', [ $this, 'display' ], 100, 2 );
}
/**
* Display settings addons.
*
* @since 1.6.6
*
* @param object $form Current form.
* @param string $panel Panel slug.
*/
public function display( $form, $panel ) {
if ( empty( $form ) || $this->get_name() !== $panel ) {
return;
}
$this->display_addons();
}
/**
* Ensure that we do not display activated addon items if those addons are not allowed according to the current license.
*
* @since 1.6.6
*
* @param array $sections Settings sections.
*
* @return array
*/
public function filter_addons( $sections ) {
$this->filter_not_allowed_addons( 'wpforms_builder_settings_sections' );
return $sections;
}
}
Admin/Education/Builder/Panel.php 0000644 00000002374 15133255232 0012654 0 ustar 00 <?php
namespace WPForms\Admin\Education\Builder;
use \WPForms\Admin\Education\AddonsListBase;
/**
* Base class for Builder/Settings, Builder/Providers, Builder/Payments Education features.
*
* @since 1.6.6
*/
abstract class Panel extends AddonsListBase {
/**
* Panel slug. Should be redefined in the real Builder/Panel class.
*
* @since 1.6.6
*
* @return string
**/
abstract protected function get_name();
/**
* Indicate if current Education feature is allowed to load.
*
* @since 1.6.6
*
* @return bool
*/
public function allow_load() {
// Load only in the Form Builder.
return wpforms_is_admin_page( 'builder' ) && ! empty( $this->get_name() );
}
/**
* Get addons for the current panel.
*
* @since 1.6.6
*/
protected function get_addons() {
return $this->addons->get_by_category( $this->get_name() );
}
/**
* Template name for rendering single addon item.
*
* @since 1.6.6
*
* @return string
*/
protected function get_single_addon_template() {
return 'education/builder/' . $this->get_name() . '-item';
}
/**
* Display addons.
*
* @since 1.6.6
*/
public function display_addons() {
$this->single_addon_template = $this->get_single_addon_template();
parent::display_addons();
}
}
Admin/Education/Builder/Fields.php 0000644 00000001417 15133255232 0013020 0 ustar 00 <?php
namespace WPForms\Admin\Education\Builder;
use \WPForms\Admin\Education\AddonsItemBase;
/**
* Base class for Builder/Fields Education feature.
*
* @since 1.6.6
*/
abstract class Fields extends AddonsItemBase {
/**
* Instance of the Education\Fields class.
*
* @since 1.6.6
*
* @var \WPForms\Admin\Education\Fields
*/
protected $fields;
/**
* Indicate if current Education feature is allowed to load.
*
* @since 1.6.6
*
* @return bool
*/
public function allow_load() {
return wp_doing_ajax() || wpforms_is_admin_page( 'builder' );
}
/**
* Init.
*
* @since 1.6.6
*/
public function init() {
parent::init();
// Store the instance of the Education\Fields class.
$this->fields = wpforms()->get( 'education_fields' );
}
}
Admin/Education/EducationInterface.php 0000644 00000000636 15133255232 0013762 0 ustar 00 <?php
namespace WPForms\Admin\Education;
/**
* Interface EducationInterface defines required methods for Education features to work properly.
*
* @since 1.6.6
*/
interface EducationInterface {
/**
* Indicate if current Education feature is allowed to load.
*
* @since 1.6.6
*
* @return bool
*/
public function allow_load();
/**
* Init.
*
* @since 1.6.6
*/
public function init();
}
Admin/Education/AddonsListBase.php 0000644 00000002756 15133255232 0013072 0 ustar 00 <?php
namespace WPForms\Admin\Education;
/**
* Base class for all "addons list" type Education features.
*
* @since 1.6.6
*/
abstract class AddonsListBase extends AddonsItemBase {
/**
* Display addons.
*
* @since 1.6.6
*/
public function display_addons() {
array_map( [ $this, 'display_single_addon' ], (array) $this->get_addons() );
}
/**
* Get addons.
*
* @since 1.6.6
*
* @return array Addons array.
*/
abstract protected function get_addons();
/**
* Ensure that we do not display activated addon items if those addons are not allowed according to the current license.
*
* @since 1.6.6
*
* @param string $hook Hook name.
*/
protected function filter_not_allowed_addons( $hook ) {
$edu_addons = wp_list_pluck( $this->get_addons(), 'slug' );
foreach ( $edu_addons as $i => $addon ) {
$edu_addons[ $i ] = strtolower( preg_replace( '/[^a-zA-Z0-9]+/', '', $addon ) );
}
if ( empty( $GLOBALS['wp_filter'][ $hook ]->callbacks ) ) {
return;
}
foreach ( $GLOBALS['wp_filter'][ $hook ]->callbacks as $priority => $hooks ) {
foreach ( $hooks as $name => $arr ) {
$class = ! empty( $arr['function'][0] ) && is_object( $arr['function'][0] ) ? strtolower( get_class( $arr['function'][0] ) ) : '';
$class = explode( '\\', $class )[0];
$class = preg_replace( '/[^a-zA-Z0-9]+/', '', $class );
if ( in_array( $class, $edu_addons, true ) ) {
unset( $GLOBALS['wp_filter'][ $hook ]->callbacks[ $priority ][ $name ] );
}
}
}
}
}
Admin/Education/Core.php 0000644 00000015135 15133255232 0011116 0 ustar 00 <?php
namespace WPForms\Admin\Education;
/**
* Education core.
*
* @since 1.6.6
*/
class Core {
/**
* Indicate if Education core is allowed to load.
*
* @since 1.6.6
*
* @return bool
*/
public function allow_load() {
return wp_doing_ajax() || wpforms_is_admin_page() || wpforms_is_admin_page( 'builder' );
}
/**
* Init.
*
* @since 1.6.6
*/
public function init() {
// Only proceed if allowed.
if ( ! $this->allow_load() ) {
return;
}
$this->hooks();
}
/**
* Hooks.
*
* @since 1.6.6
*/
protected function hooks() {
if ( wp_doing_ajax() ) {
add_action( 'wp_ajax_wpforms_education_dismiss', [ $this, 'ajax_dismiss' ] );
return;
}
add_action( 'admin_enqueue_scripts', [ $this, 'enqueues' ] );
}
/**
* Load enqueues.
*
* @since 1.6.6
*/
public function enqueues() {
$min = wpforms_get_min_suffix();
wp_enqueue_script(
'wpforms-admin-education-core',
WPFORMS_PLUGIN_URL . "assets/js/components/admin/education/core{$min}.js",
[ 'jquery', 'jquery-confirm' ],
WPFORMS_VERSION,
true
);
wp_localize_script(
'wpforms-admin-education-core',
'wpforms_education',
(array) apply_filters( 'wpforms_admin_education_strings', $this->get_js_strings() )
);
}
/**
* Localize common strings.
*
* @since 1.6.6
*
* @return array
*/
protected function get_js_strings() {
$strings = [];
$strings['ok'] = esc_html__( 'Ok', 'wpforms-lite' );
$strings['cancel'] = esc_html__( 'Cancel', 'wpforms-lite' );
$strings['close'] = esc_html__( 'Close', 'wpforms-lite' );
$strings['ajax_url'] = admin_url( 'admin-ajax.php' );
$strings['nonce'] = wp_create_nonce( 'wpforms-education' );
/* translators: %s - addon name. */
$strings['activate_prompt'] = '<p>' . esc_html( sprintf( __( 'The %s is installed but not activated. Would you like to activate it?', 'wpforms-lite' ), '%name%' ) ) . '</p>';
$strings['activate_confirm'] = esc_html__( 'Yes, activate', 'wpforms-lite' );
$strings['addon_activated'] = esc_html__( 'Addon activated', 'wpforms-lite' );
$strings['plugin_activated'] = esc_html__( 'Plugin activated', 'wpforms-lite' );
$strings['activating'] = esc_html__( 'Activating', 'wpforms-lite' );
/* translators: %s - addon name. */
$strings['install_prompt'] = '<p>' . esc_html( sprintf( __( 'The %s is not installed. Would you like to install and activate it?', 'wpforms-lite' ), '%name%' ) ) . '</p>';
$strings['install_confirm'] = esc_html__( 'Yes, install and activate', 'wpforms-lite' );
$strings['installing'] = esc_html__( 'Installing', 'wpforms-lite' );
$strings['can_install_addons'] = wpforms_can_install( 'addon' );
$strings['save_prompt'] = esc_html__( 'Almost done! Would you like to save and refresh the form builder?', 'wpforms-lite' );
$strings['save_confirm'] = esc_html__( 'Yes, save and refresh', 'wpforms-lite' );
$strings['saving'] = esc_html__( 'Saving ...', 'wpforms-lite' );
if ( ! $strings['can_install_addons'] ) {
/* translators: %s - addon name. */
$strings['install_prompt'] = '<p>' . esc_html( sprintf( __( 'The %s is not installed. Please install and activate it to use this feature.', 'wpforms-lite' ), '%name%' ) ) . '</p>';
}
$strings['upgrade'] = [
'pro' => [
'title' => esc_html__( 'is a PRO Feature', 'wpforms-lite' ),
/* translators: %s - addon name. */
'message' => '<p>' . esc_html( sprintf( __( 'We\'re sorry, the %s is not available on your plan. Please upgrade to the PRO plan to unlock all these awesome features.', 'wpforms-lite' ), '%name%' ) ) . '</p>',
'doc' => '<a href="https://wpforms.com/docs/upgrade-wpforms-lite-paid-license/?utm_source=WordPress&utm_medium=link&utm_campaign=liteplugin&utm_content=upgrade-pro#installing-wpforms" target="_blank" rel="noopener noreferrer" class="already-purchased">' . esc_html__( 'Already purchased?', 'wpforms-lite' ) . '</a>',
'button' => esc_html__( 'Upgrade to PRO', 'wpforms-lite' ),
'url' => wpforms_admin_upgrade_link( 'builder-modal' ),
'url_template' => wpforms_admin_upgrade_link( 'builder-modal-template' ),
'modal' => wpforms_get_upgrade_modal_text( 'pro' ),
],
'elite' => [
'title' => esc_html__( 'is an Elite Feature', 'wpforms-lite' ),
/* translators: %s - addon name. */
'message' => '<p>' . esc_html( sprintf( __( 'We\'re sorry, the %s is not available on your plan. Please upgrade to the Elite plan to unlock all these awesome features.', 'wpforms-lite' ), '%name%' ) ) . '</p>',
'doc' => '<a href="https://wpforms.com/docs/upgrade-wpforms-lite-paid-license/?utm_source=WordPress&utm_medium=link&utm_campaign=liteplugin&utm_content=upgrade-elite#installing-wpforms" target="_blank" rel="noopener noreferrer" class="already-purchased">' . esc_html__( 'Already purchased?', 'wpforms-lite' ) . '</a>',
'button' => esc_html__( 'Upgrade to Elite', 'wpforms-lite' ),
'url' => wpforms_admin_upgrade_link( 'builder-modal' ),
'url_template' => wpforms_admin_upgrade_link( 'builder-modal-template' ),
'modal' => wpforms_get_upgrade_modal_text( 'elite' ),
],
];
$strings['upgrade_bonus'] = wpautop(
wp_kses(
__( '<strong>Bonus:</strong> WPForms Lite users get <span>50% off</span> regular price, automatically applied at checkout.', 'wpforms-lite' ),
[
'strong' => [],
'span' => [],
]
)
);
return $strings;
}
/**
* Ajax handler for the education dismiss buttons.
*
* @since 1.6.6
*/
public function ajax_dismiss() {
// Run a security check.
check_ajax_referer( 'wpforms-education', 'nonce' );
// Check for permissions.
if ( ! wpforms_current_user_can() ) {
wp_send_json_error(
[ 'error' => esc_html__( 'You do not have permission to perform this action.', 'wpforms-lite' ) ]
);
}
$dismissed = get_user_meta( get_current_user_id(), 'wpforms_dismissed', true );
if ( empty( $dismissed ) ) {
$dismissed = [];
}
// Section is the identifier of the education feature.
// For example: in Builder/DidYouKnow feature used 'builder-did-you-know-notifications' and 'builder-did-you-know-confirmations'.
$section = ! empty( $_POST['section'] ) ? sanitize_key( wp_unslash( $_POST['section'] ) ) : '';
if ( empty( $section ) ) {
wp_send_json_error(
[ 'error' => esc_html__( 'Please specify a section.', 'wpforms-lite' ) ]
);
}
$dismissed[ 'edu-' . $section ] = time();
update_user_meta( get_current_user_id(), 'wpforms_dismissed', $dismissed );
wp_send_json_success();
}
}
Admin/Loader.php 0000644 00000003323 15133255232 0007515 0 ustar 00 <?php
namespace WPForms\Admin;
/**
* Class Loader gives ability to track/load all admin modules.
*
* @since 1.5.0
*/
class Loader {
/**
* Get the instance of a class and store it in itself.
*
* @since 1.5.0
*/
public static function get_instance() {
static $instance;
if ( ! $instance ) {
$instance = new self();
}
return $instance;
}
/**
* Loader constructor.
*
* @since 1.5.0
*/
public function __construct() {
$core_class_names = array(
'Connect',
'DashboardWidget',
'FlyoutMenu',
'Builder\LicenseAlert',
'Builder\Builder',
'Pages\Community',
'Pages\SMTP',
'Pages\Analytics',
'Entries\PrintPreview',
'Entries\DefaultScreen',
);
$class_names = \apply_filters( 'wpforms_admin_classes_available', $core_class_names );
foreach ( $class_names as $class_name ) {
$this->register_class( $class_name );
}
}
/**
* Register a new class.
*
* @since 1.5.0
*
* @param string $class_name Class name to register.
*/
public function register_class( $class_name ) {
$class_name = sanitize_text_field( $class_name );
// Load Lite class if exists.
if ( class_exists( 'WPForms\Lite\Admin\\' . $class_name ) && ! wpforms()->is_pro() ) {
$class_name = 'WPForms\Lite\Admin\\' . $class_name;
new $class_name();
return;
}
// Load Pro class if exists.
if ( class_exists( 'WPForms\Pro\Admin\\' . $class_name ) && wpforms()->is_pro() ) {
$class_name = 'WPForms\Pro\Admin\\' . $class_name;
new $class_name();
return;
}
// Load general class if neither Pro nor Lite class exists.
if ( class_exists( __NAMESPACE__ . '\\' . $class_name ) ) {
$class_name = __NAMESPACE__ . '\\' . $class_name;
new $class_name();
}
}
}
Admin/Challenge.php 0000644 00000037255 15133255232 0010204 0 ustar 00 <?php
namespace WPForms\Admin;
/**
* Challenge and guide a user to set up a first form once WPForms is installed.
*
* @since 1.5.0
* @since 1.6.2 Challenge v2
*/
class Challenge {
/**
* Number of minutes to complete the Challenge.
*
* @since 1.5.0
*
* @var int
*/
protected $minutes = 5;
/**
* Initialize.
*
* @since 1.6.2
*/
public function init() {
if ( current_user_can( wpforms_get_capability_manage_options() ) ) {
$this->hooks();
}
}
/**
* Hooks.
*
* @since 1.5.0
*/
public function hooks() {
add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_scripts' ] );
add_action( 'wpforms_builder_init', [ $this, 'init_challenge' ] );
add_action( 'admin_footer', [ $this, 'challenge_html' ] );
add_action( 'wpforms_welcome_intro_after', [ $this, 'welcome_html' ] );
add_action( 'wp_ajax_wpforms_challenge_save_option', [ $this, 'save_challenge_option_ajax' ] );
add_action( 'wp_ajax_wpforms_challenge_send_contact_form', [ $this, 'send_contact_form_ajax' ] );
}
/**
* Check if the current page is related to Challenge.
*
* @since 1.5.0
*/
public function is_challenge_page() {
return wpforms_is_admin_page() ||
$this->is_builder_page() ||
$this->is_form_embed_page();
}
/**
* Check if the current page is a forms builder page related to Challenge.
*
* @since 1.5.0
*/
public function is_builder_page() {
if ( ! wpforms_is_admin_page( 'builder' ) ) {
return false;
}
if ( ! $this->challenge_active() && ! $this->challenge_inited() ) {
return false;
}
$step = (int) $this->get_challenge_option( 'step' );
$form_id = (int) $this->get_challenge_option( 'form_id' );
if ( $form_id && $step < 2 ) {
return false;
}
$current_form_id = isset( $_GET['form_id'] ) ? (int) $_GET['form_id'] : 0; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
$is_new_form = isset( $_GET['newform'] ) ? (int) $_GET['newform'] : 0; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
if ( $is_new_form && $step !== 2 ) {
return false;
}
if ( ! $is_new_form && $form_id !== $current_form_id && $step >= 2 ) {
// In case if user skipped the Challenge by closing the browser window or exiting the builder,
// we need to set the previous Challenge as `canceled`.
// Otherwise, the Form Embed Wizard will think that the Challenge is active.
$this->set_challenge_option(
[
'status' => 'skipped',
'finished_date_gmt' => current_time( 'mysql', true ),
]
);
return false;
}
return true;
}
/**
* Check if the current page is a form embed page edit related to Challenge.
*
* @since 1.5.0
*/
public function is_form_embed_page() {
if ( ! is_admin() || ! is_user_logged_in() ) {
return false;
}
$screen = get_current_screen();
if ( ! isset( $screen->id ) || $screen->id !== 'page' ) {
return false;
}
if ( ! $this->challenge_active() ) {
return false;
}
$step = $this->get_challenge_option( 'step' );
if ( ! in_array( $step, [ 3, 4, 5 ], true ) ) {
return false;
}
$embed_page = $this->get_challenge_option( 'embed_page' );
$is_embed_page = false;
if ( isset( $screen->action ) && $screen->action === 'add' && $embed_page === 0 ) {
$is_embed_page = true;
}
if ( isset( $_GET['post'] ) && $embed_page === (int) $_GET['post'] ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
$is_embed_page = true;
}
if ( $is_embed_page && $step < 4 ) {
$this->set_challenge_option( [ 'step' => 4 ] );
}
return $is_embed_page;
}
/**
* Load scripts and styles.
*
* @since 1.5.0
*/
public function enqueue_scripts() {
if ( ! $this->challenge_can_start() && ! $this->challenge_active() ) {
return;
}
$min = wpforms_get_min_suffix();
if ( $this->is_challenge_page() ) {
wp_enqueue_style(
'wpforms-challenge',
WPFORMS_PLUGIN_URL . "assets/css/challenge{$min}.css",
[],
WPFORMS_VERSION
);
wp_enqueue_script(
'wpforms-challenge-admin',
WPFORMS_PLUGIN_URL . "assets/js/components/admin/challenge/challenge-admin{$min}.js",
[ 'jquery' ],
WPFORMS_VERSION,
true
);
wp_localize_script(
'wpforms-challenge-admin',
'wpforms_challenge_admin',
[
'nonce' => wp_create_nonce( 'wpforms_challenge_ajax_nonce' ),
'minutes_left' => absint( $this->minutes ),
'option' => $this->get_challenge_option(),
]
);
}
if ( $this->is_builder_page() || $this->is_form_embed_page() ) {
wp_enqueue_style(
'tooltipster',
WPFORMS_PLUGIN_URL . 'assets/lib/jquery.tooltipster/jquery.tooltipster.min.css',
null,
'4.2.6'
);
wp_enqueue_script(
'tooltipster',
WPFORMS_PLUGIN_URL . 'assets/lib/jquery.tooltipster/jquery.tooltipster.min.js',
[ 'jquery' ],
'4.2.6',
true
);
wp_enqueue_script(
'wpforms-challenge-core',
WPFORMS_PLUGIN_URL . "assets/js/components/admin/challenge/challenge-core{$min}.js",
[ 'jquery', 'tooltipster', 'wpforms-challenge-admin' ],
WPFORMS_VERSION,
true
);
}
if ( $this->is_builder_page() ) {
wp_enqueue_script(
'wpforms-challenge-builder',
WPFORMS_PLUGIN_URL . "assets/js/components/admin/challenge/challenge-builder{$min}.js",
[ 'jquery', 'tooltipster', 'wpforms-challenge-core', 'wpforms-builder' ],
WPFORMS_VERSION,
true
);
}
if ( $this->is_form_embed_page() ) {
wp_enqueue_style(
'wpforms-font-awesome',
WPFORMS_PLUGIN_URL . 'assets/lib/font-awesome/font-awesome.min.css',
null,
'4.7.0'
);
wp_enqueue_script(
'wpforms-challenge-embed',
WPFORMS_PLUGIN_URL . "assets/js/components/admin/challenge/challenge-embed{$min}.js",
[ 'jquery', 'tooltipster', 'wpforms-challenge-core' ],
WPFORMS_VERSION,
true
);
}
}
/**
* Get 'wpforms_challenge' option schema.
*
* @since 1.5.0
*/
public function get_challenge_option_schema() {
return [
'status' => '',
'step' => 0,
'user_id' => get_current_user_id(),
'form_id' => 0,
'embed_page' => 0,
'embed_page_title' => '',
'started_date_gmt' => '',
'finished_date_gmt' => '',
'seconds_spent' => 0,
'seconds_left' => 0,
'feedback_sent' => false,
'feedback_contact_me' => false,
'window_closed' => '',
];
}
/**
* Get Challenge parameter(s) from Challenge option.
*
* @since 1.5.0
*
* @param array|string|null $query Query using 'wpforms_challenge' schema keys.
*
* @return array|mixed
*/
public function get_challenge_option( $query = null ) {
if ( ! $query ) {
return get_option( 'wpforms_challenge' );
}
$return_single = false;
if ( ! is_array( $query ) ) {
$return_single = true;
$query = [ $query ];
}
$query = array_flip( $query );
$option = get_option( 'wpforms_challenge' );
if ( ! $option || ! is_array( $option ) ) {
return array_intersect_key( $this->get_challenge_option_schema(), $query );
}
$result = array_intersect_key( $option, $query );
if ( $return_single ) {
$result = reset( $result );
}
return $result;
}
/**
* Set Challenge parameter(s) to Challenge option.
*
* @since 1.5.0
*
* @param array $query Query using 'wpforms_challenge' schema keys.
*/
public function set_challenge_option( $query ) {
if ( empty( $query ) || ! is_array( $query ) ) {
return;
}
$schema = $this->get_challenge_option_schema();
$replace = array_intersect_key( $query, $schema );
if ( ! $replace ) {
return;
}
// Validate and sanitize the data.
foreach ( $replace as $key => $value ) {
if ( in_array( $key, [ 'step', 'user_id', 'form_id', 'embed_page', 'seconds_spent', 'seconds_left' ], true ) ) {
$replace[ $key ] = absint( $value );
continue;
}
if ( in_array( $key, [ 'feedback_sent', 'feedback_contact_me' ], true ) ) {
$replace[ $key ] = wp_validate_boolean( $value );
continue;
}
$replace[ $key ] = sanitize_text_field( $value );
}
$option = get_option( 'wpforms_challenge' );
$option = ! $option || ! is_array( $option ) ? $schema : $option;
update_option( 'wpforms_challenge', array_merge( $option, $replace ) );
}
/**
* Check if any forms are present on a site.
*
* @since 1.5.0
*/
public function website_has_forms() {
return (bool) wpforms()->form->get(
'',
[
'numberposts' => 1,
'nopaging' => false,
'fields' => 'id',
'no_found_rows' => true,
'update_post_meta_cache' => false,
'update_post_term_cache' => false,
]
);
}
/**
* Check if Challenge was started.
*
* @since 1.5.0
*/
public function challenge_started() {
return 'started' === $this->get_challenge_option( 'status' );
}
/**
* Check if Challenge was inited.
*
* @since 1.6.2
*/
public function challenge_inited() {
return 'inited' === $this->get_challenge_option( 'status' );
}
/**
* Check if Challenge was paused.
*
* @since 1.6.2
*/
public function challenge_paused() {
return 'paused' === $this->get_challenge_option( 'status' );
}
/**
* Check if Challenge was finished.
*
* @since 1.5.0
*/
public function challenge_finished() {
$status = $this->get_challenge_option( 'status' );
return in_array( $status, [ 'completed', 'canceled', 'skipped' ], true );
}
/**
* Check if Challenge is in progress.
*
* @since 1.5.0
*/
public function challenge_active() {
return ( $this->challenge_inited() || $this->challenge_started() || $this->challenge_paused() ) && ! $this->challenge_finished();
}
/**
* Force Challenge to start.
*
* @since 1.6.2
*/
public function challenge_force_start() {
return apply_filters( 'wpforms_admin_challenge_force_start', false );
}
/**
* Check if Challenge can be started.
*
* @since 1.5.0
*/
public function challenge_can_start() {
static $can_start = null;
if ( ! is_null( $can_start ) ) {
return $can_start;
}
if ( $this->challenge_force_skip() ) {
$can_start = false;
}
if ( $this->challenge_force_start() ) {
$can_start = true;
// No need to check something else in this case.
return $can_start;
}
if ( $this->challenge_finished() ) {
$can_start = false;
}
if ( $this->website_has_forms() ) {
$can_start = false;
}
if ( is_null( $can_start ) ) {
$can_start = true;
}
return $can_start;
}
/**
* Start the Challenge in Form Builder.
*
* @since 1.5.0
*/
public function init_challenge() {
if ( ! $this->challenge_can_start() ) {
return;
}
$this->set_challenge_option(
wp_parse_args(
[ 'status' => 'inited' ],
$this->get_challenge_option_schema()
)
);
}
/**
* Include Challenge HTML.
*
* @since 1.5.0
*/
public function challenge_html() {
if ( $this->challenge_force_skip() || ( $this->challenge_finished() && ! $this->challenge_force_start() ) ) {
return;
}
if ( wpforms_is_admin_page() && ! wpforms_is_admin_page( 'getting-started' ) && $this->challenge_can_start() ) {
// Before showing the Challenge in the `start` state we should reset the option.
// In this way we ensure the Challenge will not appear somewhere in the builder where it is not should be.
$this->set_challenge_option( [ 'status' => '' ] );
$this->challenge_modal_html( 'start' );
}
if ( $this->is_builder_page() ) {
$this->challenge_modal_html( 'progress' );
$this->challenge_builder_templates_html();
}
if ( $this->is_form_embed_page() ) {
$this->challenge_modal_html( 'progress' );
$this->challenge_embed_templates_html();
}
}
/**
* Include Challenge main modal window HTML.
*
* @since 1.5.0
*
* @param string $state State of Challenge ('start' or 'progress').
*/
public function challenge_modal_html( $state ) {
echo wpforms_render( // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
'admin/challenge/modal',
[
'state' => $state,
'step' => $this->get_challenge_option( 'step' ),
'minutes' => $this->minutes,
],
true
);
}
/**
* Include Challenge HTML templates specific to Form Builder.
*
* @since 1.5.0
*/
public function challenge_builder_templates_html() {
echo wpforms_render( 'admin/challenge/builder' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
/**
* Include Challenge HTML templates specific to form embed page.
*
* @since 1.5.0
*/
public function challenge_embed_templates_html() {
/**
* Filter the content of the Challenge Congrats popup footer.
*
* @since 1.7.4
*
* @param string $footer Footer markup.
*/
$congrats_popup_footer = apply_filters( 'wpforms_admin_challenge_embed_template_congrats_popup_footer', '' );
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo wpforms_render(
'admin/challenge/embed',
[
'minutes' => $this->minutes,
'congrats_popup_footer' => $congrats_popup_footer,
],
true
);
}
/**
* Include Challenge CTA on WPForms welcome activation screen.
*
* @since 1.5.0
*/
public function welcome_html() {
if ( $this->challenge_can_start() ) {
echo wpforms_render( 'admin/challenge/welcome' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
}
/**
* Save Challenge data via AJAX.
*
* @since 1.5.0
*/
public function save_challenge_option_ajax() {
check_admin_referer( 'wpforms_challenge_ajax_nonce' );
if ( empty( $_POST['option_data'] ) ) {
wp_send_json_error();
}
$schema = $this->get_challenge_option_schema();
foreach ( $schema as $key => $value ) {
if ( isset( $_POST['option_data'][ $key ] ) ) {
$query[ $key ] = sanitize_text_field( wp_unslash( $_POST['option_data'][ $key ] ) );
}
}
if ( empty( $query ) ) {
wp_send_json_error();
}
if ( ! empty( $query['status'] ) && 'started' === $query['status'] ) {
$query['started_date_gmt'] = current_time( 'mysql', true );
}
if ( ! empty( $query['status'] ) && in_array( $query['status'], [ 'completed', 'canceled', 'skipped' ], true ) ) {
$query['finished_date_gmt'] = current_time( 'mysql', true );
}
if ( ! empty( $query['status'] ) && 'skipped' === $query['status'] ) {
$query['started_date_gmt'] = current_time( 'mysql', true );
$query['finished_date_gmt'] = $query['started_date_gmt'];
}
$this->set_challenge_option( $query );
wp_send_json_success();
}
/**
* Send contact form to wpforms.com via AJAX.
*
* @since 1.5.0
*/
public function send_contact_form_ajax() {
check_admin_referer( 'wpforms_challenge_ajax_nonce' );
$url = 'https://wpforms.com/wpforms-challenge-feedback/';
$message = ! empty( $_POST['contact_data']['message'] ) ? sanitize_textarea_field( wp_unslash( $_POST['contact_data']['message'] ) ) : '';
$email = '';
if ( ! empty( $_POST['contact_data']['contact_me'] ) && 'true' === $_POST['contact_data']['contact_me'] ) {
$current_user = wp_get_current_user();
$email = $current_user->user_email;
$this->set_challenge_option( [ 'feedback_contact_me' => true ] );
}
if ( empty( $message ) && empty( $email ) ) {
wp_send_json_error();
}
$data = [
'body' => [
'wpforms' => [
'id' => 296355,
'submit' => 'wpforms-submit',
'fields' => [
2 => $message,
3 => $email,
4 => ucfirst( wpforms_get_license_type() ),
5 => wpforms()->version,
],
],
],
];
$response = wp_remote_post( $url, $data );
if ( is_wp_error( $response ) ) {
wp_send_json_error();
}
$this->set_challenge_option( [ 'feedback_sent' => true ] );
wp_send_json_success();
}
/**
* Force WPForms Challenge to skip.
*
* @since 1.7.6
*
* @return bool
*/
private function challenge_force_skip() {
return defined( 'WPFORMS_SKIP_CHALLENGE' ) && WPFORMS_SKIP_CHALLENGE;
}
}
Admin/Revisions.php 0000644 00000026203 15133255232 0010272 0 ustar 00 <?php
namespace WPForms\Admin;
use WP_Post;
/**
* Form Revisions.
*
* @since 1.7.3
*/
class Revisions {
/**
* Current Form Builder panel view.
*
* @since 1.7.3
*
* @var string
*/
private $view = 'revisions';
/**
* Current Form ID.
*
* @since 1.7.3
*
* @var int|false
*/
private $form_id = false;
/**
* Current Form.
*
* @since 1.7.3
*
* @var WP_Post|null
*/
private $form;
/**
* Current Form Revision ID.
*
* @since 1.7.3
*
* @var int|false
*/
private $revision_id = false;
/**
* Current Form Revision.
*
* @since 1.7.3
*
* @var WP_Post|null
*/
private $revision;
/**
* Whether revisions panel was already viewed by the user at least once.
*
* @since 1.7.3
*
* @var bool
*/
private $viewed;
/**
* Date format to use in UI.
*
* @since 1.7.3
*
* @var string
*/
private $date_format = 'M j';
/**
* Time format to use in UI.
*
* @since 1.7.3
*
* @var string
*/
private $time_format;
/**
* Initialize the class if preconditions are met.
*
* @since 1.7.3
*
* @return void
*/
public function init() {
if ( ! $this->allow_load() ) {
return;
}
// phpcs:disable WordPress.Security.NonceVerification.Recommended
if ( isset( $_REQUEST['view'] ) ) {
$this->view = sanitize_key( $_REQUEST['view'] );
}
if ( isset( $_REQUEST['revision_id'] ) ) {
$this->revision_id = absint( $_REQUEST['revision_id'] );
}
// phpcs:enable WordPress.Security.NonceVerification.Recommended
// Fetch revision, if needed.
if ( $this->revision_id && wp_revisions_enabled( $this->form ) ) {
$this->revision = wp_get_post_revision( $this->revision_id );
}
// Bail if we don't have a valid revision.
if ( $this->revision_id && ! $this->revision instanceof WP_Post ) {
return;
}
$this->time_format = get_option( 'time_format' );
$this->hooks();
}
/**
* Whether it is allowed to load under certain conditions.
*
* - numeric, non-zero form ID provided,
* - the form with this ID exists and was successfully fetched,
* - we're in the Form Builder or processing an ajax request.
*
* @since 1.7.3
*
* @return bool
*/
private function allow_load() {
if ( ! ( wpforms_is_admin_page( 'builder' ) || wp_doing_ajax() ) ) {
return false;
}
// phpcs:disable WordPress.Security.NonceVerification.Recommended
$id = wp_doing_ajax() && isset( $_REQUEST['id'] ) ? absint( $_REQUEST['id'] ) : false;
$id = isset( $_REQUEST['form_id'] ) && ! is_array( $_REQUEST['form_id'] ) ? absint( $_REQUEST['form_id'] ) : $id;
// phpcs:enable WordPress.Security.NonceVerification.Recommended
$this->form_id = $id;
$form_handler = wpforms()->get( 'form' );
if ( ! $form_handler ) {
return false;
}
$this->form = $form_handler->get( $this->form_id );
return $this->form_id && $this->form instanceof WP_Post;
}
/**
* Hook into WordPress lifecycle.
*
* @since 1.7.3
*/
private function hooks() {
// Restore a revision. The `admin_init` action has already fired, `current_screen` fires before headers are sent.
add_action( 'current_screen', [ $this, 'process_restore' ] );
// Refresh a rendered list of revisions on the frontend.
add_action( 'wp_ajax_wpforms_get_form_revisions', [ $this, 'fetch_revisions_list' ] );
// Mark Revisions panel as viewed when viewed for the first time. Hides the error badge.
add_action( 'wp_ajax_wpforms_mark_panel_viewed', [ $this, 'mark_panel_viewed' ] );
// Back-compat for forms created with revisions disabled.
add_action( 'wpforms_builder_init', [ $this, 'maybe_create_initial_revision' ] );
// Pass localized strings to frontend.
add_filter( 'wpforms_builder_strings', [ $this, 'get_localized_strings' ], 10, 2 );
}
/**
* Get current revision, if available.
*
* @since 1.7.3
*
* @return WP_Post|null
*/
public function get_revision() {
return $this->revision;
}
/**
* Get formatted date or time.
*
* @since 1.7.3
*
* @param string $datetime UTC datetime from the post object.
* @param string $part What to return - date or time, defaults to date.
*
* @return string
*/
public function get_formatted_datetime( $datetime, $part = 'date' ) {
if ( $part === 'time' ) {
return wpforms_datetime_format( $datetime, $this->time_format, true );
}
return wpforms_datetime_format( $datetime, $this->date_format, true );
}
/**
* Get admin (Form Builder) base URL with additional query args.
*
* @since 1.7.3
*
* @param array $query_args Additional query args to append to the base URL.
*
* @return string
*/
public function get_url( $query_args = [] ) {
$defaults = [
'page' => 'wpforms-builder',
'view' => $this->view,
'form_id' => $this->form_id,
];
return add_query_arg(
wp_parse_args( $query_args, $defaults ),
admin_url( 'admin.php' )
);
}
/**
* Determine if Revisions panel was previously viewed by current user.
*
* @since 1.7.3
*
* @return bool
*/
public function panel_viewed() {
if ( $this->viewed === null ) {
$this->viewed = (bool) get_user_meta( get_current_user_id(), 'wpforms_revisions_disabled_notice_dismissed', true );
}
return $this->viewed;
}
/**
* Mark Revisions panel as viewed by current user.
*
* @since 1.7.3
*/
public function mark_panel_viewed() {
// Run a security check.
check_ajax_referer( 'wpforms-builder', 'nonce' );
if ( ! $this->panel_viewed() ) {
$this->viewed = update_user_meta( get_current_user_id(), 'wpforms_revisions_disabled_notice_dismissed', true );
}
wp_send_json_success( [ 'updated' => $this->viewed ] );
}
/**
* Get a rendered list of all revisions.
*
* @since 1.7.3
*
* @return string
*/
public function render_revisions_list() {
return wpforms_render(
'builder/revisions/list',
$this->prepare_template_render_arguments(),
true
);
}
/**
* Prepare all arguments for the template to be rendered.
*
* Note: All data is escaped in the template.
*
* @since 1.7.3
*
* @return array
*/
private function prepare_template_render_arguments() {
$args = [
'active_class' => $this->revision ? '' : ' active',
'current_version_url' => $this->get_url(),
'author_id' => $this->form->post_author,
'revisions' => [],
];
$revisions = wp_get_post_revisions( $this->form_id );
if ( empty( $revisions ) ) {
return $args;
}
// WordPress always orders entries by `post_date` column, which contains a date and time in site's timezone configured in settings.
// This setting is per site, not per user, and it's not expected to be changed. However, if it was changed for whatever reason,
// the order of revisions will be incorrect. This is definitely an edge case, but we can prevent this from ever happening
// by sorting the results using `post_date_gmt` or `post_modified_gmt`, which contains UTC date and never changes.
uasort(
$revisions,
static function ( $a, $b ) {
return strtotime( $a->post_modified_gmt ) > strtotime( $b->post_modified_gmt ) ? -1 : 1;
}
);
// The first revision is always identical to the current version and should not be displayed in the list.
$current_revision = array_shift( $revisions );
// Display the author of current version instead of a form author.
$args['author_id'] = $current_revision->post_author;
foreach ( $revisions as $revision ) {
$time_diff = sprintf( /* translators: %s - Relative time difference, e.g. "5 minutes", "12 days". */
__( '%s ago', 'wpforms-lite' ),
human_time_diff( strtotime( $revision->post_modified_gmt . ' +0000' ) )
);
$date_time = sprintf( /* translators: %1$s - date, %2$s - time when revision was created, e.g. "Dec 25 at 12:57pm". */
__( '%1$s at %2$s', 'wpforms-lite' ),
$this->get_formatted_datetime( $revision->post_modified_gmt ),
$this->get_formatted_datetime( $revision->post_modified_gmt, 'time' )
);
$args['revisions'][] = [
'active_class' => $this->revision && $this->revision->ID === $revision->ID ? ' active' : '',
'url' => $this->get_url(
[
'revision_id' => $revision->ID,
]
),
'author_id' => $revision->post_author,
'time_diff' => $time_diff,
'date_time' => $date_time,
];
}
return $args;
}
/**
* Fetch a list of revisions via ajax.
*
* @since 1.7.3
*/
public function fetch_revisions_list() {
// Run a security check.
check_ajax_referer( 'wpforms-builder', 'nonce' );
wp_send_json_success(
[
'html' => $this->render_revisions_list(),
]
);
}
/**
* Restore the revision (if needed) and reload the Form Builder.
*
* @since 1.7.3
*
* @return void
*/
public function process_restore() {
$is_restore_request = isset( $_GET['action'] ) && $_GET['action'] === 'restore_revision';
// Bail early.
if (
! $is_restore_request ||
! $this->form_id ||
! $this->form ||
! $this->revision_id ||
! $this->revision ||
! check_admin_referer( 'restore_revision', 'wpforms_nonce' )
) {
return;
}
$restored_id = wp_restore_post_revision( $this->revision );
if ( $restored_id ) {
wp_safe_redirect(
wpforms()->get( 'revisions' )->get_url(
[
'form_id' => $restored_id,
]
)
);
exit;
}
}
/**
* Create initial revision for existing form.
*
* When a new form is created with revisions enabled, WordPress immediately creates first revision which is identical to the form. But when
* a form was created with revisions disabled, this initial revision does not exist. Revisions are saved after post update, so modifying
* a form that have no initial revision will update the post first, then a revision of this updated post will be saved. The version of
* the form that existed before this update is now gone. To avoid losing this pre-revisions state, we create this initial revision
* when the Form Builder loads, if needed.
*
* @since 1.7.3
*
* @return void
*/
public function maybe_create_initial_revision() {
// On new form creation there's no revisions yet, bail. Also, when revisions are disabled.
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
if ( isset( $_GET['newform'] ) || ! wp_revisions_enabled( $this->form ) ) {
return;
}
$revisions = wp_get_post_revisions(
$this->form_id,
[
'fields' => 'ids',
'numberposts' => 1,
]
);
if ( $revisions ) {
return;
}
$initial_revision_id = wp_save_post_revision( $this->form_id );
$initial_revision = wp_get_post_revision( $initial_revision_id );
// Initial revision should belong to the author of the original form.
if ( $initial_revision->post_author !== $this->form->post_author ) {
wp_update_post(
[
'ID' => $initial_revision_id,
'post_author' => $this->form->post_author,
]
);
}
}
/**
* Pass localized strings to frontend.
*
* @since 1.7.3
*
* @param array $strings All strings that will be passed to frontend.
* @param WP_Post $form Current form object.
*
* @return array
*/
public function get_localized_strings( $strings, $form ) {
$strings['revision_update_confirm'] = esc_html__( 'You’re about to save a form revision. Continuing will make this the current version.', 'wpforms-lite' );
return $strings;
}
}
Admin/Addons/AddonsCache.php 0000644 00000003515 15133255232 0011656 0 ustar 00 <?php
namespace WPForms\Admin\Addons;
/**
* Addons cache handler.
*
* @since 1.6.6
*/
class AddonsCache extends \WPForms\Helpers\CacheBase {
/**
* Determine if the class is allowed to load.
*
* @since 1.6.8
*
* @return bool
*/
protected function allow_load() {
// Load only in the Admin area or Form Builder.
return wp_doing_ajax() || wpforms_is_admin_page() || wpforms_is_admin_page( 'builder' );
}
/**
* Provide settings.
*
* @since 1.6.6
*
* @return array Settings array.
*/
protected function setup() {
return [
// Remote source URL.
'remote_source' => 'https://wpforms.com/wp-content/addons.json',
// Addons cache file name.
'cache_file' => 'addons.json',
/**
* Time-to-live of the addons cache file in seconds.
*
* This applies to `uploads/wpforms/cache/addons.json` file.
*
* @since 1.6.8
*
* @param integer $cache_ttl Cache time-to-live, in seconds.
* Default value: WEEK_IN_SECONDS.
*/
'cache_ttl' => (int) apply_filters( 'wpforms_admin_addons_cache_ttl', WEEK_IN_SECONDS ),
// Scheduled update action.
'update_action' => 'wpforms_admin_addons_cache_update',
];
}
/**
* Prepare addons data to store in a local cache -
* generate addons icon image file name for further use.
*
* @since 1.6.6
*
* @param array $data Raw addons data.
*
* @return array Prepared data for caching (with icons).
*/
protected function prepare_cache_data( $data ) {
if ( empty( $data ) || ! is_array( $data ) ) {
return [];
}
$addons_cache = [];
foreach ( $data as $addon ) {
// Addon icon.
$addon['icon'] = str_replace( 'wpforms-', 'addon-icon-', $addon['slug'] ) . '.png';
// Use slug as a key for further usage.
$addons_cache[ $addon['slug'] ] = $addon;
}
return $addons_cache;
}
}
Admin/Addons/Addons.php 0000644 00000017232 15133255232 0010733 0 ustar 00 <?php
namespace WPForms\Admin\Addons;
/**
* Addons data handler.
*
* @since 1.6.6
*/
class Addons {
/**
* Addons cache object.
*
* @since 1.6.6
*
* @var \WPForms\Admin\Addons\AddonsCache
*/
private $cache;
/**
* All Addons data.
*
* @since 1.6.6
*
* @var array
*/
private $addons;
/**
* Available addons data.
*
* @since 1.6.6
*
* @var array
*/
private $available_addons;
/**
* Determine if the class is allowed to load.
*
* @since 1.6.6
*
* @return bool
*/
public function allow_load() {
// Load only in the Admin area or Form Builder.
return wp_doing_ajax() || wpforms_is_admin_page() || wpforms_is_admin_page( 'builder' );
}
/**
* Initialize class.
*
* @since 1.6.6
*/
public function init() {
if ( ! $this->allow_load() ) {
return;
}
$this->cache = wpforms()->get( 'addons_cache' );
$this->addons = $this->cache->get_cached();
$this->hooks();
}
/**
* Hooks.
*
* @since 1.6.6
*/
protected function hooks() {
add_action( 'admin_init', [ $this, 'get_available' ] );
do_action( 'wpforms_admin_addons_init' );
}
/**
* Get all addons data as array.
*
* @since 1.6.6
*
* @param bool $force_cache_update Determine if we need to update cache. Default is `false`.
*
* @return array
*/
public function get_all( $force_cache_update = false ) {
return (bool) $force_cache_update ? $this->cache->update_cache() : $this->addons;
}
/**
* Get filtered addons data.
*
* Usage:
* ->get_filtered( $this->addons, [ 'category' => 'payments' ] ) - addons for the payments panel.
* ->get_filtered( $this->addons, [ 'license' => 'elite' ] ) - addons available for 'elite' license.
*
* @since 1.6.6
*
* @param array $addons Raw addons data.
* @param array $args Arguments array.
*
* @return array Addons data filtered according to given arguments.
*/
private function get_filtered( $addons, $args ) {
if ( empty( $addons ) ) {
return [];
}
$default_args = [
'category' => '',
'license' => '',
];
$args = wp_parse_args( $args, $default_args );
$filtered_addons = [];
foreach ( $addons as $addon ) {
foreach ( [ 'category', 'license' ] as $arg_key ) {
if (
! empty( $args[ $arg_key ] ) &&
! empty( $addon[ $arg_key ] ) &&
is_array( $addon[ $arg_key ] ) &&
in_array( strtolower( $args[ $arg_key ] ), $addon[ $arg_key ], true )
) {
$filtered_addons[] = $addon;
}
}
}
return $filtered_addons;
}
/**
* Get available addons data by category.
*
* @since 1.6.6
*
* @param string $category Addon category.
*
* @return array.
*/
public function get_by_category( $category ) {
return $this->get_filtered( $this->available_addons, [ 'category' => $category ] );
}
/**
* Get available addons data by license.
*
* @since 1.6.6
*
* @param string $license Addon license.
*
* @return array.
*/
public function get_by_license( $license ) {
return $this->get_filtered( $this->available_addons, [ 'license' => $license ] );
}
/**
* Get available addons data by slugs.
*
* @since 1.6.8
*
* @param array $slugs Addon slugs.
*
* @return array
*/
public function get_by_slugs( $slugs ) {
if ( empty( $slugs ) || ! is_array( $slugs ) ) {
return [];
}
$result_addons = [];
foreach ( $slugs as $slug ) {
$addon = $this->get_addon( $slug );
if ( ! empty( $addon ) ) {
$result_addons[] = $addon;
}
}
return $result_addons;
}
/**
* Get available addon data by slug.
*
* @since 1.6.6
*
* @param string $slug Addon slug, can be both "wpforms-drip" and "drip".
*
* @return array Single addon data. Empty array if addon is not found.
*/
public function get_addon( $slug ) {
$slug = 'wpforms-' . str_replace( 'wpforms-', '', sanitize_key( $slug ) );
$addon = ! empty( $this->available_addons[ $slug ] ) ? $this->available_addons[ $slug ] : [];
// In case if addon is "not available" let's try to get and prepare addon data from all addons.
if ( empty( $addon ) ) {
$addon = ! empty( $this->addons[ $slug ] ) ? $this->prepare_addon_data( $this->addons[ $slug ] ) : [];
}
return $addon;
}
/**
* Get license level of the addon.
*
* @since 1.6.6
*
* @param array|string $addon Addon data array OR addon slug.
*
* @return string License level: pro | elite.
*/
private function get_license_level( $addon ) {
if ( empty( $addon ) ) {
return '';
}
$addon = is_string( $addon ) ? $this->get_addon( $addon ) : $addon;
$addon['license'] = empty( $addon['license'] ) ? [] : (array) $addon['license'];
// TODO: convert to a class constant when we will drop PHP 5.5.
$levels = [ 'basic', 'plus', 'pro', 'elite', 'agency', 'ultimate' ];
$license = '';
foreach ( $levels as $level ) {
if ( in_array( $level, $addon['license'], true ) ) {
$license = $level;
break;
}
}
if ( empty( $license ) ) {
return '';
}
return in_array( $license, [ 'basic', 'plus', 'pro' ], true ) ? 'pro' : 'elite';
}
/**
* Determine if user's license level has access.
*
* @since 1.6.6
*
* @param array|string $addon Addon data array OR addon slug.
*
* @return bool
*/
protected function has_access( $addon ) {
return false;
}
/**
* Return array of addons available to display. All data prepared and normalized.
* "Available to display" means that addon need to be displayed as education item (addon is not installed or not activated).
*
* @since 1.6.6
*
* @return array
*/
public function get_available() {
if ( empty( $this->addons ) || ! is_array( $this->addons ) ) {
return [];
}
if ( empty( $this->available_addons ) ) {
$this->available_addons = array_map( [ $this, 'prepare_addon_data' ], $this->addons );
$this->available_addons = array_filter(
$this->available_addons,
static function( $addon ) {
return isset( $addon['status'], $addon['plugin_allow'] ) && ( $addon['status'] !== 'active' || ! $addon['plugin_allow'] );
}
);
}
return $this->available_addons;
}
/**
* Prepare addon data.
*
* @since 1.6.6
*
* @param array $addon Addon data.
*
* @return array Extended addon data.
*/
protected function prepare_addon_data( $addon ) {
if ( empty( $addon ) ) {
return [];
}
$addon['title'] = ! empty( $addon['title'] ) ? $addon['title'] : '';
$addon['slug'] = ! empty( $addon['slug'] ) ? $addon['slug'] : '';
// We need the cleared name of the addon, without the ' addon' suffix, for further use.
$addon['name'] = preg_replace( '/ addon$/i', '', $addon['title'] );
/* translators: %s - addon name. */
$addon['modal_name'] = sprintf( esc_html__( '%s addon', 'wpforms-lite' ), $addon['name'] );
$addon['clear_slug'] = str_replace( 'wpforms-', '', $addon['slug'] );
$addon['utm_content'] = ucwords( str_replace( '-', ' ', $addon['clear_slug'] ) );
$addon['license'] = empty( $addon['license'] ) ? [] : (array) $addon['license'];
$addon['license_level'] = $this->get_license_level( $addon );
$addon['icon'] = ! empty( $addon['icon'] ) ? $addon['icon'] : '';
$addon['path'] = sprintf( '%1$s/%1$s.php', $addon['slug'] );
$addon['video'] = ! empty( $addon['video'] ) ? $addon['video'] : '';
$addon['plugin_allow'] = $this->has_access( $addon );
$addon['status'] = 'missing';
$addon['action'] = 'upgrade';
$addon['page_url'] = empty( $addon['url'] ) ? '' : $addon['url'];
$addon['doc_url'] = empty( $addon['doc'] ) ? '' : $addon['doc'];
$addon['url'] = '';
static $nonce = '';
$nonce = empty( $nonce ) ? wp_create_nonce( 'wpforms-admin' ) : $nonce;
$addon['nonce'] = $nonce;
return $addon;
}
}
Logger/ListTable.php 0000644 00000030706 15133255232 0010366 0 ustar 00 <?php
namespace WPForms\Logger;
use WP_List_Table;
if ( ! class_exists( 'WP_List_Table' ) ) {
require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php';
}
/**
* Class ListTable.
*
* @since 1.6.3
*/
class ListTable extends WP_List_Table {
/**
* Record Query.
*
* @since 1.6.3
*
* @var Repository
*/
private $repository;
/**
* ListTable constructor.
*
* @since 1.6.3
*
* @param Repository $repository Repository.
*/
public function __construct( $repository ) {
$this->repository = $repository;
parent::__construct(
[
'plural' => esc_html__( 'Logs', 'wpforms-lite' ),
'singular' => esc_html__( 'Log', 'wpforms-lite' ),
]
);
$this->hooks();
add_screen_option(
'per_page',
[ 'default' => $this->get_items_per_page( $this->get_per_page_option_name() ) ]
);
set_screen_options();
}
/**
* Hooks.
*
* @since 1.7.5
*/
private function hooks() {
add_filter(
'set_screen_option_' . $this->get_per_page_option_name(),
[ $this, 'set_items_per_page_option' ],
10,
3
);
}
/**
* Handles setting the items_per_page option for this screen.
*
* @since 1.7.5
*
* @param mixed $status Default false (to skip saving the current option).
* @param string $option Screen option name.
* @param int $value Screen option value.
*
* @return int
* @noinspection PhpUnusedParameterInspection
*/
public function set_items_per_page_option( $status, $option, $value ) {
return $value;
}
/**
* Whether the table has items to display or not.
*
* @since 1.6.3
*
* @return bool
*/
public function has_items() {
// We can't use the empty function because it doesn't work with Countable object.
return (bool) count( $this->items );
}
/**
* Prepares the list of items for displaying.
*
* @since 1.6.3
*/
public function prepare_items() {
$offset = $this->get_items_offset();
$search = $this->get_request_search_query();
$types = $this->get_items_type();
$per_page = $this->get_items_per_page( $this->get_per_page_option_name() );
$this->items = $this->repository->records( $per_page, $offset, $search, $types );
$total_items = $this->get_total();
$this->set_pagination_args(
[
'total_items' => $total_items,
'per_page' => $per_page,
'total_pages' => ceil( $total_items / $per_page ),
]
);
}
/**
* Return the type of records.
*
* @since 1.6.3
*
* @return string
*/
private function get_items_type() {
return filter_input( INPUT_GET, 'log_type', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
}
/**
* Return the number of items to offset/skip for this current view.
*
* @since 1.6.3
*
* @return int
*/
private function get_items_offset() {
return $this->get_items_per_page( $this->get_per_page_option_name() ) * ( $this->get_pagenum() - 1 );
}
/**
* Return the search filter for this request, if any.
*
* @since 1.6.3
*
* @return string
*/
private function get_request_search_query() {
return filter_input( INPUT_GET, 's', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
}
/**
* Column title.
*
* @since 1.6.3
*
* @param Record $item List table item.
*
* @return string
* @noinspection PhpUnused
*/
public function column_log_title( $item ) {
return sprintf(
'<a href="#" class="js-single-log-target" data-log-id="%1$d"><strong>%2$s</strong></a>',
absint( $item->get_id() ),
esc_html( $item->get_title() )
);
}
/**
* Column message.
*
* @since 1.6.3
*
* @param Record $item List table item.
*
* @return string
* @noinspection PhpUnused
*/
public function column_message( $item ) {
$message = $item->get_message();
if ( preg_match( '/\[body].+{"error":"(.+)"}/i', $message, $m ) ) {
$message = $m[1];
}
if ( preg_match( '/\[error] => (.+)/i', $message, $m ) ) {
$message = $m[1];
}
return esc_html( $this->crop_message( $message ) );
}
/**
* Column form ID.
*
* @since 1.6.3
*
* @param Record $item List table item.
*
* @return int
* @noinspection PhpUnused
*/
public function column_form_id( $item ) {
return absint( $item->get_form_id() );
}
/**
* Column types.
*
* @since 1.6.3
*
* @param Record $item List table item.
*
* @return string
* @noinspection PhpUnused
*/
public function column_types( $item ) {
return esc_html( implode( ', ', $item->get_types( 'label' ) ) );
}
/**
* Column date.
*
* @since 1.6.3
*
* @param Record $item List table item.
*
* @return string
* @noinspection PhpUnused
*/
public function column_date( $item ) {
return esc_html( $item->get_date( 'sql-local' ) );
}
/**
* Crop message for preview on list table.
*
* @since 1.6.3
*
* @param string $message Message.
*
* @return string
*/
private function crop_message( $message ) {
return wp_html_excerpt( $message, 97, '...' );
}
/**
* Prepares the _column_headers property which is used by WP_Table_List at rendering.
* It merges the columns and the sortable columns.
*
* @since 1.6.3
*/
private function prepare_column_headers() {
$this->_column_headers = [
$this->get_columns(),
get_hidden_columns( $this->screen ),
[],
];
}
/**
* Return the columns names for rendering.
*
* @since 1.6.3
*
* @return array
*/
public function get_columns() {
return [
'log_title' => __( 'Log Title', 'wpforms-lite' ),
'message' => __( 'Message', 'wpforms-lite' ),
'form_id' => __( 'Form ID', 'wpforms-lite' ),
'types' => __( 'Types', 'wpforms-lite' ),
'date' => __( 'Date', 'wpforms-lite' ),
];
}
/**
* Header before log table.
*
* @since 1.6.3
*/
private function header() {
?>
<div class="wpforms-admin-content-header">
<h3 class="wp-heading-inline"><?php esc_html_e( 'View Logs', 'wpforms-lite' ); ?>
<?php if ( $this->get_request_search_query() ) { ?>
<span class="subtitle">
<?php
echo sprintf( /* translators: %s: search query. */
esc_html__( 'Search results for "%s"', 'wpforms-lite' ),
esc_html( $this->get_request_search_query() )
);
?>
</span>
<?php } ?>
</h3>
<?php
$this->hidden_fields();
$this->search_box( esc_html__( 'Search Logs', 'wpforms-lite' ), 'plugin' );
?>
</div>
<?php
}
/**
* Generate the table navigation above or below the table.
*
* @since 1.6.3
*
* @param string $which Which position.
*/
protected function display_tablenav( $which ) {
?>
<div class="tablenav <?php echo esc_attr( $which ); ?>">
<?php
if ( $which === 'top' ) {
$this->extra_tablenav( $which );
}
$this->pagination( $which );
?>
<br class="clear" />
</div>
<?php
}
/**
* Table list actions.
*
* @since 1.6.3
*
* @param string $which Position of navigation (top or bottom).
*/
protected function extra_tablenav( $which ) {
if ( ! $this->get_total() ) {
return;
}
$this->log_type_select();
$this->clear_all();
}
/**
* Clear all log records.
*
* @since 1.6.3
*/
private function clear_all() {
?>
<button name="clear-all" type="submit" class="button" value="1"><?php esc_html_e( 'Delete All Logs', 'wpforms-lite' ); ?></button>
<?php
}
/**
* Update URL when table showing.
* _wp_http_referer is used only on bulk actions, we remove it to keep the $_GET shorter.
*
* @since 1.6.3
*/
public function process_admin_ui() {
$uri = isset( $_SERVER['REQUEST_URI'] ) ? esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '';
// phpcs:disable WordPress.Security.NonceVerification.Recommended
if ( ! empty( $_REQUEST['_wp_http_referer'] ) || ! empty( $_REQUEST['clear-all'] ) ) {
if ( ! empty( $_REQUEST['clear-all'] ) ) {
$this->repository->clear_all();
}
wp_safe_redirect(
remove_query_arg(
[ '_wp_http_referer', '_wpnonce', 'clear-all' ],
$uri
)
);
exit;
}
// phpcs:enable WordPress.Security.NonceVerification.Recommended
}
/**
* Message to be displayed when there are no items.
*
* @since 1.6.3
*/
public function no_items() {
esc_html_e( 'No logs found.', 'wpforms-lite' );
}
/**
* Print all hidden fields.
*
* @since 1.6.3
*/
private function hidden_fields() {
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
foreach ( $_GET as $key => $value ) {
if ( $key[0] === '_' || $key === 'paged' || $key === 'ID' ) {
continue;
}
echo '<input type="hidden" name="' . esc_attr( $key ) . '" value="' . esc_attr( $value ) . '" />';
}
}
/**
* Select for choose a log type.
*
* @since 1.6.3
*/
private function log_type_select() {
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
$current_type = ! empty( $_GET['log_type'] ) ? sanitize_text_field( wp_unslash( $_GET['log_type'] ) ) : '';
?>
<select name="log_type">
<option value=""><?php esc_html_e( 'All Logs', 'wpforms-lite' ); ?></option>
<?php foreach ( Log::get_log_types() as $type_slug => $type ) { ?>
<option
value="<?php echo esc_attr( $type_slug ); ?>"
<?php selected( $type_slug, $current_type ); ?>>
<?php echo esc_html( $type ); ?>
</option>
<?php } ?>
</select>
<input type="submit" class="button" value="<?php esc_attr_e( 'Apply', 'wpforms-lite' ); ?>">
<?php
}
/**
* Popup view.
*
* @since 1.6.3
*/
public function popup_template() {
?>
<script type="text/html" id="tmpl-wpforms-log-record">
<div class="wpforms-log-popup">
<div class="wpforms-log-popup-block">
<div class="wpforms-log-popup-label"><?php esc_html_e( 'Log Title', 'wpforms-lite' ); ?></div>
<div class="wpforms-log-popup-title">{{ data.title }}</div>
</div>
<div class="wpforms-log-popup-block">
<div class="wpforms-log-popup-label"><?php esc_html_e( 'Message', 'wpforms-lite' ); ?></div>
<div class="wpforms-log-popup-message">{{{ data.message }}}</div>
</div>
<div class="wpforms-log-popup-flex wpforms-log-popup-flex-column-2">
<div>
<div class="wpforms-log-popup-label"><?php esc_html_e( 'Date', 'wpforms-lite' ); ?></div>
<div class="wpforms-log-popup-create-at">{{ data.create_at }}</div>
</div>
<div>
<div class="wpforms-log-popup-label"><?php esc_html_e( 'Types', 'wpforms-lite' ); ?></div>
<div class="wpforms-log-popup-types">{{ data.types }}</div>
</div>
</div>
<div class="wpforms-log-popup-flex wpforms-log-popup-flex-column-4">
<div>
<div class="wpforms-log-popup-label"><?php esc_html_e( 'Log ID', 'wpforms-lite' ); ?></div>
<div class="wpforms-log-popup-id">{{ data.ID }}</div>
</div>
<div>
<div class="wpforms-log-popup-label"><?php esc_html_e( 'Form ID', 'wpforms-lite' ); ?></div>
<div class="wpforms-log-popup-form-id">
<# if ( data.form_id ) { #>
<a href="{{ data.form_url }}">
<# } #>
{{ data.form_id }}
<# if ( data.form_id ) { #>
</a>
<# } #>
</div>
</div>
<div>
<div class="wpforms-log-popup-label"><?php esc_html_e( 'Entry ID', 'wpforms-lite' ); ?></div>
<div class="wpforms-log-popup-entry-id">
<# if ( data.entry_id ) { #>
<a href="{{ data.entry_url }}">
<# } #>
{{ data.entry_id }}
<# if ( data.entry_id ) { #>
</a>
<# } #>
</div>
</div>
<div>
<div class="wpforms-log-popup-label"><?php esc_html_e( 'User ID', 'wpforms-lite' ); ?></div>
<div class="wpforms-log-popup-user-id">
<# if ( data.user_id ) { #>
<a href="{{ data.user_url }}">
<# } #>
{{ data.user_id }}
<# if ( data.user_id ) { #>
</a>
<# } #>
</div>
</div>
</div>
</div>
</script>
<?php
}
/**
* Display list table page.
*
* @since 1.6.3
*/
public function display_page() {
$this->prepare_column_headers();
$this->prepare_items();
echo '<div class="wpforms-list-table wpforms-list-table--logs">';
echo '<form id="' . esc_attr( $this->_args['plural'] ) . '-filter" method="get">';
$this->header();
$this->display();
echo '</form>';
echo '</div>';
}
/**
* Check if the database table exist.
*
* @since 1.6.4
*
* @return bool
*/
public function table_exists() {
return $this->repository->table_exists();
}
/**
* Get total logs.
*
* @since 1.6.3
*
* @return int
*/
public function get_total() {
return $this->repository->get_total();
}
/**
* Gets the screen per_page option name.
*
* @since 1.7.5
*
* @return string
*/
private function get_per_page_option_name() {
return str_replace( '-', '_', $this->screen->id ) . '_per_page';
}
}
Logger/RecordQuery.php 0000644 00000003226 15133255232 0010744 0 ustar 00 <?php
namespace WPForms\Logger;
/**
* Class RecordQuery.
*
* @since 1.6.3
*/
class RecordQuery {
/**
* Build query.
*
* @since 1.6.3
*
* @param int $limit Query limit of records.
* @param int $offset Offset of records.
* @param string $search Search.
* @param string $type Type of records.
*
* @return array
*/
public function get( $limit, $offset = 0, $search = '', $type = '' ) {
global $wpdb;
//phpcs:disable WordPress.DB.DirectDatabaseQuery.NoCaching
//phpcs:disable WordPress.DB.PreparedSQL.NotPrepared
return (array) $wpdb->get_results(
$this->build_query( $limit, $offset, $search, $type )
);
//phpcs:enable WordPress.DB.DirectDatabaseQuery.NoCaching
//phpcs:enable WordPress.DB.PreparedSQL.NotPrepared
}
/**
* Build query.
*
* @since 1.6.3
*
* @param int $limit Query limit of records.
* @param int $offset Offset of records.
* @param string $search Search.
* @param string $type Type of records.
*
* @return string
*/
private function build_query( $limit, $offset = 0, $search = '', $type = '' ) {
global $wpdb;
$sql = 'SELECT SQL_CALC_FOUND_ROWS * FROM ' . Repository::get_table_name();
$where = [];
if ( ! empty( $search ) ) {
$where[] = $wpdb->prepare(
'`title` REGEXP %s OR `message` REGEXP %s',
$search,
$search
);
}
if ( ! empty( $type ) ) {
$where[] = $wpdb->prepare(
'`types` REGEXP %s',
$type
);
}
if ( $where ) {
$sql .= ' WHERE ' . implode( ' AND ', $where );
}
$sql .= ' ORDER BY `create_at` DESC, `id` DESC';
$sql .= $wpdb->prepare( ' LIMIT %d, %d', absint( $offset ), absint( $limit ) );
return $sql;
}
}
Logger/Record.php 0000644 00000010733 15133255232 0007717 0 ustar 00 <?php
namespace WPForms\Logger;
/**
* Class Record.
*
* @since 1.6.3
*/
class Record {
/**
* Record ID.
*
* @since 1.6.3
*
* @var int
*/
private $id;
/**
* Record title.
*
* @since 1.6.3
*
* @var string
*/
private $title;
/**
* Record message.
*
* @since 1.6.3
*
* @var string
*/
private $message;
/**
* Array, string, or string separated by commas types.
*
* @since 1.6.3
*
* @var array|string
*/
private $types;
/**
* Datetime of creating record.
*
* @since 1.6.3
*
* @var string
*/
private $create_at;
/**
* Record form ID.
*
* @since 1.6.3
*
* @var int
*/
private $form_id;
/**
* Record entry ID.
*
* @since 1.6.3
*
* @var int
*/
private $entry_id;
/**
* Record user ID.
*
* @since 1.6.3
*
* @var int
*/
private $user_id;
/**
* Record constructor.
*
* @since 1.6.3
*
* @param int $id Record ID.
* @param string $title Record title.
* @param string $message Record message.
* @param array|string $types Array, string, or string separated by commas types.
* @param string $create_at Datetime of creating record.
* @param int $form_id Record form ID.
* @param int $entry_id Record entry ID.
* @param int $user_id Record user ID.
*/
public function __construct( $id, $title, $message, $types, $create_at, $form_id = 0, $entry_id = 0, $user_id = 0 ) {
$this->id = $id;
$this->title = $title;
$this->message = $message;
$this->types = $types;
$this->create_at = strtotime( $create_at );
$this->form_id = $form_id;
$this->entry_id = $entry_id;
$this->user_id = $user_id;
}
/**
* Get record ID.
*
* @since 1.6.3
*
* @return int
*/
public function get_id() {
return $this->id;
}
/**
* Get record title.
*
* @since 1.6.3
*
* @return string
*/
public function get_title() {
return $this->title;
}
/**
* Get record message.
*
* @since 1.6.3
*
* @return string
*/
public function get_message() {
return $this->message;
}
/**
* Get record types.
*
* @since 1.6.3
*
* @param string $view Keys or labels.
*
* @return array
*/
public function get_types( $view = 'key' ) {
$this->types = is_array( $this->types ) ? $this->types : explode( ',', $this->types );
if ( $view === 'label' ) {
return array_intersect_key(
Log::get_log_types(),
array_flip( $this->types )
);
}
return $this->types;
}
/**
* Get date of creating record.
*
* @since 1.6.3
*
* @param string $format Date format full|short|default sql format.
*
* @return string
*/
public function get_date( $format = 'short' ) {
switch ( $format ) {
case 'short':
$date = date_i18n(
get_option( 'date_format' ),
$this->create_at + ( get_option( 'gmt_offset' ) * 3600 )
);
break;
case 'full':
$date = date_i18n(
sprintf( '%s %s', get_option( 'date_format' ), get_option( 'time_format' ) ),
$this->create_at + ( get_option( 'gmt_offset' ) * 3600 )
);
break;
case 'sql':
$date = gmdate( 'Y-m-d H:i:s', $this->create_at );
break;
case 'sql-local':
$date = date_i18n(
'Y-m-d H:i:s',
$this->create_at + ( get_option( 'gmt_offset' ) * 3600 )
);
break;
default:
$date = '';
break;
}
return $date;
}
/**
* Get form ID.
*
* @since 1.6.3
*
* @return int
*/
public function get_form_id() {
return $this->form_id;
}
/**
* Get entry ID.
*
* @since 1.6.3
*
* @return int
*/
public function get_entry_id() {
return $this->entry_id;
}
/**
* Get user ID.
*
* @since 1.6.3
*
* @return int
*/
public function get_user_id() {
return $this->user_id;
}
/**
* Create new record.
*
* @since 1.6.3
*
* @param string $title Record title.
* @param string $message Record message.
* @param array|string $types Array, string, or string separated by commas types.
* @param int $form_id Record form ID.
* @param int $entry_id Record entry ID.
* @param int $user_id Record user ID.
*
* @return Record
*/
public static function create( $title, $message, $types, $form_id = 0, $entry_id = 0, $user_id = 0 ) {
return new Record(
0,
sanitize_text_field( $title ),
wp_kses( $message, [ 'pre' => [] ] ),
$types,
gmdate( 'Y-m-d H:i:s' ),
absint( $form_id ),
absint( $entry_id ),
absint( $user_id )
);
}
}
Logger/Repository.php 0000644 00000014010 15133255232 0010650 0 ustar 00 <?php
namespace WPForms\Logger;
/**
* Class Repository.
*
* @since 1.6.3
*/
class Repository {
/**
* Cache key name for total logs.
*
* @since 1.6.3
*/
const CACHE_TOTAL_KEY = 'wpforms_logs_total';
/**
* Records query.
*
* @since 1.6.3
*
* @var \WPForms\Logger\RecordQuery
*/
private $records_query;
/**
* Records.
*
* @since 1.6.3
*
* @var \WPForms\Logger\Records
*/
private $records;
/**
* Get not-limited total query.
*
* @since 1.6.4.1
*
* @var int
*/
private $full_total;
/**
* Log constructor.
*
* @since 1.6.3
*
* @param \WPForms\Logger\RecordQuery $records_query Records query.
*/
public function __construct( $records_query ) {
$this->records_query = $records_query;
$this->full_total = false;
$this->records = new Records();
}
/**
* Get log table name.
*
* @since 1.6.3
*
* @return string
*/
public static function get_table_name() {
global $wpdb;
return $wpdb->prefix . 'wpforms_logs';
}
/**
* Create table for database.
*
* @since 1.6.3
*/
public function create_table() {
global $wpdb;
$table = self::get_table_name();
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
$charset_collate = $wpdb->get_charset_collate();
$sql = "CREATE TABLE {$table} (
id BIGINT(20) NOT NULL AUTO_INCREMENT,
title VARCHAR(255) NOT NULL,
message LONGTEXT NOT NULL,
types VARCHAR(255) NOT NULL,
create_at DATETIME NOT NULL,
form_id BIGINT(20),
entry_id BIGINT(20),
user_id BIGINT(20),
PRIMARY KEY (id)
) {$charset_collate};";
maybe_create_table( $table, $sql );
}
/**
* Create new record.
*
* @since 1.6.3
*
* @param string $title Record title.
* @param string $message Record message.
* @param array|string $types Array, string, or string separated by commas types.
* @param int $form_id Record form ID.
* @param int $entry_id Record entry ID.
* @param int $user_id Record user ID.
*/
public function add( $title, $message, $types, $form_id, $entry_id, $user_id ) {
$this->records->push(
Record::create( $title, $message, $types, $form_id, $entry_id, $user_id )
);
}
/**
* Get records.
*
* @since 1.6.3
*
* @param int $limit Query limit of records.
* @param int $offset Offset of records.
* @param string $search Search.
* @param string $type Type of records.
*
* @return \WPForms\Logger\Records
*/
public function records( $limit, $offset = 0, $search = '', $type = '' ) {
$data = $this->records_query->get( $limit, $offset, $search, $type );
$this->full_total = true;
$records = new Records();
// As we got raw data we need to convert to Record.
foreach ( $data as $row ) {
$records->push(
$this->prepare_record( $row )
);
}
return $records;
}
/**
* Get record.
*
* @since 1.6.3
*
* @param int $id Record ID.
*
* @return \WPForms\Logger\Record|null
*/
public function record( $id ) {
global $wpdb;
//phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching
$item = $wpdb->get_row(
$wpdb->prepare(
'SELECT * FROM ' . self::get_table_name() . ' WHERE id = %d', //phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
absint( $id )
)
);
if ( $item ) {
$item = $this->prepare_record( $item );
}
return $item;
}
/**
* Create record from DB row.
*
* @since 1.6.3
*
* @param object $row Row from DB.
*
* @return \WPForms\Logger\Record
*/
private function prepare_record( $row ) {
return new Record(
absint( $row->id ),
$row->title,
$row->message,
$row->types,
$row->create_at,
absint( $row->form_id ),
absint( $row->entry_id ),
absint( $row->user_id )
);
}
/**
* Save records to database.
*
* @since 1.6.3
*/
public function save() {
// We can't use the empty function because it doesn't work with Countable object.
if ( ! count( $this->records ) ) {
return;
}
global $wpdb;
$sql = 'INSERT INTO ' . self::get_table_name() . ' ( `id`, `title`, `message`, `types`, `create_at`, `form_id`, `entry_id`, `user_id` ) VALUES ';
foreach ( $this->records as $record ) {
$sql .= $wpdb->prepare(
'( NULL, %s, %s, %s, %s, %d, %d, %d ),',
$record->get_title(),
$record->get_message(),
implode( ',', $record->get_types() ),
$record->get_date( 'sql' ),
$record->get_form_id(),
$record->get_entry_id(),
$record->get_user_id()
);
}
$sql = rtrim( $sql, ',' );
//phpcs:disable WordPress.DB.DirectDatabaseQuery.NoCaching
//phpcs:disable WordPress.DB.PreparedSQL.NotPrepared
$wpdb->query( $sql );
//phpcs:enable WordPress.DB.DirectDatabaseQuery.NoCaching
//phpcs:enable WordPress.DB.PreparedSQL.NotPrepared
wp_cache_delete( self::CACHE_TOTAL_KEY );
}
/**
* Check if the database table exist.
*
* @since 1.6.4
*
* @return bool
*/
public function table_exists() {
global $wpdb;
$table = self::get_table_name();
return $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table ) ) === $table; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching
}
/**
* Get total count of logs.
*
* @since 1.6.3
*
* @return int
*/
public function get_total() {
global $wpdb;
$total = wp_cache_get( self::CACHE_TOTAL_KEY );
if ( ! $total ) {
//phpcs:disable WordPress.DB.PreparedSQL.NotPrepared
$total = $this->full_total ? $wpdb->get_var( 'SELECT FOUND_ROWS()' ) : $wpdb->get_var( 'SELECT COUNT(ID) FROM ' . self::get_table_name() );
//phpcs:enable WordPress.DB.PreparedSQL.NotPrepared
wp_cache_set( self::CACHE_TOTAL_KEY, $total, 'wpforms', DAY_IN_SECONDS );
}
return absint( $total );
}
/**
* Clear all records in Database.
*
* @since 1.6.3
*/
public function clear_all() {
global $wpdb;
//phpcs:disable WordPress.DB.DirectDatabaseQuery.NoCaching
//phpcs:disable WordPress.DB.PreparedSQL.NotPrepared
$wpdb->query( 'TRUNCATE TABLE ' . self::get_table_name() );
//phpcs:enable WordPress.DB.DirectDatabaseQuery.NoCaching
//phpcs:enable WordPress.DB.PreparedSQL.NotPrepared
}
}
Logger/Log.php 0000644 00000011361 15133255232 0007220 0 ustar 00 <?php
namespace WPForms\Logger;
/**
* Class Log.
*
* @since 1.6.3
*/
class Log {
/**
* Repository.
*
* @since 1.6.3
*
* @var Repository
*/
private $repository;
/**
* List table.
*
* @since 1.6.3
*
* @var ListTable
*/
private $list_table;
/**
* Register log hooks.
*
* @since 1.6.3
*/
public function hooks() {
$this->repository = new Repository( new RecordQuery() );
add_action( 'shutdown', [ $this->repository, 'save' ] );
add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_styles' ] );
add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_scripts' ] );
add_action( 'wp_ajax_wpforms_get_log_record', [ $this, 'get_record' ] );
}
/**
* Enqueue styles.
*
* @since 1.6.3
*/
public function enqueue_styles() {
if ( ! $this->is_logger_page() ) {
return;
}
$min = wpforms_get_min_suffix();
wp_enqueue_style(
'wpforms-tools-logger',
WPFORMS_PLUGIN_URL . "assets/css/logger{$min}.css",
[],
WPFORMS_VERSION,
'all'
);
}
/**
* Enqueue styles.
*
* @since 1.6.3
*/
public function enqueue_scripts() {
if ( ! $this->is_logger_page() ) {
return;
}
$min = wpforms_get_min_suffix();
wp_enqueue_script(
'wpforms-tools-logger',
WPFORMS_PLUGIN_URL . "assets/js/components/admin/logger/logger{$min}.js",
[ 'jquery', 'jquery-confirm', 'wp-util' ],
WPFORMS_VERSION,
true
);
}
/**
* Get log types.
*
* @since 1.6.3
*
* @return array
*/
public static function get_log_types() {
return [
'conditional_logic' => esc_html__( 'Conditional Logic', 'wpforms-lite' ),
'entry' => esc_html__( 'Entries', 'wpforms-lite' ),
'error' => esc_html__( 'Errors', 'wpforms-lite' ),
'payment' => esc_html__( 'Payment', 'wpforms-lite' ),
'provider' => esc_html__( 'Providers', 'wpforms-lite' ),
'security' => esc_html__( 'Security', 'wpforms-lite' ),
'spam' => esc_html__( 'Spam', 'wpforms-lite' ),
'log' => esc_html__( 'Log', 'wpforms-lite' ),
];
}
/**
* Determine if it a Logs page.
*
* @since 1.6.3
*
* @return bool
*/
private function is_logger_page() {
return wpforms_is_admin_page( 'tools', 'logs' );
}
/**
* Create new record.
*
* @since 1.6.3
*
* @param string $title Record title.
* @param string $message Record message.
* @param array|string $types Array, string, or string separated by commas types.
* @param int $form_id Record form ID.
* @param int $entry_id Record entry ID.
* @param int $user_id Record user ID.
*/
public function add( $title, $message, $types, $form_id = 0, $entry_id = 0, $user_id = 0 ) {
$this->repository->add( $title, $message, $types, $form_id, $entry_id, $user_id );
}
/**
* Create table for logs.
*
* @since 1.6.3
*/
public function create_table() {
$this->repository->create_table();
}
/**
* Get ListView.
*
* @since 1.6.3
*
* @return ListTable
*/
public function get_list_table() { // phpcs:ignore WPForms.PHP.HooksMethod.InvalidPlaceForAddingHooks
if ( ! $this->list_table ) {
$this->list_table = new ListTable( $this->repository );
add_action( 'admin_print_scripts', [ $this->list_table, 'popup_template' ] );
}
return $this->list_table;
}
/**
* Json config for detail information about log record.
*
* @since 1.6.3
*/
public function get_record() {
if (
! check_ajax_referer( 'wpforms-admin', 'nonce', false ) ||
! wpforms_current_user_can()
) {
wp_send_json_error( esc_html__( 'You do not have permission.', 'wpforms-lite' ) );
}
$id = filter_input( INPUT_GET, 'recordId', FILTER_VALIDATE_INT );
if ( ! $id ) {
wp_send_json_error( esc_html__( 'Record ID not found', 'wpforms-lite' ), 404 );
}
$item = $this->repository->record( $id );
if ( $item === null ) {
wp_send_json_error( esc_html__( 'No such record.', 'wpforms-lite' ), 404 );
}
wp_send_json_success(
[
'ID' => absint( $item->get_id() ),
'title' => esc_html( $item->get_title() ),
'message' => wp_kses( $item->get_message(), [ 'pre' => [] ] ),
'types' => esc_html( implode( ', ', $item->get_types( 'label' ) ) ),
'create_at' => esc_html( $item->get_date( 'full' ) ),
'form_id' => absint( $item->get_form_id() ),
'entry_id' => absint( $item->get_entry_id() ),
'user_id' => absint( $item->get_user_id() ),
'form_url' => admin_url( sprintf( 'admin.php?page=wpforms-builder&view=fields&form_id=%d', absint( $item->get_form_id() ) ) ),
'entry_url' => admin_url( sprintf( 'admin.php?page=wpforms-entries&view=details&entry_id=%d', absint( $item->get_entry_id() ) ) ),
'user_url' => esc_url( get_edit_user_link( $item->get_user_id() ) ),
]
);
}
}
Logger/Records.php 0000644 00000003702 15133255232 0010100 0 ustar 00 <?php
namespace WPForms\Logger;
use Iterator;
use Countable;
/**
* Class Records.
*
* @since 1.6.3
*/
class Records implements Countable, Iterator {
/**
* Iterator position.
*
* @since 1.6.3
*
* @var int
*/
private $iterator_position = 0;
/**
* List of log records.
*
* @since 1.6.3
*
* @var array
*/
private $list = [];
/**
* Return the current element.
*
* @since 1.6.3
*
* @return \WPForms\Logger\Record|null Return null when no items in collection.
*/
#[\ReturnTypeWillChange]
public function current() {
return $this->valid() ? $this->list[ $this->iterator_position ] : null;
}
/**
* Move forward to next element.
*
* @since 1.6.3
*/
#[\ReturnTypeWillChange]
public function next() {
++ $this->iterator_position;
}
/**
* Return the key of the current element.
*
* @since 1.6.3
*
* @return int
*/
#[\ReturnTypeWillChange]
public function key() {
return $this->iterator_position;
}
/**
* Checks if current position is valid.
*
* @since 1.6.3
*
* @return bool
*/
#[\ReturnTypeWillChange]
public function valid() {
return isset( $this->list[ $this->iterator_position ] );
}
/**
* Rewind the Iterator to the first element.
*
* @since 1.6.3
*/
#[\ReturnTypeWillChange]
public function rewind() {
$this->iterator_position = 0;
}
/**
* Count number of Record in a Queue.
*
* @since 1.6.3
*
* @return int
*/
#[\ReturnTypeWillChange]
public function count() {
return count( $this->list );
}
/**
* Push record to list.
*
* @since 1.6.3
*
* @param \WPForms\Logger\Record $record Record.
*/
#[\ReturnTypeWillChange]
public function push( $record ) {
if ( ! is_a( $record, '\WPForms\Logger\Record' ) ) {
return;
}
$this->list[] = $record;
}
/**
* Clear collection.
*
* @since 1.6.3
*/
#[\ReturnTypeWillChange]
public function clear() {
$this->list = [];
$this->iterator_position = 0;
}
}
Loader.php 0000644 00000015304 15133255232 0006467 0 ustar 00 <?php
namespace WPForms;
/**
* WPForms Class Loader.
*
* @since 1.5.8
*/
class Loader {
/**
* Classes to register.
*
* @since 1.5.8
*
* @var array
*/
private $classes = [];
/**
* Loader init.
*
* @since 1.5.8
*/
public function init() {
$this->populate_classes();
wpforms()->register_bulk( $this->classes );
}
/**
* Populate the classes to register.
*
* @since 1.5.8
*/
protected function populate_classes() {
$this->populate_admin();
$this->populate_forms_overview();
$this->populate_builder();
$this->populate_migrations();
$this->populate_capabilities();
$this->populate_tasks();
$this->populate_forms();
$this->populate_smart_tags();
$this->populate_logger();
$this->populate_education();
$this->populate_robots();
}
/**
* Populate the Forms related classes.
*
* @since 1.6.2
*/
private function populate_forms() {
$this->classes[] = [
'name' => 'Forms\Token',
'id' => 'token',
];
$this->classes[] = [
'name' => 'Forms\Honeypot',
'id' => 'honeypot',
];
$this->classes[] = [
'name' => 'Forms\Akismet',
'id' => 'akismet',
];
$this->classes[] = [
'name' => 'Forms\Submission',
'id' => 'submission',
'hook' => false,
'run' => false,
];
$this->classes[] = [
'name' => 'Forms\Locator',
'id' => 'locator',
];
}
/**
* Populate Admin related classes.
*
* @since 1.6.0
*/
private function populate_admin() {
array_push(
$this->classes,
[
'name' => 'Admin\Notice',
'id' => 'notice',
],
[
'name' => 'Admin\Revisions',
'id' => 'revisions',
'hook' => 'admin_init',
],
[
'name' => 'Admin\Addons\AddonsCache',
'id' => 'addons_cache',
],
[
'name' => 'Admin\Addons\Addons',
'id' => 'addons',
],
[
'name' => 'Admin\AdminBarMenu',
'hook' => 'init',
],
[
'name' => 'Admin\Notifications\Notifications',
'id' => 'notifications',
],
[
'name' => 'Admin\Notifications\EventDriven',
],
[
'name' => 'Admin\Entries\Edit',
'id' => 'entries_edit',
'hook' => 'admin_init',
],
[
'name' => 'Admin\Entries\Export\Export',
],
[
'name' => 'Admin\Challenge',
'id' => 'challenge',
],
[
'name' => 'Admin\FormEmbedWizard',
'hook' => 'admin_init',
],
[
'name' => 'Admin\SiteHealth',
'hook' => 'admin_init',
],
[
'name' => 'Admin\Settings\Captcha',
'hook' => 'admin_init',
],
[
'name' => 'Admin\Tools\Tools',
'hook' => 'current_screen',
],
[
'name' => 'Admin\Tools\Importers',
'hook' => 'admin_init',
'run' => 'load',
'condition' => wp_doing_ajax(),
],
[
'name' => 'Admin\Pages\Addons',
'id' => 'addons_page',
],
[
'name' => 'Admin\Pages\ConstantContact',
'hook' => 'admin_init',
],
[
'name' => 'Forms\Fields\Richtext\EntryViewContent',
]
);
}
/**
* Populate Forms Overview admin page related classes.
*
* @since 1.7.5
*/
private function populate_forms_overview() {
if ( ! wpforms_is_admin_page( 'overview' ) && ! wp_doing_ajax() ) {
return;
}
array_push(
$this->classes,
[
'name' => 'Admin\Forms\Ajax\Tags',
'id' => 'forms_tags_ajax',
],
[
'name' => 'Admin\Forms\Search',
'id' => 'forms_search',
],
[
'name' => 'Admin\Forms\Views',
'id' => 'forms_views',
],
[
'name' => 'Admin\Forms\BulkActions',
'id' => 'forms_bulk_actions',
],
[
'name' => 'Admin\Forms\Tags',
'id' => 'forms_tags',
]
);
}
/**
* Populate Form Builder related classes.
*
* @since 1.6.8
*/
private function populate_builder() {
array_push(
$this->classes,
[
'name' => 'Admin\Builder\Help',
'id' => 'builder_help',
],
[
'name' => 'Admin\Builder\Shortcuts',
],
[
'name' => 'Admin\Builder\TemplatesCache',
'id' => 'builder_templates_cache',
],
[
'name' => 'Admin\Builder\TemplateSingleCache',
'id' => 'builder_template_single',
],
[
'name' => 'Admin\Builder\Templates',
'id' => 'builder_templates',
]
);
}
/**
* Populate migration classes.
*
* @since 1.5.9
*/
private function populate_migrations() {
$this->classes[] = [
'name' => 'Migrations\Migrations',
'hook' => 'plugins_loaded',
];
}
/**
* Populate access management (capabilities) classes.
*
* @since 1.5.8
*/
private function populate_capabilities() {
array_push(
$this->classes,
[
'name' => 'Access\Capabilities',
'id' => 'access',
'hook' => 'plugins_loaded',
],
[
'name' => 'Access\Integrations',
],
[
'name' => 'Admin\Settings\Access',
'condition' => is_admin(),
]
);
}
/**
* Populate tasks related classes.
*
* @since 1.5.9
*/
private function populate_tasks() {
array_push(
$this->classes,
[
'name' => 'Tasks\Tasks',
'id' => 'tasks',
'hook' => 'init',
],
[
'name' => 'Tasks\Meta',
'id' => 'tasks_meta',
'hook' => false,
'run' => false,
]
);
}
/**
* Populate smart tags loaded classes.
*
* @since 1.6.7
*/
private function populate_smart_tags() {
array_push(
$this->classes,
[
'name' => 'SmartTags\SmartTags',
'id' => 'smart_tags',
'run' => 'hooks',
]
);
}
/**
* Populate logger loaded classes.
*
* @since 1.6.3
*/
private function populate_logger() {
array_push(
$this->classes,
[
'name' => 'Logger\Log',
'id' => 'log',
'hook' => false,
'run' => 'hooks',
]
);
}
/**
* Populate education related classes.
*
* @since 1.6.6
*/
private function populate_education() {
// Kill switch.
if ( ! (bool) apply_filters( 'wpforms_admin_education', true ) ) {
return;
}
// Education core classes.
array_push(
$this->classes,
[
'name' => 'Admin\Education\Core',
'id' => 'education',
],
[
'name' => 'Admin\Education\Fields',
'id' => 'education_fields',
]
);
// Education features classes.
$features = [
'LiteConnect',
'Builder\Captcha',
'Builder\Fields',
'Builder\Settings',
'Builder\Providers',
'Builder\Payments',
'Builder\DidYouKnow',
'Builder\Geolocation',
'Builder\Confirmations',
'Admin\DidYouKnow',
'Admin\Settings\Integrations',
'Admin\Settings\Geolocation',
'Admin\NoticeBar',
'Admin\Entries\Geolocation',
'Admin\Entries\UserJourney',
];
foreach ( $features as $feature ) {
$this->classes[] = [
'name' => 'Admin\Education\\' . $feature,
];
}
}
/**
* Populate robots loaded class.
*
* @since 1.7.0
*/
private function populate_robots() {
$this->classes[] = [
'name' => 'Robots',
'run' => 'hooks',
];
}
}
Access/Capabilities.php 0000644 00000002542 15133255232 0011053 0 ustar 00 <?php
namespace WPForms\Access;
/**
* Access/Capability management.
*
* @since 1.5.8
*/
class Capabilities {
/**
* Init class.
*
* @since 1.5.8
*/
public function init() {
}
/**
* Init conditions.
*
* @since 1.5.8.2
*/
public function init_allowed() {
return false;
}
/**
* Check permissions for currently logged in user.
*
* @since 1.5.8
*
* @param array|string $caps Capability name(s).
* @param int $id Optional. ID of the specific object to check against if capability is a "meta" cap.
* "Meta" capabilities, e.g. 'edit_post', 'edit_user', etc., are capabilities used
* by map_meta_cap() to map to other "primitive" capabilities, e.g. 'edit_posts',
* edit_others_posts', etc. Accessed via func_get_args() and passed to WP_User::has_cap(),
* then map_meta_cap().
*
* @return bool
*/
public function current_user_can( $caps = array(), $id = 0 ) {
return \current_user_can( \wpforms_get_capability_manage_options() );
}
/**
* Get a first valid capability from an array of capabilities.
*
* @since 1.5.8
*
* @param array $caps Array of capabilities to check.
*
* @return string
*/
public function get_menu_cap( $caps ) {
return \wpforms_get_capability_manage_options();
}
}
Lite/Integrations/LiteConnect/SendEntryTask.php 0000644 00000005633 15133255232 0015575 0 ustar 00 <?php
namespace WPForms\Lite\Integrations\LiteConnect;
use WPForms\Integrations\LiteConnect\API;
use WPForms\Tasks\Meta;
/**
* Class SendEntryTask.
*
* @since 1.7.4
*/
class SendEntryTask extends Integration {
/**
* Task name.
*
* @since 1.7.4
*
* @var string
*/
const LITE_CONNECT_TASK = 'wpforms_lite_connect_send_entry';
/**
* SendEntryTask constructor.
*
* @since 1.7.4
*/
public function __construct() {
parent::__construct();
$this->hooks();
}
/**
* Initialize the hooks.
*
* @since 1.7.4
*/
private function hooks() {
// Process the tasks as needed.
add_action( self::LITE_CONNECT_TASK, [ $this, 'process' ] );
}
/**
* Creates a task to submit the lite entry to the Lite Connect API via
* Action Scheduler.
*
* @since 1.7.4
*
* @param int $form_id The form ID.
* @param string $entry_data The entry data.
*/
public function create( $form_id, $entry_data ) {
$action_id = wpforms()->get( 'tasks' )
->create( self::LITE_CONNECT_TASK )
->params( $form_id, $entry_data )
->once( time() + wp_rand( 10, 60 ) * MINUTE_IN_SECONDS )
->register();
if ( is_null( $action_id ) ) {
wpforms_log(
'Lite Connect: error creating the AS task',
[
'task' => self::LITE_CONNECT_TASK,
],
[ 'type' => [ 'error' ] ]
);
}
}
/**
* Process the task to submit the entry to the Lite Connect API via
* Action Scheduler.
*
* @since 1.7.4
*
* @param int $meta_id The meta ID.
*/
public function process( $meta_id ) {
// Load task data.
$params = ( new Meta() )->get( (int) $meta_id );
list( $form_id, $entry_data ) = $params->data;
// Grab current access token. If site key or access token is not available, then it recreates the task to run later.
$access_token = $this->get_access_token( $this->get_site_key() );
if ( ! $access_token ) {
$this->create( $form_id, $entry_data );
return;
}
// Submit entry to the Lite Connect API.
$response = ( new API() )->add_form_entry( $access_token, $form_id, $entry_data );
if ( $response ) {
$response = json_decode( $response, true );
}
if ( isset( $response['error'] ) && $response['error'] === 'Access token is invalid or expired.' ) {
// Force to re-generate access token in case it is invalid.
$this->get_access_token( $this->get_site_key(), true );
}
if ( ! empty( $response['error'] ) ) {
wpforms_log(
'Lite Connect: error submitting form entry (AS task)',
[
'response' => $response,
'entry_data' => $entry_data,
],
[
'type' => [ 'error' ],
'form_id' => $form_id,
]
);
}
// Recreate the task if the request to the API fail for any reasons.
if ( ! isset( $response['status'] ) || $response['status'] !== 'success' ) {
$this->create( $form_id, $entry_data );
return;
}
// Increase the entries count if the entry has been added successfully.
$this->increase_entries_count();
}
}
Lite/Integrations/LiteConnect/LiteConnect.php 0000644 00000011665 15133255232 0015250 0 ustar 00 <?php
namespace WPForms\Lite\Integrations\LiteConnect;
/**
* Class LiteConnect for WPForms Lite.
*
* @since 1.7.4
*/
class LiteConnect extends \WPForms\Integrations\LiteConnect\LiteConnect {
/**
* The Integration object.
*
* @since 1.7.4
*
* @var Integration
*/
private $integration;
/**
* Send Entry Task object.
*
* @since 1.7.4
*
* @var SendEntryTask
*/
private $send_entry_task;
/**
* Whether Lite Connect is enabled.
*
* @since 1.7.4
*
* @return bool
*/
public static function is_enabled() {
// phpcs:disable WPForms.PHP.ValidateHooks.InvalidHookName
/**
* Determine whether LiteConnect is enabled on the WPForms > Settings admin page.
*
* @since 1.7.4
*
* @param bool $is_enabled Is LiteConnect enabled on WPForms > Settings page?
*/
return (bool) apply_filters( 'wpforms_lite_integrations_lite_connect_is_enabled', wpforms_setting( self::SETTINGS_SLUG ) );
// phpcs:enable WPForms.PHP.ValidateHooks.InvalidHookName
}
/**
* Load the integration.
*
* @since 1.7.4
*/
public function load() {
parent::load();
// Do not load if user doesn't have permissions to update settings.
if ( ! wpforms_current_user_can( wpforms_get_capability_manage_options() ) ) {
return;
}
// Hooks.
$this->hooks();
// Process any pending submissions to the API, even if the Lite Connect integration is disabled.
$this->send_entry_task = new SendEntryTask();
// It won't load if the Lite Connect integration is not enabled.
if ( ! self::is_enabled() ) {
return;
}
// We always need to instance the Integration class as part of the load process for the Lite Connect integration.
$this->integration = new Integration();
}
/**
* Hooks.
*
* @since 1.7.4
*/
private function hooks() {
// Add Lite Connect option to settings.
add_filter( 'wpforms_settings_defaults', [ $this, 'settings_option' ] );
// Automatically save the timestamp when Lite Connect was enabled first time.
add_filter( 'wpforms_update_settings', [ $this, 'update_enabled_settings' ] );
}
/**
* Add "Lite Connect: Enable Entry Backups" to the WPForms Lite settings.
*
* @since 1.7.4
*
* @param array $settings WPForms settings.
*
* @return array
*/
public function settings_option( $settings ) {
$setting = [
self::SETTINGS_SLUG => [
'id' => self::SETTINGS_SLUG,
'name' => esc_html__( 'Lite Connect', 'wpforms-lite' ),
'label' => esc_html__( 'Enable Entry Backups', 'wpforms-lite' ),
'type' => 'toggle',
'is-important' => true,
'control-class' => 'wpforms-setting-lite-connect-auto-save-toggle',
'input-attr' => 'disabled',
'desc-on' => sprintf(
wp_kses( /* translators: %s - Upgrade to WPForms PRO landing page URL. */
__( '<strong>Your form entries are not being stored locally, but are backed up remotely.</strong> If you <a href="%s" target="_blank" rel="noopener noreferrer" class="wpforms-upgrade-modal">upgrade to WPForms PRO</a>, you can restore your entries and they’ll be available in the WordPress dashboard.', 'wpforms-lite' ),
[
'a' => [
'href' => [],
'class' => [],
'target' => [],
'rel' => [],
],
'strong' => [],
]
),
esc_url( wpforms_admin_upgrade_link( 'settings-lite-connect-enabled' ) )
),
'desc-off' => sprintf(
wp_kses( /* translators: %s - Upgrade to WPForms PRO landing page URL. */
__( '<strong>Your form entries are not being stored in WordPress, and your entry backups are not active.</strong> If there\'s a problem with deliverability, you\'ll lose form entries. We recommend that you enable Entry Backups, especially if you\'re considering <a href="%s" target="_blank" rel="noopener noreferrer" class="wpforms-upgrade-modal">upgrading to WPForms PRO</a>.', 'wpforms-lite' ),
[
'a' => [
'href' => [],
'class' => [],
'target' => [],
'rel' => [],
],
'strong' => [],
]
),
esc_url( wpforms_admin_upgrade_link( 'settings-lite-connect-disabled' ) )
),
],
];
$settings['general'] = wpforms_list_insert_after( $settings['general'], 'license-key', $setting );
return $settings;
}
/**
* Automatically save the additional info when Lite Connect was enabled first time.
*
* @since 1.7.4
*
* @param array $settings WPForms settings.
*
* @return array
*/
public function update_enabled_settings( $settings ) {
if ( empty( $settings[ self::SETTINGS_SLUG ] ) ) {
return $settings;
}
$since = self::SETTINGS_SLUG . '-since';
$email = self::SETTINGS_SLUG . '-email';
if ( empty( $settings[ $since ] ) ) {
$settings[ $since ] = time();
}
if ( empty( $settings[ $email ] ) ) {
$user = wp_get_current_user();
$settings[ $email ] = $user && ! empty( $user->user_email ) ? $user->user_email : get_option( 'admin_email' );
}
return $settings;
}
}
Lite/Integrations/LiteConnect/Integration.php 0000644 00000005475 15133255232 0015326 0 ustar 00 <?php
namespace WPForms\Lite\Integrations\LiteConnect;
use WPForms\Helpers\Crypto;
/**
* Integration between Lite Connect API and WPForms Lite.
*
* @since 1.7.4
*/
class Integration extends \WPForms\Integrations\LiteConnect\Integration {
/**
* Encrypt the form entry and submit it to the Lite Connect API.
*
* If the regular wp_remote_post() request fail for any reasons, then an
* Action Scheduler task will be created to retry a couple of minutes later.
*
* @since 1.7.4
*
* @param array $entry_args The entry data.
* @param array $form_data The form data.
*
* @return false|string
*/
public function submit( $entry_args, $form_data ) {
if ( ! is_array( $entry_args ) ) {
return false;
}
$entry_args['form_data'] = $form_data;
// Encrypt entry using the WPForms Crypto class.
$entry_data = Crypto::encrypt( wp_json_encode( $entry_args ) );
// We have to start requesting site keys in ajax, turning on the LC functionality.
// First, the request to the API server will be sent.
// Second, the server will respond to our callback URL /wpforms/auth/key/nonce, and the site key will be stored in the DB.
// Third, we have to get access via a separate HTTP request.
$this->update_keys(); // Third request here.
// Submit entry to the Lite Connect API.
$response = $this->add_form_entry( $this->auth['access_token'], $entry_args['form_id'], $entry_data );
// Confirm if entry has been added successfully to the Lite Connect API.
if ( $response ) {
$response = json_decode( $response, true );
}
if ( isset( $response['error'] ) && $response['error'] === 'Access token is invalid or expired.' ) {
// Force to re-generate access token in case it is invalid.
$this->get_access_token( $this->get_site_key(), true );
}
if ( ! isset( $response['status'] ) || $response['status'] !== 'success' ) {
/**
* If Lite Connect API is not available in the add_form_entry()
* request above, then a task is created to run it later via Action
* Scheduler.
*/
( new SendEntryTask() )->create( $entry_args['form_id'], $entry_data );
}
// Increase the entries count if the entry has been added successfully.
if ( isset( $response['status'] ) && $response['status'] === 'success' ) {
$this->increase_entries_count();
}
if ( ! empty( $response['error'] ) ) {
wpforms_log(
'Lite Connect: error submitting form entry',
[
'response' => $response,
'entry_args' => $entry_args,
],
[
'type' => [ 'error' ],
'form_id' => $entry_args['form_id'],
]
);
}
return $response;
}
/**
* Increases the Lite Connect entries count.
*
* @since 1.7.4
*/
public function increase_entries_count() {
self::maybe_set_entries_count();
update_option( self::LITE_CONNECT_ENTRIES_COUNT_OPTION, self::get_entries_count() + 1 );
}
}
Lite/Reports/EntriesCount.php 0000644 00000002124 15133255232 0012232 0 ustar 00 <?php
namespace WPForms\Lite\Reports;
/**
* Generate form submissions reports.
*
* @since 1.5.4
*/
class EntriesCount {
/**
* Constructor.
*
* @since 1.5.4
*/
public function __construct() {}
/**
* Get entries count grouped by form.
* Main point of entry to fetch form entry count data from DB.
* Cache the result.
*
* @since 1.5.4
*
* @return array
*/
public function get_by_form() {
$forms = \wpforms()->form->get( '', array( 'fields' => 'ids' ) );
if ( empty( $forms ) || ! \is_array( $forms ) ) {
return array();
}
$result = array();
foreach ( $forms as $form_id ) {
$count = \absint( \get_post_meta( $form_id, 'wpforms_entries_count', true ) );
if ( empty( $count ) ) {
continue;
}
$result[ $form_id ] = array(
'form_id' => $form_id,
'count' => $count,
'title' => \get_the_title( $form_id ),
);
}
if ( ! empty( $result ) ) {
// Sort forms by entries count (desc).
\uasort(
$result,
function ( $a, $b ) {
return ( $a['count'] > $b['count'] ) ? -1 : 1;
}
);
}
return $result;
}
}
Lite/Admin/ConnectSkin.php 0000644 00000001576 15133255232 0011432 0 ustar 00 <?php
namespace WPForms\Lite\Admin;
use WPForms\Helpers\PluginSilentUpgraderSkin;
/**
* WPForms Connect Skin.
*
* WPForms Connect is our service that makes it easy for non-techy users to
* upgrade to WPForms Pro without having to manually install WPForms Pro plugin.
*
* @since 1.5.5
* @since 1.5.6.1 Extend PluginSilentUpgraderSkin and clean up the class.
*/
class ConnectSkin extends PluginSilentUpgraderSkin {
/**
* Instead of outputting HTML for errors, json_encode the errors and send them
* back to the Ajax script for processing.
*
* @since 1.5.5
*
* @param array $errors Array of errors with the install process.
*/
public function error( $errors ) {
if ( ! empty( $errors ) ) {
echo \wp_json_encode(
array(
'error' => \esc_html__( 'There was an error installing WPForms Pro. Please try again.', 'wpforms-lite' ),
)
);
die;
}
}
}
Lite/Admin/Settings/Access.php 0000644 00000014254 15133255232 0012212 0 ustar 00 <?php
namespace WPForms\Lite\Admin\Settings;
/**
* Settings Access tab.
*
* @since 1.5.8
*/
class Access {
/**
* View slug.
*
* @since 1.5.8
*
* @var string
*/
const SLUG = 'access';
/**
* Constructor.
*
* @since 1.5.8
*/
public function __construct() {
$this->hooks();
}
/**
* Hooks.
*
* @since 1.5.8
*/
public function hooks() {
add_action( 'admin_enqueue_scripts', array( $this, 'enqueues' ) );
add_filter( 'wpforms_settings_tabs', array( $this, 'add_tab' ) );
add_filter( 'wpforms_settings_defaults', array( $this, 'add_section' ) );
}
/**
* Enqueues.
*
* @since 1.5.8
*/
public function enqueues() {
if ( ! wpforms_is_admin_page( 'settings', self::SLUG ) ) {
return;
}
// Lity.
wp_enqueue_style(
'wpforms-lity',
WPFORMS_PLUGIN_URL . 'assets/lib/lity/lity.min.css',
null,
'3.0.0'
);
wp_enqueue_script(
'wpforms-lity',
WPFORMS_PLUGIN_URL . 'assets/lib/lity/lity.min.js',
array( 'jquery' ),
'3.0.0',
true
);
}
/**
* Add Access tab.
*
* @since 1.5.8
*
* @param array $tabs Array of tabs.
*
* @return array Array of tabs.
*/
public function add_tab( $tabs ) {
$tab = array(
self::SLUG => array(
'name' => esc_html__( 'Access', 'wpforms-lite' ),
'form' => false,
'submit' => false,
),
);
return wpforms_list_insert_after( $tabs, 'geolocation', $tab );
}
/**
* Add Access settings section.
*
* @since 1.5.8
*
* @param array $settings Settings sections.
*
* @return array
*/
public function add_section( $settings ) {
$section_rows = array(
'heading',
'screenshots',
'caps',
'upgrade_to_pro',
);
foreach ( $section_rows as $section_row ) {
$settings[ self::SLUG ][ self::SLUG . '-' . $section_row ] = array(
'id' => self::SLUG . '-' . $section_row,
'content' => method_exists( $this, 'output_section_row_' . $section_row ) ? $this->{ 'output_section_row_' . $section_row }() : '',
'type' => 'content',
'no_label' => true,
'class' => array( $section_row ),
);
}
return $settings;
}
/**
* Generate and output section "Heading" row HTML.
*
* @since 1.5.8
*/
public function output_section_row_heading() {
return sprintf(
'<h4>%1$s<img src="%2$s" alt="%3$s"></h4><p>%4$s</p><p>%5$s</p>',
esc_html__( 'Access Controls', 'wpforms-lite' ),
esc_url( WPFORMS_PLUGIN_URL . 'assets/images/lite-settings-access/pro-plus.svg' ),
esc_attr__( 'Pro+', 'wpforms-lite' ),
esc_html__( 'Access controls allows you to manage and customize access to WPForms functionality.', 'wpforms-lite' ),
esc_html__( 'You can easily grant or restrict access using the simple built-in controls, or use our official integrations with Members and User Role Editor plugins.', 'wpforms-lite' )
);
}
/**
* Generate and output section "Screenshots" row HTML.
*
* @since 1.5.8
*/
public function output_section_row_screenshots() {
$format = '<div class="cont">
<img src="%1$s" srcset="%2$s 2x" alt="%6$s"/>
<a href="%3$s" class="hover" data-lity data-lity-srcset="%4$s 2x" data-lity-desc="%6$s"></a>
<span>%5$s</span>
</div>';
$images_url = WPFORMS_PLUGIN_URL . 'assets/images/lite-settings-access/';
$content = sprintf(
$format,
esc_url( $images_url . 'thumbnail-access-controls.png' ),
esc_url( $images_url . 'thumbnail-access-controls@2x.png' ),
esc_url( $images_url . 'screenshot-access-controls.png' ),
esc_url( $images_url . 'screenshot-access-controls@2x.png' ),
esc_html__( 'Simple Built-in Controls', 'wpforms-lite' ),
esc_attr( esc_html__( 'Simple Built-in Controls', 'wpforms-lite' ) )
);
$content .= sprintf(
$format,
esc_url( $images_url . 'thumbnail-members.png' ),
esc_url( $images_url . 'thumbnail-members@2x.png' ),
esc_url( $images_url . 'screenshot-members.png' ),
esc_url( $images_url . 'screenshot-members@2x.png' ),
esc_html__( 'Members Integration', 'wpforms-lite' ),
esc_attr( esc_html__( 'Members Integration', 'wpforms-lite' ) )
);
$content .= sprintf(
$format,
esc_url( $images_url . 'thumbnail-user-role-editor.png' ),
esc_url( $images_url . 'thumbnail-user-role-editor@2x.png' ),
esc_url( $images_url . 'screenshot-user-role-editor.png' ),
esc_url( $images_url . 'screenshot-user-role-editor@2x.png' ),
esc_html__( 'User Role Editor Integration', 'wpforms-lite' ),
esc_attr( esc_html__( 'User Role Editor Integration', 'wpforms-lite' ) )
);
return $content;
}
/**
* Generate and output section "Capabilities" row HTML.
*
* @since 1.5.8
*/
public function output_section_row_caps() {
$caps = array(
array(
esc_html__( 'Create Forms', 'wpforms-lite' ),
esc_html__( 'Edit Forms', 'wpforms-lite' ),
esc_html__( 'Edit Others Forms', 'wpforms-lite' ),
esc_html__( 'View Forms', 'wpforms-lite' ),
esc_html__( 'View Others Forms', 'wpforms-lite' ),
),
array(
esc_html__( 'Delete Forms', 'wpforms-lite' ),
esc_html__( 'Delete Others Forms', 'wpforms-lite' ),
esc_html__( 'View Forms Entries', 'wpforms-lite' ),
esc_html__( 'View Others Forms Entries', 'wpforms-lite' ),
),
array(
esc_html__( 'Edit Forms Entries', 'wpforms-lite' ),
esc_html__( 'Edit Others Forms Entries', 'wpforms-lite' ),
esc_html__( 'Delete Forms Entries', 'wpforms-lite' ),
esc_html__( 'Delete Others Forms Entries', 'wpforms-lite' ),
),
);
$content = '<p>' . esc_html__( 'Custom access to the following capabilities…', 'wpforms-lite' ) . '</p>';
foreach ( $caps as $column ) {
$content .= '<ul>';
foreach ( $column as $cap ) {
$content .= '<li>' . $cap . '</li>';
}
$content .= '</ul>';
}
return $content;
}
/**
* Generate and output section "Upgrade to Pro" row HTML.
*
* @since 1.5.8
*/
public function output_section_row_upgrade_to_pro() {
$content = sprintf(
'<a href="%1$s" target="_blank" rel="noopener noreferrer" class="wpforms-upgrade-modal wpforms-btn wpforms-btn-lg wpforms-btn-orange">%2$s</a>',
esc_url( 'https://wpforms.com/lite-upgrade/?discount=LITEUPGRADE&utm_source=WordPress&utm_medium=settings-license&utm_campaign=liteplugin' ),
esc_html__( 'Upgrade to WPForms Pro', 'wpforms-lite' )
);
return $content;
}
}
Lite/Admin/DashboardWidget.php 0000644 00000037143 15133255232 0012246 0 ustar 00 <?php
namespace WPForms\Lite\Admin;
use WPForms\Admin\Dashboard\Widget;
/**
* Dashboard Widget shows a chart and the form entries stats in WP Dashboard.
*
* @since 1.5.0
*/
class DashboardWidget extends Widget {
/**
* Widget settings.
*
* @since 1.5.0
*
* @var array
*/
public $settings;
/**
* Constructor.
*
* @since 1.5.0
*/
public function __construct() {
add_action( 'admin_init', array( $this, 'init' ) );
}
/**
* Init class.
*
* @since 1.5.5
*/
public function init() {
// This widget should be displayed for certain high-level users only.
if ( ! wpforms_current_user_can() ) {
return;
}
global $pagenow;
// phpcs:disable WordPress.Security.NonceVerification.Recommended
$is_admin_page = $pagenow === 'index.php' && empty( $_GET['page'] );
$is_ajax_request = wp_doing_ajax() && isset( $_REQUEST['action'] ) && strpos( sanitize_key( $_REQUEST['action'] ), 'wpforms_dash_widget' ) !== false;
// phpcs:enable WordPress.Security.NonceVerification.Recommended
if ( ! $is_admin_page && ! $is_ajax_request ) {
return;
}
if ( ! apply_filters( 'wpforms_admin_dashboardwidget', true ) ) {
return;
}
$this->settings();
$this->hooks();
}
/**
* Filterable widget settings.
*
* @since 1.5.0
*/
public function settings() {
// phpcs:disable WPForms.Comments.PHPDocHooks.RequiredHookDocumentation, WPForms.PHP.ValidateHooks.InvalidHookName
$this->settings = [
// Number of forms to display in the forms list before "Show More" button appears.
'forms_list_number_to_display' => apply_filters( 'wpforms_dash_widget_forms_list_number_to_display', 5 ),
// Allow results caching to reduce DB load.
'allow_data_caching' => apply_filters( 'wpforms_dash_widget_allow_data_caching', true ),
// Transient lifetime in seconds. Defaults to the end of a current day.
'transient_lifetime' => apply_filters( 'wpforms_dash_widget_transient_lifetime', strtotime( 'tomorrow' ) - time() ),
// Determine if the forms with no entries should appear in a forms list. Once switched, the effect applies after cache expiration.
'display_forms_list_empty_entries' => apply_filters( 'wpforms_dash_widget_display_forms_list_empty_entries', true ),
];
// phpcs:enable WPForms.Comments.PHPDocHooks.RequiredHookDocumentation, WPForms.PHP.ValidateHooks.InvalidHookName
}
/**
* Widget hooks.
*
* @since 1.5.0
*/
public function hooks() {
$widget_slug = static::SLUG;
add_action( 'admin_enqueue_scripts', [ $this, 'widget_scripts' ] );
add_action( 'wp_dashboard_setup', [ $this, 'widget_register' ] );
add_action( 'admin_init', [ $this, 'hide_widget' ] );
add_action( "wp_ajax_wpforms_{$widget_slug}_save_widget_meta", [ $this, 'save_widget_meta_ajax' ] );
add_action( 'wpforms_create_form', [ static::class, 'clear_widget_cache' ] );
add_action( 'wpforms_save_form', [ static::class, 'clear_widget_cache' ] );
add_action( 'wpforms_delete_form', [ static::class, 'clear_widget_cache' ] );
add_action( 'wpforms_process_entry_save', [ static::class, 'clear_widget_cache' ] );
}
/**
* Load widget-specific scripts.
*
* @since 1.5.0
*
* @param string $hook_suffix The current admin page.
*/
public function widget_scripts( $hook_suffix ) {
if ( $hook_suffix !== 'index.php' ) {
return;
}
$min = wpforms_get_min_suffix();
wp_enqueue_style(
'wpforms-dashboard-widget',
WPFORMS_PLUGIN_URL . "assets/css/dashboard-widget{$min}.css",
[],
WPFORMS_VERSION
);
wp_enqueue_script(
'wpforms-moment',
WPFORMS_PLUGIN_URL . 'assets/lib/moment/moment.min.js',
[],
'2.22.2',
true
);
wp_enqueue_script(
'wpforms-chart',
WPFORMS_PLUGIN_URL . 'assets/lib/chart.min.js',
[ 'wpforms-moment' ],
'2.7.2',
true
);
wp_enqueue_script(
'wpforms-dashboard-widget',
WPFORMS_PLUGIN_URL . "assets/lite/js/admin/dashboard-widget{$min}.js",
[ 'jquery', 'wpforms-chart' ],
WPFORMS_VERSION,
true
);
wp_localize_script(
'wpforms-dashboard-widget',
'wpforms_dashboard_widget',
[
'nonce' => wp_create_nonce( 'wpforms_' . static::SLUG . '_nonce' ),
'slug' => static::SLUG,
'show_more_html' => esc_html__( 'Show More', 'wpforms-lite' ) . '<span class="dashicons dashicons-arrow-down"></span>',
'show_less_html' => esc_html__( 'Show Less', 'wpforms-lite' ) . '<span class="dashicons dashicons-arrow-up"></span>',
'i18n' => [
'entries' => esc_html__( 'Entries', 'wpforms-lite' ),
],
]
);
}
/**
* Register the widget.
*
* @since 1.5.0
*/
public function widget_register() {
global $wp_meta_boxes;
$widget_key = 'wpforms_reports_widget_lite';
\wp_add_dashboard_widget(
$widget_key,
\esc_html__( 'WPForms', 'wpforms-lite' ),
array( $this, 'widget_content' )
);
// Attempt to place the widget at the top.
$normal_dashboard = $wp_meta_boxes['dashboard']['normal']['core'];
$widget_instance = array( $widget_key => $normal_dashboard[ $widget_key ] );
unset( $normal_dashboard[ $widget_key ] );
$sorted_dashboard = \array_merge( $widget_instance, $normal_dashboard );
$wp_meta_boxes['dashboard']['normal']['core'] = $sorted_dashboard;
}
/**
* Load widget content.
*
* @since 1.5.0
*/
public function widget_content() {
$forms = wpforms()->get( 'form' )->get( '', [ 'fields' => 'ids' ] );
$hide_graph = (bool) $this->widget_meta( 'get', 'hide_graph' );
$no_graph_class = $hide_graph ? 'wpforms-dash-widget-no-graph' : '';
echo '<div class="wpforms-dash-widget wpforms-lite ' . esc_attr( $no_graph_class ) . '">';
if ( empty( $forms ) ) {
$this->widget_content_no_forms_html();
} else {
$this->widget_content_html( $hide_graph );
}
$plugin = $this->get_recommended_plugin();
$hide_recommended = $this->widget_meta( 'get', 'hide_recommended_block' );
if (
! empty( $plugin ) &&
! empty( $forms ) &&
! $hide_recommended
) {
$this->recommended_plugin_block_html( $plugin );
}
echo '</div><!-- .wpforms-dash-widget -->';
}
/**
* Widget content HTML if a user has no forms.
*
* @since 1.5.0
*/
public function widget_content_no_forms_html() {
$create_form_url = \add_query_arg( 'page', 'wpforms-builder', \admin_url( 'admin.php' ) );
$learn_more_url = 'https://wpforms.com/docs/creating-first-form/?utm_source=WordPress&utm_medium=link&utm_campaign=liteplugin&utm_content=dashboardwidget';
?>
<div class="wpforms-dash-widget-block wpforms-dash-widget-block-no-forms">
<img class="wpforms-dash-widget-block-sullie-logo" src="<?php echo \esc_url( WPFORMS_PLUGIN_URL . 'assets/images/sullie.png' ); ?>" alt="<?php \esc_attr_e( 'Sullie the WPForms mascot', 'wpforms-lite' ); ?>">
<h2><?php \esc_html_e( 'Create Your First Form to Start Collecting Leads', 'wpforms-lite' ); ?></h2>
<p><?php \esc_html_e( 'You can use WPForms to build contact forms, surveys, payment forms, and more with just a few clicks.', 'wpforms-lite' ); ?></p>
<?php if ( wpforms_current_user_can( 'create_forms' ) ) : ?>
<a href="<?php echo \esc_url( $create_form_url ); ?>" class="button button-primary">
<?php \esc_html_e( 'Create Your Form', 'wpforms-lite' ); ?>
</a>
<?php endif; ?>
<a href="<?php echo \esc_url( $learn_more_url ); ?>" class="button" target="_blank" rel="noopener noreferrer">
<?php \esc_html_e( 'Learn More', 'wpforms-lite' ); ?>
</a>
</div>
<?php
}
/**
* Widget content HTML.
*
* @since 1.5.0
* @since 1.7.4 Added hide graph parameter.
*
* @param bool $hide_graph Is graph hidden.
*/
public function widget_content_html( $hide_graph = false ) {
// phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped
/**
* Filters the content before the Dashboard Widget Chart block container (for Lite).
*
* @since 1.7.4
*
* @param string $chart_block_before Chart block before markup.
*/
echo apply_filters( 'wpforms_lite_admin_dashboard_widget_content_html_chart_block_before', '' );
// phpcs:enable WordPress.Security.EscapeOutput.OutputNotEscaped
if ( ! $hide_graph ) :
?>
<div class="wpforms-dash-widget-chart-block-container">
<div class="wpforms-dash-widget-block wpforms-dash-widget-chart-block">
<canvas id="wpforms-dash-widget-chart" width="400" height="300"></canvas>
</div>
<div class="wpforms-dash-widget-block-upgrade">
<div class="wpforms-dash-widget-modal">
<a href="#" class="wpforms-dash-widget-dismiss-chart-upgrade">
<span class="dashicons dashicons-no-alt"></span>
</a>
<h2><?php esc_html_e( 'View all Form Entries inside the WordPress Dashboard', 'wpforms-lite' ); ?></h2>
<p><?php esc_html_e( 'Form entries reports are not available.', 'wpforms-lite' ); ?>
<?php esc_html_e( 'Form entries are not stored in Lite.', 'wpforms-lite' ); ?>
<?php esc_html_e( 'Upgrade to Pro and get access to the reports.', 'wpforms-lite' ); ?></p>
<p>
<a href="<?php echo esc_url( wpforms_admin_upgrade_link( 'dashboard-widget', 'upgrade-to-pro' ) ); ?>" class="wpforms-dash-widget-upgrade-btn" target="_blank" rel="noopener noreferrer">
<?php esc_html_e( 'Upgrade to WPForms Pro', 'wpforms-lite' ); ?>
</a>
</p>
</div>
</div>
</div>
<?php endif; ?>
<div class="wpforms-dash-widget-block wpforms-dash-widget-block-title">
<h3><?php \esc_html_e( 'Total Entries by Form', 'wpforms-lite' ); ?></h3>
<div class="wpforms-dash-widget-settings">
<?php
$this->timespan_select_html( 0, false );
$this->widget_settings_html( false );
?>
</div>
</div>
<div id="wpforms-dash-widget-forms-list-block" class="wpforms-dash-widget-block wpforms-dash-widget-forms-list-block">
<?php $this->forms_list_block(); ?>
</div>
<?php
}
/**
* Forms list block.
*
* @since 1.5.0
*/
public function forms_list_block() {
$forms = $this->get_entries_count_by_form();
if ( empty( $forms ) ) {
$this->forms_list_block_empty_html();
} else {
$this->forms_list_block_html( $forms );
}
}
/**
* Empty forms list block HTML.
*
* @since 1.5.0
*/
public function forms_list_block_empty_html() {
?>
<p class="wpforms-error wpforms-error-no-data-forms-list">
<?php \esc_html_e( 'No entries were submitted yet.', 'wpforms-lite' ); ?>
</p>
<?php
}
/**
* Forms list block HTML.
*
* @since 1.5.0
*
* @param array $forms Forms to display in the list.
*/
public function forms_list_block_html( $forms ) {
// Number of forms to display in the forms list before "Show More" button appears.
$show_forms = $this->settings['forms_list_number_to_display'];
?>
<table id="wpforms-dash-widget-forms-list-table" cellspacing="0">
<?php foreach ( \array_values( $forms ) as $key => $form ) : ?>
<tr <?php echo $key >= $show_forms ? 'class="wpforms-dash-widget-forms-list-hidden-el"' : ''; ?> data-form-id="<?php echo \absint( $form['form_id'] ); ?>">
<td><span class="wpforms-dash-widget-form-title"><?php echo \esc_html( $form['title'] ); ?></span></td>
<td><?php echo \absint( $form['count'] ); ?></td>
</tr>
<?php endforeach; ?>
</table>
<?php if ( \count( $forms ) > $show_forms ) : ?>
<button type="button" id="wpforms-dash-widget-forms-more" class="wpforms-dash-widget-forms-more" title="<?php \esc_html_e( 'Show all forms', 'wpforms-lite' ); ?>">
<?php \esc_html_e( 'Show More', 'wpforms-lite' ); ?> <span class="dashicons dashicons-arrow-down"></span>
</button>
<?php endif; ?>
<?php
}
/**
* Recommended plugin block HTML.
*
* @since 1.5.0
* @since 1.7.3 Added plugin parameter.
*
* @param array $plugin Plugin data.
*/
public function recommended_plugin_block_html( $plugin = [] ) {
if ( ! $plugin ) {
return;
}
$install_url = wp_nonce_url(
self_admin_url( 'update.php?action=install-plugin&plugin=' . rawurlencode( $plugin['slug'] ) ),
'install-plugin_' . $plugin['slug']
);
?>
<div class="wpforms-dash-widget-recommended-plugin-block">
<p><?php esc_html_e( 'Recommended Plugin:', 'wpforms-lite' ); ?>
<strong><?php echo esc_html( $plugin['name'] ); ?></strong> -
<?php if ( wpforms_can_install( 'plugin' ) ) { ?>
<a href="<?php echo esc_url( $install_url ); ?>"><?php esc_html_e( 'Install', 'wpforms-lite' ); ?></a> |
<?php } ?>
<a href="<?php echo esc_url( $plugin['more'] ); ?>?utm_source=wpformsplugin&utm_medium=link&utm_campaign=wpformsdashboardwidget"><?php esc_html_e( 'Learn More', 'wpforms-lite' ); ?></a></p>
<button type="button" id="wpforms-dash-widget-dismiss-recommended-plugin-block" class="wpforms-dash-widget-dismiss-recommended-plugin-block" title="<?php esc_html_e( 'Dismiss recommended plugin', 'wpforms-lite' ); ?>">
<span class="dashicons dashicons-no-alt"></span>
</button>
</div>
<?php
}
/**
* Get entries count grouped by form.
* Main point of entry to fetch form entry count data from DB.
* Cache the result.
*
* @since 1.5.0
*
* @return array
*/
public function get_entries_count_by_form() {
// Allow results caching to reduce DB load.
$allow_caching = $this->settings['allow_data_caching'];
if ( $allow_caching ) {
$transient_name = 'wpforms_dash_widget_lite_entries_by_form';
$cache = \get_transient( $transient_name );
// Filter the cache to clear or alter its data.
$cache = \apply_filters( 'wpforms_dash_widget_lite_cached_data', $cache );
}
// is_array() detects cached empty searches.
if ( $allow_caching && \is_array( $cache ) ) {
return $cache;
}
$forms = \wpforms()->form->get( '', array( 'fields' => 'ids' ) );
if ( empty( $forms ) || ! \is_array( $forms ) ) {
return array();
}
$result = array();
foreach ( $forms as $form_id ) {
$count = \absint( \get_post_meta( $form_id, 'wpforms_entries_count', true ) );
if ( empty( $count ) && empty( $this->settings['display_forms_list_empty_entries'] ) ) {
continue;
}
$result[ $form_id ] = array(
'form_id' => $form_id,
'count' => $count,
'title' => \get_the_title( $form_id ),
);
}
if ( ! empty( $result ) ) {
// Sort forms by entries count (desc).
\uasort( $result, function ( $a, $b ) {
return ( $a['count'] > $b['count'] ) ? - 1 : 1;
} );
}
if ( $allow_caching ) {
// Transient lifetime in seconds. Defaults to the end of a current day.
$transient_lifetime = $this->settings['transient_lifetime'];
\set_transient( $transient_name, $result, $transient_lifetime );
}
return $result;
}
/**
* Hide dashboard widget.
* Use dashboard screen options to make it visible again.
*
* @since 1.5.0
*/
public function hide_widget() {
if ( ! \is_admin() || ! \is_user_logged_in() ) {
return;
}
if ( ! isset( $_GET['wpforms-nonce'] ) || ! \wp_verify_nonce( \sanitize_key( \wp_unslash( $_GET['wpforms-nonce'] ) ), 'wpforms_hide_dash_widget' ) ) {
return;
}
if ( ! isset( $_GET['wpforms-widget'] ) || 'hide' !== $_GET['wpforms-widget'] ) {
return;
}
$user_id = \get_current_user_id();
$metaboxhidden = \get_user_meta( $user_id, 'metaboxhidden_dashboard', true );
if ( ! \is_array( $metaboxhidden ) ) {
\update_user_meta( $user_id, 'metaboxhidden_dashboard', array( 'wpforms_reports_widget_lite' ) );
}
if ( \is_array( $metaboxhidden ) && ! \in_array( 'wpforms_reports_widget_lite', $metaboxhidden, true ) ) {
$metaboxhidden[] = 'wpforms_reports_widget_lite';
\update_user_meta( $user_id, 'metaboxhidden_dashboard', $metaboxhidden );
}
$redirect_url = \remove_query_arg( array( 'wpforms-widget', 'wpforms-nonce' ) );
\wp_safe_redirect( $redirect_url );
exit();
}
/**
* Clear dashboard widget cached data.
*
* @since 1.5.2
*/
public static function clear_widget_cache() {
delete_transient( 'wpforms_dash_widget_lite_entries_by_form' );
}
}
Lite/Admin/Connect.php 0000644 00000016061 15133255232 0010600 0 ustar 00 <?php
namespace WPForms\Lite\Admin;
use WP_Error;
use WPForms\Helpers\PluginSilentUpgrader;
/**
* WPForms Connect.
*
* WPForms Connect is our service that makes it easy for non-techy users to
* upgrade to WPForms Pro without having to manually install WPForms Pro plugin.
*
* @since 1.5.5
*/
class Connect {
/**
* Constructor.
*
* @since 1.5.5
*/
public function __construct() {
$this->hooks();
}
/**
* Hooks.
*
* @since 1.5.5
*/
public function hooks() {
\add_action( 'wpforms_settings_enqueue', array( $this, 'settings_enqueues' ) );
\add_action( 'wp_ajax_wpforms_connect_url', array( $this, 'generate_url' ) );
\add_action( 'wp_ajax_nopriv_wpforms_connect_process', array( $this, 'process' ) );
}
/**
* Settings page enqueues.
*
* @since 1.5.5
*/
public function settings_enqueues() {
$min = \wpforms_get_min_suffix();
\wp_enqueue_script(
'wpforms-connect',
\WPFORMS_PLUGIN_URL . "assets/lite/js/admin/connect{$min}.js",
array( 'jquery' ),
\WPFORMS_VERSION,
true
);
}
/**
* Generate and return WPForms Connect URL.
*
* @since 1.5.5
*/
public function generate_url() {
// Run a security check.
\check_ajax_referer( 'wpforms-admin', 'nonce' );
// Check for permissions.
if ( ! \current_user_can( 'install_plugins' ) ) {
\wp_send_json_error( [ 'message' => \esc_html__( 'You are not allowed to install plugins.', 'wpforms-lite' ) ] );
}
$key = ! empty( $_POST['key'] ) ? \sanitize_text_field( \wp_unslash( $_POST['key'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification
if ( empty( $key ) ) {
\wp_send_json_error( [ 'message' => \esc_html__( 'Please enter your license key to connect.', 'wpforms-lite' ) ] );
}
if ( wpforms()->is_pro() ) {
\wp_send_json_error( [ 'message' => \esc_html__( 'Only the Lite version can be upgraded.', 'wpforms-lite' ) ] );
}
// Verify pro version is not installed.
$active = \activate_plugin( 'wpforms/wpforms.php', false, false, true );
if ( ! \is_wp_error( $active ) ) {
// Deactivate Lite.
$plugin = \plugin_basename( WPFORMS_PLUGIN_FILE );
\deactivate_plugins( $plugin );
do_action( 'wpforms_plugin_deactivated', $plugin );
\wp_send_json_success(
[
'message' => \esc_html__( 'WPForms Pro is installed but not activated.', 'wpforms-lite' ),
'reload' => true,
]
);
}
// Generate URL.
$oth = hash( 'sha512', \wp_rand() );
\update_option( 'wpforms_connect_token', $oth );
\update_option( 'wpforms_connect', $key );
$version = WPFORMS_VERSION;
$endpoint = \admin_url( 'admin-ajax.php' );
$redirect = \admin_url( 'admin.php?page=wpforms-settings' );
$url = \add_query_arg(
[
'key' => $key,
'oth' => $oth,
'endpoint' => $endpoint,
'version' => $version,
'siteurl' => \admin_url(),
'homeurl' => \home_url(),
'redirect' => rawurldecode( base64_encode( $redirect ) ), // phpcs:ignore
'v' => 2,
],
'https://upgrade.wpforms.com'
);
\wp_send_json_success(
[
'url' => $url,
'back_url' => \add_query_arg(
[
'action' => 'wpforms_connect',
'oth' => $oth,
],
$endpoint
),
]
);
}
/**
* Process WPForms Connect.
*
* @since 1.5.5
*/
public function process() {
$error = esc_html__( 'There was an error while installing an upgrade. Please download the plugin from wpforms.com and install it manually.', 'wpforms-lite' );
// Verify params present (oth & download link).
$post_oth = ! empty( $_REQUEST['oth'] ) ? \sanitize_text_field( \wp_unslash( $_REQUEST['oth'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification
$post_url = ! empty( $_REQUEST['file'] ) ? \esc_url_raw( \wp_unslash( $_REQUEST['file'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification
if ( empty( $post_oth ) || empty( $post_url ) ) {
\wp_send_json_error( $error );
}
// Verify oth.
$oth = \get_option( 'wpforms_connect_token' );
if ( empty( $oth ) || ! hash_equals( $oth, $post_oth ) ) {
\wp_send_json_error( $error );
}
// Delete so cannot replay.
\delete_option( 'wpforms_connect_token' );
// Set the current screen to avoid undefined notices.
\set_current_screen( 'wpforms_page_wpforms-settings' );
// Prepare variables.
$url = \esc_url_raw(
\add_query_arg(
[ 'page' => 'wpforms-settings' ],
\admin_url( 'admin.php' )
)
);
// Verify pro not activated.
if ( wpforms()->is_pro() ) {
\wp_send_json_success( \esc_html__( 'Plugin installed & activated.', 'wpforms-lite' ) );
}
// Verify pro not installed.
$active = \activate_plugin( 'wpforms/wpforms.php', $url, false, true );
if ( ! \is_wp_error( $active ) ) {
$plugin = plugin_basename( WPFORMS_PLUGIN_FILE );
\deactivate_plugins( $plugin );
do_action( 'wpforms_plugin_deactivated', $plugin );
\wp_send_json_success( esc_html__( 'Plugin installed & activated.', 'wpforms-lite' ) );
}
$creds = \request_filesystem_credentials( $url, '', false, false, null );
// Check for file system permissions.
if ( false === $creds || ! \WP_Filesystem( $creds ) ) {
\wp_send_json_error(
\esc_html__( 'There was an error while installing an upgrade. Please check file system permissions and try again. Also, you can download the plugin from wpforms.com and install it manually.', 'wpforms-lite' )
);
}
/*
* We do not need any extra credentials if we have gotten this far, so let's install the plugin.
*/
// Do not allow WordPress to search/download translations, as this will break JS output.
\remove_action( 'upgrader_process_complete', [ 'Language_Pack_Upgrader', 'async_upgrade' ], 20 );
// Create the plugin upgrader with our custom skin.
$installer = new PluginSilentUpgrader( new ConnectSkin() );
// Error check.
if ( ! method_exists( $installer, 'install' ) ) {
\wp_send_json_error( $error );
}
// Check license key.
$key = \get_option( 'wpforms_connect', false );
if ( empty( $key ) ) {
\wp_send_json_error(
new WP_Error(
'403',
\esc_html__( 'No key provided.', 'wpforms-lite' )
)
);
}
$installer->install( $post_url ); // phpcs:ignore
// Flush the cache and return the newly installed plugin basename.
\wp_cache_flush();
$plugin_basename = $installer->plugin_info();
if ( $plugin_basename ) {
// Deactivate the lite version first.
$plugin = \plugin_basename( WPFORMS_PLUGIN_FILE );
\deactivate_plugins( $plugin );
do_action( 'wpforms_plugin_deactivated', $plugin );
// Activate the plugin silently.
$activated = \activate_plugin( $plugin_basename, '', false, true );
if ( ! \is_wp_error( $activated ) ) {
\add_option( 'wpforms_install', 1 );
\wp_send_json_success( \esc_html__( 'Plugin installed & activated.', 'wpforms-lite' ) );
} else {
// Reactivate the lite plugin if pro activation failed.
\activate_plugin( \plugin_basename( WPFORMS_PLUGIN_FILE ), '', false, true );
\wp_send_json_error( \esc_html__( 'Pro version installed but needs to be activated on the Plugins page inside your WordPress admin.', 'wpforms-lite' ) );
}
}
\wp_send_json_error( $error );
}
}
Lite/Admin/Pages/Addons.php 0000644 00000004214 15133255232 0011453 0 ustar 00 <?php
namespace WPForms\Lite\Admin\Pages;
/**
* Addons page for Lite.
*
* @since 1.6.7
*/
class Addons {
/**
* Page slug.
*
* @since 1.6.7
*
* @type string
*/
const SLUG = 'addons';
/**
* Determine if current class is allowed to load.
*
* @since 1.6.7
*
* @return bool
*/
public function allow_load() {
return wpforms_is_admin_page( self::SLUG );
}
/**
* Init.
*
* @since 1.6.7
*/
public function init() {
if ( ! $this->allow_load() ) {
return;
}
// Define hooks.
$this->hooks();
}
/**
* Hooks.
*
* @since 1.6.7
*/
public function hooks() {
add_action( 'admin_enqueue_scripts', [ $this, 'enqueues' ] );
add_action( 'admin_notices', [ $this, 'notices' ] );
add_action( 'wpforms_admin_page', [ $this, 'output' ] );
}
/**
* Add appropriate scripts to the Addons page.
*
* @since 1.6.7
*/
public function enqueues() {
// JavaScript.
wp_enqueue_script(
'listjs',
WPFORMS_PLUGIN_URL . 'assets/lib/list.min.js',
[ 'jquery' ],
'1.5.0'
);
}
/**
* Notices.
*
* @since 1.6.7.1
*/
public function notices() {
$notice = sprintf(
'<p><strong>%1$s</strong></p>
<p>%2$s</p>
<p>
<a href="%3$s" class="wpforms-btn wpforms-btn-orange wpforms-btn-md" rel="noopener noreferrer">
%4$s
</a>
</p>',
esc_html__( 'WPForms Addons are a PRO feature', 'wpforms-lite' ),
esc_html__( 'Please upgrade to PRO to unlock our addons, advanced form fields, and more!', 'wpforms-lite' ),
esc_url( wpforms_admin_upgrade_link( 'addons' ) ),
esc_html__( 'Upgrade Now', 'wpforms-lite' )
);
\WPForms\Admin\Notice::info(
$notice,
[ 'autop' => false ]
);
}
/**
* Render the Addons page.
*
* @since 1.6.7
*/
public function output() {
$addons = wpforms()->get( 'addons' )->get_all();
if ( empty( $addons ) ) {
return;
}
echo wpforms_render( // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
'admin/addons',
[
'upgrade_link_base' => wpforms_admin_upgrade_link( 'addons' ),
'addons' => $addons,
],
true
);
}
}
Lite/Admin/Education/LiteConnect.php 0000644 00000024732 15133255232 0013335 0 ustar 00 <?php
namespace WPForms\Lite\Admin\Education;
use WPForms\Admin\Education;
use WPForms\Integrations\LiteConnect\API;
use WPForms\Lite\Integrations\LiteConnect\LiteConnect as LiteConnectClass;
use WPForms\Lite\Integrations\LiteConnect\Integration as LiteConnectIntegration;
/**
* Admin/Settings/LiteConnect Education feature for Lite.
*
* @since 1.7.4
*/
class LiteConnect implements Education\EducationInterface {
/**
* Indicate if Lite Connect entry backup is enabled.
*
* @since 1.7.4
*
* @var int
*/
private $is_enabled;
/**
* Indicate if current Education feature is allowed to load.
*
* @since 1.7.4
*
* @return bool
*/
public function allow_load() {
// Do not load if Lite Connect integration is not allowed.
if ( ! LiteConnectClass::is_allowed() ) {
return false;
}
// Do not load if user doesn't have permissions to update settings.
if ( ! wpforms_current_user_can( wpforms_get_capability_manage_options() ) ) {
return false;
}
// Load only in certain cases.
return wp_doing_ajax() ||
wpforms_is_admin_page( 'builder' ) ||
wpforms_is_admin_page( 'settings' ) ||
wpforms_is_admin_page( 'overview' ) ||
wpforms_is_admin_page( 'entries' ) ||
$this->is_dashboard() ||
$this->is_embed_page();
}
/**
* Init.
*
* @since 1.7.4
*/
public function init() {
if ( ! $this->allow_load() ) {
return;
}
$this->is_enabled = LiteConnectClass::is_enabled() ? 1 : 0;
// Define hooks.
$this->hooks();
}
/**
* Hooks.
*
* @since 1.7.4
*/
private function hooks() {
add_action( 'admin_footer', [ $this, 'modal_template' ], 10, 2 );
add_action( 'admin_enqueue_scripts', [ $this, 'enqueues' ] );
// Ajax action.
add_action( 'wp_ajax_wpforms_update_lite_connect_enabled_setting', [ $this, 'ajax_update_lite_connect_enabled_setting' ] );
// Content filters.
add_filter( 'wpforms_lite_admin_dashboard_widget_content_html_chart_block_before', [ $this, 'dashboard_widget_before_content' ] );
add_filter( 'wpforms_builder_output_before_toolbar', [ $this, 'top_bar_content' ] );
add_filter( 'wpforms_admin_challenge_embed_template_congrats_popup_footer', [ $this, 'challenge_popup_footer_content' ] );
}
/**
* Check whether it is the Dashboard admin page.
*
* @since 1.7.4
*
* @return bool
*/
private function is_dashboard() {
global $pagenow;
return $pagenow === 'index.php';
}
/**
* Check whether it is the form embedding admin page (Edit Post or Edit Page).
*
* @since 1.7.4
*
* @return bool
*/
private function is_embed_page() {
if ( function_exists( 'get_current_screen' ) ) {
return wpforms()->get( 'challenge' )->is_form_embed_page();
}
global $pagenow;
return in_array( $pagenow, [ 'edit.php', 'post.php', 'post-new.php' ], true );
}
/**
* Load enqueues.
*
* @since 1.7.4
*/
public function enqueues() {
$min = wpforms_get_min_suffix();
// On the Dashboard and form embedding pages we should load additional scripts and styles.
if ( $this->is_dashboard() || $this->is_embed_page() ) {
$this->dashboard_enqueues();
}
wp_enqueue_script(
'wpforms-lite-admin-education-lite-connect',
WPFORMS_PLUGIN_URL . "assets/lite/js/admin/education/lite-connect{$min}.js",
[ 'jquery' ],
WPFORMS_VERSION,
true
);
wp_localize_script(
'wpforms-lite-admin-education-lite-connect',
'wpforms_education_lite_connect',
$this->get_js_strings()
);
}
/**
* Dashboard enqueues.
*
* @since 1.7.4
*/
private function dashboard_enqueues() {
$min = wpforms_get_min_suffix();
// jQuery confirm.
wp_enqueue_script(
'jquery-confirm',
WPFORMS_PLUGIN_URL . 'assets/lib/jquery.confirm/jquery-confirm.min.js',
[ 'jquery' ],
'3.3.2',
true
);
wp_enqueue_style(
'jquery-confirm',
WPFORMS_PLUGIN_URL . 'assets/lib/jquery.confirm/jquery-confirm.min.css',
[],
'3.3.2'
);
// FontAwesome.
wp_enqueue_style(
'wpforms-font-awesome',
WPFORMS_PLUGIN_URL . 'assets/lib/font-awesome/font-awesome.min.css',
null,
'4.7.0'
);
// Dashboard Education styles.
wp_enqueue_style(
'wpforms-lite-admin-education-lite-connect',
WPFORMS_PLUGIN_URL . "assets/lite/css/dashboard-education{$min}.css",
[],
WPFORMS_VERSION
);
}
/**
* Confirmation modal template.
*
* @since 1.7.4
*/
public function modal_template() {
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo wpforms_render( 'education/lite-connect-modal' );
}
/**
* Get localize strings.
*
* @since 1.7.4
*
* @return array
*/
private function get_js_strings() {
return [
'ajax_url' => admin_url( 'admin-ajax.php' ),
'nonce' => wp_create_nonce( 'wpforms-lite-connect-toggle' ),
'is_enabled' => $this->is_enabled,
'enable_modal' => [
'confirm' => esc_html__( 'Enable Entry Backups', 'wpforms-lite' ),
'cancel' => esc_html__( 'No Thanks', 'wpforms-lite' ),
],
'disable_modal' => [
'title' => esc_html__( 'Are you sure?', 'wpforms-lite' ),
'content' => esc_html__( 'If you disable Lite Connect, you will no longer be able to restore your entries when you upgrade to WPForms Pro.', 'wpforms-lite' ),
'confirm' => esc_html__( 'Disable Entry Backups', 'wpforms-lite' ),
'cancel' => esc_html__( 'Cancel', 'wpforms-lite' ),
],
'update_result' => [
'enabled_title' => esc_html__( 'Entry Backups Enabled', 'wpforms-lite' ),
'enabled' => esc_html__( 'Awesome! If you decide to upgrade to WPForms Pro, you can restore your entries and will have instant access to reports.', 'wpforms-lite' ),
'disabled_title' => esc_html__( 'Entry Backups Disabled', 'wpforms-lite' ),
'disabled' => esc_html__( 'Form Entry Backups were successfully disabled.', 'wpforms-lite' ),
'error_title' => esc_html__( 'Error', 'wpforms-lite' ),
'error' => esc_html__( 'Unfortunately, the error occurs while updating Form Entry Backups setting. Please try again later.', 'wpforms-lite' ),
'close' => esc_html__( 'Close', 'wpforms-lite' ),
],
];
}
/**
* Generate Lite Connect entries information.
*
* @since 1.7.4
*
* @return string
*/
private function get_lite_connect_entries_since_info() {
$entries_count = LiteConnectIntegration::get_new_entries_count();
$enabled_since = LiteConnectIntegration::get_enabled_since();
$string = sprintf(
esc_html( /* translators: %d - Backed up entries count. */
_n(
'%d entry backed up',
'%d entries backed up',
$entries_count,
'wpforms-lite'
)
),
absint( $entries_count )
);
if ( ! empty( $enabled_since ) ) {
$string .= ' ' . sprintf(
/* translators: %s - Time when Lite Connect was enabled. */
esc_html__( 'since %s', 'wpforms-lite' ),
esc_html( date_i18n( 'M j, Y', $enabled_since + get_option( 'gmt_offset' ) * 3600 ) )
);
}
return $string;
}
/**
* Add content before the Chart block in the Dashboard Widget.
*
* @since 1.7.4
*
* @param string $content Content.
*
* @return string
*/
public function dashboard_widget_before_content( $content ) {
$toggle = wpforms_panel_field_toggle_control(
[
'control-class' => 'wpforms-setting-lite-connect-auto-save-toggle',
],
'wpforms-setting-lite-connect-enabled',
'',
esc_html__( 'Enable Form Entry Backups', 'wpforms-lite' ),
$this->is_enabled,
'disabled'
);
return wpforms_render(
'education/admin/lite-connect/dashboard-widget-before',
[
'toggle' => $toggle,
'is_enabled' => $this->is_enabled,
'entries_since_info' => $this->get_lite_connect_entries_since_info(),
],
true
);
}
/**
* Add top bar before the toolbar in the Form Builder.
*
* @since 1.7.4
*
* @param string $content Content before the toolbar. Defaults to empty string.
*
* @return string
*/
public function top_bar_content( $content ) {
if ( $this->is_enabled ) {
return $content;
}
$dismissed = get_user_meta( get_current_user_id(), 'wpforms_dismissed', true );
// Skip when top bar is dismissed.
if ( ! empty( $dismissed['edu-builder-lite-connect-top-bar'] ) ) {
return $content;
}
$toggle = wpforms_panel_field_toggle_control(
[
'control-class' => 'wpforms-setting-lite-connect-auto-save-toggle',
],
'wpforms-setting-lite-connect-enabled',
'',
esc_html__( 'Enable Form Entry Backups for Free', 'wpforms-lite' ),
$this->is_enabled,
'disabled'
);
return wpforms_render(
'education/builder/lite-connect/top-bar',
[
'toggle' => $toggle,
'is_enabled' => $this->is_enabled,
],
true
);
}
/**
* Challenge Congrats popup footer.
*
* @since 1.7.4
*
* @param string $content Footer content.
*
* @return string
*/
public function challenge_popup_footer_content( $content ) {
if ( $this->is_enabled ) {
return $content;
}
$toggle = wpforms_panel_field_toggle_control(
[
'control-class' => 'wpforms-setting-lite-connect-auto-save-toggle',
],
'wpforms-setting-lite-connect-enabled',
'',
esc_html__( 'Enable Form Entry Backups for Free', 'wpforms-lite' ),
$this->is_enabled,
'disabled'
);
return wpforms_render(
'education/admin/lite-connect/challenge-popup-footer',
[
'toggle' => $toggle,
'is_enabled' => $this->is_enabled,
],
true
);
}
/**
* AJAX action: update Lite Connect Enabled setting.
*
* @since 1.7.4
*/
public function ajax_update_lite_connect_enabled_setting() {
// Run a security check.
check_ajax_referer( 'wpforms-lite-connect-toggle', 'nonce' );
// Check for permissions.
if ( ! wpforms_current_user_can( wpforms_get_capability_manage_options() ) ) {
wp_send_json_error( esc_html__( 'You do not have permission.', 'wpforms-lite' ) );
}
$slug = LiteConnectClass::SETTINGS_SLUG;
$settings = get_option( 'wpforms_settings', [] );
$settings[ $slug ] = ! empty( $_POST['value'] );
wpforms_update_settings( $settings );
if ( ! $settings[ $slug ] ) {
wp_send_json_success( '' );
}
// Reset generate key attempts counter.
update_option( API::GENERATE_KEY_ATTEMPT_COUNTER_OPTION, 0 );
// We have to start requesting site keys in ajax, turning on the LC functionality.
// First, the request to the API server will be sent.
// Second, the server will respond to our callback URL /wpforms/auth/key/nonce, and the site key will be stored in the DB.
// Third, we have to get access via a separate HTTP request.
( new LiteConnectIntegration() )->update_keys(); // First request here.
wp_send_json_success( $this->get_lite_connect_entries_since_info() );
}
}
Lite/Admin/Education/Admin/NoticeBar.php 0000644 00000002221 15133255232 0014011 0 ustar 00 <?php
namespace WPForms\Lite\Admin\Education\Admin;
use \WPForms\Admin\Education;
/**
* Admin/NoticeBar Education feature for Lite.
*
* @since 1.6.6
*/
class NoticeBar implements Education\EducationInterface {
/**
* Indicate if current Education feature is allowed to load.
*
* @since 1.6.6
*
* @return bool
*/
public function allow_load() {
return wpforms_is_admin_page();
}
/**
* Init.
*
* @since 1.6.6
*/
public function init() {
if ( ! $this->allow_load() ) {
return;
}
// Define hooks.
$this->hooks();
}
/**
* Hooks.
*
* @since 1.6.6
*/
public function hooks() {
add_action( 'wpforms_admin_header_before', [ $this, 'display' ] );
}
/**
* Notice bar display message.
*
* @since 1.6.6
*/
public function display() {
$dismissed = get_user_meta( get_current_user_id(), 'wpforms_dismissed', true );
if ( ! empty( $dismissed['edu-admin-notice-bar'] ) ) {
return;
}
echo wpforms_render( // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
'education/admin/notice-bar',
[
'upgrade_link' => wpforms_admin_upgrade_link( 'notice-bar' ),
],
true
);
}
}
Lite/Admin/Education/Admin/DidYouKnow.php 0000644 00000013634 15133255232 0014211 0 ustar 00 <?php
namespace WPForms\Lite\Admin\Education\Admin;
use WP_List_Table;
use WPForms\Admin\Education\EducationInterface;
use WPForms\Lite\Integrations\LiteConnect\LiteConnect;
use WPForms\Lite\Integrations\LiteConnect\Integration as LiteConnectIntegration;
/**
* Admin/DidYouKnow Education feature.
*
* @since 1.7.4
*/
class DidYouKnow implements EducationInterface {
/**
* Indicate if current Education feature is allowed to load.
*
* @since 1.7.4
*
* @return bool
*/
public function allow_load() {
// Load only on the `All Forms` admin page.
return wpforms_is_admin_page( 'overview' );
}
/**
* Init.
*
* @since 1.7.4
*/
public function init() {
if ( ! $this->allow_load() ) {
return;
}
// Define hooks.
$this->hooks();
}
/**
* Hooks.
*
* @since 1.7.4
*/
private function hooks() {
add_action( 'wpforms_admin_overview_after_rows', [ $this, 'display' ] );
}
/**
* Messages.
*
* @since 1.7.4
*
* @return array
*/
private function messages() {
return [
[
'slug' => 'lite-connect',
'is_allowed' => LiteConnect::is_allowed(),
'cont_class' => LiteConnect::is_enabled() ? 'wpforms-education-lite-connect-setting wpforms-hidden' : 'wpforms-education-lite-connect-setting',
'title' => esc_html__( 'Entries are not stored in WPForms Lite', 'wpforms-lite' ),
'desc' => esc_html__( 'Entries are available through email notifications. If you enable Entry Backups, you can restore them once you upgrade to WPForms Pro.', 'wpforms-lite' ),
'more_title' => esc_html__( 'Enable Entry Backups', 'wpforms-lite' ),
'more_link' => admin_url( 'admin.php?page=wpforms-settings' ),
'icon' => '<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 20 14"><path fill="#fff" d="M16.78 6.1a3 3 0 0 0-4.47-3.56A4.97 4.97 0 0 0 3 5v.28A4.48 4.48 0 0 0 4.5 14H16a4 4 0 0 0 4-4c0-1.9-1.38-3.53-3.22-3.9Zm-4.37 2-.35.34A.75.75 0 0 1 11 8.4l-1-1.07v3.91c0 .44-.34.75-.75.75h-.5a.72.72 0 0 1-.75-.75v-3.9L6.97 8.4a.75.75 0 0 1-1.06.03l-.35-.35c-.31-.3-.31-.78 0-1.06l2.9-2.9a.74.74 0 0 1 1.04 0l2.9 2.9c.32.28.32.75 0 1.06Z"/></svg>',
'item' => 1,
'enabled' => [
'cont_class' => LiteConnect::is_enabled() ? 'wpforms-education-lite-connect-enabled-info' : 'wpforms-education-lite-connect-enabled-info wpforms-hidden',
'title' => esc_html__( 'Entries Backups Are Enabled', 'wpforms-lite' ),
'more_title' => esc_html__( 'Restore Form Entries', 'wpforms-lite' ),
'more_link' => wpforms_admin_upgrade_link( 'forms-overview', 'restore-entries' ),
'more_class' => 'wpforms-is-enabled',
'desc' => $this->get_lite_connect_entries_since_info(),
],
],
];
}
/**
* Random message.
*
* @since 1.7.4
*/
private function message_rnd() {
$messages = $this->messages();
return $messages[ array_rand( $messages ) ];
}
/**
* Display message.
*
* @since 1.7.4
*
* @param WP_List_Table $wp_list_table Instance of WP_List_Table.
*/
public function display( $wp_list_table ) {
$dismissed = get_user_meta( get_current_user_id(), 'wpforms_dismissed', true );
// Do not display the message if it was dismissed.
if ( ! empty( $dismissed['edu-admin-did-you-know-overview'] ) ) {
return;
}
$message = $this->message_rnd();
// Display the message only if it is allowed.
if ( isset( $message['is_allowed'] ) && empty( $message['is_allowed'] ) ) {
return;
}
echo wpforms_render( // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
'education/admin/did-you-know',
[
'slug' => ! empty( $message['slug'] ) ? $message['slug'] : '',
'cols' => $wp_list_table->get_column_count(),
'icon' => ! empty( $message['icon'] ) ? $message['icon'] : '',
'title' => ! empty( $message['title'] ) ? $message['title'] : esc_html__( 'Did You Know?', 'wpforms-lite' ),
'desc' => ! empty( $message['desc'] ) ? $message['desc'] : '',
'more_title' => ! empty( $message['more_title'] ) ? $message['more_title'] : esc_html__( 'Learn More', 'wpforms-lite' ),
'more_link' => ! empty( $message['more_link'] ) ? $message['more_link'] : '',
'more_class' => ! empty( $message['more_class'] ) ? $message['more_class'] : '',
'cont_class' => ! empty( $message['cont_class'] ) ? $message['cont_class'] : '',
'enabled_title' => ! empty( $message['enabled']['title'] ) ? $message['enabled']['title'] : esc_html__( 'Did You Know?', 'wpforms-lite' ),
'enabled_desc' => ! empty( $message['enabled']['desc'] ) ? $message['enabled']['desc'] : '',
'enabled_more_title' => ! empty( $message['enabled']['more_title'] ) ? $message['enabled']['more_title'] : esc_html__( 'Learn More', 'wpforms-lite' ),
'enabled_more_link' => ! empty( $message['enabled']['more_link'] ) ? $message['enabled']['more_link'] : '',
'enabled_more_class' => ! empty( $message['enabled']['more_class'] ) ? $message['enabled']['more_class'] : '',
'enabled_cont_class' => ! empty( $message['enabled']['cont_class'] ) ? $message['enabled']['cont_class'] : '',
],
true
);
}
/**
* Generate Lite Connect entries information.
*
* @since 1.7.4
*
* @return string
*/
private function get_lite_connect_entries_since_info() {
$entries_count = LiteConnectIntegration::get_new_entries_count();
$enabled_since = LiteConnectIntegration::get_enabled_since();
$string = sprintf(
esc_html( /* translators: %d - Backed up entries count. */
_n(
'%d entry backed up',
'%d entries backed up',
$entries_count,
'wpforms-lite'
)
),
absint( $entries_count )
);
if ( ! empty( $enabled_since ) ) {
$string .= ' ' . sprintf(
/* translators: %s - Time when Lite Connect was enabled. */
esc_html__( 'since %s', 'wpforms-lite' ),
esc_html( date_i18n( 'M j, Y', $enabled_since + get_option( 'gmt_offset' ) * 3600 ) )
);
}
return $string;
}
}
Lite/Admin/Education/Builder/Confirmations.php 0000644 00000003475 15133255232 0015330 0 ustar 00 <?php
namespace WPForms\Lite\Admin\Education\Builder;
use WPForms_Builder_Panel_Settings;
use WPForms\Admin\Education\EducationInterface;
/**
* Confirmations Education feature.
*
* @since 1.6.9
*/
class Confirmations implements EducationInterface {
/**
* Indicate if current Education feature is allowed to load.
*
* @since 1.6.9
*
* @return bool
*/
public function allow_load() {
return wpforms_is_admin_page( 'builder' );
}
/**
* Init.
*
* @since 1.6.9
*/
public function init() {
if ( ! $this->allow_load() ) {
return;
}
$this->hooks();
}
/**
* Load hooks.
*
* @since 1.6.9
*/
private function hooks() {
add_action( 'wpforms_lite_form_settings_confirmations_single_after', [ $this, 'entry_preview_settings' ], 10, 2 );
}
/**
* Add education settings located in confirmation inside the message block.
*
* @since 1.6.9
*
* @param WPForms_Builder_Panel_Settings $settings Builder panel settings.
* @param int $field_id Field ID.
*/
public function entry_preview_settings( $settings, $field_id ) {
wpforms_panel_field(
'toggle',
'confirmations',
'message_entry_preview',
$settings->form_data,
esc_html__( 'Show entry preview after confirmation', 'wpforms-lite' ),
[
'input_id' => 'wpforms-panel-field-confirmations-message_entry_preview-' . absint( $field_id ),
'input_class' => 'wpforms-panel-field-confirmations-message_entry_preview education-modal',
'parent' => 'settings',
'subsection' => absint( $field_id ),
'pro_badge' => true,
'data' => [
'action' => 'upgrade',
'name' => esc_html__( 'Show Entry Preview', 'wpforms-lite' ),
'licence' => 'pro',
],
'attrs' => [
'disabled' => 'disabled',
],
'value' => false,
]
);
}
}
Lite/Admin/Education/Builder/DidYouKnow.php 0000644 00000004164 15133255232 0014545 0 ustar 00 <?php
namespace WPForms\Lite\Admin\Education\Builder;
use \WPForms\Admin\Education\EducationInterface;
/**
* Builder/DidYouKnow Education feature.
*
* @since 1.6.6
*/
class DidYouKnow implements EducationInterface {
/**
* Indicate if current Education feature is allowed to load.
*
* @since 1.6.6
*
* @return bool
*/
public function allow_load() {
return wpforms_is_admin_page( 'builder' );
}
/**
* Init.
*
* @since 1.6.6
*/
public function init() {
if ( ! $this->allow_load() ) {
return;
}
// Define hooks.
$this->hooks();
}
/**
* Hooks.
*
* @since 1.6.6
*/
public function hooks() {
add_action( 'wpforms_builder_settings_notifications_after', [ $this, 'notifications' ] );
add_action( 'wpforms_builder_settings_confirmations_after', [ $this, 'confirmations' ] );
}
/**
* Display on the Notifications panel.
*
* @since 1.6.6
*/
public function notifications() {
$this->display(
'notifications',
[ 'desc' => esc_html__( 'You can have multiple notifications with conditional logic.', 'wpforms-lite' ) ]
);
}
/**
* Display on the Confirmations panel.
*
* @since 1.6.6
*/
public function confirmations() {
$this->display(
'confirmations',
[ 'desc' => esc_html__( 'You can have multiple confirmations with conditional logic.', 'wpforms-lite' ) ]
);
}
/**
* Display message.
*
* @since 1.6.6
*
* @param string $section Form builder section/area (slug).
* @param array $settings Notice settings array.
*/
private function display( $section, $settings ) {
$dismissed = get_user_meta( get_current_user_id(), 'wpforms_dismissed', true );
// Check if not dismissed.
if ( ! empty( $dismissed[ 'edu-builder-did-you-know-' . $section ] ) ) {
return;
}
echo wpforms_render( // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
'education/builder/did-you-know',
[
'desc' => $settings['desc'],
'more' => ! empty( $settings['more'] ) ? $settings['more'] : '',
'link' => wpforms_admin_upgrade_link( 'Form Builder DYK', ucfirst( $section ) ),
'section' => $section,
],
true
);
}
}
Lite/Admin/Education/Builder/Fields.php 0000644 00000005730 15133255232 0013717 0 ustar 00 <?php
namespace WPForms\Lite\Admin\Education\Builder;
use \WPForms\Admin\Education;
/**
* Builder/Fields Education for Lite.
*
* @since 1.6.6
*/
class Fields extends Education\Builder\Fields {
/**
* Hooks.
*
* @since 1.6.6
*/
public function hooks() {
add_filter( 'wpforms_builder_fields_buttons', [ $this, 'add_fields' ], 500 );
add_filter( 'wpforms_builder_field_button_attributes', [ $this, 'fields_attributes' ], 100, 2 );
add_action( 'wpforms_field_options_after_advanced-options', [ $this, 'field_conditional_logic' ] );
}
/**
* Add fields.
*
* @since 1.6.6
*
* @param array $fields Form fields.
*
* @return array
*/
public function add_fields( $fields ) {
foreach ( $fields as $group => $group_data ) {
$edu_fields = $this->fields->get_by_group( $group );
$edu_fields = $this->fields->set_values( $edu_fields, 'class', 'education-modal', 'empty' );
foreach ( $edu_fields as $edu_field ) {
// Skip if in the current group already exist field of this type.
if ( ! empty( wp_list_filter( $group_data, [ 'type' => $edu_field['type'] ] ) ) ) {
continue;
}
$addon = ! empty( $edu_field['addon'] ) ? $this->addons->get_addon( $edu_field['addon'] ) : [];
if ( ! empty( $addon ) ) {
$edu_field['license'] = isset( $addon['license_level'] ) ? $addon['license_level'] : '';
}
$fields[ $group ]['fields'][] = $edu_field;
}
}
return $fields;
}
/**
* Display conditional logic settings section for fields inside the form builder.
*
* @since 1.6.6
*
* @param array $field Field data.
*/
public function field_conditional_logic( $field ) {
// Certain fields don't support conditional logic.
if ( in_array( $field['type'], [ 'pagebreak', 'divider', 'hidden' ], true ) ) {
return;
}
?>
<div class="wpforms-field-option-group wpforms-field-option-group-conditionals">
<a href="#"
class="wpforms-field-option-group-toggle education-modal"
data-name="<?php esc_attr_e( 'Smart Conditional Logic', 'wpforms-lite' ); ?>"
data-utm-content="Smart Conditional Logic">
<?php esc_html_e( 'Smart Logic', 'wpforms-lite' ); ?>
</a>
</div>
<?php
}
/**
* Adjust attributes on field buttons.
*
* @since 1.6.6
*
* @param array $atts Button attributes.
* @param array $field Button properties.
*
* @return array Attributes array.
*/
public function fields_attributes( $atts, $field ) {
$atts['data']['utm-content'] = ! empty( $field['name_en'] ) ? $field['name_en'] : '';
if ( ! empty( $field['class'] ) && $field['class'] === 'education-modal' ) {
$atts['class'][] = 'wpforms-not-available';
}
if ( empty( $field['addon'] ) ) {
return $atts;
}
$addon = $this->addons->get_addon( $field['addon'] );
if ( empty( $addon ) ) {
return $atts;
}
if ( ! empty( $addon['video'] ) ) {
$atts['data']['video'] = $addon['video'];
}
if ( ! empty( $field['license'] ) ) {
$atts['data']['license'] = $field['license'];
}
return $atts;
}
}
Lite/Admin/Education/Core.php 0000644 00000001630 15133255232 0012006 0 ustar 00 <?php
namespace WPForms\Lite\Admin\Education;
/**
* Education core for Lite.
*
* @since 1.6.6
*/
class Core extends \WPForms\Admin\Education\Core {
/**
* Hooks.
*
* @since 1.6.6
*/
protected function hooks() {
parent::hooks();
add_action( 'admin_enqueue_scripts', [ $this, 'enqueues' ] );
}
/**
* Load enqueues.
*
* @since 1.6.6
*/
public function enqueues() {
parent::enqueues();
$min = wpforms_get_min_suffix();
wp_enqueue_script(
'wpforms-lite-admin-education-core',
WPFORMS_PLUGIN_URL . "assets/lite/js/admin/education/core{$min}.js",
[ 'wpforms-admin-education-core' ],
WPFORMS_VERSION,
false
);
// Builder Education styles.
if ( wpforms_is_admin_page( 'builder' ) ) {
wp_enqueue_style(
'wpforms-lite-admin-education-builder',
WPFORMS_PLUGIN_URL . "assets/lite/css/builder-education{$min}.css",
[],
WPFORMS_VERSION
);
}
}
}
Helpers/Templates.php 0000644 00000014437 15133255232 0010627 0 ustar 00 <?php
namespace WPForms\Helpers;
/**
* Template related helper methods.
*
* @since 1.5.4
*/
class Templates {
/**
* Return a list of paths to check for template locations
*
* @since 1.5.4
*
* @return array
*/
public static function get_theme_template_paths() {
$template_dir = 'wpforms';
$file_paths = array(
1 => \trailingslashit( \get_stylesheet_directory() ) . $template_dir,
10 => \trailingslashit( \get_template_directory() ) . $template_dir,
100 => \trailingslashit( \WPFORMS_PLUGIN_DIR ) . 'templates',
);
$file_paths = \apply_filters( 'wpforms_helpers_templates_get_theme_template_paths', $file_paths );
// Sort the file paths based on priority.
\ksort( $file_paths, SORT_NUMERIC );
return \array_map( 'trailingslashit', $file_paths );
}
/**
* Locate a template and return the path for inclusion.
*
* @since 1.5.4
*
* @param string $template_name Template name.
*
* @return string
*/
public static function locate( $template_name ) {
// Trim off any slashes from the template name.
$template_name = \ltrim( $template_name, '/' );
if ( empty( $template_name ) ) {
return \apply_filters( 'wpforms_helpers_templates_locate', '', $template_name );
}
$located = '';
// Try locating this template file by looping through the template paths.
foreach ( self::get_theme_template_paths() as $template_path ) {
if ( \file_exists( $template_path . $template_name ) ) {
$located = $template_path . $template_name;
break;
}
}
return \apply_filters( 'wpforms_helpers_templates_locate', $located, $template_name );
}
/**
* Include a template.
* Use 'require' if $args are passed or 'load_template' if not.
*
* @since 1.5.4
*
* @param string $template_name Template name.
* @param array $args Arguments.
* @param bool $extract Extract arguments.
*
* @throws \RuntimeException If extract() tries to modify the scope.
*/
public static function include_html( $template_name, $args = array(), $extract = false ) {
$template_name .= '.php';
// Allow 3rd party plugins to filter template file from their plugin.
$located = \apply_filters( 'wpforms_helpers_templates_include_html_located', self::locate( $template_name ), $template_name, $args, $extract );
$args = \apply_filters( 'wpforms_helpers_templates_include_html_args', $args, $template_name, $extract );
if ( empty( $located ) || ! \is_readable( $located ) ) {
return;
}
// Load template WP way if no arguments were passed.
if ( empty( $args ) ) {
\load_template( $located, false );
return;
}
$extract = \apply_filters( 'wpforms_helpers_templates_include_html_extract_args', $extract, $template_name, $args );
if ( $extract && \is_array( $args ) ) {
$created_vars_count = extract( $args, EXTR_SKIP ); // phpcs:ignore WordPress.PHP.DontExtract
// Protecting existing scope from modification.
if ( count( $args ) !== $created_vars_count ) {
throw new \RuntimeException( 'Extraction failed: variable names are clashing with the existing ones.' );
}
}
require $located;
}
/**
* Like self::include_html, but returns the HTML instead of including.
*
* @since 1.5.4
*
* @param string $template_name Template name.
* @param array $args Arguments.
* @param bool $extract Extract arguments.
*
* @return string
*/
public static function get_html( $template_name, $args = array(), $extract = false ) {
\ob_start();
self::include_html( $template_name, $args, $extract );
return \ob_get_clean();
}
/**
* Validate that a file path is safe and within the expected path(s).
*
* Author Scott Kingsley Clark, Pods Framework.
* Refactored to reduce cyclomatic complexity.
*
* @since 1.7.5.5
*
* @link https://github.com/pods-framework/pods/commit/ea53471e58e638dec06957edc38f9fa86607652c
*
* @param string $path The file path.
* @param null|array|string $paths_to_check The list of path types to check, defaults to just checking 'wpforms'.
* Available: 'wpforms', 'plugins', 'theme',
* or 'all' to check all supported paths.
*
* @return false|string False if the path was not allowed or did not exist, otherwise it returns the normalized path.
*/
public static function validate_safe_path( $path, $paths_to_check = null ) {
static $available_checks;
if ( ! $available_checks ) {
$available_checks = [
'wpforms' => realpath( WPFORMS_PLUGIN_DIR ),
'plugins' => [
realpath( WP_PLUGIN_DIR ),
realpath( WPMU_PLUGIN_DIR ),
],
'theme' => [
realpath( get_stylesheet_directory() ),
realpath( get_template_directory() ),
],
];
$available_checks['plugins'] = array_unique( array_filter( $available_checks['plugins'] ) );
$available_checks['theme'] = array_unique( array_filter( $available_checks['theme'] ) );
$available_checks = array_filter( $available_checks );
}
$paths_to_check = $paths_to_check === null ? [ 'wpforms' ] : $paths_to_check;
$paths_to_check = $paths_to_check === 'all' ? array_keys( $available_checks ) : $paths_to_check;
$paths_to_check = (array) $paths_to_check;
if ( empty( $paths_to_check ) ) {
return false;
}
$path = wp_normalize_path( trim( (string) $path ) );
$match_count = 1;
// Replace the ../ usage as many times as it may need to be replaced.
while ( $match_count ) {
$path = str_replace( '../', '', $path, $match_count );
}
$path = realpath( $path );
foreach ( $paths_to_check as $check_type ) {
if ( self::has_match( $path, $available_checks, $check_type ) ) {
return $path;
}
}
return false;
}
/**
* Whether path matches.
*
* @since 1.7.5.5
*
* @param string|bool $path Path.
* @param array $available_checks Available checks.
* @param string $check_type Check type.
*
* @return bool
*/
private static function has_match( $path, $available_checks, $check_type ) {
if ( ! $path || ! isset( $available_checks[ $check_type ] ) ) {
return false;
}
$check_type_paths = (array) $available_checks[ $check_type ];
foreach ( $check_type_paths as $path_to_check ) {
if ( 0 === strpos( $path, $path_to_check ) && file_exists( $path ) ) {
return true;
}
}
return false;
}
}
Helpers/File.php 0000644 00000000636 15133255232 0007544 0 ustar 00 <?php
namespace WPForms\Helpers;
/**
* Class File.
*
* @since 1.6.5
*/
class File {
/**
* Remove UTF-8 BOM signature if it presents.
*
* @since 1.6.5
*
* @param string $string String to process.
*
* @return string
*/
public static function remove_utf8_bom( $string ) {
if ( strpos( bin2hex( $string ), 'efbbbf' ) === 0 ) {
$string = substr( $string, 3 );
}
return $string;
}
}
Helpers/Chain.php 0000644 00000020420 15133255232 0007700 0 ustar 00 <?php
namespace WPForms\Helpers;
/**
* Chain monad, useful for chaining certain array or string related functions.
*
* @since 1.5.6
*
* @method Chain array_change_key_case()
* @method Chain array_chunk()
* @method Chain array_column()
* @method Chain array_combine()
* @method Chain array_count_values()
* @method Chain array_diff_assoc()
* @method Chain array_diff_key()
* @method Chain array_diff_uassoc()
* @method Chain array_diff_ukey()
* @method Chain array_diff(array $var)
* @method Chain array_fill_keys()
* @method Chain array_fill()
* @method Chain array_filter()
* @method Chain array_flip()
* @method Chain array_intersect_assoc()
* @method Chain array_intersect_key()
* @method Chain array_intersect_uassoc()
* @method Chain array_intersect_ukey()
* @method Chain array_intersect(array $var)
* @method Chain array_key_first()
* @method Chain array_key_last()
* @method Chain array_keys()
* @method Chain array_map()
* @method Chain array_merge_recursive()
* @method Chain array_merge(array $var)
* @method Chain array_pad()
* @method Chain array_pop()
* @method Chain array_product()
* @method Chain array_rand()
* @method Chain array_reduce()
* @method Chain array_replace_recursive()
* @method Chain array_replace()
* @method Chain array_reverse()
* @method Chain array_shift()
* @method Chain array_slice()
* @method Chain array_splice()
* @method Chain array_sum()
* @method Chain array_udiff_assoc()
* @method Chain array_udiff_uassoc()
* @method Chain array_udiff()
* @method Chain array_uintersect_assoc()
* @method Chain array_uintersect_uassoc()
* @method Chain array_uintersect()
* @method Chain array_unique()
* @method Chain array_values()
* @method Chain count()
* @method Chain current()
* @method Chain end()
* @method Chain key()
* @method Chain next()
* @method Chain prev()
* @method Chain range()
* @method Chain reset()
* @method Chain ltrim()
* @method Chain rtrim()
* @method Chain md5()
* @method Chain str_getcsv()
* @method Chain str_ireplace()
* @method Chain str_pad()
* @method Chain str_repeat()
* @method Chain str_rot13()
* @method Chain str_shuffle()
* @method Chain str_split()
* @method Chain str_word_count()
* @method Chain strcasecmp()
* @method Chain strchr()
* @method Chain strcmp()
* @method Chain strcoll()
* @method Chain strcspn()
* @method Chain strip_tags()
* @method Chain stripcslashes()
* @method Chain stripos()
* @method Chain stripslashes()
* @method Chain stristr()
* @method Chain strlen()
* @method Chain strnatcasecmp()
* @method Chain strnatcmp()
* @method Chain strncasecmp()
* @method Chain strncmp()
* @method Chain strpbrk()
* @method Chain strpos()
* @method Chain strrchr()
* @method Chain strrev()
* @method Chain strripos()
* @method Chain strrpos()
* @method Chain strspn()
* @method Chain strstr()
* @method Chain strtok()
* @method Chain strtolower()
* @method Chain strtoupper()
* @method Chain strtr()
* @method Chain substr_compare()
* @method Chain substr_count()
* @method Chain substr_replace()
* @method Chain substr()
* @method Chain trim()
* @method Chain ucfirst()
* @method Chain ucwords()
* @method Chain vfprintf()
* @method Chain vprintf()
* @method Chain vsprintf()
* @method Chain wordwrap()
*/
class Chain {
/**
* Current value.
*
* @since 1.5.6
*
* @var mixed
*/
private $value;
/**
* Class constructor.
*
* @since 1.5.6
*
* @param mixed $value Current value to start working with.
*/
public function __construct( $value ) {
$this->value = $value;
}
/**
* Bind some function to value.
*
* @since 1.5.6
*
* @param mixed $fn Some function.
*
* @return Chain
*/
public function bind( $fn ) {
$this->value = $fn( $this->value );
return $this;
}
/**
* Get value.
*
* @since 1.5.6
*
* @return mixed
*/
public function value() {
return $this->value;
}
/**
* Magic call.
*
* @since 1.5.6
*
* @param string $name Method name.
* @param array $params Parameters.
*
* @throws \BadFunctionCallException Invalid function is called.
*
* @return Chain
*/
public function __call( $name, $params ) {
if ( in_array( $name, $this->allowed_methods(), true ) ) {
$params = null === $params ? array() : $params;
array_unshift( $params, $this->value );
$this->value = call_user_func_array( $name, array_values( $params ) );
return $this;
}
throw new \BadFunctionCallException( "Provided function { $name } is not allowed. See Chain::allowed_methods()." );
}
/**
* Join array elements with a string.
*
* @since 1.5.6
*
* @param string $glue Defaults to an empty string.
*
* @return Chain
*/
public function implode( $glue = '' ) {
$this->value = implode( $glue, $this->value );
return $this;
}
/**
* Split a string by a string.
*
* @since 1.5.6
*
* @param string $delimiter The boundary string.
*
* @return Chain
*/
public function explode( $delimiter ) {
$this->value = explode( $delimiter, $this->value );
return $this;
}
/**
* Apply the callback to the elements of the given arrays.
*
* @since 1.5.6
*
* @param callable $cb Callback.
*
* @return Chain
*/
public function map( $cb ) {
$this->value = array_map( $cb, $this->value );
return $this;
}
/**
* Pop array.
*
* @since 1.5.6
*
* @return Chain
*/
public function pop() {
$this->value = array_pop( $this->value );
return $this;
}
/**
* Run first or second callback based on a condition.
*
* @since 1.5.6
*
* @param callable $condition Condition function.
* @param callable $true_result If condition will return true we run this function.
* @param callable $false_result If condition will return false we run this function.
*
* @return Chain
*/
public function iif( $condition, $true_result, $false_result = null ) {
if ( ! is_callable( $false_result ) ) {
$false_result = function() {
return '';
};
}
$this->value = array_map(
function( $el ) use ( $condition, $true_result, $false_result ) {
if ( call_user_func( $condition, $el ) ) {
return call_user_func( $true_result, $el );
}
return call_user_func( $false_result, $el );
},
$this->value
);
return $this;
}
/**
* All allowed methods to work with data.
*
* @since 1.5.6
*
* @return array
*/
public function allowed_methods() {
return [
'array_change_key_case',
'array_chunk',
'array_column',
'array_combine',
'array_count_values',
'array_diff_assoc',
'array_diff_key',
'array_diff_uassoc',
'array_diff_ukey',
'array_diff',
'array_fill_keys',
'array_fill',
'array_filter',
'array_flip',
'array_intersect_assoc',
'array_intersect_key',
'array_intersect_uassoc',
'array_intersect_ukey',
'array_intersect',
'array_key_first',
'array_key_last',
'array_keys',
'array_map',
'array_merge_recursive',
'array_merge',
'array_pad',
'array_pop',
'array_product',
'array_rand',
'array_reduce',
'array_replace_recursive',
'array_replace',
'array_reverse',
'array_shift',
'array_slice',
'array_splice',
'array_sum',
'array_udiff_assoc',
'array_udiff_uassoc',
'array_udiff',
'array_uintersect_assoc',
'array_uintersect_uassoc',
'array_uintersect',
'array_unique',
'array_values',
'count',
'current',
'end',
'key',
'next',
'prev',
'range',
'reset',
'implode',
'ltrim',
'rtrim',
'md5',
'str_getcsv',
'str_ireplace',
'str_pad',
'str_repeat',
'str_rot13',
'str_shuffle',
'str_split',
'str_word_count',
'strcasecmp',
'strchr',
'strcmp',
'strcoll',
'strcspn',
'strip_tags',
'stripcslashes',
'stripos',
'stripslashes',
'stristr',
'strlen',
'strnatcasecmp',
'strnatcmp',
'strncasecmp',
'strncmp',
'strpbrk',
'strpos',
'strrchr',
'strrev',
'strripos',
'strrpos',
'strspn',
'strstr',
'strtok',
'strtolower',
'strtoupper',
'strtr',
'substr_compare',
'substr_count',
'substr_replace',
'substr',
'trim',
'ucfirst',
'ucwords',
'vfprintf',
'vprintf',
'vsprintf',
'wordwrap',
];
}
/**
* Create myself.
*
* @since 1.5.6
*
* @param mixed $value Current.
*
* @return Chain
*/
public static function of( $value = null ) {
return new self( $value );
}
}
Helpers/PluginSilentUpgrader.php 0000644 00000056357 15133255232 0013007 0 ustar 00 <?php
namespace WPForms\Helpers;
use WP_Error;
use WP_Upgrader;
use WP_Filesystem_Base;
/** \WP_Upgrader class */
require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
/** \Plugin_Upgrader class */
require_once ABSPATH . 'wp-admin/includes/class-plugin-upgrader.php';
/**
* In WP 5.3 a PHP 5.6 splat operator (...$args) was added to \WP_Upgrader_Skin::feedback().
* We need to remove all calls to *Skin::feedback() method, as we can't override it in own Skins
* without breaking support for PHP 5.3-5.5.
*
* @internal Please do not use this class outside of core WPForms development. May be removed at any time.
*
* @since 1.5.6.1
*/
class PluginSilentUpgrader extends \Plugin_Upgrader {
/**
* Run an upgrade/installation.
*
* Attempt to download the package (if it is not a local file), unpack it, and
* install it in the destination folder.
*
* @since 1.5.6.1
*
* @param array $options {
* Array or string of arguments for upgrading/installing a package.
*
* @type string $package The full path or URI of the package to install.
* Default empty.
* @type string $destination The full path to the destination folder.
* Default empty.
* @type bool $clear_destination Whether to delete any files already in the
* destination folder. Default false.
* @type bool $clear_working Whether to delete the files form the working
* directory after copying to the destination.
* Default false.
* @type bool $abort_if_destination_exists Whether to abort the installation if the destination
* folder already exists. When true, `$clear_destination`
* should be false. Default true.
* @type bool $is_multi Whether this run is one of multiple upgrade/installation
* actions being performed in bulk. When true, the skin
* WP_Upgrader::header() and WP_Upgrader::footer()
* aren't called. Default false.
* @type array $hook_extra Extra arguments to pass to the filter hooks called by
* WP_Upgrader::run().
* }
* @return array|false|WP_error The result from self::install_package() on success, otherwise a WP_Error,
* or false if unable to connect to the filesystem.
*/
public function run( $options ) {
$defaults = array(
'package' => '', // Please always pass this.
'destination' => '', // And this
'clear_destination' => false,
'abort_if_destination_exists' => true, // Abort if the Destination directory exists, Pass clear_destination as false please
'clear_working' => true,
'is_multi' => false,
'hook_extra' => array(), // Pass any extra $hook_extra args here, this will be passed to any hooked filters.
);
$options = wp_parse_args( $options, $defaults );
/**
* Filter the package options before running an update.
*
* See also {@see 'upgrader_process_complete'}.
*
* @since 4.3.0
*
* @param array $options {
* Options used by the upgrader.
*
* @type string $package Package for update.
* @type string $destination Update location.
* @type bool $clear_destination Clear the destination resource.
* @type bool $clear_working Clear the working resource.
* @type bool $abort_if_destination_exists Abort if the Destination directory exists.
* @type bool $is_multi Whether the upgrader is running multiple times.
* @type array $hook_extra {
* Extra hook arguments.
*
* @type string $action Type of action. Default 'update'.
* @type string $type Type of update process. Accepts 'plugin', 'theme', or 'core'.
* @type bool $bulk Whether the update process is a bulk update. Default true.
* @type string $plugin Path to the plugin file relative to the plugins directory.
* @type string $theme The stylesheet or template name of the theme.
* @type string $language_update_type The language pack update type. Accepts 'plugin', 'theme',
* or 'core'.
* @type object $language_update The language pack update offer.
* }
* }
*/
$options = apply_filters( 'upgrader_package_options', $options );
if ( ! $options['is_multi'] ) { // call $this->header separately if running multiple times
$this->skin->header();
}
// Connect to the Filesystem first.
$res = $this->fs_connect( array( WP_CONTENT_DIR, $options['destination'] ) );
// Mainly for non-connected filesystem.
if ( ! $res ) {
if ( ! $options['is_multi'] ) {
$this->skin->footer();
}
return false;
}
$this->skin->before();
if ( is_wp_error( $res ) ) {
$this->skin->error( $res );
$this->skin->after();
if ( ! $options['is_multi'] ) {
$this->skin->footer();
}
return $res;
}
/*
* Download the package (Note, This just returns the filename
* of the file if the package is a local file)
*/
$download = $this->download_package( $options['package'], true );
// Allow for signature soft-fail.
// WARNING: This may be removed in the future.
if ( is_wp_error( $download ) && $download->get_error_data( 'softfail-filename' ) ) {
// Don't output the 'no signature could be found' failure message for now.
if ( (string) $download->get_error_code() !== 'signature_verification_no_signature' || WP_DEBUG ) {
// Outout the failure error as a normal feedback, and not as an error:
//$this->skin->feedback( $download->get_error_message() );
// Report this failure back to WordPress.org for debugging purposes.
wp_version_check(
array(
'signature_failure_code' => $download->get_error_code(),
'signature_failure_data' => $download->get_error_data(),
)
);
}
// Pretend this error didn't happen.
$download = $download->get_error_data( 'softfail-filename' );
}
if ( is_wp_error( $download ) ) {
$this->skin->error( $download );
$this->skin->after();
if ( ! $options['is_multi'] ) {
$this->skin->footer();
}
return $download;
}
$delete_package = ( (string) $download !== (string) $options['package'] ); // Do not delete a "local" file.
// Unzips the file into a temporary directory.
$working_dir = $this->unpack_package( $download, $delete_package );
if ( is_wp_error( $working_dir ) ) {
$this->skin->error( $working_dir );
$this->skin->after();
if ( ! $options['is_multi'] ) {
$this->skin->footer();
}
return $working_dir;
}
// With the given options, this installs it to the destination directory.
$result = $this->install_package(
array(
'source' => $working_dir,
'destination' => $options['destination'],
'clear_destination' => $options['clear_destination'],
'abort_if_destination_exists' => $options['abort_if_destination_exists'],
'clear_working' => $options['clear_working'],
'hook_extra' => $options['hook_extra'],
)
);
$this->skin->set_result( $result );
if ( is_wp_error( $result ) ) {
$this->skin->error( $result );
//$this->skin->feedback( 'process_failed' );
} else {
// Installation succeeded.
//$this->skin->feedback( 'process_success' );
}
$this->skin->after();
if ( ! $options['is_multi'] ) {
/**
* Fire when the upgrader process is complete.
*
* See also {@see 'upgrader_package_options'}.
*
* @since 3.6.0
* @since 3.7.0 Added to WP_Upgrader::run().
* @since 4.6.0 `$translations` was added as a possible argument to `$hook_extra`.
*
* @param WP_Upgrader $this WP_Upgrader instance. In other contexts, $this, might be a
* Theme_Upgrader, Plugin_Upgrader, Core_Upgrade, or
* Language_Pack_Upgrader instance.
* @param array $hook_extra {
* Array of bulk item update data.
*
* @type string $action Type of action. Default 'update'.
* @type string $type Type of update process. Accepts 'plugin', 'theme', 'translation', or 'core'.
* @type bool $bulk Whether the update process is a bulk update. Default true.
* @type array $plugins Array of the basename paths of the plugins' main files.
* @type array $themes The theme slugs.
* @type array $translations {
* Array of translations update data.
*
* @type string $language The locale the translation is for.
* @type string $type Type of translation. Accepts 'plugin', 'theme', or 'core'.
* @type string $slug Text domain the translation is for. The slug of a theme/plugin or
* 'default' for core translations.
* @type string $version The version of a theme, plugin, or core.
* }
* }
*/
do_action( 'upgrader_process_complete', $this, $options['hook_extra'] );
$this->skin->footer();
}
return $result;
}
/**
* Toggle maintenance mode for the site.
*
* Create/delete the maintenance file to enable/disable maintenance mode.
*
* @since 2.8.0
*
* @global WP_Filesystem_Base $wp_filesystem Subclass
*
* @param bool $enable True to enable maintenance mode, false to disable.
*/
public function maintenance_mode( $enable = false ) {
global $wp_filesystem;
$file = $wp_filesystem->abspath() . '.maintenance';
if ( $enable ) {
//$this->skin->feedback( 'maintenance_start' );
// Create maintenance file to signal that we are upgrading
$maintenance_string = '<?php $upgrading = ' . time() . '; ?>';
$wp_filesystem->delete( $file );
$wp_filesystem->put_contents( $file, $maintenance_string, FS_CHMOD_FILE );
} elseif ( ! $enable && $wp_filesystem->exists( $file ) ) {
//$this->skin->feedback( 'maintenance_end' );
$wp_filesystem->delete( $file );
}
}
/**
* Download a package.
*
* @since 2.8.0
* @since 5.5.0 Added the `$hook_extra` parameter.
*
* @param string $package The URI of the package. If this is the full path to an
* existing local file, it will be returned untouched.
* @param bool $check_signatures Whether to validate file signatures. Default false.
* @param array $hook_extra Extra arguments to pass to the filter hooks. Default empty array.
* @return string|WP_Error The full path to the downloaded package file, or a WP_Error object.
*/
public function download_package( $package, $check_signatures = false, $hook_extra = array() ) {
/**
* Filters whether to return the package.
*
* @since 3.7.0
* @since 5.5.0 Added the `$hook_extra` parameter.
*
* @param bool $reply Whether to bail without returning the package.
* Default false.
* @param string $package The package file name.
* @param WP_Upgrader $this The WP_Upgrader instance.
* @param array $hook_extra Extra arguments passed to hooked filters.
*/
$reply = apply_filters( 'upgrader_pre_download', false, $package, $this, $hook_extra );
if ( false !== $reply ) {
return $reply;
}
if ( ! preg_match( '!^(http|https|ftp)://!i', $package ) && file_exists( $package ) ) { // Local file or remote?
return $package; // Must be a local file.
}
if ( empty( $package ) ) {
return new WP_Error( 'no_package', $this->strings['no_package'] );
}
//$this->skin->feedback( 'downloading_package', $package );
$download_file = download_url( $package, 300, $check_signatures );
if ( is_wp_error( $download_file ) && ! $download_file->get_error_data( 'softfail-filename' ) ) {
return new WP_Error( 'download_failed', $this->strings['download_failed'], $download_file->get_error_message() );
}
return $download_file;
}
/**
* Unpack a compressed package file.
*
* @since 2.8.0
*
* @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass.
*
* @param string $package Full path to the package file.
* @param bool $delete_package Optional. Whether to delete the package file after attempting
* to unpack it. Default true.
* @return string|WP_Error The path to the unpacked contents, or a WP_Error on failure.
*/
public function unpack_package( $package, $delete_package = true ) {
global $wp_filesystem;
//$this->skin->feedback( 'unpack_package' );
$upgrade_folder = $wp_filesystem->wp_content_dir() . 'upgrade/';
//Clean up contents of upgrade directory beforehand.
$upgrade_files = $wp_filesystem->dirlist( $upgrade_folder );
if ( ! empty( $upgrade_files ) ) {
foreach ( $upgrade_files as $file ) {
$wp_filesystem->delete( $upgrade_folder . $file['name'], true );
}
}
// We need a working directory - Strip off any .tmp or .zip suffixes
$working_dir = $upgrade_folder . basename( basename( $package, '.tmp' ), '.zip' );
// Clean up working directory
if ( $wp_filesystem->is_dir( $working_dir ) ) {
$wp_filesystem->delete( $working_dir, true );
}
// Unzip package to working directory
$result = unzip_file( $package, $working_dir );
// Once extracted, delete the package if required.
if ( $delete_package ) {
unlink( $package );
}
if ( is_wp_error( $result ) ) {
$wp_filesystem->delete( $working_dir, true );
if ( $result->get_error_code() === 'incompatible_archive' ) {
return new WP_Error( 'incompatible_archive', $this->strings['incompatible_archive'], $result->get_error_data() );
}
return $result;
}
return $working_dir;
}
/**
* Install a package.
*
* Copies the contents of a package form a source directory, and installs them in
* a destination directory. Optionally removes the source. It can also optionally
* clear out the destination folder if it already exists.
*
* @since 2.8.0
*
* @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass.
* @global array $wp_theme_directories
*
* @param array|string $args {
* Optional. Array or string of arguments for installing a package. Default empty array.
*
* @type string $source Required path to the package source. Default empty.
* @type string $destination Required path to a folder to install the package in.
* Default empty.
* @type bool $clear_destination Whether to delete any files already in the destination
* folder. Default false.
* @type bool $clear_working Whether to delete the files form the working directory
* after copying to the destination. Default false.
* @type bool $abort_if_destination_exists Whether to abort the installation if
* the destination folder already exists. Default true.
* @type array $hook_extra Extra arguments to pass to the filter hooks called by
* WP_Upgrader::install_package(). Default empty array.
* }
*
* @return array|WP_Error The result (also stored in `WP_Upgrader::$result`), or a WP_Error on failure.
*/
public function install_package( $args = array() ) {
global $wp_filesystem, $wp_theme_directories;
$defaults = array(
'source' => '', // Please always pass this
'destination' => '', // and this
'clear_destination' => false,
'clear_working' => false,
'abort_if_destination_exists' => true,
'hook_extra' => array(),
);
$args = wp_parse_args( $args, $defaults );
// These were previously extract()'d.
$source = $args['source'];
$destination = $args['destination'];
$clear_destination = $args['clear_destination'];
wpforms_set_time_limit( 300 );
if ( empty( $source ) || empty( $destination ) ) {
return new WP_Error( 'bad_request', $this->strings['bad_request'] );
}
//$this->skin->feedback( 'installing_package' );
/**
* Filter the install response before the installation has started.
*
* Returning a truthy value, or one that could be evaluated as a WP_Error
* will effectively short-circuit the installation, returning that value
* instead.
*
* @since 2.8.0
*
* @param bool|WP_Error $response Response.
* @param array $hook_extra Extra arguments passed to hooked filters.
*/
$res = apply_filters( 'upgrader_pre_install', true, $args['hook_extra'] );
if ( is_wp_error( $res ) ) {
return $res;
}
// Retain the Original source and destinations.
$remote_source = $args['source'];
$local_destination = $destination;
$source_files = array_keys( $wp_filesystem->dirlist( $remote_source ) );
$remote_destination = $wp_filesystem->find_folder( $local_destination );
$count_source_files = count( $source_files );
// Locate which directory to copy to the new folder, This is based on the actual folder holding the files.
if ( $count_source_files === 1 && $wp_filesystem->is_dir( trailingslashit( $args['source'] ) . $source_files[0] . '/' ) ) { // Only one folder? Then we want its contents.
$source = trailingslashit( $args['source'] ) . trailingslashit( $source_files[0] );
} elseif ( $count_source_files === 0 ) {
return new WP_Error( 'incompatible_archive_empty', $this->strings['incompatible_archive'], $this->strings['no_files'] ); // There are no files?
} else { // It's only a single file, the upgrader will use the folder name of this file as the destination folder. Folder name is based on zip filename.
$source = trailingslashit( $args['source'] );
}
/**
* Filter the source file location for the upgrade package.
*
* @since 2.8.0
* @since 4.4.0 The $hook_extra parameter became available.
*
* @param string $source File source location.
* @param string $remote_source Remote file source location.
* @param WP_Upgrader $this WP_Upgrader instance.
* @param array $hook_extra Extra arguments passed to hooked filters.
*/
$source = apply_filters( 'upgrader_source_selection', $source, $remote_source, $this, $args['hook_extra'] );
if ( is_wp_error( $source ) ) {
return $source;
}
// Has the source location changed? If so, we need a new source_files list.
if ( $source !== $remote_source ) {
$source_files = array_keys( $wp_filesystem->dirlist( $source ) );
}
/*
* Protection against deleting files in any important base directories.
* Theme_Upgrader & Plugin_Upgrader also trigger this, as they pass the
* destination directory (WP_PLUGIN_DIR / wp-content/themes) intending
* to copy the directory into the directory, whilst they pass the source
* as the actual files to copy.
*/
$protected_directories = array( ABSPATH, WP_CONTENT_DIR, WP_PLUGIN_DIR, WP_CONTENT_DIR . '/themes' );
if ( is_array( $wp_theme_directories ) ) {
$protected_directories = array_merge( $protected_directories, $wp_theme_directories );
}
if ( in_array( $destination, $protected_directories ) ) {
$remote_destination = trailingslashit( $remote_destination ) . trailingslashit( basename( $source ) );
$destination = trailingslashit( $destination ) . trailingslashit( basename( $source ) );
}
if ( $clear_destination ) {
// We're going to clear the destination if there's something there.
$removed = $this->clear_destination( $remote_destination );
/**
* Filter whether the upgrader cleared the destination.
*
* @since 2.8.0
*
* @param mixed $removed Whether the destination was cleared. true on success, WP_Error on failure.
* @param string $local_destination The local package destination.
* @param string $remote_destination The remote package destination.
* @param array $hook_extra Extra arguments passed to hooked filters.
*/
$removed = apply_filters( 'upgrader_clear_destination', $removed, $local_destination, $remote_destination, $args['hook_extra'] );
if ( is_wp_error( $removed ) ) {
return $removed;
}
} elseif ( $args['abort_if_destination_exists'] && $wp_filesystem->exists( $remote_destination ) ) {
// If we're not clearing the destination folder and something exists there already, Bail.
// But first check to see if there are actually any files in the folder.
$_files = $wp_filesystem->dirlist( $remote_destination );
if ( ! empty( $_files ) ) {
$wp_filesystem->delete( $remote_source, true ); // Clear out the source files.
return new WP_Error( 'folder_exists', $this->strings['folder_exists'], $remote_destination );
}
}
// Create destination if needed.
if ( ! $wp_filesystem->exists( $remote_destination ) ) {
if ( ! $wp_filesystem->mkdir( $remote_destination, FS_CHMOD_DIR ) ) {
return new WP_Error( 'mkdir_failed_destination', $this->strings['mkdir_failed'], $remote_destination );
}
}
// Copy new version of item into place.
$result = copy_dir( $source, $remote_destination );
if ( is_wp_error( $result ) ) {
if ( $args['clear_working'] ) {
$wp_filesystem->delete( $remote_source, true );
}
return $result;
}
// Clear the Working folder?
if ( $args['clear_working'] ) {
$wp_filesystem->delete( $remote_source, true );
}
$destination_name = basename( str_replace( $local_destination, '', $destination ) );
if ( $destination_name === '.' ) {
$destination_name = '';
}
$this->result = compact( 'source', 'source_files', 'destination', 'destination_name', 'local_destination', 'remote_destination', 'clear_destination' );
/**
* Filter the installation response after the installation has finished.
*
* @since 2.8.0
*
* @param bool $response Installation response.
* @param array $hook_extra Extra arguments passed to hooked filters.
* @param array $result Installation result data.
*/
$res = apply_filters( 'upgrader_post_install', true, $args['hook_extra'], $this->result );
if ( is_wp_error( $res ) ) {
$this->result = $res;
return $res;
}
// Bombard the calling function will all the info which we've just used.
return $this->result;
}
/**
* Install a plugin package.
*
* @since 1.6.3
*
* @param string $package The full local path or URI of the package.
* @param array $args Optional. Other arguments for installing a plugin package. Default empty array.
*
* @return bool|\WP_Error True if the installation was successful, false or a WP_Error otherwise.
*/
public function install( $package, $args = array() ) {
$result = parent::install( $package, $args );
if ( true === $result ) {
do_action( 'wpforms_plugin_installed', $package );
}
return $result;
}
}
Helpers/Transient.php 0000644 00000016402 15133255232 0010632 0 ustar 00 <?php
namespace WPForms\Helpers;
/**
* WPForms Transients implementation.
*
* @since 1.6.3.1
*/
class Transient {
/**
* Transient option name prefix.
*
* @since 1.6.3.1
*
* @var string
*/
const OPTION_PREFIX = '_wpforms_transient_';
/**
* Transient timeout option name prefix.
*
* @since 1.6.3.1
*
* @var string
*/
const TIMEOUT_PREFIX = '_wpforms_transient_timeout_';
/**
* Get the value of a transient.
*
* If the transient does not exist, does not have a value, or has expired,
* then the return value will be false.
*
* @since 1.6.3.1
*
* @param string $transient Transient name. Expected to not be SQL-escaped.
*
* @return mixed Value of transient.
*/
public static function get( $transient ) {
$transient_option = self::OPTION_PREFIX . $transient;
$transient_timeout = self::TIMEOUT_PREFIX . $transient;
$alloptions = wp_load_alloptions();
// If option is not in alloptions, it is not autoloaded and thus has a timeout to check.
if ( ! isset( $alloptions[ $transient_option ] ) ) {
$is_expired = self::is_expired( $transient );
}
// Return the data if it's not expired.
if ( empty( $is_expired ) ) {
return self::get_option( $transient );
}
delete_option( $transient_option );
delete_option( $transient_timeout );
return false;
}
/**
* Set/update the value of a transient.
*
* You do not need to serialize values. If the value needs to be serialized, then
* it will be serialized before it is set.
*
* @since 1.6.3.1
*
* @param string $transient Transient name. Expected to not be SQL-escaped. Must be
* 164 characters or fewer.
* @param mixed $value Transient value. Must be serializable if non-scalar.
* Expected to not be SQL-escaped.
* @param int $expiration Optional. Time until expiration in seconds. Default 0 (no expiration).
*
* @return bool False if value was not set and true if value was set.
*/
public static function set( $transient, $value, $expiration = 0 ) {
if ( false === self::get_option( $transient ) ) {
return self::add( $transient, $value, $expiration );
}
return self::update( $transient, $value, $expiration );
}
/**
* Create a new transient with a given value.
*
* Internal method, use Transient::set() instead.
*
* @since 1.6.3.1
*
* @param string $transient Transient name. Expected to not be SQL-escaped. Must be
* 164 characters or fewer.
* @param mixed $value Transient value. Must be serializable if non-scalar.
* Expected to not be SQL-escaped.
* @param int $expiration Optional. Time until expiration in seconds. Default 0 (no expiration).
*
* @return bool False if value was not set and true if value was set.
*/
private static function add( $transient, $value, $expiration ) {
if ( $expiration ) {
add_option( self::TIMEOUT_PREFIX . $transient, time() + $expiration, '', 'no' );
}
// If there's an expiration, the option won't be autoloaded.
return add_option( self::OPTION_PREFIX . $transient, $value, '', $expiration ? 'no' : 'yes' );
}
/**
* Update the value of a transient.
*
* Internal method, use Transient::set() instead.
*
* @since 1.6.3.1
*
* @param string $transient Transient name. Expected to not be SQL-escaped. Must be
* 164 characters or fewer.
* @param mixed $value Transient value. Must be serializable if non-scalar.
* Expected to not be SQL-escaped.
* @param int $expiration Optional. Time until expiration in seconds. Default 0 (no expiration).
*
* @return bool False if value was not set and true if value was set.
*/
private static function update( $transient, $value, $expiration ) {
$transient_option = self::OPTION_PREFIX . $transient;
$transient_timeout = self::TIMEOUT_PREFIX . $transient;
if ( ! $expiration ) {
return update_option( $transient_option, $value );
}
$timeout = self::get_timeout( $transient );
if ( false !== $timeout ) {
update_option( $transient_timeout, time() + $expiration );
return update_option( $transient_option, $value );
}
// If expiration is requested, but the transient has no timeout option,
// delete, then re-create transient rather than update.
delete_option( $transient_option );
add_option( $transient_timeout, time() + $expiration, '', 'no' );
return add_option( $transient_option, $value, '', 'no' );
}
/**
* Delete a transient.
*
* @since 1.6.3.1
*
* @param string $transient Transient name. Expected to not be SQL-escaped.
*
* @return bool true if successful, false otherwise
*/
public static function delete( $transient ) {
$result = delete_option( self::OPTION_PREFIX . $transient );
if ( $result ) {
delete_option( self::TIMEOUT_PREFIX . $transient );
}
return $result;
}
/**
* Delete all WPForms transients.
*
* @since 1.6.3.1
*
* @return int|false Number of rows affected/selected or false on error
*/
public static function delete_all() {
global $wpdb;
return $wpdb->query( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching
$wpdb->prepare(
"DELETE FROM {$wpdb->options}
WHERE option_name LIKE %s",
$wpdb->esc_like( self::OPTION_PREFIX ) . '%'
)
);
}
/**
* Delete all expired WPForms transients.
*
* The multi-table delete syntax is used to delete the transient record
* from table a, and the corresponding transient_timeout record from table b.
*
* @since 1.6.3.1
*
* @return int|false Number of rows affected/selected or false on error
*/
public static function delete_all_expired() {
global $wpdb;
return $wpdb->query( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching
$wpdb->prepare(
"DELETE a, b FROM {$wpdb->options} a, {$wpdb->options} b
WHERE a.option_name LIKE %s
AND a.option_name NOT LIKE %s
AND b.option_name = CONCAT( %s, SUBSTRING( a.option_name, %d ) )
AND b.option_value < %d",
$wpdb->esc_like( self::OPTION_PREFIX ) . '%',
$wpdb->esc_like( self::TIMEOUT_PREFIX ) . '%',
self::TIMEOUT_PREFIX,
strlen( self::OPTION_PREFIX ) + 1,
time()
)
);
}
/**
* Check if transient is expired.
*
* @since 1.6.3.1
*
* @param string $transient Transient name. Expected to not be SQL-escaped.
*
* @return bool true if expired, false otherwise
*/
public static function is_expired( $transient ) {
$timeout = self::get_timeout( $transient );
// If there's no timeout data found, the transient is considered to be valid.
if ( false === $timeout ) {
return false;
}
if ( $timeout >= time() ) {
return false;
}
return true;
}
/**
* Get a transient option value.
*
* @since 1.6.3.1
*
* @param string $transient Transient name. Expected to not be SQL-escaped.
*
* @return mixed Value set for the option.
*/
private static function get_option( $transient ) {
return get_option( self::OPTION_PREFIX . $transient );
}
/**
* Get a transient timeout option value.
*
* @since 1.6.3.1
*
* @param string $transient Transient name. Expected to not be SQL-escaped.
*
* @return mixed Value set for the option.
*/
private static function get_timeout( $transient ) {
return get_option( self::TIMEOUT_PREFIX . $transient );
}
}
Helpers/Crypto.php 0000644 00000005552 15133255232 0010147 0 ustar 00 <?php
namespace WPForms\Helpers;
/**
* Class for encryption functionality.
*
* @since 1.6.1.2
*
* @link https://www.php.net/manual/en/intro.sodium.php
*/
class Crypto {
/**
* Get a secret key for encrypt/decrypt.
*
* @since 1.6.1.2
*
* @return string
*/
public static function get_secret_key() {
$secret_key = get_option( 'wpforms_crypto_secret_key' );
// If we already have the secret, send it back.
if ( false !== $secret_key ) {
return base64_decode( $secret_key ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
}
// We don't have a secret, so let's generate one.
$secret_key = sodium_crypto_secretbox_keygen();
add_option( 'wpforms_crypto_secret_key', base64_encode( $secret_key ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
return $secret_key;
}
/**
* Encrypt a message.
*
* @since 1.6.1.2
*
* @param string $message Message to encrypt.
* @param string $key Encryption key.
*
* @return string
*/
public static function encrypt( $message, $key = '' ) {
// Create a nonce for this operation. It will be stored and recovered in the message itself.
$nonce = random_bytes(
SODIUM_CRYPTO_SECRETBOX_NONCEBYTES
);
if ( empty( $key ) ) {
$key = self::get_secret_key();
}
// Encrypt message and combine with nonce.
$cipher = base64_encode( // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
$nonce .
sodium_crypto_secretbox(
$message,
$nonce,
$key
)
);
try {
sodium_memzero( $message );
sodium_memzero( $key );
} catch ( \Exception $e ) {
return $cipher;
}
return $cipher;
}
/**
* Decrypt a message.
*
* @since 1.6.1.2
*
* @param string $encrypted Encrypted message.
* @param string $key Encryption key.
*
* @return string
*/
public static function decrypt( $encrypted, $key = '' ) {
// Unpack base64 message.
$decoded = base64_decode( $encrypted ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
if ( false === $decoded ) {
return false;
}
if ( mb_strlen( $decoded, '8bit' ) < ( SODIUM_CRYPTO_SECRETBOX_NONCEBYTES + SODIUM_CRYPTO_SECRETBOX_MACBYTES ) ) {
return false;
}
// Pull nonce and ciphertext out of unpacked message.
$nonce = mb_substr( $decoded, 0, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, '8bit' );
$ciphertext = mb_substr( $decoded, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, null, '8bit' );
if ( empty( $key ) ) {
$key = self::get_secret_key();
}
// Decrypt it.
$message = sodium_crypto_secretbox_open(
$ciphertext,
$nonce,
$key
);
// Check for decrpytion failures.
if ( false === $message ) {
return false;
}
try {
sodium_memzero( $ciphertext );
sodium_memzero( $key );
} catch ( \Exception $e ) {
return $message;
}
return $message;
}
}
Helpers/CacheBase.php 0000644 00000012747 15133255232 0010471 0 ustar 00 <?php
namespace WPForms\Helpers;
/**
* Remote data cache handler.
*
* Usage example in `WPForms\Admin\Addons\AddonsCache` and `WPForms\Admin\Builder\TemplatesCache`.
*
* @since 1.6.8
*/
abstract class CacheBase {
/**
* Indicates whether the cache was updated during the current run.
*
* @since 1.6.8
*
* @var bool
*/
protected static $updated = false;
/**
* Settings.
*
* @since 1.6.8
*
* @var array
*/
protected $settings;
/**
* Determine if the class is allowed to load.
*
* @since 1.6.8
*
* @return bool
*/
abstract protected function allow_load();
/**
* Initialize.
*
* @since 1.6.8
*/
public function init() {
if ( ! $this->allow_load() ) {
return;
}
$this->update_settings();
// Quit if settings didn't provided.
if (
empty( $this->settings['remote_source'] ) ||
empty( $this->settings['cache_file'] )
) {
return;
}
$this->hooks();
}
/**
* Base hooks.
*
* @since 1.6.8
*/
private function hooks() {
add_action( 'shutdown', [ $this, 'cache_dir_complete' ] );
if ( empty( $this->settings['update_action'] ) ) {
return;
}
// Schedule recurring updates.
add_action( 'admin_init', [ $this, 'schedule_update_cache' ] );
add_action( $this->settings['update_action'], [ $this, 'update_cache' ] );
}
/**
* Set up settings.
*
* @since 1.6.8
*/
private function update_settings() {
$default_settings = [
// Remote source URL.
// For instance: 'https://wpforms.com/wp-content/addons.json'.
'remote_source' => '',
// Cache file.
// Just file name. For instance: 'addons.json'.
'cache_file' => '',
// Cache time to live in seconds.
'cache_ttl' => WEEK_IN_SECONDS,
// Scheduled update action.
// For instance: 'wpforms_admin_addons_cache_update'.
'update_action' => '',
];
$this->settings = wp_parse_args( $this->setup(), $default_settings );
}
/**
* Provide settings.
*
* @since 1.6.8
*
* @return array Settings array.
*/
abstract protected function setup();
/**
* Get cache directory path.
*
* @since 1.6.8
*/
protected function get_cache_dir() {
$upload_dir = wpforms_upload_dir();
$upload_path = ! empty( $upload_dir['path'] )
? trailingslashit( wp_normalize_path( $upload_dir['path'] ) )
: trailingslashit( WP_CONTENT_DIR ) . 'uploads/wpforms/';
return $upload_path . 'cache/';
}
/**
* Get cached data.
*
* @since 1.6.8
*
* @return array Cached data.
*/
public function get_cached() {
$cache_modified_time = 0;
$current_time = time();
$cache_file = $this->get_cache_dir() . $this->settings['cache_file'];
if ( is_file( $cache_file ) && is_readable( $cache_file ) ) {
clearstatcache( true, $cache_file );
$cache_modified_time = (int) filemtime( $cache_file );
$data = json_decode( file_get_contents( $cache_file ), true );
}
if (
! empty( $data ) &&
$cache_modified_time + $this->settings['cache_ttl'] > $current_time
) {
return $data;
}
// This code should execute when the method was called for the first time,
// Next update_cache() should be executed as scheduled.
// Also, we will try to update the cache only if the latest unsuccessful try has been 10 (or more) minutes ago.
if ( $cache_modified_time + 600 < $current_time ) {
return $this->update_cache();
}
return [];
}
/**
* Update cached data with actual data retrieved from the remote source.
*
* @since 1.6.8
*
* @return array
*/
public function update_cache() {
$wpforms_key = 'lite';
if ( wpforms()->is_pro() ) {
$wpforms_key = wpforms_get_license_key();
}
$request = wp_remote_get(
add_query_arg( 'tgm-updater-key', $wpforms_key, $this->settings['remote_source'] ),
[
'timeout' => 10,
'user-agent' => wpforms_get_default_user_agent(),
]
);
if ( is_wp_error( $request ) ) {
return [];
}
$json = wp_remote_retrieve_body( $request );
if ( empty( $json ) ) {
return [];
}
$data = $this->prepare_cache_data( json_decode( $json, true ) );
$dir = $this->get_cache_dir();
// Just return the data if can't create the cache directory.
if ( ! wp_mkdir_p( $dir ) ) {
return $data;
}
// phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_file_put_contents
file_put_contents(
$dir . $this->settings['cache_file'],
wp_json_encode( $data )
);
self::$updated = true;
return $data;
}
/**
* Schedule updates.
*
* @since 1.6.8
*/
public function schedule_update_cache() {
// Just skip if not need to register scheduled action.
if ( empty( $this->settings['update_action'] ) ) {
return;
}
$tasks = wpforms()->get( 'tasks' );
if ( $tasks->is_scheduled( $this->settings['update_action'] ) !== false ) {
return;
}
$tasks->create( $this->settings['update_action'] )
->recurring( time() + $this->settings['cache_ttl'], $this->settings['cache_ttl'] )
->params()
->register();
}
/**
* Complete the cache directory.
*
* @since 1.6.8
*/
public function cache_dir_complete() {
if ( ! self::$updated ) {
return;
}
wpforms_create_upload_dir_htaccess_file();
wpforms_create_index_html_file( $this->get_cache_dir() );
}
/**
* Prepare data to store in a local cache.
*
* @since 1.6.8
*
* @param array $data Raw data received by the remote request.
*
* @return array Prepared data for caching.
*/
protected function prepare_cache_data( $data ) {
if ( empty( $data ) || ! is_array( $data ) ) {
return [];
}
return $data;
}
}
Helpers/PluginSilentUpgraderSkin.php 0000644 00000002224 15133255232 0013614 0 ustar 00 <?php
namespace WPForms\Helpers;
/** \WP_Upgrader_Skin class */
require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader-skin.php';
/**
* Class PluginSilentUpgraderSkin.
*
* @internal Please do not use this class outside of core WPForms development. May be removed at any time.
*
* @since 1.5.6.1
*/
class PluginSilentUpgraderSkin extends \WP_Upgrader_Skin {
/**
* Empty out the header of its HTML content and only check to see if it has
* been performed or not.
*
* @since 1.5.6.1
*/
public function header() {
}
/**
* Empty out the footer of its HTML contents.
*
* @since 1.5.6.1
*/
public function footer() {
}
/**
* Instead of outputting HTML for errors, just return them.
* Ajax request will just ignore it.
*
* @since 1.5.6.1
*
* @param array $errors Array of errors with the install process.
*
* @return array
*/
public function error( $errors ) {
return $errors;
}
/**
* Empty out JavaScript output that calls function to decrement the update counts.
*
* @since 1.5.6.1
*
* @param string $type Type of update count to decrement.
*/
public function decrement_update_count( $type ) {
}
}
Emails/Styler.php 0000644 00000005123 15133255232 0007753 0 ustar 00 <?php
namespace WPForms\Emails;
use TijsVerkoyen\CssToInlineStyles\CssToInlineStyles;
use WPForms\Helpers\Templates;
/**
* Styler class inline style email templates.
*
* @since 1.5.4
*/
class Styler {
/**
* Email message with no styles.
*
* @since 1.5.4
*
* @var string
*/
protected $email;
/**
* Email style templates names.
*
* @since 1.5.4
*
* @var array
*/
protected $style_templates;
/**
* Email style overrides.
*
* @since 1.5.4
*
* @var array
*/
protected $style_overrides;
/**
* Email message with inline styles.
*
* @since 1.5.4
*
* @var string
*/
protected $styled_email;
/**
* Constructor.
*
* @since 1.5.4
*
* @param string $email Email with no styles.
* @param array $style_templates Email style templates.
* @param array $style_overrides Email style overrides.
*/
public function __construct( $email, $style_templates, $style_overrides ) {
$this->email = $email;
$this->style_templates = \is_array( $style_templates ) ? $style_templates : array();
$this->style_overrides = \is_array( $style_overrides ) ? $style_overrides : array();
}
/**
* Template style overrides.
*
* @since 1.5.4
*
* @return array
*/
protected function get_style_overrides() {
$defaults = array(
'email_background_color' => \wpforms_setting( 'email-background-color', '#e9eaec' ),
);
$overrides = \wp_parse_args( $this->style_overrides, $defaults );
return \apply_filters( 'wpforms_emails_mailer_get_style_overrides', $overrides, $this );
}
/**
* Locate template name matching styles.
*
* @since 1.5.4
*
* @param string $name Template file name part.
*
* @return string
*/
protected function get_styles( $name = 'style' ) {
if ( ! \array_key_exists( $name, $this->style_templates ) ) {
return '';
}
return Templates::get_html(
$this->style_templates[ $name ],
$this->get_style_overrides(),
true
);
}
/**
* Final processing of the template markup.
*
* @since 1.5.4
*/
public function process_markup() {
$this->styled_email = ( new CssToInlineStyles() )->convert( $this->email, $this->get_styles() );
$queries = '<style type="text/css">' . $this->get_styles( 'queries' ) . "</style>\n</head>";
// Inject media queries, CssToInlineStyles strips them.
$this->styled_email = \str_replace( '</head>', $queries, $this->styled_email );
}
/**
* Get an email with inline styles.
*
* @since 1.5.4
*
* @return string
*/
public function get() {
if ( empty( $this->styled_email ) ) {
$this->process_markup();
}
return $this->styled_email;
}
}
Emails/Templates/Summary.php 0000644 00000001573 15133255232 0012071 0 ustar 00 <?php
namespace WPForms\Emails\Templates;
/**
* Email Summaries email template class.
*
* @since 1.5.4
*/
class Summary extends General {
/**
* Template slug.
*
* @since 1.5.4
*
* @var string
*/
const TEMPLATE_SLUG = 'summary';
/**
* Get header image URL from settings.
*
* @since 1.5.4
*
* @return array
*/
protected function get_header_image() {
$img = array(
'url' => \wpforms_setting( 'email-header-image' ),
);
if ( ! empty( $img['url'] ) ) {
return $img;
}
// Set specific percentage WPForms logo width for modern email clients.
$this->set_args(
array(
'style' => array(
'header_image_max_width' => '45%',
),
)
);
// Set specific WPForms logo width in pixels for MS Outlook and old email clients.
return array(
'url' => \WPFORMS_PLUGIN_URL . 'assets/images/logo.png',
'width' => 250,
);
}
}
Emails/Templates/General.php 0000644 00000021446 15133255232 0012012 0 ustar 00 <?php
namespace WPForms\Emails\Templates;
use WPForms\Emails\Styler;
use WPForms\Helpers\Templates;
/**
* Base email template class.
*
* @since 1.5.4
*/
class General {
/**
* Template slug.
*
* @since 1.5.4
*
* @var string
*/
const TEMPLATE_SLUG = 'general';
/**
* Email message.
*
* @since 1.5.4
*
* @var string
*/
protected $message;
/**
* Content is plain text type.
*
* @since 1.5.4
*
* @var bool
*/
protected $plain_text;
/**
* Dynamic {{tags}}.
*
* @since 1.5.4
*
* @var array
*/
protected $tags;
/**
* Header/footer/body arguments.
*
* @since 1.5.4
*
* @var array
*/
protected $args;
/**
* Final email content.
*
* @since 1.5.4
*
* @var string
*/
protected $content;
/**
* Constructor.
*
* @since 1.5.4
*
* @param string $message Email message.
*/
public function __construct( $message = '' ) {
$this->set_message( $message );
$this->plain_text = 'default' !== \wpforms_setting( 'email-template', 'default' );
$this->set_initial_args();
}
/**
* Set initial arguments to use in a template.
*
* @since 1.5.4
*/
public function set_initial_args() {
$header_args = array(
'title' => \esc_html__( 'WPForms', 'wpforms-lite' ),
);
if ( ! $this->plain_text ) {
$header_args['header_image'] = $this->get_header_image();
}
$args = array(
'header' => $header_args,
'body' => array( 'message' => $this->get_message() ),
'footer' => array(),
'style' => array(),
);
$args = \apply_filters( 'wpforms_emails_templates_general_set_initial_args', $args, $this );
$this->set_args( $args );
}
/**
* Get the template slug.
*
* @since 1.5.4
*
* @return string
*/
public function get_slug() {
return static::TEMPLATE_SLUG;
}
/**
* Get the template parent slug.
*
* @since 1.5.4
*
* @return string
*/
public function get_parent_slug() {
return self::TEMPLATE_SLUG;
}
/**
* Get the message.
*
* @since 1.5.4
*
* @return string
*/
public function get_message() {
return \apply_filters( 'wpforms_emails_templates_general_get_message', $this->message, $this );
}
/**
* Get the dynamic tags.
*
* @since 1.5.4
*
* @return array
*/
public function get_tags() {
return \apply_filters( 'wpforms_emails_templates_general_get_tags', $this->tags, $this );
}
/**
* Get header/footer/body arguments
*
* @since 1.5.4
*
* @param string $type Header/footer/body.
*
* @return array
*/
public function get_args( $type ) {
if ( ! empty( $type ) ) {
return isset( $this->args[ $type ] ) ? \apply_filters( 'wpforms_emails_templates_general_get_args_' . $type, $this->args[ $type ], $this ) : array();
}
return \apply_filters( 'wpforms_emails_templates_general_get_args', $this->args, $this );
}
/**
* Set email message.
*
* @since 1.5.4
*
* @param string $message Email message.
*
* @return General
*/
public function set_message( $message ) {
$message = \apply_filters( 'wpforms_emails_templates_general_set_message', $message, $this );
if ( ! \is_string( $message ) ) {
return $this;
}
$this->message = $message;
return $this;
}
/**
* Set the dynamic tags.
*
* @since 1.5.4
*
* @param array $tags Tags to set.
*
* @return General
*/
public function set_tags( $tags ) {
$tags = \apply_filters( 'wpforms_emails_templates_general_set_tags', $tags, $this );
if ( ! \is_array( $tags ) ) {
return $this;
}
$this->tags = $tags;
return $this;
}
/**
* Set header/footer/body/style arguments to use in a template.
*
* @since 1.5.4
*
* @param array $args Arguments to set.
* @param bool $merge Merge the arguments with existing once or replace.
*
* @return General
*/
public function set_args( $args, $merge = true ) {
$args = \apply_filters( 'wpforms_emails_templates_general_set_args', $args, $this );
if ( empty( $args ) || ! \is_array( $args ) ) {
return $this;
}
foreach ( $args as $type => $value ) {
if ( ! \is_array( $value ) ) {
continue;
}
if ( ! isset( $this->args[ $type ] ) || ! \is_array( $this->args[ $type ] ) ) {
$this->args[ $type ] = array();
}
$this->args[ $type ] = $merge ? \array_merge( $this->args[ $type ], $value ) : $value;
}
return $this;
}
/**
* Process and replace any dynamic tags.
*
* @since 1.5.4
*
* @param string $content Content to make replacements in.
*
* @return string
*/
public function process_tags( $content ) {
$tags = $this->get_tags();
if ( empty( $tags ) ) {
return $content;
}
foreach ( $tags as $tag => $value ) {
$content = \str_replace( $tag, $value, $content );
}
return $content;
}
/**
* Conditionally modify email template name.
*
* @since 1.5.4
*
* @param string $name Base template name.
*
* @return string
*/
protected function get_full_template_name( $name ) {
$name = \sanitize_file_name( $name );
if ( $this->plain_text ) {
$name .= '-plain';
}
$template = 'emails/' . $this->get_slug() . '-' . $name;
if ( ! Templates::locate( $template . '.php' ) ) {
$template = 'emails/' . $this->get_parent_slug() . '-' . $name;
}
return \apply_filters( 'wpforms_emails_templates_general_get_full_template_name', $template, $this );
}
/**
* Get header image URL from settings.
*
* @since 1.5.4
*
* @return array
*/
protected function get_header_image() {
/**
* Additional 'width' key with an integer value can be added to $img array to control image's width in pixels.
* This setting helps to scale an image in some versions of MS Outlook and old email clients.
* Percentage 'width' values have no effect in MS Outlook and will be sanitized as integer by an email template..
*
* Example:
*
* $img = array(
* 'url' => \wpforms_setting( 'email-header-image' ),
* 'width' => 150,
* );
*
*
* To set percentage values for the modern email clients, use $this->set_args() method:
*
* $this->set_args(
* array(
* 'style' => array(
* 'header_image_max_width' => '45%',
* ),
* )
*);
*
* Both pixel and percentage approaches work well with 'wpforms_emails_templates_general_get_header_image' filter or this class extension.
*/
$img = array(
'url' => \wpforms_setting( 'email-header-image' ),
);
return \apply_filters( 'wpforms_emails_templates_general_get_header_image', $img, $this );
}
/**
* Get content part HTML.
*
* @since 1.5.4
*
* @param string $name Name of the content part.
*
* @return string
*/
protected function get_content_part( $name ) {
if ( ! \is_string( $name ) ) {
return '';
}
$html = Templates::get_html(
$this->get_full_template_name( $name ),
$this->get_args( $name ),
true
);
return \apply_filters( 'wpforms_emails_templates_general_get_content_part', $html, $name, $this );
}
/**
* Assemble all content parts in an array.
*
* @since 1.5.4
*
* @return array
*/
protected function get_content_parts() {
$parts = array(
'header' => $this->get_content_part( 'header' ),
'body' => $this->get_content_part( 'body' ),
'footer' => $this->get_content_part( 'footer' ),
);
return \apply_filters( 'wpforms_emails_templates_general_get_content_parts', $parts, $this );
}
/**
* Apply inline styling and save email content.
*
* @since 1.5.4
*
* @param string $content Content with no styling applied.
*/
protected function save_styled( $content ) {
if ( empty( $content ) ) {
$this->content = '';
return;
}
if ( $this->plain_text ) {
$this->content = \wp_strip_all_tags( $content );
return;
}
$style_templates = array(
'style' => $this->get_full_template_name( 'style' ),
'queries' => $this->get_full_template_name( 'queries' ),
);
$styler = new Styler( $content, $style_templates, $this->get_args( 'style' ) );
$this->content = \apply_filters( 'wpforms_emails_templates_general_save_styled_content', $styler->get(), $this );
}
/**
* Build an email including styling.
*
* @since 1.5.4
*
* @param bool $force Rebuild the content if it was already built and saved.
*/
protected function build( $force = false ) {
if ( $this->content && ! $force ) {
return;
}
$content = \implode( $this->get_content_parts() );
if ( empty( $content ) ) {
return;
}
$content = $this->process_tags( $content );
if ( ! $this->plain_text ) {
$content = \make_clickable( $content );
}
$content = \apply_filters( 'wpforms_emails_templates_general_build_content', $content, $this );
$this->save_styled( $content );
}
/**
* Return final email.
*
* @since 1.5.4
*
* @param bool $force Rebuild the content if it was already built and saved.
*
* @return string
*/
public function get( $force = false ) {
$this->build( $force );
return $this->content;
}
}
Emails/Mailer.php 0000644 00000024076 15133255232 0007712 0 ustar 00 <?php
namespace WPForms\Emails;
use WPForms\Emails\Templates\General;
/**
* Mailer class to wrap wp_mail().
*
* @since 1.5.4
*/
class Mailer {
/**
* Array or comma-separated list of email addresses to send message.
*
* @since 1.5.4
*
* @var string|string[]
*/
private $to_email;
/**
* CC addresses (comma delimited).
*
* @since 1.5.4
*
* @var string
*/
private $cc;
/**
* From address.
*
* @since 1.5.4
*
* @var string
*/
private $from_address;
/**
* From name.
*
* @since 1.5.4
*
* @var string
*/
private $from_name;
/**
* Reply to address.
*
* @since 1.5.4
*
* @var string
*/
private $reply_to;
/**
* Email headers.
*
* @since 1.5.4
*
* @var string
*/
private $headers;
/**
* Email content type.
*
* @since 1.5.4
*
* @var string
*/
private $content_type;
/**
* Email attachments.
*
* @since 1.5.4
*
* @var string
*/
private $attachments;
/**
* Email subject.
*
* @since 1.5.4
*
* @var string
*/
private $subject;
/**
* Email message.
*
* @since 1.5.4
*
* @var string
*/
private $message;
/**
* Email template.
*
* @since 1.5.4
*
* @var General
*/
private $template;
/**
* Set a property.
*
* @since 1.5.4
*
* @param string $key Property name.
* @param string $value Property value.
*/
public function __set( $key, $value ) {
$this->$key = $value;
}
/**
* Get a property.
*
* @since 1.5.4
*
* @param string $key Property name.
*
* @return string
*/
public function __get( $key ) {
return $this->$key;
}
/**
* Check if a property exists.
*
* @since 1.5.4
*
* @param string $key Property name.
*
* @return bool
*/
public function __isset( $key ) {
return isset( $this->key );
}
/**
* Unset a property.
*
* @since 1.5.4
*
* @param string $key Property name.
*/
public function __unset( $key ) {
unset( $this->key );
}
/**
* Email kill switch if needed.
*
* @since 1.5.4
*
* @return bool
*/
public function is_email_disabled() {
return (bool) \apply_filters( 'wpforms_emails_mailer_is_email_disabled', false, $this );
}
/**
* Sanitize the string.
*
* @uses \wpforms_decode_string()
*
* @since 1.5.4
* @since 1.6.0 Deprecated param: $linebreaks. This is handled by wpforms_decode_string().
*
* @param string $string String that may contain tags.
*
* @return string
*/
public function sanitize( $string = '' ) {
return \wpforms_decode_string( $string );
}
/**
* Get the email from name.
*
* @since 1.5.4
*
* @return string
*/
public function get_from_name() {
$this->from_name = $this->from_name ? $this->sanitize( $this->from_name ) : \get_bloginfo( 'name' );
return \apply_filters( 'wpforms_emails_mailer_get_from_name', $this->from_name, $this );
}
/**
* Get the email from address.
*
* @since 1.5.4
*
* @return string
*/
public function get_from_address() {
$this->from_address = $this->from_address ? $this->sanitize( $this->from_address ) : \get_option( 'admin_email' );
return \apply_filters( 'wpforms_emails_mailer_get_from_address', $this->from_address, $this );
}
/**
* Get the email reply to address.
*
* @since 1.5.4
*
* @return string
*/
public function get_reply_to_address() {
if ( empty( $this->reply_to ) || ! \is_email( $this->reply_to ) ) {
$this->reply_to = $this->from_address;
}
$this->reply_to = $this->sanitize( $this->reply_to );
if ( empty( $this->reply_to ) || ! \is_email( $this->reply_to ) ) {
$this->reply_to = \get_option( 'admin_email' );
}
return \apply_filters( 'wpforms_emails_mailer_get_reply_to_address', $this->reply_to, $this );
}
/**
* Get the email carbon copy addresses.
*
* @since 1.5.4
*
* @return string The email carbon copy addresses.
*/
public function get_cc_address() {
if ( empty( $this->cc ) ) {
return \apply_filters( 'wpforms_emails_mailer_get_cc_address', $this->cc, $this );
}
$this->cc = $this->sanitize( $this->cc );
$addresses = \array_map( 'trim', \explode( ',', $this->cc ) );
foreach ( $addresses as $key => $address ) {
if ( ! \is_email( $address ) ) {
unset( $addresses[ $key ] );
}
}
$this->cc = \implode( ',', $addresses );
return \apply_filters( 'wpforms_emails_mailer_get_cc_address', $this->cc, $this );
}
/**
* Get the email content type.
*
* @since 1.5.4
*
* @return string The email content type.
*/
public function get_content_type() {
$is_html = 'default' === \wpforms_setting( 'email-template', 'default' );
if ( ! $this->content_type && $is_html ) {
$this->content_type = \apply_filters( 'wpforms_emails_mailer_get_content_type_default', 'text/html', $this );
} elseif ( ! $is_html ) {
$this->content_type = 'text/plain';
}
return \apply_filters( 'wpforms_emails_mailer_get_content_type', $this->content_type, $this );
}
/**
* Get the email message.
*
* @since 1.5.4
*
* @return string The email message.
*/
public function get_message() {
if ( empty( $this->message ) && ! empty( $this->template ) ) {
$this->message = $this->template->get();
}
return \apply_filters( 'wpforms_emails_mailer_get_message', $this->message, $this );
}
/**
* Get the email headers.
*
* @since 1.5.4
*
* @return string The email headers.
*/
public function get_headers() {
if ( $this->headers ) {
return \apply_filters( 'wpforms_emails_mailer_get_headers', $this->headers, $this );
}
$this->headers = "From: {$this->get_from_name()} <{$this->get_from_address()}>\r\n";
if ( $this->get_reply_to_address() ) {
$this->headers .= "Reply-To: {$this->get_reply_to_address()}\r\n";
}
if ( $this->get_cc_address() ) {
$this->headers .= "Cc: {$this->get_cc_address()}\r\n";
}
$this->headers .= "Content-Type: {$this->get_content_type()}; charset=utf-8\r\n";
return \apply_filters( 'wpforms_emails_mailer_get_headers', $this->headers, $this );
}
/**
* Get the email attachments.
*
* @since 1.5.4
*
* @return string
*/
public function get_attachments() {
return \apply_filters( 'wpforms_emails_mailer_get_attachments', $this->attachments, $this );
}
/**
* Set email address to send to.
*
* @since 1.5.4
*
* @param string|string[] $email Array or comma-separated list of email addresses to send message.
*
* @return Mailer
*/
public function to_email( $email ) {
if ( is_string( $email ) ) {
$email = explode( ',', $email );
}
$this->to_email = \apply_filters( 'wpforms_emails_mailer_to_email', $email, $this );
return $this;
}
/**
* Set email subject.
*
* @since 1.5.4
*
* @param string $subject Email subject.
*
* @return Mailer
*/
public function subject( $subject ) {
$subject = $this->sanitize( $subject );
$this->subject = \apply_filters( 'wpforms_emails_mailer_subject', $subject, $this );
return $this;
}
/**
* Set email message (body).
*
* @since 1.5.4
*
* @param string $message Email message.
*
* @return Mailer
*/
public function message( $message ) {
$this->message = \apply_filters( 'wpforms_emails_mailer_message', $message, $this );
return $this;
}
/**
* Set email template.
*
* @since 1.5.4
*
* @param General $template Email template.
*
* @return Mailer
*/
public function template( General $template ) {
$this->template = \apply_filters( 'wpforms_emails_mailer_template', $template, $this );
return $this;
}
/**
* Get email errors.
*
* @since 1.5.4
*
* @return array
*/
protected function get_errors() {
$errors = [];
foreach ( (array) $this->to_email as $email ) {
if ( ! \is_email( $email ) ) {
$errors[] = sprintf( /* translators: %s - invalid email. */ esc_html__( '[WPForms\Emails\Mailer] Invalid email address %s.', 'wpforms-lite' ), $email );
}
}
if ( empty( $this->subject ) ) {
$errors[] = \esc_html__( '[WPForms\Emails\Mailer] Empty subject line.', 'wpforms-lite' );
}
if ( empty( $this->get_message() ) ) {
$errors[] = \esc_html__( '[WPForms\Emails\Mailer] Empty message.', 'wpforms-lite' );
}
return $errors;
}
/**
* Log given email errors.
*
* @since 1.5.4
*
* @param array $errors Errors to log.
*/
protected function log_errors( $errors ) {
if ( empty( $errors ) || ! \is_array( $errors ) ) {
return;
}
foreach ( $errors as $error ) {
\wpforms_log(
$error,
array(
'to_email' => $this->to_email,
'subject' => $this->subject,
'message' => \wp_trim_words( $this->get_message() ),
),
array(
'type' => 'error',
)
);
}
}
/**
* Send the email.
*
* @since 1.5.4
*
* @return bool
*/
public function send() {
if ( ! \did_action( 'init' ) && ! \did_action( 'admin_init' ) ) {
\_doing_it_wrong( __FUNCTION__, \esc_html__( 'You cannot send emails with WPForms\Emails\Mailer until init/admin_init has been reached.', 'wpforms-lite' ), null );
return false;
}
// Don't send anything if emails have been disabled.
if ( $this->is_email_disabled() ) {
return false;
}
$errors = $this->get_errors();
if ( $errors ) {
$this->log_errors( $errors );
return false;
}
$this->send_before();
$sent = \wp_mail(
$this->to_email,
$this->subject,
$this->get_message(),
$this->get_headers(),
$this->get_attachments()
);
$this->send_after();
return $sent;
}
/**
* Add filters / actions before the email is sent.
*
* @since 1.5.4
*/
public function send_before() {
\do_action( 'wpforms_emails_mailer_send_before', $this );
\add_filter( 'wp_mail_from', array( $this, 'get_from_address' ) );
\add_filter( 'wp_mail_from_name', array( $this, 'get_from_name' ) );
\add_filter( 'wp_mail_content_type', array( $this, 'get_content_type' ) );
}
/**
* Remove filters / actions after the email is sent.
*
* @since 1.5.4
*/
public function send_after() {
\do_action( 'wpforms_emails_mailer_send_after', $this );
\remove_filter( 'wp_mail_from', array( $this, 'get_from_address' ) );
\remove_filter( 'wp_mail_from_name', array( $this, 'get_from_name' ) );
\remove_filter( 'wp_mail_content_type', array( $this, 'get_content_type' ) );
}
}
Emails/FetchInfoBlocksTask.php 0000644 00000004111 15133255232 0012313 0 ustar 00 <?php
namespace WPForms\Emails;
use WPForms\Tasks\Task;
/**
* Action Scheduler task to fetch and cache Email Summaries Info Blocks.
*
* @since 1.6.4
*/
class FetchInfoBlocksTask extends Task {
/**
* Action name for this task.
*
* @since 1.6.4
*/
const ACTION = 'wpforms_email_summaries_fetch_info_blocks';
/**
* Option name to store the timestamp of the last run.
*
* @since 1.6.4
*/
const LAST_RUN = 'wpforms_email_summaries_fetch_info_blocks_last_run';
/**
* Class constructor.
*
* @since 1.6.4
*/
public function __construct() {
parent::__construct( self::ACTION );
$this->init();
}
/**
* Initialize the task with all the proper checks.
*
* @since 1.6.4
*/
public function init() {
$this->hooks();
$tasks = wpforms()->get( 'tasks' );
// Add new if none exists.
if ( $tasks->is_scheduled( self::ACTION ) !== false ) {
return;
}
$this->recurring( $this->generate_start_date(), WEEK_IN_SECONDS )->register();
}
/**
* Add hooks.
*
* @since 1.7.3
*/
private function hooks() {
// Register the action handler.
add_action( self::ACTION, [ $this, 'process' ] );
}
/**
* Randomly pick a timestamp which is not more than 1 week in the future
* starting before Email Summaries dispatch happens.
*
* @since 1.6.4
*
* @return int
*/
private function generate_start_date() {
$tracking = [];
$tracking['days'] = wp_rand( 0, 6 ) * DAY_IN_SECONDS;
$tracking['hours'] = wp_rand( 0, 23 ) * HOUR_IN_SECONDS;
$tracking['minutes'] = wp_rand( 0, 59 ) * MINUTE_IN_SECONDS;
$tracking['seconds'] = wp_rand( 0, 59 );
return strtotime( 'previous monday 1pm' ) + array_sum( $tracking );
}
/**
* Process the task.
*
* @since 1.6.4
*/
public function process() {
$last_run = get_option( self::LAST_RUN );
// Make sure we do not run it more than once a day.
if (
$last_run !== false &&
( time() - $last_run ) < DAY_IN_SECONDS
) {
return;
}
( new InfoBlocks() )->cache_all();
// Update the last run option to the current timestamp.
update_option( self::LAST_RUN, time() );
}
}
Emails/Summaries.php 0000644 00000016472 15133255232 0010447 0 ustar 00 <?php
namespace WPForms\Emails;
/**
* Email Summaries main class.
*
* @since 1.5.4
*/
class Summaries {
/**
* Constructor.
*
* @since 1.5.4
*/
public function __construct() {
$this->hooks();
$summaries_disabled = $this->is_disabled();
if ( $summaries_disabled && \wp_next_scheduled( 'wpforms_email_summaries_cron' ) ) {
\wp_clear_scheduled_hook( 'wpforms_email_summaries_cron' );
}
if ( ! $summaries_disabled && ! \wp_next_scheduled( 'wpforms_email_summaries_cron' ) ) {
\wp_schedule_event( $this->get_first_cron_date_gmt(), 'wpforms_email_summaries_weekly', 'wpforms_email_summaries_cron' );
}
}
/**
* Get the instance of a class and store it in itself.
*
* @since 1.5.4
*/
public static function get_instance() {
static $instance;
if ( ! $instance ) {
$instance = new self();
}
return $instance;
}
/**
* Email Summaries hooks.
*
* @since 1.5.4
*/
public function hooks() {
\add_filter( 'wpforms_settings_defaults', array( $this, 'disable_summaries_setting' ) );
\add_action( 'wpforms_settings_updated', array( $this, 'deregister_fetch_info_blocks_task' ) );
if ( ! $this->is_disabled() ) {
\add_action( 'init', array( $this, 'preview' ) );
\add_filter( 'cron_schedules', array( $this, 'add_weekly_cron_schedule' ) );
\add_action( 'wpforms_email_summaries_cron', array( $this, 'cron' ) );
\add_filter( 'wpforms_tasks_get_tasks', array( $this, 'register_fetch_info_blocks_task' ) );
}
}
/**
* Check if Email Summaries are disabled in settings.
*
* @since 1.5.4
*
* @return bool
*/
protected function is_disabled() {
return (bool) apply_filters( 'wpforms_emails_summaries_is_disabled', (bool) \wpforms_setting( 'email-summaries-disable' ) );
}
/**
* Add "Disable Email Summaries" to WPForms settings.
*
* @since 1.5.4
*
* @param array $settings WPForms settings.
*
* @return mixed
*/
public function disable_summaries_setting( $settings ) {
if ( (bool) apply_filters( 'wpforms_emails_summaries_is_disabled', false ) ) {
return $settings;
}
$url = \add_query_arg(
array(
'wpforms_email_template' => 'summary',
'wpforms_email_preview' => '1',
),
\admin_url()
);
$desc = \esc_html__( 'Disable Email Summaries weekly delivery.', 'wpforms-lite' );
if ( ! $this->is_disabled() ) {
$desc .= '<br><a href="' . $url . '" target="_blank">' . \esc_html__( 'View Email Summary Example', 'wpforms-lite' ) . '</a>';
}
$settings['misc']['email-summaries-disable'] = array(
'id' => 'email-summaries-disable',
'name' => \esc_html__( 'Disable Email Summaries', 'wpforms-lite' ),
'desc' => $desc,
'type' => 'checkbox',
);
return $settings;
}
/**
* Preview Email Summary.
*
* @since 1.5.4
*/
public function preview() {
if ( ! wpforms_current_user_can() ) {
return;
}
if ( ! isset( $_GET['wpforms_email_preview'], $_GET['wpforms_email_template'] ) ) { // phpcs:ignore
return;
}
if ( $_GET['wpforms_email_template'] !== 'summary' ) { // phpcs:ignore
return;
}
$args = [
'body' => [
'entries' => $this->get_entries(),
'info_block' => ( new InfoBlocks() )->get_next(),
],
];
$template = ( new Templates\Summary() )->set_args( $args );
/**
* Filters the summaries email template.
*
* @since 1.5.4
*
* @param Templates\Summary $template Default summaries email template.
*/
$template = apply_filters( 'wpforms_emails_summaries_template', $template );
$content = $template->get();
if ( wpforms_setting( 'email-template', 'default' ) !== 'default' ) {
$content = wpautop( $content );
}
echo $content; // phpcs:ignore
exit;
}
/**
* Get next cron occurrence date.
*
* @since 1.5.4
*
* @return int
*/
protected function get_first_cron_date_gmt() {
$date = \absint( \strtotime( 'next monday 2pm' ) - ( \get_option( 'gmt_offset' ) * \HOUR_IN_SECONDS ) );
return $date ? $date : \time();
}
/**
* Add custom Email Summaries cron schedule.
*
* @since 1.5.4
*
* @param array $schedules WP cron schedules.
*
* @return array
*/
public function add_weekly_cron_schedule( $schedules ) {
$schedules['wpforms_email_summaries_weekly'] = array(
'interval' => \WEEK_IN_SECONDS,
'display' => \esc_html__( 'Weekly WPForms Email Summaries', 'wpforms-lite' ),
);
return $schedules;
}
/**
* Email Summaries cron callback.
*
* @since 1.5.4
*/
public function cron() {
$entries = $this->get_entries();
// Email won't be sent if there are no form entries.
if ( empty( $entries ) ) {
return;
}
$info_blocks = new InfoBlocks();
$next_block = $info_blocks->get_next();
$args = [
'body' => [
'entries' => $entries,
'info_block' => $next_block,
],
];
$template = ( new Templates\Summary() )->set_args( $args );
/** This filter is documented in preview() method above. */
$template = apply_filters( 'wpforms_emails_summaries_template', $template );
$content = $template->get();
if ( ! $content ) {
return;
}
$parsed_home_url = wp_parse_url( home_url() );
$site_domain = $parsed_home_url['host'];
if ( is_multisite() && isset( $parsed_home_url['path'] ) ) {
$site_domain .= $parsed_home_url['path'];
}
$subject = sprintf(
/* translators: %s - site domain. */
esc_html__( 'Your Weekly WPForms Summary for %s', 'wpforms-lite' ),
$site_domain
);
/**
* Filters the summaries email subject.
*
* @since 1.5.4
*
* @param string $subject Default summaries email subject.
*/
$subject = apply_filters( 'wpforms_emails_summaries_cron_subject', $subject );
/**
* Filters the summaries recipient email address.
*
* @since 1.5.4
*
* @param string $option Default summaries recipient email address.
*/
$to_email = apply_filters( 'wpforms_emails_summaries_cron_to_email', get_option( 'admin_email' ) );
$sent = ( new Mailer() )
->template( $template )
->subject( $subject )
->to_email( $to_email )
->send();
if ( $sent === true ) {
$info_blocks->register_sent( $next_block );
}
}
/**
* Get form entries.
*
* @since 1.5.4
*
* @return array
*/
protected function get_entries() {
if ( wpforms()->is_pro() ) {
$entries_count = new \WPForms\Pro\Reports\EntriesCount();
$results = $entries_count->get_by( 'form', 0, 7, 'previous sunday' );
} else {
$entries_count = new \WPForms\Lite\Reports\EntriesCount();
$results = $entries_count->get_by_form();
}
return $results;
}
/**
* Register Action Scheduler task to fetch and cache Info Blocks.
*
* @since 1.6.4
*
* @param \WPForms\Tasks\Task[] $tasks List of task classes.
*
* @return array
*/
public static function register_fetch_info_blocks_task( $tasks ) {
$tasks[] = FetchInfoBlocksTask::class;
return $tasks;
}
/**
* Deregister Action Scheduler task to fetch and cache Info Blocks.
*
* @since 1.6.4
*/
public function deregister_fetch_info_blocks_task() {
if ( ! $this->is_disabled() ) {
return;
}
// Deregister the task.
( new FetchInfoBlocksTask() )->cancel();
// Delete last run time record.
delete_option( FetchInfoBlocksTask::LAST_RUN );
// Remove the cache file if it exists.
$file_name = ( new InfoBlocks() )->get_cache_file_path();
if ( file_exists( $file_name ) ) {
@unlink( $file_name ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
}
}
}
Emails/InfoBlocks.php 0000644 00000011614 15133255232 0010524 0 ustar 00 <?php
namespace WPForms\Emails;
/**
* Fetching and formatting Info Blocks for Email Summaries class.
*
* @since 1.5.4
*/
class InfoBlocks {
/**
* Source of info blocks content.
*
* @since 1.5.4
*/
const SOURCE_URL = 'https://wpforms.com/wp-content/email-summaries.json';
/**
* Get info blocks info from the cache file or remote.
*
* @since 1.6.4
*
* @return array
*/
public function get_all() {
$cache_file = $this->get_cache_file_path();
if ( empty( $cache_file ) || ! is_readable( $cache_file ) ) {
return $this->fetch_all();
}
$contents = file_get_contents( $cache_file );
$contents = json_decode( $contents, true );
return $this->verify_fetched( $contents );
}
/**
* Fetch info blocks info from remote.
*
* @since 1.5.4
*
* @return array
*/
public function fetch_all() {
$info = [];
$res = wp_remote_get(
self::SOURCE_URL,
[
'timeout' => 10,
'user-agent' => wpforms_get_default_user_agent(),
]
);
if ( is_wp_error( $res ) ) {
return $info;
}
$body = wp_remote_retrieve_body( $res );
if ( empty( $body ) ) {
return $info;
}
$body = json_decode( $body, true );
return $this->verify_fetched( $body );
}
/**
* Verify fetched blocks data.
*
* @since 1.5.4
*
* @param array $fetched Fetched blocks data.
*
* @return array
*/
protected function verify_fetched( $fetched ) {
$info = array();
if ( ! \is_array( $fetched ) ) {
return $info;
}
foreach ( $fetched as $item ) {
if ( empty( $item['id'] ) ) {
continue;
}
$id = \absint( $item['id'] );
if ( empty( $id ) ) {
continue;
}
$info[ $id ] = $item;
}
return $info;
}
/**
* Get info blocks relevant to customer's licence.
*
* @since 1.5.4
*
* @return array
*/
protected function get_by_license() {
$data = $this->get_all();
$filtered = array();
if ( empty( $data ) || ! \is_array( $data ) ) {
return $filtered;
}
$license_type = \wpforms_setting( 'type', false, 'wpforms_license' );
foreach ( $data as $key => $item ) {
if ( ! isset( $item['type'] ) || ! \is_array( $item['type'] ) ) {
continue;
}
if ( ! \in_array( $license_type, $item['type'], true ) ) {
continue;
}
$filtered[ $key ] = $item;
}
return $filtered;
}
/**
* Get the first block with a valid id.
* Needed to ignore blocks with invalid/missing ids.
*
* @since 1.5.4
*
* @param array $data Blocks array.
*
* @return array
*/
protected function get_first_with_id( $data ) {
if ( empty( $data ) || ! \is_array( $data ) ) {
return array();
}
foreach ( $data as $item ) {
$item_id = \absint( $item['id'] );
if ( ! empty( $item_id ) ) {
return $item;
}
}
return array();
}
/**
* Get next info block that wasn't sent yet.
*
* @since 1.5.4
*
* @return array
*/
public function get_next() {
$data = $this->get_by_license();
$block = array();
if ( empty( $data ) || ! \is_array( $data ) ) {
return $block;
}
$blocks_sent = \get_option( 'wpforms_emails_infoblocks_sent' );
if ( empty( $blocks_sent ) || ! \is_array( $blocks_sent ) ) {
$block = $this->get_first_with_id( $data );
}
if ( empty( $block ) ) {
$data = \array_diff_key( $data, \array_flip( $blocks_sent ) );
$block = $this->get_first_with_id( $data );
}
return $block;
}
/**
* Register a block as sent.
*
* @since 1.5.4
*
* @param array $info_block Info block.
*/
public function register_sent( $info_block ) {
$block_id = isset( $info_block['id'] ) ? \absint( $info_block['id'] ) : false;
if ( empty( $block_id ) ) {
return;
}
$option_name = 'wpforms_email_summaries_info_blocks_sent';
$blocks = \get_option( $option_name );
if ( empty( $blocks ) || ! \is_array( $blocks ) ) {
\update_option( $option_name, array( $block_id ) );
return;
}
if ( \in_array( $block_id, $blocks, true ) ) {
return;
}
$blocks[] = $block_id;
\update_option( $option_name, $blocks );
}
/**
* Get a path of the blocks cache file.
*
* @since 1.6.4
*
* @return string
*/
public function get_cache_file_path() {
$upload_dir = wpforms_upload_dir();
if ( ! isset( $upload_dir['path'] ) ) {
return '';
}
$cache_dir = trailingslashit( $upload_dir['path'] ) . 'cache';
return wp_normalize_path( trailingslashit( $cache_dir ) . 'email-summaries.json' );
}
/**
* Fetch and cache blocks in a file.
*
* @since 1.6.4
*/
public function cache_all() {
$file_path = $this->get_cache_file_path();
if ( empty( $file_path ) ) {
return;
}
$dir = dirname( $file_path );
if ( ! wp_mkdir_p( $dir ) ) {
return;
}
wpforms_create_index_html_file( $dir );
wpforms_create_upload_dir_htaccess_file();
$info_blocks = $this->fetch_all();
file_put_contents( $file_path, wp_json_encode( $info_blocks ) ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_file_put_contents
}
}
Tasks/Meta.php 0000644 00000012635 15133255232 0007240 0 ustar 00 <?php
namespace WPForms\Tasks;
/**
* Class Meta helps to manage the tasks meta information
* between Action Scheduler and WPForms hooks arguments.
* We can't pass arguments longer than >191 chars in JSON to AS,
* so we need to store them somewhere (and clean from time to time).
*
* @since 1.5.9
*/
class Meta extends \WPForms_DB {
/**
* Primary key (unique field) for the database table.
*
* @since 1.5.9
*
* @var string
*/
public $primary_key = 'id';
/**
* Database type identifier.
*
* @since 1.5.9
*
* @var string
*/
public $type = 'tasks_meta';
/**
* Primary class constructor.
*
* @since 1.5.9
*/
public function __construct() {
$this->table_name = self::get_table_name();
}
/**
* Get the DB table name.
*
* @since 1.5.9
*
* @return string
*/
public static function get_table_name() {
global $wpdb;
return $wpdb->prefix . 'wpforms_tasks_meta';
}
/**
* Get table columns.
*
* @since 1.5.9
*/
public function get_columns() {
return array(
'id' => '%d',
'action' => '%s',
'data' => '%s',
'date' => '%s',
);
}
/**
* Default column values.
*
* @since 1.5.9
*
* @return array
*/
public function get_column_defaults() {
return array(
'action' => '',
'data' => '',
'date' => gmdate( 'Y-m-d H:i:s' ),
);
}
/**
* Create custom entry meta database table.
* Used in migration and on plugin activation.
*
* @since 1.5.9
*/
public function create_table() {
global $wpdb;
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
$charset_collate = '';
if ( ! empty( $wpdb->charset ) ) {
$charset_collate .= "DEFAULT CHARACTER SET {$wpdb->charset}";
}
if ( ! empty( $wpdb->collate ) ) {
$charset_collate .= " COLLATE {$wpdb->collate}";
}
$sql = "CREATE TABLE IF NOT EXISTS {$this->table_name} (
id bigint(20) NOT NULL AUTO_INCREMENT,
action varchar(255) NOT NULL,
data longtext NOT NULL,
date datetime NOT NULL,
PRIMARY KEY (id)
) {$charset_collate};";
dbDelta( $sql );
}
/**
* Remove queue records for a defined period of time in the past.
* Calling this method will remove queue records that are older than $period seconds.
*
* @since 1.5.9
*
* @param string $action Action that should be cleaned up.
* @param int $interval Number of seconds from now.
*
* @return int Number of removed tasks meta records.
*/
public function clean_by( $action, $interval ) {
global $wpdb;
if ( empty( $action ) || empty( $interval ) ) {
return 0;
}
$table = self::get_table_name();
$action = sanitize_key( $action );
$date = gmdate( 'Y-m-d H:i:s', time() - (int) $interval );
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching
return (int) $wpdb->query(
$wpdb->prepare(
"DELETE FROM `$table` WHERE action = %s AND date < %s", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
$action,
$date
)
);
}
/**
* Inserts a new record into the database.
*
* @since 1.5.9
*
* @param array $data Column data.
* @param string $type Optional. Data type context.
*
* @return int ID for the newly inserted record. 0 otherwise.
*/
public function add( $data, $type = '' ) {
if ( empty( $data['action'] ) || ! is_string( $data['action'] ) ) {
return 0;
}
$data['action'] = sanitize_key( $data['action'] );
if ( isset( $data['data'] ) ) {
$data['data'] = $this->prepare_data( $data['data'] );
}
if ( empty( $type ) ) {
$type = $this->type;
}
return parent::add( $data, $type );
}
/**
* Prepare data.
*
* @since 1.7.0
*
* @param array $data Meta data.
*
* @return string
*/
private function prepare_data( $data ) {
$string = wp_json_encode( $data );
if ( $string === false ) {
$string = '';
}
/*
* We are encoding the string representation of all the data
* to make sure that nothing can harm the database.
* This is not an encryption, and we need this data later as is,
* so we are using one of the fastest way to do that.
* This data is removed from DB on a daily basis.
*/
// phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
return base64_encode( $string );
}
/**
* Retrieve a row from the database based on a given row ID.
*
* @since 1.5.9
*
* @param int $meta_id Meta ID.
*
* @return null|object
*/
public function get( $meta_id ) {
$meta = parent::get( $meta_id );
if ( empty( $meta ) || empty( $meta->data ) ) {
return $meta;
}
// phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
$decoded = base64_decode( $meta->data );
if ( $decoded === false || ! is_string( $decoded ) ) {
$meta->data = '';
} else {
$meta->data = json_decode( $decoded, true );
}
return $meta;
}
/**
* Get meta ID by action name and params.
*
* @since 1.7.0
*
* @param string $action Action name.
* @param array $params Action params.
*
* @return int
*/
public function get_meta_id( $action, $params ) {
global $wpdb;
$table = self::get_table_name();
$action = sanitize_key( $action );
$params = $this->prepare_data( array_values( $params ) );
return absint(
$wpdb->get_var( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching
$wpdb->prepare(
"SELECT id FROM `$table` WHERE action = %s AND data = %s LIMIT 1", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
$action,
$params
)
)
);
}
}
Tasks/Tasks.php 0000644 00000015612 15133255232 0007435 0 ustar 00 <?php
namespace WPForms\Tasks;
use ActionScheduler_Action;
use WPForms\Helpers\Transient;
use WPForms\Tasks\Actions\EntryEmailsMetaCleanupTask;
use WPForms\Tasks\Actions\EntryEmailsTask;
use WPForms\Tasks\Actions\FormsLocatorScanTask;
use WPForms\Tasks\Actions\AsyncRequestTask;
/**
* Class Tasks manages the tasks queue and provides API to work with it.
*
* @since 1.5.9
*/
class Tasks {
/**
* Group that will be assigned to all actions.
*
* @since 1.5.9
*/
const GROUP = 'wpforms';
/**
* Actions setting name.
*
* @since 1.7.3
*/
const ACTIONS = 'actions';
/**
* WPForms pending or in-progress actions.
*
* @since 1.7.3
*
* @var array
*/
private $active_actions;
/**
* Perform certain things on class init.
*
* @since 1.5.9
*/
public function init() {
// Get WPForms pending or in-progress actions.
$this->active_actions = $this->get_active_actions();
// Register WPForms tasks.
foreach ( $this->get_tasks() as $task ) {
if ( ! is_subclass_of( $task, Task::class ) ) {
continue;
}
new $task();
}
$this->hooks();
}
/**
* Hooks.
*
* @since 1.7.5
*/
public function hooks() {
add_action( 'delete_expired_transients', [ Transient::class, 'delete_all_expired' ], 11 );
add_action( 'admin_menu', [ $this, 'admin_hide_as_menu' ], PHP_INT_MAX );
/*
* By default we send emails in the same process as the form submission is done.
* That means that when many emails are set in form Notifications -
* the form submission can take a while because of all those emails that are sending in the background.
* Since WPForms 1.6.0 users can enable a new option in Settings > Emails,
* called "Optimize Email Sending", to send email in async way.
* This feature was enabled for WPForms 1.5.9, but some users were not happy.
*/
if ( ! (bool) wpforms_setting( 'email-async', false ) ) {
add_filter( 'wpforms_tasks_entry_emails_trigger_send_same_process', '__return_true' );
}
add_action( EntryEmailsTask::ACTION, [ EntryEmailsTask::class, 'process' ] );
add_action( 'action_scheduler_after_execute', [ $this, 'clear_action_meta' ], PHP_INT_MAX, 2 );
}
/**
* Get the list of WPForms default scheduled tasks.
* Tasks, that are fired under certain specific circumstances
* (like sending form submission email notifications)
* are not listed here.
*
* @since 1.5.9
*
* @return Task[] List of tasks classes.
*/
public function get_tasks() {
if ( ! $this->is_usable() ) {
return [];
}
$tasks = [
EntryEmailsMetaCleanupTask::class,
FormsLocatorScanTask::class,
AsyncRequestTask::class,
];
// phpcs:disable WPForms.PHP.ValidateHooks.InvalidHookName
/**
* Filters the task class list to initialize.
*
* @since 1.5.9
*
* @param array $tasks Task class list.
*/
return apply_filters( 'wpforms_tasks_get_tasks', $tasks );
// phpcs:enable WPForms.PHP.ValidateHooks.InvalidHookName
}
/**
* Hide Action Scheduler admin area when not in debug mode.
*
* @since 1.5.9
*/
public function admin_hide_as_menu() {
// Filter to redefine that WPForms hides Tools > Action Scheduler menu item.
if ( apply_filters( 'wpforms_tasks_admin_hide_as_menu', ! wpforms_debug() ) ) {
remove_submenu_page( 'tools.php', 'action-scheduler' );
}
}
/**
* Create a new task.
* Used for "inline" tasks, that require additional information
* from the plugin runtime before they can be scheduled.
*
* Example:
* wpforms()->get( 'tasks' )
* ->create( 'i_am_the_dude' )
* ->async()
* ->params( 'The Big Lebowski', 1998 )
* ->register();
*
* This `i_am_the_dude` action will be later processed as:
* add_action( 'i_am_the_dude', 'thats_what_you_call_me' );
*
* Function `thats_what_you_call_me()` will receive `$meta_id` param,
* and you will be able to receive all params from the action like this:
* $params = ( new Meta() )->get( (int) $meta_id );
* list( $name, $year ) = $params->data;
*
* @since 1.5.9
*
* @param string $action Action that will be used as a hook.
*
* @return \WPForms\Tasks\Task
*/
public function create( $action ) {
return new Task( $action );
}
/**
* Cancel all the AS actions for a group.
*
* @since 1.5.9
*
* @param string $group Group to cancel all actions for.
*/
public function cancel_all( $group = '' ) {
if ( empty( $group ) ) {
$group = self::GROUP;
} else {
$group = sanitize_key( $group );
}
if ( class_exists( 'ActionScheduler_DBStore' ) ) {
\ActionScheduler_DBStore::instance()->cancel_actions_by_group( $group );
$this->active_actions = $this->get_active_actions();
}
}
/**
* Whether ActionScheduler thinks that it has migrated or not.
*
* @since 1.5.9.3
*
* @return bool
*/
public function is_usable() {
// No tasks if ActionScheduler wasn't loaded.
if ( ! class_exists( 'ActionScheduler_DataController' ) ) {
return false;
}
return \ActionScheduler_DataController::is_migration_complete();
}
/**
* Whether task has been scheduled and is pending or in-progress.
*
* @since 1.6.0
*
* @param string $hook Hook to check for.
*
* @return bool|null
*/
public function is_scheduled( $hook ) {
if ( ! function_exists( 'as_has_scheduled_action' ) ) {
return null;
}
if ( in_array( $hook, $this->active_actions, true ) ) {
return true;
}
// Action is not in the array, so it is not scheduled or belongs to another group.
return as_has_scheduled_action( $hook );
}
/**
* Get all WPForms pending or in-progress actions.
*
* @since 1.7.3
*/
private function get_active_actions() {
global $wpdb;
$group = self::GROUP;
$sql = "SELECT a.hook FROM {$wpdb->prefix}actionscheduler_actions a
JOIN {$wpdb->prefix}actionscheduler_groups g ON g.group_id = a.group_id
WHERE g.slug = '$group' AND a.status IN ('in-progress', 'pending')";
// phpcs:disable WordPress.DB.DirectDatabaseQuery.NoCaching
// phpcs:disable WordPress.DB.PreparedSQL.NotPrepared
$results = $wpdb->get_results( $sql, 'ARRAY_N' );
// phpcs:enable WordPress.DB.DirectDatabaseQuery.NoCaching
// phpcs:enable WordPress.DB.PreparedSQL.NotPrepared
return $results ? array_merge( ...$results ) : [];
}
/**
* Clear the meta after action complete.
* Fired before an action is marked as completed.
*
* @since 1.7.5
*
* @param integer $action_id Action ID.
* @param ActionScheduler_Action $action Action name.
*/
public function clear_action_meta( $action_id, $action ) {
$action_schedule = $action->get_schedule();
if ( $action_schedule === null || $action_schedule->is_recurring() ) {
return;
}
$hook_name = $action->get_hook();
if ( ! $this->is_scheduled( $hook_name ) ) {
return;
}
$hook_args = $action->get_args();
if ( ! isset( $hook_args['tasks_meta_id'] ) ) {
return;
}
$meta = new Meta();
$meta->delete( $hook_args['tasks_meta_id'] );
}
}
Tasks/Actions/FormsLocatorScanTask.php 0000644 00000027103 15133255232 0014010 0 ustar 00 <?php
// phpcs:disable Generic.Commenting.DocComment.MissingShort
/** @noinspection PhpUnnecessaryCurlyVarSyntaxInspection */
/** @noinspection SqlResolve */
// phpcs:enable Generic.Commenting.DocComment.MissingShort
namespace WPForms\Tasks\Actions;
use WP_Post;
use WP_Query;
use WP_Screen;
use WPForms\Forms\Locator;
use WPForms\Tasks\Meta;
use WPForms\Tasks\Task;
use WPForms\Tasks\Tasks;
/**
* Class FormLocatorScanTask.
*
* @since 1.7.4
*/
class FormsLocatorScanTask extends Task {
/**
* Scan action name for this task.
*
* @since 1.7.4
*/
const SCAN_ACTION = 'wpforms_process_forms_locator_scan';
/**
* Re-scan action name for this task.
*
* @since 1.7.4
*/
const RESCAN_ACTION = 'wpforms_process_forms_locator_rescan';
/**
* Save action name for this task.
*
* @since 1.7.4
*/
const SAVE_ACTION = 'wpforms_process_forms_locator_save';
/**
* Delete action name for this task.
*
* @since 1.7.4
*/
const DELETE_ACTION = 'wpforms_process_forms_locator_delete';
/**
* Scan status option name.
*
* @since 1.7.4
*/
const SCAN_STATUS = 'wpforms_process_forms_locator_status';
/**
* Scan status "In Progress".
*
* @since 1.7.4
*/
const SCAN_STATUS_IN_PROGRESS = 'in progress';
/**
* Scan status "Completed".
*
* @since 1.7.4
*/
const SCAN_STATUS_COMPLETED = 'completed';
/**
* Locations query arg.
*
* @since 1.7.4
*/
const LOCATIONS_QUERY_ARG = 'locations';
/**
* Chunk size to use in get_form_locations().
* Specifies how many posts to load for scanning in one db request.
* Affects memory usage.
*
* @since 1.7.4
*/
const CHUNK_SIZE = 50;
/**
* Locator class instance.
*
* @since 1.7.4
*
* @var Locator
*/
private $locator;
/**
* Tasks class instance.
*
* @since 1.7.4
*
* @var Tasks
*/
private $tasks;
/**
* Task recurring interval in seconds.
*
* @since 1.7.4
*
* @var int
*/
private $interval;
/**
* Class constructor.
*
* @since 1.7.4
*/
public function __construct() {
parent::__construct( self::SCAN_ACTION );
$this->init();
}
/**
* Initialize the task with all the proper checks.
*
* @since 1.7.4
*/
public function init() {
$this->locator = wpforms()->get( 'locator' );
/**
* Give developers an ability to modify task interval.
*
* @since 1.7.4
*
* @param int $interval The task recurring interval in seconds. If <= 0, the task will be cancelled.
*/
$this->interval = (int) apply_filters( 'wpforms_tasks_actions_forms_locator_scan_task_interval', DAY_IN_SECONDS );
$this->hooks();
$this->tasks = wpforms()->get( 'tasks' );
// Do not add a new one if scheduled.
if ( $this->tasks->is_scheduled( self::SCAN_ACTION ) !== false ) {
if ( $this->interval <= 0 ) {
$this->cancel();
}
return;
}
$this->add_scan_task();
}
/**
* Add scan task.
*
* @since 1.7.4
*/
private function add_scan_task() {
if ( $this->interval <= 0 ) {
return;
}
// Add a new task if none exists.
$this->recurring( time(), $this->interval )
->params()
->register();
}
/**
* Add hooks.
*
* @since 1.7.4
*/
private function hooks() {
// Register hidden action for testing and support.
add_action( 'current_screen', [ $this, 'maybe_run_actions_in_admin' ] );
// Register Action Scheduler actions.
add_action( self::SCAN_ACTION, [ $this, 'scan' ] );
add_action( self::RESCAN_ACTION, [ $this, 'rescan' ] );
add_action( self::SAVE_ACTION, [ $this, 'save' ] );
add_action( self::DELETE_ACTION, [ $this, 'delete' ] );
add_action( 'action_scheduler_after_process_queue', [ $this, 'after_process_queue' ] );
}
/**
* Maybe rescan or delete locations.
* Hidden undocumented actions for tests and support.
*
* @since 1.7.4
*
* @param WP_Screen $current_screen Current WP_Screen object.
*/
public function maybe_run_actions_in_admin( $current_screen ) {
// phpcs:disable WordPress.Security.NonceVerification.Recommended
if (
! $current_screen ||
$current_screen->id !== 'toplevel_page_wpforms-overview' ||
! isset( $_GET[ self::LOCATIONS_QUERY_ARG ] ) ||
! wpforms_debug()
) {
return;
}
if ( $_GET[ self::LOCATIONS_QUERY_ARG ] === 'delete' ) {
$this->delete();
}
if ( $_GET[ self::LOCATIONS_QUERY_ARG ] === 'scan' ) {
$this->rescan();
}
// phpcs:enable WordPress.Security.NonceVerification.Recommended
wp_safe_redirect( remove_query_arg( [ self::LOCATIONS_QUERY_ARG ] ) );
exit;
}
/**
* Run scan task.
*
* @since 1.7.4
*/
public function scan() {
if ( ! $this->tasks ) {
return;
}
// Bail out if scan is already in progress.
if ( self::SCAN_STATUS_IN_PROGRESS === (string) get_option( self::SCAN_STATUS ) ) {
return;
}
// Mark that scan is in progress.
update_option( self::SCAN_STATUS, self::SCAN_STATUS_IN_PROGRESS );
$this->log( 'Forms Locator scan action started.' );
// This part of the scan shouldn't take more than 1 second even on big sites.
$post_ids = $this->search_in_posts();
$post_locations = $this->get_form_locations( $post_ids );
$widget_locations = $this->locator->search_in_widgets();
$locations = array_merge( $post_locations, $widget_locations );
$form_location_metas = $this->get_form_location_metas( $locations );
/**
* This part of the scan can take a while.
* Saving hundreds of metas with a potentially very high number of locations could be time and memory consuming.
* That is why we perform save via Action Scheduler.
*/
$meta_chunks = array_chunk( $form_location_metas, self::CHUNK_SIZE, true );
$count = count( $meta_chunks );
foreach ( $meta_chunks as $index => $meta_chunk ) {
$this->tasks->create( self::SAVE_ACTION )->async()->params( $meta_chunk, $index, $count )->register();
}
$this->log( 'Save tasks created.' );
}
/**
* Run immediate scan.
*
* @since 1.7.4
*/
public function rescan() {
$this->cancel();
$this->add_scan_task();
}
/**
* Save form locations.
*
* @since 1.7.4
*
* @param int $meta_id Action meta id.
*/
public function save( $meta_id ) {
$params = ( new Meta() )->get( $meta_id );
if ( ! $params ) {
return;
}
list( $meta_chunk, $index, $count ) = $params->data;
foreach ( $meta_chunk as $form_id => $meta ) {
update_post_meta( $form_id, Locator::LOCATIONS_META, $meta );
}
$this->log(
sprintf(
'Forms Locator save action %1$d/%2$d completed.',
$index + 1,
$count
)
);
}
/**
* Delete form locations.
*
* @since 1.7.4
*/
public function delete() {
global $wpdb;
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching
$wpdb->query(
$wpdb->prepare(
"DELETE FROM {$wpdb->postmeta} WHERE meta_key = %s",
Locator::LOCATIONS_META
)
);
delete_option( self::SCAN_STATUS );
wp_cache_flush();
}
/**
* After process queue action.
* Delete transient to indicate that scanning is completed.
*
* @since 1.7.4
*/
public function after_process_queue() {
if ( $this->tasks->is_scheduled( self::SAVE_ACTION ) ) {
return;
}
// Mark that scan is finished.
if ( (string) get_option( self::SCAN_STATUS ) === self::SCAN_STATUS_IN_PROGRESS ) {
update_option( self::SCAN_STATUS, self::SCAN_STATUS_COMPLETED );
$this->log( 'Forms Locator scan action completed.' );
}
}
/**
* Search form in posts.
*
* @since 1.7.4
*
* @return int[]
*/
private function search_in_posts() {
global $wpdb;
$post_statuses = wpforms_wpdb_prepare_in( $this->locator->get_post_statuses() );
$post_types = wpforms_wpdb_prepare_in( $this->locator->get_post_types() );
// phpcs:disable WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
$ids = $wpdb->get_col(
"SELECT p.ID
FROM (SELECT ID
FROM {$wpdb->posts}
WHERE post_status IN ({$post_statuses}) AND post_type IN ({$post_types}) ) AS ids
INNER JOIN {$wpdb->posts} as p ON ids.ID = p.ID
WHERE p.post_content REGEXP '\\\[wpforms|wpforms/form-selector'"
);
// phpcs:enable WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
return array_map( 'intval', $ids );
}
/**
* Filters the SELECT clause of the query.
* Get minimal set of fields from the post record.
*
* @since 1.7.4
*
* @param string $fields The SELECT clause of the query.
* @param WP_Query $query The WP_Query instance (passed by reference).
*
* @return string
*
* @noinspection PhpUnusedParameterInspection
*/
public function posts_fields_filter( $fields, $query ) {
global $wpdb;
$fields_arr = [ 'ID', 'post_title', 'post_status', 'post_type', 'post_content', 'post_name' ];
$fields_arr = array_map(
static function ( $field ) use ( $wpdb ) {
return "$wpdb->posts." . $field;
},
$fields_arr
);
return implode( ', ', $fields_arr );
}
/**
* Get form locations.
*
* @since 1.7.4
*
* @param int[] $post_ids Post IDs.
*
* @return array
*/
private function get_form_locations( $post_ids ) { // phpcs:ignore WPForms.PHP.HooksMethod.InvalidPlaceForAddingHooks
/**
* Block caching here, as caching produces unneeded db requests in
* update_object_term_cache() and update_postmeta_cache().
*/
$query_args = [
'post_type' => $this->locator->get_post_types(),
'post_status' => $this->locator->get_post_statuses(),
'post__in' => $post_ids,
'no_found_rows' => true,
'posts_per_page' => - 1,
'cache_results' => false,
];
// Get form locations by chunks to prevent out of memory issue.
$post_id_chunks = array_chunk( $post_ids, self::CHUNK_SIZE );
$locations = [];
add_filter( 'posts_fields', [ $this, 'posts_fields_filter' ], 10, 2 );
foreach ( $post_id_chunks as $post_id_chunk ) {
$query_args['post__in'] = $post_id_chunk;
$query = new WP_Query( $query_args );
$locations = $this->get_form_locations_from_posts( $query->posts, $locations );
}
remove_filter( 'posts_fields', [ $this, 'posts_fields_filter' ] );
return $locations;
}
/**
* Get locations from posts.
*
* @since 1.7.4
*
* @param WP_Post[] $posts Posts.
* @param array $locations Locations.
*
* @return array
*/
private function get_form_locations_from_posts( $posts, $locations = [] ) {
$home_url = home_url();
foreach ( $posts as $post ) {
$form_ids = $this->locator->get_form_ids( $post->post_content );
if ( ! $form_ids ) {
continue;
}
$url = get_permalink( $post );
$url = ( $url === false || is_wp_error( $url ) ) ? '' : $url;
$url = str_replace( $home_url, '', $url );
foreach ( $form_ids as $form_id ) {
$locations[] = [
'type' => $post->post_type,
'title' => $post->post_title,
'form_id' => $form_id,
'id' => $post->ID,
'status' => $post->post_status,
'url' => $url,
];
}
}
return $locations;
}
/**
* Get form location metas.
*
* @param array $locations Locations.
*
* @since 1.7.4
*
* @return array
*/
private function get_form_location_metas( $locations ) {
$metas = [];
foreach ( $locations as $location ) {
$metas[ $location['form_id'] ][] = $location;
}
return $metas;
}
/**
* Log message to WPForms logger and standard debug.log file.
*
* @since 1.7.4
*
* @param string $message The error message that should be logged.
*
* @noinspection ForgottenDebugOutputInspection
*/
private function log( $message ) {
if ( defined( 'WPFORMS_DEBUG' ) && WPFORMS_DEBUG ) {
// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
error_log( $message );
wpforms_log( 'Forms Locator', $message, [ 'type' => 'log' ] );
}
}
}
Tasks/Actions/EntryEmailsMetaCleanupTask.php 0000644 00000004164 15133255232 0015146 0 ustar 00 <?php
namespace WPForms\Tasks\Actions;
use WPForms\Tasks\Task;
use WPForms\Tasks\Meta;
/**
* Class EntryEmailsMetaCleanupTask.
*
* @since 1.5.9
*/
class EntryEmailsMetaCleanupTask extends Task {
/**
* Action name for this task.
*
* @since 1.5.9
*/
const ACTION = 'wpforms_process_entry_emails_meta_cleanup';
/**
* Class constructor.
*
* @since 1.5.9
*/
public function __construct() {
parent::__construct( self::ACTION );
$this->init();
}
/**
* Initialize the task with all the proper checks.
*
* @since 1.5.9
*/
public function init() {
// Register the action handler.
$this->hooks();
$tasks = wpforms()->get( 'tasks' );
$email_async = wpforms_setting( 'email-async' );
// Add new if none exists.
if ( $tasks->is_scheduled( self::ACTION ) !== false ) {
// Cancel scheduled action if email async option is not set.
if ( ! $email_async ) {
$this->cancel();
}
return;
}
// Do not schedule action if email async option is not set.
if ( ! $email_async ) {
return;
}
// phpcs:disable WPForms.PHP.ValidateHooks.InvalidHookName
/**
* Filters the email cleanup task interval.
*
* @since 1.5.9
*
* @param int $interval Interval in seconds.
*/
$interval = (int) apply_filters( 'wpforms_tasks_entry_emails_meta_cleanup_interval', DAY_IN_SECONDS );
// phpcs:enable WPForms.PHP.ValidateHooks.InvalidHookName
$this->recurring( strtotime( 'tomorrow' ), $interval )
->params( $interval )
->register();
}
/**
* Add hooks.
*
* @since 1.7.3
*/
private function hooks() {
add_action( self::ACTION, [ $this, 'process' ] );
}
/**
* Perform the cleanup action: remove outdated meta for entry emails task.
*
* @since 1.5.9
*
* @param int $meta_id ID for meta information for a task.
*/
public function process( $meta_id ) {
$task_meta = new Meta();
$meta = $task_meta->get( (int) $meta_id );
// We should actually receive something.
if ( empty( $meta ) || empty( $meta->data ) ) {
return;
}
list( $interval ) = $meta->data;
$task_meta->clean_by( EntryEmailsTask::ACTION, (int) $interval );
}
}
Tasks/Actions/AsyncRequestTask.php 0000644 00000002130 15133255232 0013210 0 ustar 00 <?php
namespace WPForms\Tasks\Actions;
use WPForms\Tasks\Task;
use WPForms\Tasks\Meta;
/**
* Class AsyncRequestTask is responsible to send information in the background.
*
* @since 1.7.5
*/
class AsyncRequestTask extends Task {
/**
* Action name for this task.
*
* @since 1.7.5
*/
const ACTION = 'wpforms_process_async_request';
/**
* Class constructor.
*
* @since 1.7.5
*/
public function __construct() {
// Task functionality is needed on cron request only.
if ( ! ( defined( 'DOING_CRON' ) && DOING_CRON ) ) {
return;
}
parent::__construct( self::ACTION );
$this->hooks();
}
/**
* Add hooks.
*
* @since 1.7.5
*/
private function hooks() {
// Register the migrate action.
add_action( self::ACTION, [ $this, 'process' ] );
}
/**
* Send usage tracking to the server.
*
* @since 1.7.5
*
* @param int $meta_id Action meta id.
*/
public static function process( $meta_id ) {
$params = ( new Meta() )->get( $meta_id );
if ( ! $params ) {
return;
}
list( $url, $args ) = $params->data;
wp_remote_get( $url, $args );
}
}
Tasks/Actions/Migration173Task.php 0000644 00000012324 15133255232 0012754 0 ustar 00 <?php
namespace WPForms\Tasks\Actions;
use WPForms\Tasks\Meta;
use WPForms\Tasks\Task;
use WPForms\Tasks\Tasks;
use WPForms_Entry_Fields_Handler;
use WPForms_Entry_Handler;
/**
* Class Migration173Task.
*
* @since 1.7.3
*/
class Migration173Task extends Task {
/**
* Action name for this task.
*
* @since 1.7.3
*/
const ACTION = 'wpforms_process_migration_173';
/**
* Status option name.
*
* @since 1.7.3
*/
const STATUS = 'wpforms_process_migration_173_status';
/**
* Start status.
*
* @since 1.7.3
*/
const START = 'start';
/**
* In progress status.
*
* @since 1.7.3
*/
const IN_PROGRESS = 'in progress';
/**
* Completed status.
*
* @since 1.7.3
*/
const COMPLETED = 'completed';
/**
* Chunk size to use.
* Specifies how many entries to load for scanning in one db request.
* Affects memory usage.
*
* @since 1.7.3
*/
const CHUNK_SIZE = 50;
/**
* Entry handler.
*
* @since 1.7.3
*
* @var WPForms_Entry_Handler
*/
private $entry_handler;
/**
* Entry fields handler.
*
* @since 1.7.3
*
* @var WPForms_Entry_Fields_Handler
*/
private $entry_fields_handler;
/**
* Class constructor.
*
* @since 1.7.3
*/
public function __construct() {
parent::__construct( self::ACTION );
}
/**
* Initialize the task with all the proper checks.
*
* @since 1.7.3
*/
public function init() {
$this->entry_handler = wpforms()->get( 'entry' );
$this->entry_fields_handler = wpforms()->get( 'entry_fields' );
if ( ! $this->entry_handler || ! $this->entry_fields_handler ) {
return;
}
// Bail out if migration is not started or completed.
$status = get_option( self::STATUS );
if ( ! $status || $status === self::COMPLETED ) {
return;
}
// Mark that migration is in progress.
update_option( self::STATUS, self::IN_PROGRESS );
$this->hooks();
$tasks = wpforms()->get( 'tasks' );
// Add new if none exists.
if ( $tasks->is_scheduled( self::ACTION ) !== false ) {
return;
}
// Init migration.
$this->init_migration( $tasks );
}
/**
* Add hooks.
*
* @since 1.7.3
*/
private function hooks() {
// Register the migrate action.
add_action( self::ACTION, [ $this, 'migrate' ] );
// Register after process queue action.
add_action( 'action_scheduler_after_process_queue', [ $this, 'after_process_queue' ] );
}
/**
* Migrate an entry.
*
* @since 1.7.3
*
* @param int $meta_id Action meta id.
*/
public function migrate( $meta_id ) {
$params = ( new Meta() )->get( $meta_id );
if ( ! $params ) {
return;
}
list( $entry_id_chunk ) = $params->data;
foreach ( $entry_id_chunk as $entry_id ) {
$this->save_entry( $entry_id );
}
}
/**
* After process queue action.
* Set status as completed.
*
* @since 1.7.3
*/
public function after_process_queue() {
if ( as_has_scheduled_action( self::ACTION ) ) {
return;
}
// Mark that migration is finished.
update_option( self::STATUS, self::COMPLETED );
}
/**
* Init migration.
*
* @since 1.7.3
*
* @param Tasks $tasks Tasks class instance.
*/
private function init_migration( $tasks ) {
// This part of the migration shouldn't take more than 1 second even on big sites.
$entry_ids = $this->get_legacy_entry_ids();
if ( ! $entry_ids ) {
// Mark that migration is completed.
update_option( self::STATUS, self::COMPLETED );
return;
}
/**
* This part of the migration can take a while.
* Saving hundreds of entries with a potentially very high number of entry fields could be time and memory consuming.
* That is why we perform save via Action Scheduler.
*/
$entry_id_chunks = array_chunk( $entry_ids, self::CHUNK_SIZE, true );
foreach ( $entry_id_chunks as $entry_id_chunk ) {
$tasks->create( self::ACTION )->async()->params( $entry_id_chunk )->register();
}
}
/**
* Get entry ids which do not have relevant entry field records.
*
* @since 1.7.3
*
* @return int[]
*/
private function get_legacy_entry_ids() {
global $wpdb;
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.NoCaching
$entries = $wpdb->get_results(
"
SELECT e.entry_id FROM {$this->entry_handler->table_name} e
LEFT JOIN {$this->entry_fields_handler->table_name} ef
ON e.entry_id=ef.entry_id
WHERE
e.status IN( 'partial', 'abandoned' ) AND
ef.entry_id IS NULL"
);
// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.NoCaching
if ( ! $entries || ! is_array( $entries ) ) {
return [];
}
return array_map( 'intval', wp_list_pluck( $entries, 'entry_id' ) );
}
/**
* Save entry properly.
*
* @since 1.7.3
*
* @param int $entry_id Entry id.
*/
private function save_entry( $entry_id ) {
$entry = $this->entry_handler->get( $entry_id );
if ( ! $entry || ! isset( $entry->form_id, $entry->fields, $entry->date_modified ) ) {
return;
}
$fields = json_decode( $entry->fields, true );
if ( ! is_array( $fields ) ) {
return;
}
$form_data = [
'id' => (int) $entry->form_id,
'date' => $entry->date_modified,
];
$this->entry_fields_handler->save( $fields, $form_data, $entry_id, true );
}
}
Tasks/Actions/Migration175Task.php 0000644 00000025345 15133255232 0012765 0 ustar 00 <?php
namespace WPForms\Tasks\Actions;
use WPForms\Tasks\Task;
use WPForms\Tasks\Tasks;
use WPForms_Entry_Handler;
use WPForms_Entry_Meta_Handler;
/**
* Class Migration175Task.
*
* @since 1.7.5
*/
class Migration175Task extends Task {
/**
* Action name for this task.
*
* @since 1.7.5
*/
const ACTION = 'wpforms_process_migration_175';
/**
* Status option name.
*
* @since 1.7.5
*/
const STATUS = 'wpforms_process_migration_175_status';
/**
* Start status.
*
* @since 1.7.5
*/
const START = 'start';
/**
* In progress status.
*
* @since 1.7.5
*/
const IN_PROGRESS = 'in progress';
/**
* Completed status.
*
* @since 1.7.5
*/
const COMPLETED = 'completed';
/**
* Chunk size to use.
* Specifies how many entries to convert in one db request.
*
* @since 1.7.5
*/
const CHUNK_SIZE = 5000;
/**
* Chunk size of the migration task.
* Specifies how many entry ids to load at once for further conversion.
*
* @since 1.7.5
*/
const TASK_CHUNK_SIZE = self::CHUNK_SIZE * 10;
/**
* Entry handler.
*
* @since 1.7.5
*
* @var WPForms_Entry_Handler
*/
private $entry_handler;
/**
* Entry meta handler.
*
* @since 1.7.5
*
* @var WPForms_Entry_Meta_Handler
*/
private $entry_meta_handler;
/**
* Temporary table name.
*
* @since 1.7.5
*
* @var string
*/
private $temp_table_name;
/**
* Class constructor.
*
* @since 1.7.5
*/
public function __construct() {
parent::__construct( self::ACTION );
}
/**
* Initialize the task with all the proper checks.
*
* @since 1.7.5
*/
public function init() {
global $wpdb;
$this->entry_handler = wpforms()->get( 'entry' );
$this->entry_meta_handler = wpforms()->get( 'entry_meta' );
$this->temp_table_name = "{$wpdb->prefix}wpforms_temp_entry_ids";
if ( ! $this->entry_handler || ! $this->entry_meta_handler ) {
return;
}
// Bail out if migration is not started or completed.
$status = get_option( self::STATUS );
if ( ! $status || $status === self::COMPLETED ) {
return;
}
$this->hooks();
if ( $status === self::START ) {
// Mark that migration is in progress.
update_option( self::STATUS, self::IN_PROGRESS );
// Alter entry meta table.
$this->alter_entry_meta_table();
// Init migration.
$this->init_migration();
}
}
/**
* Modify field in the entry meta table.
*
* @since 1.7.5
*/
private function alter_entry_meta_table() {
global $wpdb;
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
$wpdb->query( "ALTER TABLE {$this->entry_meta_handler->table_name} MODIFY type VARCHAR(255)" );
}
/**
* Add index to a table.
*
* @since 1.7.5
*
* @param string $table_name Table.
* @param string $index_name Index name.
* @param string $key_part Key part.
*
* @return void
*/
private function add_index( $table_name, $index_name, $key_part ) {
global $wpdb;
// Check id index already exists.
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.NoCaching
$result = $wpdb->get_var(
"SELECT COUNT(1) IndexIsThere
FROM INFORMATION_SCHEMA.STATISTICS
WHERE table_schema = DATABASE()
AND table_name = '$table_name'
AND index_name = '$index_name'"
);
if ( $result === '1' ) {
return;
}
// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.NoCaching
// Change the column length for the wp_wpforms_entry_meta.type column to 255 and add an index.
// phpcs:disable WordPress.DB.DirectDatabaseQuery.SchemaChange, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.NoCaching
$wpdb->query( "CREATE INDEX $index_name ON $table_name ( $key_part )" );
// phpcs:enable WordPress.DB.DirectDatabaseQuery.SchemaChange, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.NoCaching
}
/**
* Add hooks.
*
* @since 1.7.5
*/
private function hooks() {
// Register the migrate action.
add_action( self::ACTION, [ $this, 'migrate' ] );
// Register after process queue action.
add_action( 'action_scheduler_after_process_queue', [ $this, 'after_process_queue' ] );
}
/**
* Migrate an entry.
*
* @param int $action_index Action index.
*
* @since 1.7.5
*/
public function migrate( $action_index ) {
global $wpdb;
$db_indexes = [
- 3 => [
'table_name' => $this->entry_meta_handler->table_name,
'index_name' => 'form_id',
'key_part' => 'form_id',
],
- 2 => [
'table_name' => $this->entry_meta_handler->table_name,
'index_name' => 'type',
'key_part' => 'type',
],
- 1 => [
'table_name' => $this->entry_meta_handler->table_name,
'index_name' => 'data',
'key_part' => 'data(32)',
],
];
// We create indexes in the background as it could take significant time on big database.
if ( array_key_exists( $action_index, $db_indexes ) ) {
$this->add_index(
$db_indexes[ $action_index ]['table_name'],
$db_indexes[ $action_index ]['index_name'],
$db_indexes[ $action_index ]['key_part']
);
return;
}
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.NoCaching
// The query length in migrate_payment_data() is about 500 chars for 1 entry (7 metas).
// The length of the query is defined by MAX_ALLOWED_PACKET variable, which defaults to 4 MB on MySQL 5.7.
// We increase MAX_ALLOWED_PACKET variable to fit number of entries specified in self::CHUNK_SIZE.
$new_max_allowed_packet = 500 * self::CHUNK_SIZE;
$max_allowed_packet = (int) $wpdb->get_var( "SHOW VARIABLES LIKE 'MAX_ALLOWED_PACKET'", 1 );
if ( $new_max_allowed_packet > $max_allowed_packet ) {
$wpdb->query( "SET MAX_ALLOWED_PACKET = $new_max_allowed_packet" );
}
// Using OFFSET makes a way longer request, as MySQL has to access all rows before OFFSET.
// We follow very fast way with indexed column (id > $action_index).
$entry_ids = $wpdb->get_col(
$wpdb->prepare(
"SELECT entry_id FROM $this->temp_table_name
WHERE id > %d LIMIT %d",
$action_index,
self::TASK_CHUNK_SIZE
)
);
$i = 0;
$entry_ids_count = count( $entry_ids );
// This cycle is twice less memory consuming than array_chunk( $entry_ids ).
while ( $i < $entry_ids_count ) {
$entry_ids_chunk = array_slice( $entry_ids, $i, self::CHUNK_SIZE );
$this->migrate_payment_data( implode( ',', $entry_ids_chunk ) );
$i += self::CHUNK_SIZE;
}
if ( $new_max_allowed_packet > $max_allowed_packet ) {
$wpdb->query( "SET MAX_ALLOWED_PACKET = $max_allowed_packet" );
}
// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.NoCaching
}
/**
* After process queue action.
* Set status as completed.
*
* @since 1.7.5
*/
public function after_process_queue() {
$tasks = wpforms()->get( 'tasks' );
if ( ! $tasks || $tasks->is_scheduled( self::ACTION ) ) {
return;
}
$this->drop_temp_table();
// Mark that migration is finished.
update_option( self::STATUS, self::COMPLETED );
}
/**
* Init migration.
*
* @since 1.7.5
*/
private function init_migration() {
// Get all payment entries.
$count = $this->get_unprocessed_payment_entry_ids();
if ( ! $count ) {
$this->drop_temp_table();
}
// We need 3 preliminary steps to create indexes.
$index = - 3;
while ( $index < $count ) {
// We do not use Task class here as we do not need meta. So, we reduce number of DB requests.
as_enqueue_async_action(
self::ACTION,
[ $index ],
Tasks::GROUP
);
$index = $index < 0 ? $index + 1 : $index + self::CHUNK_SIZE;
}
}
/**
* Migrate payment data to the correct table.
*
* @param string $entry_ids_list List of entry ids.
*
* @since 1.7.5
*/
private function migrate_payment_data( $entry_ids_list ) {
global $wpdb;
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.NoCaching
$wpdb->query(
"SELECT entry_id, form_id, user_id, status, meta, date
FROM {$this->entry_handler->table_name}
WHERE entry_id IN ( $entry_ids_list )"
);
// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.NoCaching
$values = [];
foreach ( $wpdb->last_result as $entry ) {
$meta = json_decode( $entry->meta, true );
if ( ! is_array( $meta ) ) {
continue;
}
foreach ( $meta as $meta_key => $meta_value ) {
// If meta_key doesn't begin with `payment_`, prefix it.
$meta_key = strpos( $meta_key, 'payment_' ) === 0 ? $meta_key : "payment_$meta_key";
// We do not use $wpdb->prepare here, as it is 5 times slower.
// Prepare takes 1.3 sec to prepare 1000 entries (6000 meta records).
// It is incomparable with the two queries here.
// With sprintf, total processing time of this method is 0.15 sec for 1000 entries.
$values[] = sprintf(
"( %d, %d, %d, '%s', '%s', '%s', '%s' )",
$entry->entry_id,
$entry->form_id,
$entry->user_id,
$entry->status,
$meta_key,
$meta_value,
$entry->date
);
}
}
$values = implode( ', ', $values );
// The following query length is about 500 chars for 1 entry (7 metas).
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.NoCaching
$wpdb->query(
"INSERT INTO {$this->entry_meta_handler->table_name}
( entry_id, form_id, user_id, status, type, data, date )
VALUES $values"
);
// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.NoCaching
}
/**
* Get entry ids which do not have relevant entry field records.
* Store them in a temporary table.
*
* @since 1.7.5
*
* @return int
*/
private function get_unprocessed_payment_entry_ids() {
global $wpdb;
$this->drop_temp_table();
// phpcs:disable WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
$wpdb->query(
"CREATE TABLE $this->temp_table_name
(
id BIGINT AUTO_INCREMENT PRIMARY KEY,
entry_id BIGINT NOT NULL
)"
);
$wpdb->query(
"INSERT INTO $this->temp_table_name (entry_id)
SELECT entry_id
FROM {$this->entry_handler->table_name}
WHERE type = 'payment'
AND entry_id NOT IN
(SELECT entry_id FROM {$this->entry_meta_handler->table_name} WHERE type LIKE 'payment_%')"
);
return $wpdb->rows_affected;
}
/**
* Drop temporary table.
*
* @since 1.7.5
*/
private function drop_temp_table() {
global $wpdb;
$wpdb->query( "DROP TABLE IF EXISTS $this->temp_table_name" );
}
}
Tasks/Actions/EntryEmailsTask.php 0000644 00000002532 15133255232 0013024 0 ustar 00 <?php
namespace WPForms\Tasks\Actions;
use WPForms\Tasks\Task;
use WPForms\Tasks\Meta;
/**
* Class EntryEmailsTask is responsible for defining how to send emails,
* when the form was submitted.
*
* @since 1.5.9
*/
class EntryEmailsTask extends Task {
/**
* Action name for this task.
*
* @since 1.5.9
*/
const ACTION = 'wpforms_process_entry_emails';
/**
* Class constructor.
*
* @since 1.5.9
*/
public function __construct() {
parent::__construct( self::ACTION );
$this->async();
}
/**
* Get the data from Tasks meta table, check/unpack it and
* send the email straight away.
*
* @since 1.5.9
* @since 1.5.9.3 Send immediately instead of calling \WPForms_Process::entry_email() method.
*
* @param int $meta_id ID for meta information for a task.
*/
public static function process( $meta_id ) {
$task_meta = new Meta();
$meta = $task_meta->get( (int) $meta_id );
// We should actually receive something.
if ( empty( $meta ) || empty( $meta->data ) ) {
return;
}
// We expect a certain number of params.
if ( count( $meta->data ) !== 5 ) {
return;
}
// We expect a certain meta data structure for this task.
list( $to, $subject, $message, $headers, $attachments ) = $meta->data;
// Let's do this NOW, finally.
wp_mail( $to, $subject, $message, $headers, $attachments );
}
}
Tasks/Task.php 0000644 00000013371 15133255232 0007252 0 ustar 00 <?php
namespace WPForms\Tasks;
/**
* Class Task.
*
* @since 1.5.9
*/
class Task {
/**
* This task is async (runs asap).
*
* @since 1.5.9
*/
const TYPE_ASYNC = 'async';
/**
* This task is a recurring.
*
* @since 1.5.9
*/
const TYPE_RECURRING = 'scheduled';
/**
* This task is run once.
*
* @since 1.5.9
*/
const TYPE_ONCE = 'once';
/**
* Type of the task.
*
* @since 1.5.9
*
* @var string
*/
private $type;
/**
* Action that will be used as a hook.
*
* @since 1.5.9
*
* @var string
*/
private $action;
/**
* Task meta ID.
*
* @since 1.5.9
*
* @var int
*/
private $meta_id;
/**
* All the params that should be passed to the hook.
*
* @since 1.5.9
*
* @var array
*/
private $params;
/**
* When the first instance of the job will run.
* Used for ONCE ane RECURRING tasks.
*
* @since 1.5.9
*
* @var int
*/
private $timestamp;
/**
* How long to wait between runs.
* Used for RECURRING tasks.
*
* @since 1.5.9
*
* @var int
*/
private $interval;
/**
* Task meta.
*
* @since 1.7.0
*
* @var Meta
*/
private $meta;
/**
* Task constructor.
*
* @since 1.5.9
*
* @param string $action Action of the task.
*
* @throws \InvalidArgumentException When action is not a string.
* @throws \UnexpectedValueException When action is empty.
*/
public function __construct( $action ) {
if ( ! is_string( $action ) ) {
throw new \InvalidArgumentException( 'Task action should be a string.' );
}
$this->action = sanitize_key( $action );
$this->meta = new Meta();
if ( empty( $this->action ) ) {
throw new \UnexpectedValueException( 'Task action cannot be empty.' );
}
}
/**
* Define the type of the task as async.
*
* @since 1.5.9
*
* @return \WPForms\Tasks\Task
*/
public function async() {
$this->type = self::TYPE_ASYNC;
return $this;
}
/**
* Define the type of the task as recurring.
*
* @since 1.5.9
*
* @param int $timestamp When the first instance of the job will run.
* @param int $interval How long to wait between runs.
*
* @return \WPForms\Tasks\Task
*/
public function recurring( $timestamp, $interval ) {
$this->type = self::TYPE_RECURRING;
$this->timestamp = (int) $timestamp;
$this->interval = (int) $interval;
return $this;
}
/**
* Define the type of the task as one-time.
*
* @since 1.5.9
*
* @param int $timestamp When the first instance of the job will run.
*
* @return \WPForms\Tasks\Task
*/
public function once( $timestamp ) {
$this->type = self::TYPE_ONCE;
$this->timestamp = (int) $timestamp;
return $this;
}
/**
* Pass any number of params that should be saved to Meta table.
*
* @since 1.5.9
*
* @return \WPForms\Tasks\Task
*/
public function params() {
$this->params = func_get_args();
return $this;
}
/**
* Register the action.
* Should be the final call in a chain.
*
* @since 1.5.9
*
* @return null|string Action ID.
*/
public function register() {
$action_id = null;
// No processing if ActionScheduler is not usable.
if ( ! wpforms()->get( 'tasks' )->is_usable() ) {
return $action_id;
}
// Save data to tasks meta table.
if ( $this->params !== null ) {
$this->meta_id = $this->meta->add(
[
'action' => $this->action,
'data' => $this->params,
]
);
if ( empty( $this->meta_id ) ) {
return $action_id;
}
}
// Prevent 500 errors when Action Scheduler tables don't exist.
try {
switch ( $this->type ) {
case self::TYPE_ASYNC:
$action_id = $this->register_async();
break;
case self::TYPE_RECURRING:
$action_id = $this->register_recurring();
break;
case self::TYPE_ONCE:
$action_id = $this->register_once();
break;
}
} catch ( \RuntimeException $exception ) {
$action_id = null;
}
return $action_id;
}
/**
* Register the async task.
*
* @since 1.5.9
*
* @return null|string Action ID.
*/
protected function register_async() {
if ( ! function_exists( 'as_enqueue_async_action' ) ) {
return null;
}
return as_enqueue_async_action(
$this->action,
[ 'tasks_meta_id' => $this->meta_id ],
Tasks::GROUP
);
}
/**
* Register the recurring task.
*
* @since 1.5.9
*
* @return null|string Action ID.
*/
protected function register_recurring() {
if ( ! function_exists( 'as_schedule_recurring_action' ) ) {
return null;
}
return as_schedule_recurring_action(
$this->timestamp,
$this->interval,
$this->action,
[ 'tasks_meta_id' => $this->meta_id ],
Tasks::GROUP
);
}
/**
* Register the one-time task.
*
* @since 1.5.9
*
* @return null|string Action ID.
*/
protected function register_once() {
if ( ! function_exists( 'as_schedule_single_action' ) ) {
return null;
}
return as_schedule_single_action(
$this->timestamp,
$this->action,
[ 'tasks_meta_id' => $this->meta_id ],
Tasks::GROUP
);
}
/**
* Cancel all occurrences of this task.
*
* @since 1.6.1
*
* @return null|bool|string Null if no matching action found,
* false if AS library is missing,
* true if scheduled task has no params,
* string of the scheduled action ID if a scheduled action was found and unscheduled.
*/
public function cancel() {
if ( ! function_exists( 'as_unschedule_all_actions' ) ) {
return false;
}
if ( $this->params === null ) {
as_unschedule_all_actions( $this->action );
return true;
}
$this->meta_id = $this->meta->get_meta_id( $this->action, $this->params );
if ( $this->meta_id === null ) {
return null;
}
return as_unschedule_action( $this->action, [ 'tasks_meta_id' => $this->meta_id ], Tasks::GROUP );
}
}
SmartTags/SmartTag/FormId.php 0000644 00000000735 15133255232 0012072 0 ustar 00 <?php
namespace WPForms\SmartTags\SmartTag;
/**
* Class FormId.
*
* @since 1.6.7
*/
class FormId extends SmartTag {
/**
* Get smart tag value.
*
* @since 1.6.7
*
* @param array $form_data Form data.
* @param array $fields List of fields.
* @param string $entry_id Entry ID.
*
* @return int
*/
public function get_value( $form_data, $fields = [], $entry_id = '' ) {
return ! empty( $form_data['id'] ) ? absint( $form_data['id'] ) : 0;
}
}
SmartTags/SmartTag/FieldValueId.php 0000644 00000002053 15133255232 0013202 0 ustar 00 <?php
namespace WPForms\SmartTags\SmartTag;
/**
* Class FieldValueId.
*
* @since 1.6.7
*/
class FieldValueId extends SmartTag {
/**
* Get smart tag value.
*
* @since 1.6.7
*
* @param array $form_data Form data.
* @param array $fields List of fields.
* @param string $entry_id Entry ID.
*
* @return string
*/
public function get_value( $form_data, $fields = [], $entry_id = '' ) {
$attributes = $this->get_attributes();
if ( ! isset( $attributes['field_value_id'] ) || $attributes['field_value_id'] === '' ) {
return '';
}
$field_id = $attributes['field_value_id'];
if ( ! isset( $fields[ $field_id ] ) || $fields[ $field_id ] === '' ) {
return '';
}
if ( isset( $fields[ $field_id ]['value_raw'] ) && ! is_array( $fields[ $field_id ]['value_raw'] ) && (string) $fields[ $field_id ]['value_raw'] !== '' ) {
return wp_kses_post( wp_unslash( $fields[ $field_id ]['value_raw'] ) );
}
return isset( $fields[ $field_id ]['value'] ) ? wp_kses_post( wp_unslash( $fields[ $field_id ]['value'] ) ) : '';
}
}
SmartTags/SmartTag/UrlLogout.php 0000644 00000000704 15133255232 0012642 0 ustar 00 <?php
namespace WPForms\SmartTags\SmartTag;
/**
* Class UrlLogout.
*
* @since 1.6.7
*/
class UrlLogout extends SmartTag {
/**
* Get smart tag value.
*
* @since 1.6.7
*
* @param array $form_data Form data.
* @param array $fields List of fields.
* @param string $entry_id Entry ID.
*
* @return string
*/
public function get_value( $form_data, $fields = [], $entry_id = '' ) {
return esc_url( wp_logout_url() );
}
}
SmartTags/SmartTag/UserIp.php 0000644 00000000700 15133255232 0012111 0 ustar 00 <?php
namespace WPForms\SmartTags\SmartTag;
/**
* Class UserIp.
*
* @since 1.6.7
*/
class UserIp extends SmartTag {
/**
* Get smart tag value.
*
* @since 1.6.7
*
* @param array $form_data Form data.
* @param array $fields List of fields.
* @param string $entry_id Entry ID.
*
* @return string
*/
public function get_value( $form_data, $fields = [], $entry_id = '' ) {
return esc_html( wpforms_get_ip() );
}
}
SmartTags/SmartTag/AdminEmail.php 0000644 00000000731 15133255232 0012706 0 ustar 00 <?php
namespace WPForms\SmartTags\SmartTag;
/**
* Class AdminEmail.
*
* @since 1.6.7
*/
class AdminEmail extends SmartTag {
/**
* Get smart tag value.
*
* @since 1.6.7
*
* @param array $form_data Form data.
* @param array $fields List of fields.
* @param string $entry_id Entry ID.
*
* @return string
*/
public function get_value( $form_data, $fields = [], $entry_id = '' ) {
return sanitize_email( get_option( 'admin_email' ) );
}
}
SmartTags/SmartTag/UserMeta.php 0000644 00000001256 15133255232 0012436 0 ustar 00 <?php
namespace WPForms\SmartTags\SmartTag;
/**
* Class UserMeta.
*
* @since 1.6.7
*/
class UserMeta extends SmartTag {
/**
* Get smart tag value.
*
* @since 1.6.7
*
* @param array $form_data Form data.
* @param array $fields List of fields.
* @param string $entry_id Entry ID.
*
* @return string
*/
public function get_value( $form_data, $fields = [], $entry_id = '' ) {
$attributes = $this->get_attributes();
if ( empty( $attributes['key'] ) ) {
return '';
}
return is_user_logged_in() ?
wp_kses_post(
get_user_meta(
get_current_user_id(),
sanitize_text_field( $attributes['key'] ),
true
)
) :
'';
}
}
SmartTags/SmartTag/AuthorId.php 0000644 00000001346 15133255232 0012430 0 ustar 00 <?php
namespace WPForms\SmartTags\SmartTag;
/**
* Class AuthorId.
*
* @since 1.6.7
*/
class AuthorId extends SmartTag {
/**
* Get smart tag value.
*
* @since 1.6.7
*
* @param array $form_data Form data.
* @param array $fields List of fields.
* @param string $entry_id Entry ID.
*
* @return int
*/
public function get_value( $form_data, $fields = [], $entry_id = '' ) {
$id = get_the_author_meta( 'ID' );
if ( empty( $id ) && ! empty( $_POST['wpforms']['author'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
$id = get_the_author_meta( 'ID', absint( $_POST['wpforms']['author'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
}
return absint( $id );
}
}
SmartTags/SmartTag/UserId.php 0000644 00000000730 15133255232 0012100 0 ustar 00 <?php
namespace WPForms\SmartTags\SmartTag;
/**
* Class UserId.
*
* @since 1.6.7
*/
class UserId extends SmartTag {
/**
* Get smart tag value.
*
* @since 1.6.7
*
* @param array $form_data Form data.
* @param array $fields List of fields.
* @param string $entry_id Entry ID.
*
* @return int|string
*/
public function get_value( $form_data, $fields = [], $entry_id = '' ) {
return is_user_logged_in() ? get_current_user_id() : '';
}
}
SmartTags/SmartTag/UserFirstName.php 0000644 00000001210 15133255232 0013426 0 ustar 00 <?php
namespace WPForms\SmartTags\SmartTag;
use WP_User;
/**
* Class UserFirstName.
*
* @since 1.6.7
*/
class UserFirstName extends SmartTag {
/**
* Get smart tag value.
*
* @since 1.6.7
*
* @param array $form_data Form data.
* @param array $fields List of fields.
* @param string $entry_id Entry ID.
*
* @return string
*/
public function get_value( $form_data, $fields = [], $entry_id = '' ) {
$current_user = wp_get_current_user();
if ( ! $current_user instanceof WP_User ) {
return '';
}
return $current_user->exists() ? esc_html( wp_strip_all_tags( $current_user->user_firstname ) ) : '';
}
}
SmartTags/SmartTag/UrlLostPassword.php 0000644 00000000722 15133255232 0014035 0 ustar 00 <?php
namespace WPForms\SmartTags\SmartTag;
/**
* Class UrlRegister.
*
* @since 1.6.7
*/
class UrlLostPassword extends SmartTag {
/**
* Get smart tag value.
*
* @since 1.6.7
*
* @param array $form_data Form data.
* @param array $fields List of fields.
* @param string $entry_id Entry ID.
*
* @return string
*/
public function get_value( $form_data, $fields = [], $entry_id = '' ) {
return esc_url( wp_lostpassword_url() );
}
}
SmartTags/SmartTag/PageId.php 0000644 00000001421 15133255232 0012034 0 ustar 00 <?php
namespace WPForms\SmartTags\SmartTag;
/**
* Class PageId.
*
* @since 1.6.7
*/
class PageId extends SmartTag {
/**
* Get smart tag value.
*
* @since 1.6.7
*
* @param array $form_data Form data.
* @param array $fields List of fields.
* @param string $entry_id Entry ID.
*
* @return int|string
*/
public function get_value( $form_data, $fields = [], $entry_id = '' ) {
// phpcs:disable WordPress.Security.NonceVerification.Missing
if ( ! empty( $_POST['page_id'] ) ) {
return absint( $_POST['page_id'] );
}
// phpcs:enable WordPress.Security.NonceVerification.Missing
// We should not return any value on pages that don't belong to the page type.
return is_singular() || ( is_front_page() && is_page() ) ? get_the_ID() : '';
}
}
SmartTags/SmartTag/FieldHtmlId.php 0000644 00000002561 15133255232 0013036 0 ustar 00 <?php
namespace WPForms\SmartTags\SmartTag;
/**
* Class FieldHtmlId.
*
* @since 1.6.7
*/
class FieldHtmlId extends SmartTag {
/**
* Get smart tag value.
*
* @since 1.6.7
*
* @param array $form_data Form data.
* @param array $fields List of fields.
* @param string $entry_id Entry ID.
*
* @return string
*/
public function get_value( $form_data, $fields = [], $entry_id = '' ) {
$attributes = $this->get_attributes();
if ( ! isset( $attributes['field_html_id'] ) || ! is_numeric( $attributes['field_html_id'] ) || $attributes['field_html_id'] < 0 ) {
return '';
}
$field_id = absint( $attributes['field_html_id'] );
if ( empty( $fields[ $field_id ] ) ) {
return '';
}
$value = ! isset( $fields[ $field_id ]['value'] ) || (string) $fields[ $field_id ]['value'] === ''
? '<em>' . esc_html__( '(empty)', 'wpforms-lite' ) . '</em>'
: wp_kses_post( wp_unslash( $fields[ $field_id ]['value'] ) );
/**
* Modify value for the {field_html_id="123"} tag.
*
* @since 1.4.0
*
* @param string $value Smart tag value.
* @param array $field The field.
* @param array $form_data Processed form settings/data, prepared to be used later.
* @param string $context Context usage.
*/
return (string) apply_filters( 'wpforms_html_field_value', $value, $fields[ $field_id ], $form_data, 'smart-tag' );
}
}
SmartTags/SmartTag/UserFullName.php 0000644 00000001253 15133255232 0013250 0 ustar 00 <?php
namespace WPForms\SmartTags\SmartTag;
use WP_User;
/**
* Class UserFullName.
*
* @since 1.6.7
*/
class UserFullName extends SmartTag {
/**
* Get smart tag value.
*
* @since 1.6.7
*
* @param array $form_data Form data.
* @param array $fields List of fields.
* @param string $entry_id Entry ID.
*
* @return string
*/
public function get_value( $form_data, $fields = [], $entry_id = '' ) {
$current_user = wp_get_current_user();
if ( ! $current_user instanceof WP_User ) {
return '';
}
return $current_user->exists() ? esc_html( wp_strip_all_tags( $current_user->user_firstname . ' ' . $current_user->user_lastname ) ) : '';
}
}
SmartTags/SmartTag/Generic.php 0000644 00000000666 15133255232 0012271 0 ustar 00 <?php
namespace WPForms\SmartTags\SmartTag;
/**
* Class Generic.
*
* @since 1.6.7.1
*/
class Generic extends SmartTag {
/**
* Mock for the get_value method.
*
* @since 1.6.7.1
*
* @param array $form_data Form data.
* @param array $fields List of fields.
* @param string $entry_id Entry ID.
*
* @return null
*/
public function get_value( $form_data, $fields = [], $entry_id = '' ) {
return null;
}
}
SmartTags/SmartTag/UrlLogin.php 0000644 00000000701 15133255232 0012436 0 ustar 00 <?php
namespace WPForms\SmartTags\SmartTag;
/**
* Class UrlLogin.
*
* @since 1.6.7
*/
class UrlLogin extends SmartTag {
/**
* Get smart tag value.
*
* @since 1.6.7
*
* @param array $form_data Form data.
* @param array $fields List of fields.
* @param string $entry_id Entry ID.
*
* @return string
*/
public function get_value( $form_data, $fields = [], $entry_id = '' ) {
return esc_url( wp_login_url() );
}
}
SmartTags/SmartTag/UserLastName.php 0000644 00000001205 15133255232 0013246 0 ustar 00 <?php
namespace WPForms\SmartTags\SmartTag;
use WP_User;
/**
* Class UserLastName.
*
* @since 1.6.7
*/
class UserLastName extends SmartTag {
/**
* Get smart tag value.
*
* @since 1.6.7
*
* @param array $form_data Form data.
* @param array $fields List of fields.
* @param string $entry_id Entry ID.
*
* @return string
*/
public function get_value( $form_data, $fields = [], $entry_id = '' ) {
$current_user = wp_get_current_user();
if ( ! $current_user instanceof WP_User ) {
return '';
}
return $current_user->exists() ? esc_html( wp_strip_all_tags( $current_user->user_lastname ) ) : '';
}
}
SmartTags/SmartTag/UrlReferer.php 0000644 00000000720 15133255232 0012761 0 ustar 00 <?php
namespace WPForms\SmartTags\SmartTag;
/**
* Class UrlReferer.
*
* @since 1.6.7
*/
class UrlReferer extends SmartTag {
/**
* Get smart tag value.
*
* @since 1.6.7
*
* @param array $form_data Form data.
* @param array $fields List of fields.
* @param string $entry_id Entry ID.
*
* @return string
*/
public function get_value( $form_data, $fields = [], $entry_id = '' ) {
return esc_url( (string) wp_get_referer() );
}
}
SmartTags/SmartTag/SmartTag.php 0000644 00000003263 15133255232 0012433 0 ustar 00 <?php
namespace WPForms\SmartTags\SmartTag;
/**
* Class SmartTag.
*
* @since 1.6.7
*/
abstract class SmartTag {
/**
* Full smart tag.
* For example: {smart_tag attr="1" attr2="true"}.
*
* @since 1.6.7
*
* @var string
*/
protected $smart_tag;
/**
* List of attributes.
*
* @since 1.6.7
*
* @var array
*/
protected $attributes = [];
/**
* SmartTag constructor.
*
* @since 1.6.7
*
* @param string $smart_tag Full smart tag.
*/
public function __construct( $smart_tag ) {
$this->smart_tag = $smart_tag;
}
/**
* Get smart tag value.
*
* @since 1.6.7
*
* @param array $form_data Form data.
* @param array $fields List of fields.
* @param string $entry_id Entry ID.
*
* @return string
*/
abstract public function get_value( $form_data, $fields = [], $entry_id = '' );
/**
* Get list of smart tag attributes.
*
* @since 1.6.7
*
* @return array
*/
public function get_attributes() {
if ( ! empty( $this->attributes ) ) {
return $this->attributes;
}
/**
* (\w+) an attribute name and also the first capturing group. Lowercase or uppercase letters, digits, underscore.
* = the equal sign.
* (["\']) single or double quote, the second capturing group.
* (.+?) an attribute value within the quotes, and also the third capturing group. Any number of any characters except new line. Lazy mode - match as few characters as possible to allow multiple attributes on one line.
* \2 - repeat the second capturing group.
*/
preg_match_all( '/(\w+)=(["\'])(.+?)\2/', $this->smart_tag, $attributes );
$this->attributes = array_combine( $attributes[1], $attributes[3] );
return $this->attributes;
}
}
SmartTags/SmartTag/Date.php 0000644 00000001142 15133255232 0011560 0 ustar 00 <?php
namespace WPForms\SmartTags\SmartTag;
/**
* Class Date.
*
* @since 1.6.7
*/
class Date extends SmartTag {
/**
* Get smart tag value.
*
* @since 1.6.7
*
* @param array $form_data Form data.
* @param array $fields List of fields.
* @param string $entry_id Entry ID.
*
* @return string
*/
public function get_value( $form_data, $fields = [], $entry_id = '' ) {
$attributes = $this->get_attributes();
$format = ! empty( $attributes['format'] ) ? $attributes['format'] : get_option( 'date_format' );
return wpforms_datetime_format( time(), $format, true );
}
}
SmartTags/SmartTag/AuthorEmail.php 0000644 00000001454 15133255232 0013123 0 ustar 00 <?php
namespace WPForms\SmartTags\SmartTag;
/**
* Class AuthorEmail.
*
* @since 1.6.7
*/
class AuthorEmail extends SmartTag {
/**
* Get smart tag value.
*
* @since 1.6.7
*
* @param array $form_data Form data.
* @param array $fields List of fields.
* @param string $entry_id Entry ID.
*
* @return string
*/
public function get_value( $form_data, $fields = [], $entry_id = '' ) {
$email = get_the_author_meta( 'user_email' );
if ( empty( $email ) && ! empty( $_POST['wpforms']['author'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
$email = get_the_author_meta( 'user_email', absint( $_POST['wpforms']['author'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
}
return ! empty( $email ) ? sanitize_email( $email ) : '';
}
}
SmartTags/SmartTag/FieldId.php 0000644 00000002467 15133255232 0012216 0 ustar 00 <?php
namespace WPForms\SmartTags\SmartTag;
/**
* Class FieldId.
*
* @since 1.6.7
*/
class FieldId extends SmartTag {
/**
* Get smart tag value.
*
* @since 1.6.7
*
* @param array $form_data Form data.
* @param array $fields List of fields.
* @param string $entry_id Entry ID.
*
* @return string
*/
public function get_value( $form_data, $fields = [], $entry_id = '' ) {
$attributes = $this->get_attributes();
if ( ! isset( $attributes['field_id'] ) || $attributes['field_id'] === '' ) {
return '';
}
$field_parts = explode( '|', $attributes['field_id'] );
$field_id = $field_parts[0];
if ( ! isset( $fields[ $field_id ] ) || $fields[ $field_id ] === '' ) {
return '';
}
$field_key = ! empty( $field_parts[1] ) ? sanitize_key( $field_parts[1] ) : 'value';
$value = isset( $fields[ $field_id ][ $field_key ] ) ? wp_kses_post( wp_unslash( $fields[ $field_id ][ $field_key ] ) ) : '';
/**
* Modify value for the `field_id` smart tag.
*
* @since 1.5.3
* @deprecated 1.6.7
*
* @see This filter is documented in wp-includes/plugin.php
*
* @param string Smart tag value.
*/
return (string) apply_filters_deprecated(
'wpforms_field_smart_tag_value',
[ $value ],
'1.6.7',
'wpforms_smarttags_process_field_id_value'
);
}
}
SmartTags/SmartTag/PageTitle.php 0000644 00000003056 15133255232 0012567 0 ustar 00 <?php
namespace WPForms\SmartTags\SmartTag;
/**
* Class PageTitle.
*
* @since 1.6.7
*/
class PageTitle extends SmartTag {
/**
* Get smart tag value.
*
* @since 1.6.7
*
* @param array $form_data Form data.
* @param array $fields List of fields.
* @param string $entry_id Entry ID.
*
* @return string
*/
public function get_value( $form_data, $fields = [], $entry_id = '' ) {
// phpcs:disable WordPress.Security.NonceVerification.Missing
if ( ! empty( $_POST['page_title'] ) ) {
return sanitize_text_field( wp_unslash( $_POST['page_title'] ) );
}
// phpcs:enable WordPress.Security.NonceVerification.Missing
/*
* In most cases `wp_title()` returns the value we're going to use, except:
* - on static front page (we can use page title as a fallback),
* - on standard front page with the latest post (we can use the site name as a fallback).
*/
if ( is_front_page() ) {
return wp_kses_post( is_page() ? get_the_title( get_the_ID() ) : get_bloginfo( 'name' ) );
}
global $wp_filter;
// Back up all callbacks.
$callbacks = $wp_filter['wp_title']->callbacks;
// Unset all callbacks.
$wp_filter['wp_title']->callbacks = [];
// Get the raw value.
$title = trim( wp_title( '', false ) );
// Run through the default transformations WordPress does on this hook.
$title = wptexturize( $title );
$title = convert_chars( $title );
$title = esc_html( $title );
$title = capital_P_dangit( $title );
// Restore all callbacks.
$wp_filter['wp_title']->callbacks = $callbacks;
return $title;
}
}
SmartTags/SmartTag/QueryVar.php 0000644 00000002416 15133255232 0012466 0 ustar 00 <?php
namespace WPForms\SmartTags\SmartTag;
/**
* Class QueryVar.
*
* @since 1.6.7
*/
class QueryVar extends SmartTag {
/**
* Get smart tag value.
*
* @since 1.6.7
* @since 1.7.6 Added support for ajax submissions.
*
* @param array $form_data Form data.
* @param array $fields List of fields.
* @param string $entry_id Entry ID.
*
* @return string
*/
public function get_value( $form_data, $fields = [], $entry_id = '' ) {
$attributes = $this->get_attributes();
if ( empty( $attributes['key'] ) ) {
return '';
}
// phpcs:disable WordPress.Security.NonceVerification.Recommended
if ( ! empty( $_GET[ $attributes['key'] ] ) ) {
return esc_html( sanitize_text_field( wp_unslash( $_GET[ $attributes['key'] ] ) ) );
}
// phpcs:enable WordPress.Security.NonceVerification.Recommended
// phpcs:disable WordPress.Security.NonceVerification.Missing
if ( empty( $_POST['page_url'] ) ) {
return '';
}
$query = wp_parse_url( esc_url_raw( wp_unslash( $_POST['page_url'] ) ), PHP_URL_QUERY );
// phpcs:enable WordPress.Security.NonceVerification.Missing
parse_str( $query, $results );
return ! empty( $results[ $attributes['key'] ] ) ? esc_html( sanitize_text_field( wp_unslash( $results[ $attributes['key'] ] ) ) ) : '';
}
}
SmartTags/SmartTag/UniqueValue.php 0000644 00000003622 15133255232 0013153 0 ustar 00 <?php
namespace WPForms\SmartTags\SmartTag;
/**
* Class UniqueValue.
*
* @since 1.7.5
*/
class UniqueValue extends SmartTag {
/**
* Default length of the unique value to be generated.
*
* @since 1.7.5
*
* @var int
*/
const DEFAULT_LENGTH = 16;
/**
* Default format of the unique value to be generated.
*
* @since 1.7.5
*
* @var string
*/
const DEFAULT_FORMAT = 'alphanumeric';
/**
* Get smart tag value.
*
* @since 1.7.5
*
* @param array $form_data Form data.
* @param array $fields List of fields.
* @param string $entry_id Entry ID.
*
* @return string
*/
public function get_value( $form_data, $fields = [], $entry_id = '' ) {
$length = self::DEFAULT_LENGTH;
$format = self::DEFAULT_FORMAT;
$attributes = $this->get_attributes();
if ( array_key_exists( 'length', $attributes ) ) {
$length = max( $length, absint( $attributes['length'] ) );
}
if ( array_key_exists( 'format', $attributes ) && ! empty( $attributes['format'] ) ) {
$format = $attributes['format'];
}
return $this->generate_string( $length, $format );
}
/**
* Generates a random string in defined format.
*
* @since 1.7.5
*
* @param int $length Optional. The length of string to generate.
* @param string $format The format of string to generate. Accepts 'alphanumeric',
* 'numeric', and 'alpha'. Default 'alphanumeric'.
*
* @return string
*/
private function generate_string( $length = 16, $format = 'alphanumeric' ) {
$alpha = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$numbers = '0123456789';
switch ( strtolower( $format ) ) {
case 'numeric':
$chars = $numbers;
break;
case 'alpha':
$chars = $alpha;
break;
default:
$chars = $alpha . $numbers;
break;
}
$chars = str_pad( $chars, $length, $chars );
return substr( str_shuffle( $chars ), 0, $length );
}
}
SmartTags/SmartTag/PageUrl.php 0000644 00000001130 15133255232 0012237 0 ustar 00 <?php
namespace WPForms\SmartTags\SmartTag;
/**
* Class PageUrl.
*
* @since 1.6.7
*/
class PageUrl extends SmartTag {
/**
* Get smart tag value.
*
* @since 1.6.7
*
* @param array $form_data Form data.
* @param array $fields List of fields.
* @param string $entry_id Entry ID.
*
* @return string
*/
public function get_value( $form_data, $fields = [], $entry_id = '' ) {
// phpcs:ignore WordPress.Security.NonceVerification
return empty( $_POST['page_url'] ) ? esc_url( wpforms_current_url() ) : esc_url( esc_url_raw( wp_unslash( $_POST['page_url'] ) ) );
}
}
SmartTags/SmartTag/UrlRegister.php 0000644 00000000716 15133255232 0013160 0 ustar 00 <?php
namespace WPForms\SmartTags\SmartTag;
/**
* Class UrlRegister.
*
* @since 1.6.7
*/
class UrlRegister extends SmartTag {
/**
* Get smart tag value.
*
* @since 1.6.7
*
* @param array $form_data Form data.
* @param array $fields List of fields.
* @param string $entry_id Entry ID.
*
* @return string
*/
public function get_value( $form_data, $fields = [], $entry_id = '' ) {
return esc_url( wp_registration_url() );
}
}
SmartTags/SmartTag/UserEmail.php 0000644 00000001155 15133255232 0012575 0 ustar 00 <?php
namespace WPForms\SmartTags\SmartTag;
use WP_User;
/**
* Class UserEmail.
*
* @since 1.6.7
*/
class UserEmail extends SmartTag {
/**
* Get smart tag value.
*
* @since 1.6.7
*
* @param array $form_data Form data.
* @param array $fields List of fields.
* @param string $entry_id Entry ID.
*
* @return string
*/
public function get_value( $form_data, $fields = [], $entry_id = '' ) {
$current_user = wp_get_current_user();
if ( ! $current_user instanceof WP_User ) {
return '';
}
return $current_user->exists() ? sanitize_email( $current_user->user_email ) : '';
}
}
SmartTags/SmartTag/FormName.php 0000644 00000002105 15133255232 0012407 0 ustar 00 <?php
namespace WPForms\SmartTags\SmartTag;
/**
* Class FormName.
*
* @since 1.6.7
*/
class FormName extends SmartTag {
/**
* Get smart tag value.
*
* @since 1.6.7
*
* @param array $form_data Form data.
* @param array $fields List of fields.
* @param string $entry_id Entry ID.
*
* @return string
*/
public function get_value( $form_data, $fields = [], $entry_id = '' ) {
// TODO: Remove the conditional after Form Page v1.5.0 addon release.
// The Form Pages addon rewrites the form_title setting for it's internal needs,
// so we want to first check if we have a saved title for the form, and if so,
// we will use that for the form title smart tag.
if ( isset( $form_data['settings']['form_name'] ) && $form_data['settings']['form_name'] !== '' ) {
return esc_html( wp_strip_all_tags( $form_data['settings']['form_name'] ) );
}
if ( ! isset( $form_data['settings']['form_title'] ) || $form_data['settings']['form_title'] === '' ) {
return '';
}
return esc_html( wp_strip_all_tags( $form_data['settings']['form_title'] ) );
}
}
SmartTags/SmartTag/AuthorDisplay.php 0000644 00000001456 15133255232 0013503 0 ustar 00 <?php
namespace WPForms\SmartTags\SmartTag;
/**
* Class AuthorDisplay.
*
* @since 1.6.7
*/
class AuthorDisplay extends SmartTag {
/**
* Get smart tag value.
*
* @since 1.6.7
*
* @param array $form_data Form data.
* @param array $fields List of fields.
* @param string $entry_id Entry ID.
*
* @return string
*/
public function get_value( $form_data, $fields = [], $entry_id = '' ) {
$name = get_the_author();
// phpcs:disable WordPress.Security.NonceVerification.Missing
if ( empty( $name ) && ! empty( $_POST['wpforms']['author'] ) ) {
$name = get_the_author_meta( 'display_name', absint( $_POST['wpforms']['author'] ) );
}
// phpcs:enable WordPress.Security.NonceVerification.Missing
return ! empty( $name ) ? esc_html( wp_strip_all_tags( $name ) ) : '';
}
}
SmartTags/SmartTag/UserDisplay.php 0000644 00000001202 15133255232 0013144 0 ustar 00 <?php
namespace WPForms\SmartTags\SmartTag;
use WP_User;
/**
* Class UserDisplay.
*
* @since 1.6.7
*/
class UserDisplay extends SmartTag {
/**
* Get smart tag value.
*
* @since 1.6.7
*
* @param array $form_data Form data.
* @param array $fields List of fields.
* @param string $entry_id Entry ID.
*
* @return string
*/
public function get_value( $form_data, $fields = [], $entry_id = '' ) {
$current_user = wp_get_current_user();
if ( ! $current_user instanceof WP_User ) {
return '';
}
return $current_user->exists() ? esc_html( wp_strip_all_tags( $current_user->display_name ) ) : '';
}
}
SmartTags/SmartTags.php 0000644 00000024520 15133255232 0011073 0 ustar 00 <?php
namespace WPForms\SmartTags;
use WPForms\SmartTags\SmartTag\Generic;
use WPForms\SmartTags\SmartTag\SmartTag;
/**
* Class SmartTags.
*
* @since 1.6.7
*/
class SmartTags {
/**
* List of smart tags.
*
* @since 1.6.7
*
* @var array
*/
protected $smart_tags = [];
/**
* Hooks.
*
* @since 1.6.7
*/
public function hooks() {
add_filter( 'wpforms_process_smart_tags', [ $this, 'process' ], 10, 4 );
add_filter( 'wpforms_builder_enqueues_smart_tags', [ $this, 'builder' ] );
}
/**
* Approved smart tags.
*
* @codeCoverageIgnore
*
* @since 1.0.0
* @deprecated 1.6.7
*
* @param string $return Type of data to return.
*
* @return string|array
*/
public function get( $return = 'array' ) {
_deprecated_argument( __METHOD__, '1.6.7 of the WPForms plugin' );
_deprecated_function( __METHOD__, '1.6.7 of the WPForms plugin', __CLASS__ . '::get_smart_tags()' );
$tags = $this->get_smart_tags();
if ( $return !== 'list' ) {
return $tags;
}
// Return formatted list.
$output = '<ul class="smart-tags-list">';
foreach ( $tags as $key => $tag ) {
$output .= '<li><a href="#" data-value="' . esc_attr( $key ) . '">' . esc_html( $tag ) . '</a></li>';
}
$output .= '</ul>';
return $output;
}
/**
* Get list of smart tags.
*
* @since 1.6.7
*
* @return array
*/
public function get_smart_tags() {
if ( ! empty( $this->smart_tags ) ) {
return $this->smart_tags;
}
/**
* Modify smart tags list.
*
* @since 1.4.0
*
* @param array $tags The list of smart tags.
*/
$this->smart_tags = (array) apply_filters(
'wpforms_smart_tags',
$this->smart_tags_list()
);
return $this->smart_tags;
}
/**
* Get list of registered smart tags.
*
* @since 1.6.7
*
* @return array
*/
protected function smart_tags_list() {
return [
'admin_email' => esc_html__( 'Site Administrator Email', 'wpforms-lite' ),
'field_id' => esc_html__( 'Field ID', 'wpforms-lite' ),
'field_html_id' => esc_html__( 'Field HTML ID', 'wpforms-lite' ),
'field_value_id' => esc_html__( 'Field Value', 'wpforms-lite' ),
'form_id' => esc_html__( 'Form ID', 'wpforms-lite' ),
'form_name' => esc_html__( 'Form Name', 'wpforms-lite' ),
'page_title' => esc_html__( 'Embedded Post/Page Title', 'wpforms-lite' ),
'page_url' => esc_html__( 'Embedded Post/Page URL', 'wpforms-lite' ),
'page_id' => esc_html__( 'Embedded Post/Page ID', 'wpforms-lite' ),
'date' => esc_html__( 'Date', 'wpforms-lite' ),
'query_var' => esc_html__( 'Query String Variable', 'wpforms-lite' ),
'user_ip' => esc_html__( 'User IP Address', 'wpforms-lite' ),
'user_id' => esc_html__( 'User ID', 'wpforms-lite' ),
'user_display' => esc_html__( 'User Display Name', 'wpforms-lite' ),
'user_full_name' => esc_html__( 'User Full Name', 'wpforms-lite' ),
'user_first_name' => esc_html__( 'User First Name', 'wpforms-lite' ),
'user_last_name' => esc_html__( 'User Last Name', 'wpforms-lite' ),
'user_email' => esc_html__( 'User Email', 'wpforms-lite' ),
'user_meta' => esc_html__( 'User Meta', 'wpforms-lite' ),
'author_id' => esc_html__( 'Author ID', 'wpforms-lite' ),
'author_display' => esc_html__( 'Author Name', 'wpforms-lite' ),
'author_email' => esc_html__( 'Author Email', 'wpforms-lite' ),
'url_referer' => esc_html__( 'Referrer URL', 'wpforms-lite' ),
'url_login' => esc_html__( 'Login URL', 'wpforms-lite' ),
'url_logout' => esc_html__( 'Logout URL', 'wpforms-lite' ),
'url_register' => esc_html__( 'Register URL', 'wpforms-lite' ),
'url_lost_password' => esc_html__( 'Lost Password URL', 'wpforms-lite' ),
'unique_value' => esc_html__( 'Unique Value', 'wpforms-lite' ),
];
}
/**
* Get all smart tags in the content.
*
* @since 1.6.7
*
* @param string $content Content.
*
* @return array
*/
private function get_all_smart_tags( $content ) {
/**
* A smart tag should start and end with a curly brace.
* ([a-z0-9_]+) a smart tag name and also the first capturing group. Lowercase letters, digits, and an underscore.
* (|[ =][^\n}]*) - second capturing group:
* | no characters at all or the following:
* [ =][^\n}]* space or equal sign and any number of any characters except new line and closing curly brace.
*/
preg_match_all( '~{([a-z0-9_]+)(|[ =][^\n}]*)}~', $content, $smart_tags );
return array_combine( $smart_tags[0], $smart_tags[1] );
}
/**
* Process smart tags.
*
* @since 1.6.7
*
* @param string $content Content.
* @param array $form_data Form data.
* @param array $fields List of fields.
* @param string $entry_id Entry ID.
*
* @return string
*/
public function process( $content, $form_data, $fields = [], $entry_id = '' ) {
$smart_tags = $this->get_all_smart_tags( $content );
if ( empty( $smart_tags ) ) {
return $content;
}
foreach ( $smart_tags as $smart_tag => $tag_name ) {
$class_name = $this->get_smart_tag_class_name( $tag_name );
$smart_tag_object = new $class_name( $smart_tag );
/**
* Modify the smart tag value.
*
* @since 1.6.7
* @since 1.6.7.1 Added the 5th argument.
*
* @param scalar|null $value Smart Tag value.
* @param array $form_data Form data.
* @param string $fields List of fields.
* @param int $entry_id Entry ID.
* @param SmartTag $smart_tag_object The smart tag object or the Generic object for those cases when class unregistered.
*/
$value = apply_filters(
"wpforms_smarttags_process_{$tag_name}_value",
$smart_tag_object->get_value( $form_data, $fields, $entry_id ),
$form_data,
$fields,
$entry_id,
$smart_tag_object
);
/**
* Modify a smart tag value.
*
* @since 1.6.7.1
*
* @param scalar|null $value Smart Tag value.
* @param string $tag_name Smart tag name.
* @param array $form_data Form data.
* @param string $fields List of fields.
* @param int $entry_id Entry ID.
* @param SmartTag $smart_tag_object The smart tag object or the Generic object for those cases when class unregistered.
*/
$value = apply_filters(
'wpforms_smarttags_process_value',
$value,
$tag_name,
$form_data,
$fields,
$entry_id,
$smart_tag_object
);
if ( ! is_null( $value ) ) {
$content = $this->replace( $smart_tag, $value, $content );
}
/**
* Modify content with smart tags.
*
* @since 1.4.0
* @since 1.6.7.1 Added 3rd, 4th, 5th, 6th arguments.
*
* @param string $content Content of the Smart Tag.
* @param string $tag_name Tag name of the Smart Tag.
* @param array $form_data Form data.
* @param string $fields List of fields.
* @param int $entry_id Entry ID.
* @param SmartTag $smart_tag_object The smart tag object or the Generic object for those cases when class unregistered.
*/
$content = (string) apply_filters(
'wpforms_smart_tag_process',
$content,
$tag_name,
$form_data,
$fields,
$entry_id,
$smart_tag_object
);
}
return $content;
}
/**
* Determine if the smart tag is registered.
*
* @since 1.6.7
*
* @param string $smart_tag_name Smart tag name.
*
* @return bool
*/
protected function has_smart_tag( $smart_tag_name ) {
return array_key_exists( $smart_tag_name, $this->get_smart_tags() );
}
/**
* Get smart tag class name.
*
* @since 1.6.7
*
* @param string $smart_tag_name Smart tag name.
*
* @return string
*/
protected function get_smart_tag_class_name( $smart_tag_name ) {
if ( ! $this->has_smart_tag( $smart_tag_name ) ) {
return Generic::class;
}
$class_name = str_replace( ' ', '', ucwords( str_replace( '_', ' ', $smart_tag_name ) ) );
$full_class_name = '\\WPForms\\SmartTags\\SmartTag\\' . $class_name;
if ( class_exists( $full_class_name ) ) {
return $full_class_name;
}
/**
* Modify a smart tag class name that describes the smart tag logic.
*
* @since 1.6.7
*
* @param string $class_name The value.
* @param string $smart_tag_name Smart tag name.
*/
$full_class_name = apply_filters( 'wpforms_smarttags_get_smart_tag_class_name', '', $smart_tag_name );
return class_exists( $full_class_name ) ? $full_class_name : Generic::class;
}
/**
* Retrieve the builder's special tags.
*
* @since 1.6.7
*
* @return array
*/
protected function get_replacement_builder_tags() {
return [
'date' => 'date format="m/d/Y"',
'query_var' => 'query_var key=""',
'user_meta' => 'user_meta key=""',
];
}
/**
* Hide smart tags in the builder.
*
* @since 1.6.7
*
* @return array
*/
protected function get_hidden_builder_tags() {
return [
'field_id',
'field_html_id',
'field_value_id',
];
}
/**
* Builder tags.
*
* @since 1.6.7
*
* @return array
*/
public function builder() {
$smart_tags = $this->get_smart_tags();
$replacement_tags = $this->get_replacement_builder_tags();
$hidden_tags = $this->get_hidden_builder_tags();
foreach ( $replacement_tags as $tag => $replacement_tag ) {
$smart_tags = wpforms_array_insert( $smart_tags, [ $replacement_tag => $smart_tags[ $tag ] ], $tag );
unset( $smart_tags[ $tag ] );
}
foreach ( $hidden_tags as $hidden_tag ) {
unset( $smart_tags[ $hidden_tag ] );
}
return $smart_tags;
}
/**
* Replace a found smart tag with the final value.
*
* @since 1.6.7
*
* @param string $tag The tag.
* @param string $value The value.
* @param string $content Content.
*
* @return string
*/
private function replace( $tag, $value, $content ) {
return str_replace( $tag, strip_shortcodes( $value ), $content );
}
/**
* Replace a found smart tag with the final value.
*
* @codeCoverageIgnore
*
* @since 1.5.9
* @deprecated 1.6.7
*
* @param string $tag The tag.
* @param string $value The value.
* @param string $content Content.
*
* @return string
*/
public function parse( $tag, $value, $content ) {
_deprecated_function( __METHOD__, '1.6.7 of the WPForms plugin' );
return $this->replace( $tag, $value, $content );
}
}
Iri.php 0000604 00000071666 15133277060 0006020 0 ustar 00 <?php
/**
* IRI parser/serialiser/normaliser
*
* @package Requests\Utilities
*/
namespace WpOrg\Requests;
use WpOrg\Requests\Exception;
use WpOrg\Requests\Exception\InvalidArgument;
use WpOrg\Requests\Ipv6;
use WpOrg\Requests\Port;
use WpOrg\Requests\Utility\InputValidator;
/**
* IRI parser/serialiser/normaliser
*
* Copyright (c) 2007-2010, Geoffrey Sneddon and Steve Minutillo.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the SimplePie Team nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package Requests\Utilities
* @author Geoffrey Sneddon
* @author Steve Minutillo
* @copyright 2007-2009 Geoffrey Sneddon and Steve Minutillo
* @license https://opensource.org/licenses/bsd-license.php
* @link http://hg.gsnedders.com/iri/
*
* @property string $iri IRI we're working with
* @property-read string $uri IRI in URI form, {@see \WpOrg\Requests\Iri::to_uri()}
* @property string $scheme Scheme part of the IRI
* @property string $authority Authority part, formatted for a URI (userinfo + host + port)
* @property string $iauthority Authority part of the IRI (userinfo + host + port)
* @property string $userinfo Userinfo part, formatted for a URI (after '://' and before '@')
* @property string $iuserinfo Userinfo part of the IRI (after '://' and before '@')
* @property string $host Host part, formatted for a URI
* @property string $ihost Host part of the IRI
* @property string $port Port part of the IRI (after ':')
* @property string $path Path part, formatted for a URI (after first '/')
* @property string $ipath Path part of the IRI (after first '/')
* @property string $query Query part, formatted for a URI (after '?')
* @property string $iquery Query part of the IRI (after '?')
* @property string $fragment Fragment, formatted for a URI (after '#')
* @property string $ifragment Fragment part of the IRI (after '#')
*/
class Iri {
/**
* Scheme
*
* @var string|null
*/
protected $scheme = null;
/**
* User Information
*
* @var string|null
*/
protected $iuserinfo = null;
/**
* ihost
*
* @var string|null
*/
protected $ihost = null;
/**
* Port
*
* @var string|null
*/
protected $port = null;
/**
* ipath
*
* @var string
*/
protected $ipath = '';
/**
* iquery
*
* @var string|null
*/
protected $iquery = null;
/**
* ifragment|null
*
* @var string
*/
protected $ifragment = null;
/**
* Normalization database
*
* Each key is the scheme, each value is an array with each key as the IRI
* part and value as the default value for that part.
*
* @var array
*/
protected $normalization = array(
'acap' => array(
'port' => Port::ACAP,
),
'dict' => array(
'port' => Port::DICT,
),
'file' => array(
'ihost' => 'localhost',
),
'http' => array(
'port' => Port::HTTP,
),
'https' => array(
'port' => Port::HTTPS,
),
);
/**
* Return the entire IRI when you try and read the object as a string
*
* @return string
*/
public function __toString() {
return $this->get_iri();
}
/**
* Overload __set() to provide access via properties
*
* @param string $name Property name
* @param mixed $value Property value
*/
public function __set($name, $value) {
if (method_exists($this, 'set_' . $name)) {
call_user_func(array($this, 'set_' . $name), $value);
}
elseif (
$name === 'iauthority'
|| $name === 'iuserinfo'
|| $name === 'ihost'
|| $name === 'ipath'
|| $name === 'iquery'
|| $name === 'ifragment'
) {
call_user_func(array($this, 'set_' . substr($name, 1)), $value);
}
}
/**
* Overload __get() to provide access via properties
*
* @param string $name Property name
* @return mixed
*/
public function __get($name) {
// isset() returns false for null, we don't want to do that
// Also why we use array_key_exists below instead of isset()
$props = get_object_vars($this);
if (
$name === 'iri' ||
$name === 'uri' ||
$name === 'iauthority' ||
$name === 'authority'
) {
$method = 'get_' . $name;
$return = $this->$method();
}
elseif (array_key_exists($name, $props)) {
$return = $this->$name;
}
// host -> ihost
elseif (($prop = 'i' . $name) && array_key_exists($prop, $props)) {
$name = $prop;
$return = $this->$prop;
}
// ischeme -> scheme
elseif (($prop = substr($name, 1)) && array_key_exists($prop, $props)) {
$name = $prop;
$return = $this->$prop;
}
else {
trigger_error('Undefined property: ' . get_class($this) . '::' . $name, E_USER_NOTICE);
$return = null;
}
if ($return === null && isset($this->normalization[$this->scheme][$name])) {
return $this->normalization[$this->scheme][$name];
}
else {
return $return;
}
}
/**
* Overload __isset() to provide access via properties
*
* @param string $name Property name
* @return bool
*/
public function __isset($name) {
return (method_exists($this, 'get_' . $name) || isset($this->$name));
}
/**
* Overload __unset() to provide access via properties
*
* @param string $name Property name
*/
public function __unset($name) {
if (method_exists($this, 'set_' . $name)) {
call_user_func(array($this, 'set_' . $name), '');
}
}
/**
* Create a new IRI object, from a specified string
*
* @param string|Stringable|null $iri
*
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $iri argument is not a string, Stringable or null.
*/
public function __construct($iri = null) {
if ($iri !== null && InputValidator::is_string_or_stringable($iri) === false) {
throw InvalidArgument::create(1, '$iri', 'string|Stringable|null', gettype($iri));
}
$this->set_iri($iri);
}
/**
* Create a new IRI object by resolving a relative IRI
*
* Returns false if $base is not absolute, otherwise an IRI.
*
* @param \WpOrg\Requests\Iri|string $base (Absolute) Base IRI
* @param \WpOrg\Requests\Iri|string $relative Relative IRI
* @return \WpOrg\Requests\Iri|false
*/
public static function absolutize($base, $relative) {
if (!($relative instanceof self)) {
$relative = new self($relative);
}
if (!$relative->is_valid()) {
return false;
}
elseif ($relative->scheme !== null) {
return clone $relative;
}
if (!($base instanceof self)) {
$base = new self($base);
}
if ($base->scheme === null || !$base->is_valid()) {
return false;
}
if ($relative->get_iri() !== '') {
if ($relative->iuserinfo !== null || $relative->ihost !== null || $relative->port !== null) {
$target = clone $relative;
$target->scheme = $base->scheme;
}
else {
$target = new self;
$target->scheme = $base->scheme;
$target->iuserinfo = $base->iuserinfo;
$target->ihost = $base->ihost;
$target->port = $base->port;
if ($relative->ipath !== '') {
if ($relative->ipath[0] === '/') {
$target->ipath = $relative->ipath;
}
elseif (($base->iuserinfo !== null || $base->ihost !== null || $base->port !== null) && $base->ipath === '') {
$target->ipath = '/' . $relative->ipath;
}
elseif (($last_segment = strrpos($base->ipath, '/')) !== false) {
$target->ipath = substr($base->ipath, 0, $last_segment + 1) . $relative->ipath;
}
else {
$target->ipath = $relative->ipath;
}
$target->ipath = $target->remove_dot_segments($target->ipath);
$target->iquery = $relative->iquery;
}
else {
$target->ipath = $base->ipath;
if ($relative->iquery !== null) {
$target->iquery = $relative->iquery;
}
elseif ($base->iquery !== null) {
$target->iquery = $base->iquery;
}
}
$target->ifragment = $relative->ifragment;
}
}
else {
$target = clone $base;
$target->ifragment = null;
}
$target->scheme_normalization();
return $target;
}
/**
* Parse an IRI into scheme/authority/path/query/fragment segments
*
* @param string $iri
* @return array
*/
protected function parse_iri($iri) {
$iri = trim($iri, "\x20\x09\x0A\x0C\x0D");
$has_match = preg_match('/^((?P<scheme>[^:\/?#]+):)?(\/\/(?P<authority>[^\/?#]*))?(?P<path>[^?#]*)(\?(?P<query>[^#]*))?(#(?P<fragment>.*))?$/', $iri, $match);
if (!$has_match) {
throw new Exception('Cannot parse supplied IRI', 'iri.cannot_parse', $iri);
}
if ($match[1] === '') {
$match['scheme'] = null;
}
if (!isset($match[3]) || $match[3] === '') {
$match['authority'] = null;
}
if (!isset($match[5])) {
$match['path'] = '';
}
if (!isset($match[6]) || $match[6] === '') {
$match['query'] = null;
}
if (!isset($match[8]) || $match[8] === '') {
$match['fragment'] = null;
}
return $match;
}
/**
* Remove dot segments from a path
*
* @param string $input
* @return string
*/
protected function remove_dot_segments($input) {
$output = '';
while (strpos($input, './') !== false || strpos($input, '/.') !== false || $input === '.' || $input === '..') {
// A: If the input buffer begins with a prefix of "../" or "./",
// then remove that prefix from the input buffer; otherwise,
if (strpos($input, '../') === 0) {
$input = substr($input, 3);
}
elseif (strpos($input, './') === 0) {
$input = substr($input, 2);
}
// B: if the input buffer begins with a prefix of "/./" or "/.",
// where "." is a complete path segment, then replace that prefix
// with "/" in the input buffer; otherwise,
elseif (strpos($input, '/./') === 0) {
$input = substr($input, 2);
}
elseif ($input === '/.') {
$input = '/';
}
// C: if the input buffer begins with a prefix of "/../" or "/..",
// where ".." is a complete path segment, then replace that prefix
// with "/" in the input buffer and remove the last segment and its
// preceding "/" (if any) from the output buffer; otherwise,
elseif (strpos($input, '/../') === 0) {
$input = substr($input, 3);
$output = substr_replace($output, '', (strrpos($output, '/') ?: 0));
}
elseif ($input === '/..') {
$input = '/';
$output = substr_replace($output, '', (strrpos($output, '/') ?: 0));
}
// D: if the input buffer consists only of "." or "..", then remove
// that from the input buffer; otherwise,
elseif ($input === '.' || $input === '..') {
$input = '';
}
// E: move the first path segment in the input buffer to the end of
// the output buffer, including the initial "/" character (if any)
// and any subsequent characters up to, but not including, the next
// "/" character or the end of the input buffer
elseif (($pos = strpos($input, '/', 1)) !== false) {
$output .= substr($input, 0, $pos);
$input = substr_replace($input, '', 0, $pos);
}
else {
$output .= $input;
$input = '';
}
}
return $output . $input;
}
/**
* Replace invalid character with percent encoding
*
* @param string $text Input string
* @param string $extra_chars Valid characters not in iunreserved or
* iprivate (this is ASCII-only)
* @param bool $iprivate Allow iprivate
* @return string
*/
protected function replace_invalid_with_pct_encoding($text, $extra_chars, $iprivate = false) {
// Normalize as many pct-encoded sections as possible
$text = preg_replace_callback('/(?:%[A-Fa-f0-9]{2})+/', array($this, 'remove_iunreserved_percent_encoded'), $text);
// Replace invalid percent characters
$text = preg_replace('/%(?![A-Fa-f0-9]{2})/', '%25', $text);
// Add unreserved and % to $extra_chars (the latter is safe because all
// pct-encoded sections are now valid).
$extra_chars .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~%';
// Now replace any bytes that aren't allowed with their pct-encoded versions
$position = 0;
$strlen = strlen($text);
while (($position += strspn($text, $extra_chars, $position)) < $strlen) {
$value = ord($text[$position]);
// Start position
$start = $position;
// By default we are valid
$valid = true;
// No one byte sequences are valid due to the while.
// Two byte sequence:
if (($value & 0xE0) === 0xC0) {
$character = ($value & 0x1F) << 6;
$length = 2;
$remaining = 1;
}
// Three byte sequence:
elseif (($value & 0xF0) === 0xE0) {
$character = ($value & 0x0F) << 12;
$length = 3;
$remaining = 2;
}
// Four byte sequence:
elseif (($value & 0xF8) === 0xF0) {
$character = ($value & 0x07) << 18;
$length = 4;
$remaining = 3;
}
// Invalid byte:
else {
$valid = false;
$length = 1;
$remaining = 0;
}
if ($remaining) {
if ($position + $length <= $strlen) {
for ($position++; $remaining; $position++) {
$value = ord($text[$position]);
// Check that the byte is valid, then add it to the character:
if (($value & 0xC0) === 0x80) {
$character |= ($value & 0x3F) << (--$remaining * 6);
}
// If it is invalid, count the sequence as invalid and reprocess the current byte:
else {
$valid = false;
$position--;
break;
}
}
}
else {
$position = $strlen - 1;
$valid = false;
}
}
// Percent encode anything invalid or not in ucschar
if (
// Invalid sequences
!$valid
// Non-shortest form sequences are invalid
|| $length > 1 && $character <= 0x7F
|| $length > 2 && $character <= 0x7FF
|| $length > 3 && $character <= 0xFFFF
// Outside of range of ucschar codepoints
// Noncharacters
|| ($character & 0xFFFE) === 0xFFFE
|| $character >= 0xFDD0 && $character <= 0xFDEF
|| (
// Everything else not in ucschar
$character > 0xD7FF && $character < 0xF900
|| $character < 0xA0
|| $character > 0xEFFFD
)
&& (
// Everything not in iprivate, if it applies
!$iprivate
|| $character < 0xE000
|| $character > 0x10FFFD
)
) {
// If we were a character, pretend we weren't, but rather an error.
if ($valid) {
$position--;
}
for ($j = $start; $j <= $position; $j++) {
$text = substr_replace($text, sprintf('%%%02X', ord($text[$j])), $j, 1);
$j += 2;
$position += 2;
$strlen += 2;
}
}
}
return $text;
}
/**
* Callback function for preg_replace_callback.
*
* Removes sequences of percent encoded bytes that represent UTF-8
* encoded characters in iunreserved
*
* @param array $regex_match PCRE match
* @return string Replacement
*/
protected function remove_iunreserved_percent_encoded($regex_match) {
// As we just have valid percent encoded sequences we can just explode
// and ignore the first member of the returned array (an empty string).
$bytes = explode('%', $regex_match[0]);
// Initialize the new string (this is what will be returned) and that
// there are no bytes remaining in the current sequence (unsurprising
// at the first byte!).
$string = '';
$remaining = 0;
// Loop over each and every byte, and set $value to its value
for ($i = 1, $len = count($bytes); $i < $len; $i++) {
$value = hexdec($bytes[$i]);
// If we're the first byte of sequence:
if (!$remaining) {
// Start position
$start = $i;
// By default we are valid
$valid = true;
// One byte sequence:
if ($value <= 0x7F) {
$character = $value;
$length = 1;
}
// Two byte sequence:
elseif (($value & 0xE0) === 0xC0) {
$character = ($value & 0x1F) << 6;
$length = 2;
$remaining = 1;
}
// Three byte sequence:
elseif (($value & 0xF0) === 0xE0) {
$character = ($value & 0x0F) << 12;
$length = 3;
$remaining = 2;
}
// Four byte sequence:
elseif (($value & 0xF8) === 0xF0) {
$character = ($value & 0x07) << 18;
$length = 4;
$remaining = 3;
}
// Invalid byte:
else {
$valid = false;
$remaining = 0;
}
}
// Continuation byte:
else {
// Check that the byte is valid, then add it to the character:
if (($value & 0xC0) === 0x80) {
$remaining--;
$character |= ($value & 0x3F) << ($remaining * 6);
}
// If it is invalid, count the sequence as invalid and reprocess the current byte as the start of a sequence:
else {
$valid = false;
$remaining = 0;
$i--;
}
}
// If we've reached the end of the current byte sequence, append it to Unicode::$data
if (!$remaining) {
// Percent encode anything invalid or not in iunreserved
if (
// Invalid sequences
!$valid
// Non-shortest form sequences are invalid
|| $length > 1 && $character <= 0x7F
|| $length > 2 && $character <= 0x7FF
|| $length > 3 && $character <= 0xFFFF
// Outside of range of iunreserved codepoints
|| $character < 0x2D
|| $character > 0xEFFFD
// Noncharacters
|| ($character & 0xFFFE) === 0xFFFE
|| $character >= 0xFDD0 && $character <= 0xFDEF
// Everything else not in iunreserved (this is all BMP)
|| $character === 0x2F
|| $character > 0x39 && $character < 0x41
|| $character > 0x5A && $character < 0x61
|| $character > 0x7A && $character < 0x7E
|| $character > 0x7E && $character < 0xA0
|| $character > 0xD7FF && $character < 0xF900
) {
for ($j = $start; $j <= $i; $j++) {
$string .= '%' . strtoupper($bytes[$j]);
}
}
else {
for ($j = $start; $j <= $i; $j++) {
$string .= chr(hexdec($bytes[$j]));
}
}
}
}
// If we have any bytes left over they are invalid (i.e., we are
// mid-way through a multi-byte sequence)
if ($remaining) {
for ($j = $start; $j < $len; $j++) {
$string .= '%' . strtoupper($bytes[$j]);
}
}
return $string;
}
protected function scheme_normalization() {
if (isset($this->normalization[$this->scheme]['iuserinfo']) && $this->iuserinfo === $this->normalization[$this->scheme]['iuserinfo']) {
$this->iuserinfo = null;
}
if (isset($this->normalization[$this->scheme]['ihost']) && $this->ihost === $this->normalization[$this->scheme]['ihost']) {
$this->ihost = null;
}
if (isset($this->normalization[$this->scheme]['port']) && $this->port === $this->normalization[$this->scheme]['port']) {
$this->port = null;
}
if (isset($this->normalization[$this->scheme]['ipath']) && $this->ipath === $this->normalization[$this->scheme]['ipath']) {
$this->ipath = '';
}
if (isset($this->ihost) && empty($this->ipath)) {
$this->ipath = '/';
}
if (isset($this->normalization[$this->scheme]['iquery']) && $this->iquery === $this->normalization[$this->scheme]['iquery']) {
$this->iquery = null;
}
if (isset($this->normalization[$this->scheme]['ifragment']) && $this->ifragment === $this->normalization[$this->scheme]['ifragment']) {
$this->ifragment = null;
}
}
/**
* Check if the object represents a valid IRI. This needs to be done on each
* call as some things change depending on another part of the IRI.
*
* @return bool
*/
public function is_valid() {
$isauthority = $this->iuserinfo !== null || $this->ihost !== null || $this->port !== null;
if ($this->ipath !== '' &&
(
$isauthority && $this->ipath[0] !== '/' ||
(
$this->scheme === null &&
!$isauthority &&
strpos($this->ipath, ':') !== false &&
(strpos($this->ipath, '/') === false ? true : strpos($this->ipath, ':') < strpos($this->ipath, '/'))
)
)
) {
return false;
}
return true;
}
public function __wakeup() {
$class_props = get_class_vars( __CLASS__ );
$string_props = array( 'scheme', 'iuserinfo', 'ihost', 'port', 'ipath', 'iquery', 'ifragment' );
$array_props = array( 'normalization' );
foreach ( $class_props as $prop => $default_value ) {
if ( in_array( $prop, $string_props, true ) && ! is_string( $this->$prop ) ) {
throw new UnexpectedValueException();
} elseif ( in_array( $prop, $array_props, true ) && ! is_array( $this->$prop ) ) {
throw new UnexpectedValueException();
}
$this->$prop = null;
}
}
/**
* Set the entire IRI. Returns true on success, false on failure (if there
* are any invalid characters).
*
* @param string $iri
* @return bool
*/
protected function set_iri($iri) {
static $cache;
if (!$cache) {
$cache = array();
}
if ($iri === null) {
return true;
}
$iri = (string) $iri;
if (isset($cache[$iri])) {
list($this->scheme,
$this->iuserinfo,
$this->ihost,
$this->port,
$this->ipath,
$this->iquery,
$this->ifragment,
$return) = $cache[$iri];
return $return;
}
$parsed = $this->parse_iri($iri);
$return = $this->set_scheme($parsed['scheme'])
&& $this->set_authority($parsed['authority'])
&& $this->set_path($parsed['path'])
&& $this->set_query($parsed['query'])
&& $this->set_fragment($parsed['fragment']);
$cache[$iri] = array($this->scheme,
$this->iuserinfo,
$this->ihost,
$this->port,
$this->ipath,
$this->iquery,
$this->ifragment,
$return);
return $return;
}
/**
* Set the scheme. Returns true on success, false on failure (if there are
* any invalid characters).
*
* @param string $scheme
* @return bool
*/
protected function set_scheme($scheme) {
if ($scheme === null) {
$this->scheme = null;
}
elseif (!preg_match('/^[A-Za-z][0-9A-Za-z+\-.]*$/', $scheme)) {
$this->scheme = null;
return false;
}
else {
$this->scheme = strtolower($scheme);
}
return true;
}
/**
* Set the authority. Returns true on success, false on failure (if there are
* any invalid characters).
*
* @param string $authority
* @return bool
*/
protected function set_authority($authority) {
static $cache;
if (!$cache) {
$cache = array();
}
if ($authority === null) {
$this->iuserinfo = null;
$this->ihost = null;
$this->port = null;
return true;
}
if (isset($cache[$authority])) {
list($this->iuserinfo,
$this->ihost,
$this->port,
$return) = $cache[$authority];
return $return;
}
$remaining = $authority;
if (($iuserinfo_end = strrpos($remaining, '@')) !== false) {
$iuserinfo = substr($remaining, 0, $iuserinfo_end);
$remaining = substr($remaining, $iuserinfo_end + 1);
}
else {
$iuserinfo = null;
}
if (($port_start = strpos($remaining, ':', (strpos($remaining, ']') ?: 0))) !== false) {
$port = substr($remaining, $port_start + 1);
if ($port === false || $port === '') {
$port = null;
}
$remaining = substr($remaining, 0, $port_start);
}
else {
$port = null;
}
$return = $this->set_userinfo($iuserinfo) &&
$this->set_host($remaining) &&
$this->set_port($port);
$cache[$authority] = array($this->iuserinfo,
$this->ihost,
$this->port,
$return);
return $return;
}
/**
* Set the iuserinfo.
*
* @param string $iuserinfo
* @return bool
*/
protected function set_userinfo($iuserinfo) {
if ($iuserinfo === null) {
$this->iuserinfo = null;
}
else {
$this->iuserinfo = $this->replace_invalid_with_pct_encoding($iuserinfo, '!$&\'()*+,;=:');
$this->scheme_normalization();
}
return true;
}
/**
* Set the ihost. Returns true on success, false on failure (if there are
* any invalid characters).
*
* @param string $ihost
* @return bool
*/
protected function set_host($ihost) {
if ($ihost === null) {
$this->ihost = null;
return true;
}
if (substr($ihost, 0, 1) === '[' && substr($ihost, -1) === ']') {
if (Ipv6::check_ipv6(substr($ihost, 1, -1))) {
$this->ihost = '[' . Ipv6::compress(substr($ihost, 1, -1)) . ']';
}
else {
$this->ihost = null;
return false;
}
}
else {
$ihost = $this->replace_invalid_with_pct_encoding($ihost, '!$&\'()*+,;=');
// Lowercase, but ignore pct-encoded sections (as they should
// remain uppercase). This must be done after the previous step
// as that can add unescaped characters.
$position = 0;
$strlen = strlen($ihost);
while (($position += strcspn($ihost, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ%', $position)) < $strlen) {
if ($ihost[$position] === '%') {
$position += 3;
}
else {
$ihost[$position] = strtolower($ihost[$position]);
$position++;
}
}
$this->ihost = $ihost;
}
$this->scheme_normalization();
return true;
}
/**
* Set the port. Returns true on success, false on failure (if there are
* any invalid characters).
*
* @param string $port
* @return bool
*/
protected function set_port($port) {
if ($port === null) {
$this->port = null;
return true;
}
if (strspn($port, '0123456789') === strlen($port)) {
$this->port = (int) $port;
$this->scheme_normalization();
return true;
}
$this->port = null;
return false;
}
/**
* Set the ipath.
*
* @param string $ipath
* @return bool
*/
protected function set_path($ipath) {
static $cache;
if (!$cache) {
$cache = array();
}
$ipath = (string) $ipath;
if (isset($cache[$ipath])) {
$this->ipath = $cache[$ipath][(int) ($this->scheme !== null)];
}
else {
$valid = $this->replace_invalid_with_pct_encoding($ipath, '!$&\'()*+,;=@:/');
$removed = $this->remove_dot_segments($valid);
$cache[$ipath] = array($valid, $removed);
$this->ipath = ($this->scheme !== null) ? $removed : $valid;
}
$this->scheme_normalization();
return true;
}
/**
* Set the iquery.
*
* @param string $iquery
* @return bool
*/
protected function set_query($iquery) {
if ($iquery === null) {
$this->iquery = null;
}
else {
$this->iquery = $this->replace_invalid_with_pct_encoding($iquery, '!$&\'()*+,;=:@/?', true);
$this->scheme_normalization();
}
return true;
}
/**
* Set the ifragment.
*
* @param string $ifragment
* @return bool
*/
protected function set_fragment($ifragment) {
if ($ifragment === null) {
$this->ifragment = null;
}
else {
$this->ifragment = $this->replace_invalid_with_pct_encoding($ifragment, '!$&\'()*+,;=:@/?');
$this->scheme_normalization();
}
return true;
}
/**
* Convert an IRI to a URI (or parts thereof)
*
* @param string|bool $iri IRI to convert (or false from {@see \WpOrg\Requests\Iri::get_iri()})
* @return string|false URI if IRI is valid, false otherwise.
*/
protected function to_uri($iri) {
if (!is_string($iri)) {
return false;
}
static $non_ascii;
if (!$non_ascii) {
$non_ascii = implode('', range("\x80", "\xFF"));
}
$position = 0;
$strlen = strlen($iri);
while (($position += strcspn($iri, $non_ascii, $position)) < $strlen) {
$iri = substr_replace($iri, sprintf('%%%02X', ord($iri[$position])), $position, 1);
$position += 3;
$strlen += 2;
}
return $iri;
}
/**
* Get the complete IRI
*
* @return string|false
*/
protected function get_iri() {
if (!$this->is_valid()) {
return false;
}
$iri = '';
if ($this->scheme !== null) {
$iri .= $this->scheme . ':';
}
if (($iauthority = $this->get_iauthority()) !== null) {
$iri .= '//' . $iauthority;
}
$iri .= $this->ipath;
if ($this->iquery !== null) {
$iri .= '?' . $this->iquery;
}
if ($this->ifragment !== null) {
$iri .= '#' . $this->ifragment;
}
return $iri;
}
/**
* Get the complete URI
*
* @return string
*/
protected function get_uri() {
return $this->to_uri($this->get_iri());
}
/**
* Get the complete iauthority
*
* @return string|null
*/
protected function get_iauthority() {
if ($this->iuserinfo === null && $this->ihost === null && $this->port === null) {
return null;
}
$iauthority = '';
if ($this->iuserinfo !== null) {
$iauthority .= $this->iuserinfo . '@';
}
if ($this->ihost !== null) {
$iauthority .= $this->ihost;
}
if ($this->port !== null) {
$iauthority .= ':' . $this->port;
}
return $iauthority;
}
/**
* Get the complete authority
*
* @return string
*/
protected function get_authority() {
$iauthority = $this->get_iauthority();
if (is_string($iauthority)) {
return $this->to_uri($iauthority);
}
else {
return $iauthority;
}
}
}
Ipv6.php 0000604 00000013007 15133277060 0006102 0 ustar 00 <?php
/**
* Class to validate and to work with IPv6 addresses
*
* @package Requests\Utilities
*/
namespace WpOrg\Requests;
use WpOrg\Requests\Exception\InvalidArgument;
use WpOrg\Requests\Utility\InputValidator;
/**
* Class to validate and to work with IPv6 addresses
*
* This was originally based on the PEAR class of the same name, but has been
* entirely rewritten.
*
* @package Requests\Utilities
*/
final class Ipv6 {
/**
* Uncompresses an IPv6 address
*
* RFC 4291 allows you to compress consecutive zero pieces in an address to
* '::'. This method expects a valid IPv6 address and expands the '::' to
* the required number of zero pieces.
*
* Example: FF01::101 -> FF01:0:0:0:0:0:0:101
* ::1 -> 0:0:0:0:0:0:0:1
*
* @author Alexander Merz <alexander.merz@web.de>
* @author elfrink at introweb dot nl
* @author Josh Peck <jmp at joshpeck dot org>
* @copyright 2003-2005 The PHP Group
* @license https://opensource.org/licenses/bsd-license.php
*
* @param string|Stringable $ip An IPv6 address
* @return string The uncompressed IPv6 address
*
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed argument is not a string or a stringable object.
*/
public static function uncompress($ip) {
if (InputValidator::is_string_or_stringable($ip) === false) {
throw InvalidArgument::create(1, '$ip', 'string|Stringable', gettype($ip));
}
$ip = (string) $ip;
if (substr_count($ip, '::') !== 1) {
return $ip;
}
list($ip1, $ip2) = explode('::', $ip);
$c1 = ($ip1 === '') ? -1 : substr_count($ip1, ':');
$c2 = ($ip2 === '') ? -1 : substr_count($ip2, ':');
if (strpos($ip2, '.') !== false) {
$c2++;
}
if ($c1 === -1 && $c2 === -1) {
// ::
$ip = '0:0:0:0:0:0:0:0';
} elseif ($c1 === -1) {
// ::xxx
$fill = str_repeat('0:', 7 - $c2);
$ip = str_replace('::', $fill, $ip);
} elseif ($c2 === -1) {
// xxx::
$fill = str_repeat(':0', 7 - $c1);
$ip = str_replace('::', $fill, $ip);
} else {
// xxx::xxx
$fill = ':' . str_repeat('0:', 6 - $c2 - $c1);
$ip = str_replace('::', $fill, $ip);
}
return $ip;
}
/**
* Compresses an IPv6 address
*
* RFC 4291 allows you to compress consecutive zero pieces in an address to
* '::'. This method expects a valid IPv6 address and compresses consecutive
* zero pieces to '::'.
*
* Example: FF01:0:0:0:0:0:0:101 -> FF01::101
* 0:0:0:0:0:0:0:1 -> ::1
*
* @see \WpOrg\Requests\Ipv6::uncompress()
*
* @param string $ip An IPv6 address
* @return string The compressed IPv6 address
*/
public static function compress($ip) {
// Prepare the IP to be compressed.
// Note: Input validation is handled in the `uncompress()` method, which is the first call made in this method.
$ip = self::uncompress($ip);
$ip_parts = self::split_v6_v4($ip);
// Replace all leading zeros
$ip_parts[0] = preg_replace('/(^|:)0+([0-9])/', '\1\2', $ip_parts[0]);
// Find bunches of zeros
if (preg_match_all('/(?:^|:)(?:0(?::|$))+/', $ip_parts[0], $matches, PREG_OFFSET_CAPTURE)) {
$max = 0;
$pos = null;
foreach ($matches[0] as $match) {
if (strlen($match[0]) > $max) {
$max = strlen($match[0]);
$pos = $match[1];
}
}
$ip_parts[0] = substr_replace($ip_parts[0], '::', $pos, $max);
}
if ($ip_parts[1] !== '') {
return implode(':', $ip_parts);
} else {
return $ip_parts[0];
}
}
/**
* Splits an IPv6 address into the IPv6 and IPv4 representation parts
*
* RFC 4291 allows you to represent the last two parts of an IPv6 address
* using the standard IPv4 representation
*
* Example: 0:0:0:0:0:0:13.1.68.3
* 0:0:0:0:0:FFFF:129.144.52.38
*
* @param string $ip An IPv6 address
* @return string[] [0] contains the IPv6 represented part, and [1] the IPv4 represented part
*/
private static function split_v6_v4($ip) {
if (strpos($ip, '.') !== false) {
$pos = strrpos($ip, ':');
$ipv6_part = substr($ip, 0, $pos);
$ipv4_part = substr($ip, $pos + 1);
return [$ipv6_part, $ipv4_part];
} else {
return [$ip, ''];
}
}
/**
* Checks an IPv6 address
*
* Checks if the given IP is a valid IPv6 address
*
* @param string $ip An IPv6 address
* @return bool true if $ip is a valid IPv6 address
*/
public static function check_ipv6($ip) {
// Note: Input validation is handled in the `uncompress()` method, which is the first call made in this method.
$ip = self::uncompress($ip);
list($ipv6, $ipv4) = self::split_v6_v4($ip);
$ipv6 = explode(':', $ipv6);
$ipv4 = explode('.', $ipv4);
if (count($ipv6) === 8 && count($ipv4) === 1 || count($ipv6) === 6 && count($ipv4) === 4) {
foreach ($ipv6 as $ipv6_part) {
// The section can't be empty
if ($ipv6_part === '') {
return false;
}
// Nor can it be over four characters
if (strlen($ipv6_part) > 4) {
return false;
}
// Remove leading zeros (this is safe because of the above)
$ipv6_part = ltrim($ipv6_part, '0');
if ($ipv6_part === '') {
$ipv6_part = '0';
}
// Check the value is valid
$value = hexdec($ipv6_part);
if (dechex($value) !== strtolower($ipv6_part) || $value < 0 || $value > 0xFFFF) {
return false;
}
}
if (count($ipv4) === 4) {
foreach ($ipv4 as $ipv4_part) {
$value = (int) $ipv4_part;
if ((string) $value !== $ipv4_part || $value < 0 || $value > 0xFF) {
return false;
}
}
}
return true;
} else {
return false;
}
}
}
Response.php 0000604 00000010271 15133277060 0007054 0 ustar 00 <?php
/**
* HTTP response class
*
* Contains a response from \WpOrg\Requests\Requests::request()
*
* @package Requests
*/
namespace WpOrg\Requests;
use WpOrg\Requests\Cookie\Jar;
use WpOrg\Requests\Exception;
use WpOrg\Requests\Exception\Http;
use WpOrg\Requests\Response\Headers;
/**
* HTTP response class
*
* Contains a response from \WpOrg\Requests\Requests::request()
*
* @package Requests
*/
class Response {
/**
* Response body
*
* @var string
*/
public $body = '';
/**
* Raw HTTP data from the transport
*
* @var string
*/
public $raw = '';
/**
* Headers, as an associative array
*
* @var \WpOrg\Requests\Response\Headers Array-like object representing headers
*/
public $headers = [];
/**
* Status code, false if non-blocking
*
* @var integer|boolean
*/
public $status_code = false;
/**
* Protocol version, false if non-blocking
*
* @var float|boolean
*/
public $protocol_version = false;
/**
* Whether the request succeeded or not
*
* @var boolean
*/
public $success = false;
/**
* Number of redirects the request used
*
* @var integer
*/
public $redirects = 0;
/**
* URL requested
*
* @var string
*/
public $url = '';
/**
* Previous requests (from redirects)
*
* @var array Array of \WpOrg\Requests\Response objects
*/
public $history = [];
/**
* Cookies from the request
*
* @var \WpOrg\Requests\Cookie\Jar Array-like object representing a cookie jar
*/
public $cookies = [];
/**
* Constructor
*/
public function __construct() {
$this->headers = new Headers();
$this->cookies = new Jar();
}
/**
* Is the response a redirect?
*
* @return boolean True if redirect (3xx status), false if not.
*/
public function is_redirect() {
$code = $this->status_code;
return in_array($code, [300, 301, 302, 303, 307], true) || $code > 307 && $code < 400;
}
/**
* Throws an exception if the request was not successful
*
* @param boolean $allow_redirects Set to false to throw on a 3xx as well
*
* @throws \WpOrg\Requests\Exception If `$allow_redirects` is false, and code is 3xx (`response.no_redirects`)
* @throws \WpOrg\Requests\Exception\Http On non-successful status code. Exception class corresponds to "Status" + code (e.g. {@see \WpOrg\Requests\Exception\Http\Status404})
*/
public function throw_for_status($allow_redirects = true) {
if ($this->is_redirect()) {
if ($allow_redirects !== true) {
throw new Exception('Redirection not allowed', 'response.no_redirects', $this);
}
} elseif (!$this->success) {
$exception = Http::get_class($this->status_code);
throw new $exception(null, $this);
}
}
/**
* JSON decode the response body.
*
* The method parameters are the same as those for the PHP native `json_decode()` function.
*
* @link https://php.net/json-decode
*
* @param bool|null $associative Optional. When `true`, JSON objects will be returned as associative arrays;
* When `false`, JSON objects will be returned as objects.
* When `null`, JSON objects will be returned as associative arrays
* or objects depending on whether `JSON_OBJECT_AS_ARRAY` is set in the flags.
* Defaults to `true` (in contrast to the PHP native default of `null`).
* @param int $depth Optional. Maximum nesting depth of the structure being decoded.
* Defaults to `512`.
* @param int $options Optional. Bitmask of JSON_BIGINT_AS_STRING, JSON_INVALID_UTF8_IGNORE,
* JSON_INVALID_UTF8_SUBSTITUTE, JSON_OBJECT_AS_ARRAY, JSON_THROW_ON_ERROR.
* Defaults to `0` (no options set).
*
* @return array
*
* @throws \WpOrg\Requests\Exception If `$this->body` is not valid json.
*/
public function decode_body($associative = true, $depth = 512, $options = 0) {
$data = json_decode($this->body, $associative, $depth, $options);
if (json_last_error() !== JSON_ERROR_NONE) {
$last_error = json_last_error_msg();
throw new Exception('Unable to parse JSON data: ' . $last_error, 'response.invalid', $this);
}
return $data;
}
}
Exception/Transport.php 0000604 00000000364 15133277060 0011212 0 ustar 00 <?php
/**
* Transport Exception
*
* @package Requests\Exceptions
*/
namespace WpOrg\Requests\Exception;
use WpOrg\Requests\Exception;
/**
* Transport Exception
*
* @package Requests\Exceptions
*/
class Transport extends Exception {}
Exception/ArgumentCount.php 0000604 00000002664 15133277060 0012016 0 ustar 00 <?php
namespace WpOrg\Requests\Exception;
use WpOrg\Requests\Exception;
/**
* Exception for when an incorrect number of arguments are passed to a method.
*
* Typically, this exception is used when all arguments for a method are optional,
* but certain arguments need to be passed together, i.e. a method which can be called
* with no arguments or with two arguments, but not with one argument.
*
* Along the same lines, this exception is also used if a method expects an array
* with a certain number of elements and the provided number of elements does not comply.
*
* @package Requests\Exceptions
* @since 2.0.0
*/
final class ArgumentCount extends Exception {
/**
* Create a new argument count exception with a standardized text.
*
* @param string $expected The argument count expected as a phrase.
* For example: `at least 2 arguments` or `exactly 1 argument`.
* @param int $received The actual argument count received.
* @param string $type Exception type.
*
* @return \WpOrg\Requests\Exception\ArgumentCount
*/
public static function create($expected, $received, $type) {
// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_debug_backtrace
$stack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
return new self(
sprintf(
'%s::%s() expects %s, %d given',
$stack[1]['class'],
$stack[1]['function'],
$expected,
$received
),
$type
);
}
}
Exception/Http.php 0000604 00000003006 15133277060 0010131 0 ustar 00 <?php
/**
* Exception based on HTTP response
*
* @package Requests\Exceptions
*/
namespace WpOrg\Requests\Exception;
use WpOrg\Requests\Exception;
use WpOrg\Requests\Exception\Http\StatusUnknown;
/**
* Exception based on HTTP response
*
* @package Requests\Exceptions
*/
class Http extends Exception {
/**
* HTTP status code
*
* @var integer
*/
protected $code = 0;
/**
* Reason phrase
*
* @var string
*/
protected $reason = 'Unknown';
/**
* Create a new exception
*
* There is no mechanism to pass in the status code, as this is set by the
* subclass used. Reason phrases can vary, however.
*
* @param string|null $reason Reason phrase
* @param mixed $data Associated data
*/
public function __construct($reason = null, $data = null) {
if ($reason !== null) {
$this->reason = $reason;
}
$message = sprintf('%d %s', $this->code, $this->reason);
parent::__construct($message, 'httpresponse', $data, $this->code);
}
/**
* Get the status message.
*
* @return string
*/
public function getReason() {
return $this->reason;
}
/**
* Get the correct exception class for a given error code
*
* @param int|bool $code HTTP status code, or false if unavailable
* @return string Exception class name to use
*/
public static function get_class($code) {
if (!$code) {
return StatusUnknown::class;
}
$class = sprintf('\WpOrg\Requests\Exception\Http\Status%d', $code);
if (class_exists($class)) {
return $class;
}
return StatusUnknown::class;
}
}
Exception/Transport/Curl.php 0000604 00000002565 15133277060 0012124 0 ustar 00 <?php
/**
* CURL Transport Exception.
*
* @package Requests\Exceptions
*/
namespace WpOrg\Requests\Exception\Transport;
use WpOrg\Requests\Exception\Transport;
/**
* CURL Transport Exception.
*
* @package Requests\Exceptions
*/
final class Curl extends Transport {
const EASY = 'cURLEasy';
const MULTI = 'cURLMulti';
const SHARE = 'cURLShare';
/**
* cURL error code
*
* @var integer
*/
protected $code = -1;
/**
* Which type of cURL error
*
* EASY|MULTI|SHARE
*
* @var string
*/
protected $type = 'Unknown';
/**
* Clear text error message
*
* @var string
*/
protected $reason = 'Unknown';
/**
* Create a new exception.
*
* @param string $message Exception message.
* @param string $type Exception type.
* @param mixed $data Associated data, if applicable.
* @param int $code Exception numerical code, if applicable.
*/
public function __construct($message, $type, $data = null, $code = 0) {
if ($type !== null) {
$this->type = $type;
}
if ($code !== null) {
$this->code = (int) $code;
}
if ($message !== null) {
$this->reason = $message;
}
$message = sprintf('%d %s', $this->code, $this->reason);
parent::__construct($message, $this->type, $data, $this->code);
}
/**
* Get the error message.
*
* @return string
*/
public function getReason() {
return $this->reason;
}
}
Exception/Http/Status306.php 0000604 00000000714 15133277060 0011650 0 ustar 00 <?php
/**
* Exception for 306 Switch Proxy responses
*
* @package Requests\Exceptions
*/
namespace WpOrg\Requests\Exception\Http;
use WpOrg\Requests\Exception\Http;
/**
* Exception for 306 Switch Proxy responses
*
* @package Requests\Exceptions
*/
final class Status306 extends Http {
/**
* HTTP status code
*
* @var integer
*/
protected $code = 306;
/**
* Reason phrase
*
* @var string
*/
protected $reason = 'Switch Proxy';
}
Exception/Http/Status402.php 0000604 00000000730 15133277060 0011643 0 ustar 00 <?php
/**
* Exception for 402 Payment Required responses
*
* @package Requests\Exceptions
*/
namespace WpOrg\Requests\Exception\Http;
use WpOrg\Requests\Exception\Http;
/**
* Exception for 402 Payment Required responses
*
* @package Requests\Exceptions
*/
final class Status402 extends Http {
/**
* HTTP status code
*
* @var integer
*/
protected $code = 402;
/**
* Reason phrase
*
* @var string
*/
protected $reason = 'Payment Required';
}
Exception/Http/Status405.php 0000604 00000000736 15133277060 0011654 0 ustar 00 <?php
/**
* Exception for 405 Method Not Allowed responses
*
* @package Requests\Exceptions
*/
namespace WpOrg\Requests\Exception\Http;
use WpOrg\Requests\Exception\Http;
/**
* Exception for 405 Method Not Allowed responses
*
* @package Requests\Exceptions
*/
final class Status405 extends Http {
/**
* HTTP status code
*
* @var integer
*/
protected $code = 405;
/**
* Reason phrase
*
* @var string
*/
protected $reason = 'Method Not Allowed';
}
Exception/Http/Status414.php 0000604 00000000747 15133277060 0011656 0 ustar 00 <?php
/**
* Exception for 414 Request-URI Too Large responses
*
* @package Requests\Exceptions
*/
namespace WpOrg\Requests\Exception\Http;
use WpOrg\Requests\Exception\Http;
/**
* Exception for 414 Request-URI Too Large responses
*
* @package Requests\Exceptions
*/
final class Status414 extends Http {
/**
* HTTP status code
*
* @var integer
*/
protected $code = 414;
/**
* Reason phrase
*
* @var string
*/
protected $reason = 'Request-URI Too Large';
}
Exception/Http/Status413.php 0000604 00000000760 15133277060 0011650 0 ustar 00 <?php
/**
* Exception for 413 Request Entity Too Large responses
*
* @package Requests\Exceptions
*/
namespace WpOrg\Requests\Exception\Http;
use WpOrg\Requests\Exception\Http;
/**
* Exception for 413 Request Entity Too Large responses
*
* @package Requests\Exceptions
*/
final class Status413 extends Http {
/**
* HTTP status code
*
* @var integer
*/
protected $code = 413;
/**
* Reason phrase
*
* @var string
*/
protected $reason = 'Request Entity Too Large';
}
Exception/Http/Status428.php 0000604 00000001107 15133277060 0011652 0 ustar 00 <?php
/**
* Exception for 428 Precondition Required responses
*
* @link https://tools.ietf.org/html/rfc6585
*
* @package Requests\Exceptions
*/
namespace WpOrg\Requests\Exception\Http;
use WpOrg\Requests\Exception\Http;
/**
* Exception for 428 Precondition Required responses
*
* @link https://tools.ietf.org/html/rfc6585
*
* @package Requests\Exceptions
*/
final class Status428 extends Http {
/**
* HTTP status code
*
* @var integer
*/
protected $code = 428;
/**
* Reason phrase
*
* @var string
*/
protected $reason = 'Precondition Required';
}
Exception/Http/Status502.php 0000604 00000000711 15133277060 0011643 0 ustar 00 <?php
/**
* Exception for 502 Bad Gateway responses
*
* @package Requests\Exceptions
*/
namespace WpOrg\Requests\Exception\Http;
use WpOrg\Requests\Exception\Http;
/**
* Exception for 502 Bad Gateway responses
*
* @package Requests\Exceptions
*/
final class Status502 extends Http {
/**
* HTTP status code
*
* @var integer
*/
protected $code = 502;
/**
* Reason phrase
*
* @var string
*/
protected $reason = 'Bad Gateway';
}
Exception/Http/Status505.php 0000604 00000000766 15133277060 0011660 0 ustar 00 <?php
/**
* Exception for 505 HTTP Version Not Supported responses
*
* @package Requests\Exceptions
*/
namespace WpOrg\Requests\Exception\Http;
use WpOrg\Requests\Exception\Http;
/**
* Exception for 505 HTTP Version Not Supported responses
*
* @package Requests\Exceptions
*/
final class Status505 extends Http {
/**
* HTTP status code
*
* @var integer
*/
protected $code = 505;
/**
* Reason phrase
*
* @var string
*/
protected $reason = 'HTTP Version Not Supported';
}
Exception/Http/Status404.php 0000604 00000000703 15133277060 0011645 0 ustar 00 <?php
/**
* Exception for 404 Not Found responses
*
* @package Requests\Exceptions
*/
namespace WpOrg\Requests\Exception\Http;
use WpOrg\Requests\Exception\Http;
/**
* Exception for 404 Not Found responses
*
* @package Requests\Exceptions
*/
final class Status404 extends Http {
/**
* HTTP status code
*
* @var integer
*/
protected $code = 404;
/**
* Reason phrase
*
* @var string
*/
protected $reason = 'Not Found';
}
Exception/Http/StatusUnknown.php 0000604 00000001712 15133277060 0012776 0 ustar 00 <?php
/**
* Exception for unknown status responses
*
* @package Requests\Exceptions
*/
namespace WpOrg\Requests\Exception\Http;
use WpOrg\Requests\Exception\Http;
use WpOrg\Requests\Response;
/**
* Exception for unknown status responses
*
* @package Requests\Exceptions
*/
final class StatusUnknown extends Http {
/**
* HTTP status code
*
* @var integer|bool Code if available, false if an error occurred
*/
protected $code = 0;
/**
* Reason phrase
*
* @var string
*/
protected $reason = 'Unknown';
/**
* Create a new exception
*
* If `$data` is an instance of {@see \WpOrg\Requests\Response}, uses the status
* code from it. Otherwise, sets as 0
*
* @param string|null $reason Reason phrase
* @param mixed $data Associated data
*/
public function __construct($reason = null, $data = null) {
if ($data instanceof Response) {
$this->code = (int) $data->status_code;
}
parent::__construct($reason, $data);
}
}
Exception/Http/Status403.php 0000604 00000000703 15133277060 0011644 0 ustar 00 <?php
/**
* Exception for 403 Forbidden responses
*
* @package Requests\Exceptions
*/
namespace WpOrg\Requests\Exception\Http;
use WpOrg\Requests\Exception\Http;
/**
* Exception for 403 Forbidden responses
*
* @package Requests\Exceptions
*/
final class Status403 extends Http {
/**
* HTTP status code
*
* @var integer
*/
protected $code = 403;
/**
* Reason phrase
*
* @var string
*/
protected $reason = 'Forbidden';
}
Exception/Http/Status431.php 0000604 00000001145 15133277060 0011646 0 ustar 00 <?php
/**
* Exception for 431 Request Header Fields Too Large responses
*
* @link https://tools.ietf.org/html/rfc6585
*
* @package Requests\Exceptions
*/
namespace WpOrg\Requests\Exception\Http;
use WpOrg\Requests\Exception\Http;
/**
* Exception for 431 Request Header Fields Too Large responses
*
* @link https://tools.ietf.org/html/rfc6585
*
* @package Requests\Exceptions
*/
final class Status431 extends Http {
/**
* HTTP status code
*
* @var integer
*/
protected $code = 431;
/**
* Reason phrase
*
* @var string
*/
protected $reason = 'Request Header Fields Too Large';
}
Exception/Http/Status504.php 0000604 00000000725 15133277060 0011652 0 ustar 00 <?php
/**
* Exception for 504 Gateway Timeout responses
*
* @package Requests\Exceptions
*/
namespace WpOrg\Requests\Exception\Http;
use WpOrg\Requests\Exception\Http;
/**
* Exception for 504 Gateway Timeout responses
*
* @package Requests\Exceptions
*/
final class Status504 extends Http {
/**
* HTTP status code
*
* @var integer
*/
protected $code = 504;
/**
* Reason phrase
*
* @var string
*/
protected $reason = 'Gateway Timeout';
}
Exception/Http/Status503.php 0000604 00000000741 15133277060 0011647 0 ustar 00 <?php
/**
* Exception for 503 Service Unavailable responses
*
* @package Requests\Exceptions
*/
namespace WpOrg\Requests\Exception\Http;
use WpOrg\Requests\Exception\Http;
/**
* Exception for 503 Service Unavailable responses
*
* @package Requests\Exceptions
*/
final class Status503 extends Http {
/**
* HTTP status code
*
* @var integer
*/
protected $code = 503;
/**
* Reason phrase
*
* @var string
*/
protected $reason = 'Service Unavailable';
}
Exception/Http/Status429.php 0000604 00000001163 15133277060 0011655 0 ustar 00 <?php
/**
* Exception for 429 Too Many Requests responses
*
* @link https://tools.ietf.org/html/draft-nottingham-http-new-status-04
*
* @package Requests\Exceptions
*/
namespace WpOrg\Requests\Exception\Http;
use WpOrg\Requests\Exception\Http;
/**
* Exception for 429 Too Many Requests responses
*
* @link https://tools.ietf.org/html/draft-nottingham-http-new-status-04
*
* @package Requests\Exceptions
*/
final class Status429 extends Http {
/**
* HTTP status code
*
* @var integer
*/
protected $code = 429;
/**
* Reason phrase
*
* @var string
*/
protected $reason = 'Too Many Requests';
}
Exception/Http/Status412.php 0000604 00000000741 15133277060 0011646 0 ustar 00 <?php
/**
* Exception for 412 Precondition Failed responses
*
* @package Requests\Exceptions
*/
namespace WpOrg\Requests\Exception\Http;
use WpOrg\Requests\Exception\Http;
/**
* Exception for 412 Precondition Failed responses
*
* @package Requests\Exceptions
*/
final class Status412 extends Http {
/**
* HTTP status code
*
* @var integer
*/
protected $code = 412;
/**
* Reason phrase
*
* @var string
*/
protected $reason = 'Precondition Failed';
}
Exception/Http/Status415.php 0000604 00000000752 15133277060 0011653 0 ustar 00 <?php
/**
* Exception for 415 Unsupported Media Type responses
*
* @package Requests\Exceptions
*/
namespace WpOrg\Requests\Exception\Http;
use WpOrg\Requests\Exception\Http;
/**
* Exception for 415 Unsupported Media Type responses
*
* @package Requests\Exceptions
*/
final class Status415 extends Http {
/**
* HTTP status code
*
* @var integer
*/
protected $code = 415;
/**
* Reason phrase
*
* @var string
*/
protected $reason = 'Unsupported Media Type';
}
Exception/Http/Status500.php 0000604 00000000747 15133277060 0011652 0 ustar 00 <?php
/**
* Exception for 500 Internal Server Error responses
*
* @package Requests\Exceptions
*/
namespace WpOrg\Requests\Exception\Http;
use WpOrg\Requests\Exception\Http;
/**
* Exception for 500 Internal Server Error responses
*
* @package Requests\Exceptions
*/
final class Status500 extends Http {
/**
* HTTP status code
*
* @var integer
*/
protected $code = 500;
/**
* Reason phrase
*
* @var string
*/
protected $reason = 'Internal Server Error';
}
Exception/Http/Status418.php 0000604 00000001054 15133277060 0011652 0 ustar 00 <?php
/**
* Exception for 418 I'm A Teapot responses
*
* @link https://tools.ietf.org/html/rfc2324
*
* @package Requests\Exceptions
*/
namespace WpOrg\Requests\Exception\Http;
use WpOrg\Requests\Exception\Http;
/**
* Exception for 418 I'm A Teapot responses
*
* @link https://tools.ietf.org/html/rfc2324
*
* @package Requests\Exceptions
*/
final class Status418 extends Http {
/**
* HTTP status code
*
* @var integer
*/
protected $code = 418;
/**
* Reason phrase
*
* @var string
*/
protected $reason = "I'm A Teapot";
}
Exception/Http/Status416.php 0000604 00000001005 15133277060 0011644 0 ustar 00 <?php
/**
* Exception for 416 Requested Range Not Satisfiable responses
*
* @package Requests\Exceptions
*/
namespace WpOrg\Requests\Exception\Http;
use WpOrg\Requests\Exception\Http;
/**
* Exception for 416 Requested Range Not Satisfiable responses
*
* @package Requests\Exceptions
*/
final class Status416 extends Http {
/**
* HTTP status code
*
* @var integer
*/
protected $code = 416;
/**
* Reason phrase
*
* @var string
*/
protected $reason = 'Requested Range Not Satisfiable';
}
Exception/Http/Status411.php 0000604 00000000725 15133277060 0011647 0 ustar 00 <?php
/**
* Exception for 411 Length Required responses
*
* @package Requests\Exceptions
*/
namespace WpOrg\Requests\Exception\Http;
use WpOrg\Requests\Exception\Http;
/**
* Exception for 411 Length Required responses
*
* @package Requests\Exceptions
*/
final class Status411 extends Http {
/**
* HTTP status code
*
* @var integer
*/
protected $code = 411;
/**
* Reason phrase
*
* @var string
*/
protected $reason = 'Length Required';
}
Exception/Http/Status400.php 0000604 00000000711 15133277060 0011640 0 ustar 00 <?php
/**
* Exception for 400 Bad Request responses
*
* @package Requests\Exceptions
*/
namespace WpOrg\Requests\Exception\Http;
use WpOrg\Requests\Exception\Http;
/**
* Exception for 400 Bad Request responses
*
* @package Requests\Exceptions
*/
final class Status400 extends Http {
/**
* HTTP status code
*
* @var integer
*/
protected $code = 400;
/**
* Reason phrase
*
* @var string
*/
protected $reason = 'Bad Request';
}
Exception/Http/Status407.php 0000604 00000000777 15133277060 0011663 0 ustar 00 <?php
/**
* Exception for 407 Proxy Authentication Required responses
*
* @package Requests\Exceptions
*/
namespace WpOrg\Requests\Exception\Http;
use WpOrg\Requests\Exception\Http;
/**
* Exception for 407 Proxy Authentication Required responses
*
* @package Requests\Exceptions
*/
final class Status407 extends Http {
/**
* HTTP status code
*
* @var integer
*/
protected $code = 407;
/**
* Reason phrase
*
* @var string
*/
protected $reason = 'Proxy Authentication Required';
}
Exception/Http/Status409.php 0000604 00000000700 15133277060 0011647 0 ustar 00 <?php
/**
* Exception for 409 Conflict responses
*
* @package Requests\Exceptions
*/
namespace WpOrg\Requests\Exception\Http;
use WpOrg\Requests\Exception\Http;
/**
* Exception for 409 Conflict responses
*
* @package Requests\Exceptions
*/
final class Status409 extends Http {
/**
* HTTP status code
*
* @var integer
*/
protected $code = 409;
/**
* Reason phrase
*
* @var string
*/
protected $reason = 'Conflict';
}
Exception/Http/Status304.php 0000604 00000000714 15133277060 0011646 0 ustar 00 <?php
/**
* Exception for 304 Not Modified responses
*
* @package Requests\Exceptions
*/
namespace WpOrg\Requests\Exception\Http;
use WpOrg\Requests\Exception\Http;
/**
* Exception for 304 Not Modified responses
*
* @package Requests\Exceptions
*/
final class Status304 extends Http {
/**
* HTTP status code
*
* @var integer
*/
protected $code = 304;
/**
* Reason phrase
*
* @var string
*/
protected $reason = 'Not Modified';
}
Exception/Http/Status511.php 0000604 00000001145 15133277060 0011645 0 ustar 00 <?php
/**
* Exception for 511 Network Authentication Required responses
*
* @link https://tools.ietf.org/html/rfc6585
*
* @package Requests\Exceptions
*/
namespace WpOrg\Requests\Exception\Http;
use WpOrg\Requests\Exception\Http;
/**
* Exception for 511 Network Authentication Required responses
*
* @link https://tools.ietf.org/html/rfc6585
*
* @package Requests\Exceptions
*/
final class Status511 extends Http {
/**
* HTTP status code
*
* @var integer
*/
protected $code = 511;
/**
* Reason phrase
*
* @var string
*/
protected $reason = 'Network Authentication Required';
}
Exception/Http/Status410.php 0000604 00000000664 15133277060 0011650 0 ustar 00 <?php
/**
* Exception for 410 Gone responses
*
* @package Requests\Exceptions
*/
namespace WpOrg\Requests\Exception\Http;
use WpOrg\Requests\Exception\Http;
/**
* Exception for 410 Gone responses
*
* @package Requests\Exceptions
*/
final class Status410 extends Http {
/**
* HTTP status code
*
* @var integer
*/
protected $code = 410;
/**
* Reason phrase
*
* @var string
*/
protected $reason = 'Gone';
}
Exception/Http/Status417.php 0000604 00000000736 15133277060 0011657 0 ustar 00 <?php
/**
* Exception for 417 Expectation Failed responses
*
* @package Requests\Exceptions
*/
namespace WpOrg\Requests\Exception\Http;
use WpOrg\Requests\Exception\Http;
/**
* Exception for 417 Expectation Failed responses
*
* @package Requests\Exceptions
*/
final class Status417 extends Http {
/**
* HTTP status code
*
* @var integer
*/
protected $code = 417;
/**
* Reason phrase
*
* @var string
*/
protected $reason = 'Expectation Failed';
}
Exception/Http/Status501.php 0000604 00000000725 15133277060 0011647 0 ustar 00 <?php
/**
* Exception for 501 Not Implemented responses
*
* @package Requests\Exceptions
*/
namespace WpOrg\Requests\Exception\Http;
use WpOrg\Requests\Exception\Http;
/**
* Exception for 501 Not Implemented responses
*
* @package Requests\Exceptions
*/
final class Status501 extends Http {
/**
* HTTP status code
*
* @var integer
*/
protected $code = 501;
/**
* Reason phrase
*
* @var string
*/
protected $reason = 'Not Implemented';
}
Exception/Http/Status305.php 0000604 00000000703 15133277060 0011645 0 ustar 00 <?php
/**
* Exception for 305 Use Proxy responses
*
* @package Requests\Exceptions
*/
namespace WpOrg\Requests\Exception\Http;
use WpOrg\Requests\Exception\Http;
/**
* Exception for 305 Use Proxy responses
*
* @package Requests\Exceptions
*/
final class Status305 extends Http {
/**
* HTTP status code
*
* @var integer
*/
protected $code = 305;
/**
* Reason phrase
*
* @var string
*/
protected $reason = 'Use Proxy';
}
Exception/Http/Status408.php 0000604 00000000725 15133277060 0011655 0 ustar 00 <?php
/**
* Exception for 408 Request Timeout responses
*
* @package Requests\Exceptions
*/
namespace WpOrg\Requests\Exception\Http;
use WpOrg\Requests\Exception\Http;
/**
* Exception for 408 Request Timeout responses
*
* @package Requests\Exceptions
*/
final class Status408 extends Http {
/**
* HTTP status code
*
* @var integer
*/
protected $code = 408;
/**
* Reason phrase
*
* @var string
*/
protected $reason = 'Request Timeout';
}
Exception/Http/Status406.php 0000604 00000000722 15133277060 0011650 0 ustar 00 <?php
/**
* Exception for 406 Not Acceptable responses
*
* @package Requests\Exceptions
*/
namespace WpOrg\Requests\Exception\Http;
use WpOrg\Requests\Exception\Http;
/**
* Exception for 406 Not Acceptable responses
*
* @package Requests\Exceptions
*/
final class Status406 extends Http {
/**
* HTTP status code
*
* @var integer
*/
protected $code = 406;
/**
* Reason phrase
*
* @var string
*/
protected $reason = 'Not Acceptable';
}
Exception/Http/Status401.php 0000604 00000000714 15133277060 0011644 0 ustar 00 <?php
/**
* Exception for 401 Unauthorized responses
*
* @package Requests\Exceptions
*/
namespace WpOrg\Requests\Exception\Http;
use WpOrg\Requests\Exception\Http;
/**
* Exception for 401 Unauthorized responses
*
* @package Requests\Exceptions
*/
final class Status401 extends Http {
/**
* HTTP status code
*
* @var integer
*/
protected $code = 401;
/**
* Reason phrase
*
* @var string
*/
protected $reason = 'Unauthorized';
}
Exception/InvalidArgument.php 0000604 00000002122 15133277060 0012301 0 ustar 00 <?php
namespace WpOrg\Requests\Exception;
use InvalidArgumentException;
/**
* Exception for an invalid argument passed.
*
* @package Requests\Exceptions
* @since 2.0.0
*/
final class InvalidArgument extends InvalidArgumentException {
/**
* Create a new invalid argument exception with a standardized text.
*
* @param int $position The argument position in the function signature. 1-based.
* @param string $name The argument name in the function signature.
* @param string $expected The argument type expected as a string.
* @param string $received The actual argument type received.
*
* @return \WpOrg\Requests\Exception\InvalidArgument
*/
public static function create($position, $name, $expected, $received) {
// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_debug_backtrace
$stack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
return new self(
sprintf(
'%s::%s(): Argument #%d (%s) must be of type %s, %s given',
$stack[1]['class'],
$stack[1]['function'],
$position,
$name,
$expected,
$received
)
);
}
}
Capability.php 0000604 00000001214 15133277060 0007334 0 ustar 00 <?php
/**
* Capability interface declaring the known capabilities.
*
* @package Requests\Utilities
*/
namespace WpOrg\Requests;
/**
* Capability interface declaring the known capabilities.
*
* This is used as the authoritative source for which capabilities can be queried.
*
* @package Requests\Utilities
*/
interface Capability {
/**
* Support for SSL.
*
* @var string
*/
const SSL = 'ssl';
/**
* Collection of all capabilities supported in Requests.
*
* Note: this does not automatically mean that the capability will be supported for your chosen transport!
*
* @var string[]
*/
const ALL = [
self::SSL,
];
}
Response/Headers.php 0000604 00000006035 15133277060 0010432 0 ustar 00 <?php
/**
* Case-insensitive dictionary, suitable for HTTP headers
*
* @package Requests
*/
namespace WpOrg\Requests\Response;
use WpOrg\Requests\Exception;
use WpOrg\Requests\Exception\InvalidArgument;
use WpOrg\Requests\Utility\CaseInsensitiveDictionary;
use WpOrg\Requests\Utility\FilteredIterator;
/**
* Case-insensitive dictionary, suitable for HTTP headers
*
* @package Requests
*/
class Headers extends CaseInsensitiveDictionary {
/**
* Get the given header
*
* Unlike {@see \WpOrg\Requests\Response\Headers::getValues()}, this returns a string. If there are
* multiple values, it concatenates them with a comma as per RFC2616.
*
* Avoid using this where commas may be used unquoted in values, such as
* Set-Cookie headers.
*
* @param string $offset Name of the header to retrieve.
* @return string|null Header value
*/
public function offsetGet($offset) {
if (is_string($offset)) {
$offset = strtolower($offset);
}
if (!isset($this->data[$offset])) {
return null;
}
return $this->flatten($this->data[$offset]);
}
/**
* Set the given item
*
* @param string $offset Item name
* @param string $value Item value
*
* @throws \WpOrg\Requests\Exception On attempting to use dictionary as list (`invalidset`)
*/
public function offsetSet($offset, $value) {
if ($offset === null) {
throw new Exception('Object is a dictionary, not a list', 'invalidset');
}
if (is_string($offset)) {
$offset = strtolower($offset);
}
if (!isset($this->data[$offset])) {
$this->data[$offset] = [];
}
$this->data[$offset][] = $value;
}
/**
* Get all values for a given header
*
* @param string $offset Name of the header to retrieve.
* @return array|null Header values
*
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed argument is not valid as an array key.
*/
public function getValues($offset) {
if (!is_string($offset) && !is_int($offset)) {
throw InvalidArgument::create(1, '$offset', 'string|int', gettype($offset));
}
if (is_string($offset)) {
$offset = strtolower($offset);
}
if (!isset($this->data[$offset])) {
return null;
}
return $this->data[$offset];
}
/**
* Flattens a value into a string
*
* Converts an array into a string by imploding values with a comma, as per
* RFC2616's rules for folding headers.
*
* @param string|array $value Value to flatten
* @return string Flattened value
*
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed argument is not a string or an array.
*/
public function flatten($value) {
if (is_string($value)) {
return $value;
}
if (is_array($value)) {
return implode(',', $value);
}
throw InvalidArgument::create(1, '$value', 'string|array', gettype($value));
}
/**
* Get an iterator for the data
*
* Converts the internally stored values to a comma-separated string if there is more
* than one value for a key.
*
* @return \ArrayIterator
*/
public function getIterator() {
return new FilteredIterator($this->data, [$this, 'flatten']);
}
}
Session.php 0000604 00000021623 15133277060 0006704 0 ustar 00 <?php
/**
* Session handler for persistent requests and default parameters
*
* @package Requests\SessionHandler
*/
namespace WpOrg\Requests;
use WpOrg\Requests\Cookie\Jar;
use WpOrg\Requests\Exception\InvalidArgument;
use WpOrg\Requests\Iri;
use WpOrg\Requests\Requests;
use WpOrg\Requests\Utility\InputValidator;
/**
* Session handler for persistent requests and default parameters
*
* Allows various options to be set as default values, and merges both the
* options and URL properties together. A base URL can be set for all requests,
* with all subrequests resolved from this. Base options can be set (including
* a shared cookie jar), then overridden for individual requests.
*
* @package Requests\SessionHandler
*/
class Session {
/**
* Base URL for requests
*
* URLs will be made absolute using this as the base
*
* @var string|null
*/
public $url = null;
/**
* Base headers for requests
*
* @var array
*/
public $headers = [];
/**
* Base data for requests
*
* If both the base data and the per-request data are arrays, the data will
* be merged before sending the request.
*
* @var array
*/
public $data = [];
/**
* Base options for requests
*
* The base options are merged with the per-request data for each request.
* The only default option is a shared cookie jar between requests.
*
* Values here can also be set directly via properties on the Session
* object, e.g. `$session->useragent = 'X';`
*
* @var array
*/
public $options = [];
/**
* Create a new session
*
* @param string|Stringable|null $url Base URL for requests
* @param array $headers Default headers for requests
* @param array $data Default data for requests
* @param array $options Default options for requests
*
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $url argument is not a string, Stringable or null.
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $headers argument is not an array.
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $data argument is not an array.
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $options argument is not an array.
*/
public function __construct($url = null, $headers = [], $data = [], $options = []) {
if ($url !== null && InputValidator::is_string_or_stringable($url) === false) {
throw InvalidArgument::create(1, '$url', 'string|Stringable|null', gettype($url));
}
if (is_array($headers) === false) {
throw InvalidArgument::create(2, '$headers', 'array', gettype($headers));
}
if (is_array($data) === false) {
throw InvalidArgument::create(3, '$data', 'array', gettype($data));
}
if (is_array($options) === false) {
throw InvalidArgument::create(4, '$options', 'array', gettype($options));
}
$this->url = $url;
$this->headers = $headers;
$this->data = $data;
$this->options = $options;
if (empty($this->options['cookies'])) {
$this->options['cookies'] = new Jar();
}
}
/**
* Get a property's value
*
* @param string $name Property name.
* @return mixed|null Property value, null if none found
*/
public function __get($name) {
if (isset($this->options[$name])) {
return $this->options[$name];
}
return null;
}
/**
* Set a property's value
*
* @param string $name Property name.
* @param mixed $value Property value
*/
public function __set($name, $value) {
$this->options[$name] = $value;
}
/**
* Remove a property's value
*
* @param string $name Property name.
*/
public function __isset($name) {
return isset($this->options[$name]);
}
/**
* Remove a property's value
*
* @param string $name Property name.
*/
public function __unset($name) {
unset($this->options[$name]);
}
/**#@+
* @see \WpOrg\Requests\Session::request()
* @param string $url
* @param array $headers
* @param array $options
* @return \WpOrg\Requests\Response
*/
/**
* Send a GET request
*/
public function get($url, $headers = [], $options = []) {
return $this->request($url, $headers, null, Requests::GET, $options);
}
/**
* Send a HEAD request
*/
public function head($url, $headers = [], $options = []) {
return $this->request($url, $headers, null, Requests::HEAD, $options);
}
/**
* Send a DELETE request
*/
public function delete($url, $headers = [], $options = []) {
return $this->request($url, $headers, null, Requests::DELETE, $options);
}
/**#@-*/
/**#@+
* @see \WpOrg\Requests\Session::request()
* @param string $url
* @param array $headers
* @param array $data
* @param array $options
* @return \WpOrg\Requests\Response
*/
/**
* Send a POST request
*/
public function post($url, $headers = [], $data = [], $options = []) {
return $this->request($url, $headers, $data, Requests::POST, $options);
}
/**
* Send a PUT request
*/
public function put($url, $headers = [], $data = [], $options = []) {
return $this->request($url, $headers, $data, Requests::PUT, $options);
}
/**
* Send a PATCH request
*
* Note: Unlike {@see \WpOrg\Requests\Session::post()} and {@see \WpOrg\Requests\Session::put()},
* `$headers` is required, as the specification recommends that should send an ETag
*
* @link https://tools.ietf.org/html/rfc5789
*/
public function patch($url, $headers, $data = [], $options = []) {
return $this->request($url, $headers, $data, Requests::PATCH, $options);
}
/**#@-*/
/**
* Main interface for HTTP requests
*
* This method initiates a request and sends it via a transport before
* parsing.
*
* @see \WpOrg\Requests\Requests::request()
*
* @param string $url URL to request
* @param array $headers Extra headers to send with the request
* @param array|null $data Data to send either as a query string for GET/HEAD requests, or in the body for POST requests
* @param string $type HTTP request type (use \WpOrg\Requests\Requests constants)
* @param array $options Options for the request (see {@see \WpOrg\Requests\Requests::request()})
* @return \WpOrg\Requests\Response
*
* @throws \WpOrg\Requests\Exception On invalid URLs (`nonhttp`)
*/
public function request($url, $headers = [], $data = [], $type = Requests::GET, $options = []) {
$request = $this->merge_request(compact('url', 'headers', 'data', 'options'));
return Requests::request($request['url'], $request['headers'], $request['data'], $type, $request['options']);
}
/**
* Send multiple HTTP requests simultaneously
*
* @see \WpOrg\Requests\Requests::request_multiple()
*
* @param array $requests Requests data (see {@see \WpOrg\Requests\Requests::request_multiple()})
* @param array $options Global and default options (see {@see \WpOrg\Requests\Requests::request()})
* @return array Responses (either \WpOrg\Requests\Response or a \WpOrg\Requests\Exception object)
*
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $requests argument is not an array or iterable object with array access.
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $options argument is not an array.
*/
public function request_multiple($requests, $options = []) {
if (InputValidator::has_array_access($requests) === false || InputValidator::is_iterable($requests) === false) {
throw InvalidArgument::create(1, '$requests', 'array|ArrayAccess&Traversable', gettype($requests));
}
if (is_array($options) === false) {
throw InvalidArgument::create(2, '$options', 'array', gettype($options));
}
foreach ($requests as $key => $request) {
$requests[$key] = $this->merge_request($request, false);
}
$options = array_merge($this->options, $options);
// Disallow forcing the type, as that's a per request setting
unset($options['type']);
return Requests::request_multiple($requests, $options);
}
public function __wakeup() {
throw new \LogicException( __CLASS__ . ' should never be unserialized' );
}
/**
* Merge a request's data with the default data
*
* @param array $request Request data (same form as {@see \WpOrg\Requests\Session::request_multiple()})
* @param boolean $merge_options Should we merge options as well?
* @return array Request data
*/
protected function merge_request($request, $merge_options = true) {
if ($this->url !== null) {
$request['url'] = Iri::absolutize($this->url, $request['url']);
$request['url'] = $request['url']->uri;
}
if (empty($request['headers'])) {
$request['headers'] = [];
}
$request['headers'] = array_merge($this->headers, $request['headers']);
if (empty($request['data'])) {
if (is_array($this->data)) {
$request['data'] = $this->data;
}
} elseif (is_array($request['data']) && is_array($this->data)) {
$request['data'] = array_merge($this->data, $request['data']);
}
if ($merge_options === true) {
$request['options'] = array_merge($this->options, $request['options']);
// Disallow forcing the type, as that's a per request setting
unset($request['options']['type']);
}
return $request;
}
}
Auth.php 0000604 00000001534 15133277060 0006161 0 ustar 00 <?php
/**
* Authentication provider interface
*
* @package Requests\Authentication
*/
namespace WpOrg\Requests;
use WpOrg\Requests\Hooks;
/**
* Authentication provider interface
*
* Implement this interface to act as an authentication provider.
*
* Parameters should be passed via the constructor where possible, as this
* makes it much easier for users to use your provider.
*
* @see \WpOrg\Requests\Hooks
*
* @package Requests\Authentication
*/
interface Auth {
/**
* Register hooks as needed
*
* This method is called in {@see \WpOrg\Requests\Requests::request()} when the user
* has set an instance as the 'auth' option. Use this callback to register all the
* hooks you'll need.
*
* @see \WpOrg\Requests\Hooks::register()
* @param \WpOrg\Requests\Hooks $hooks Hook system
*/
public function register(Hooks $hooks);
}
Cookie/Jar.php 0000604 00000010413 15133277060 0007201 0 ustar 00 <?php
/**
* Cookie holder object
*
* @package Requests\Cookies
*/
namespace WpOrg\Requests\Cookie;
use ArrayAccess;
use ArrayIterator;
use IteratorAggregate;
use ReturnTypeWillChange;
use WpOrg\Requests\Cookie;
use WpOrg\Requests\Exception;
use WpOrg\Requests\Exception\InvalidArgument;
use WpOrg\Requests\HookManager;
use WpOrg\Requests\Iri;
use WpOrg\Requests\Response;
/**
* Cookie holder object
*
* @package Requests\Cookies
*/
class Jar implements ArrayAccess, IteratorAggregate {
/**
* Actual item data
*
* @var array
*/
protected $cookies = [];
/**
* Create a new jar
*
* @param array $cookies Existing cookie values
*
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed argument is not an array.
*/
public function __construct($cookies = []) {
if (is_array($cookies) === false) {
throw InvalidArgument::create(1, '$cookies', 'array', gettype($cookies));
}
$this->cookies = $cookies;
}
/**
* Normalise cookie data into a \WpOrg\Requests\Cookie
*
* @param string|\WpOrg\Requests\Cookie $cookie Cookie header value, possibly pre-parsed (object).
* @param string $key Optional. The name for this cookie.
* @return \WpOrg\Requests\Cookie
*/
public function normalize_cookie($cookie, $key = '') {
if ($cookie instanceof Cookie) {
return $cookie;
}
return Cookie::parse($cookie, $key);
}
/**
* Check if the given item exists
*
* @param string $offset Item key
* @return boolean Does the item exist?
*/
#[ReturnTypeWillChange]
public function offsetExists($offset) {
return isset($this->cookies[$offset]);
}
/**
* Get the value for the item
*
* @param string $offset Item key
* @return string|null Item value (null if offsetExists is false)
*/
#[ReturnTypeWillChange]
public function offsetGet($offset) {
if (!isset($this->cookies[$offset])) {
return null;
}
return $this->cookies[$offset];
}
/**
* Set the given item
*
* @param string $offset Item name
* @param string $value Item value
*
* @throws \WpOrg\Requests\Exception On attempting to use dictionary as list (`invalidset`)
*/
#[ReturnTypeWillChange]
public function offsetSet($offset, $value) {
if ($offset === null) {
throw new Exception('Object is a dictionary, not a list', 'invalidset');
}
$this->cookies[$offset] = $value;
}
/**
* Unset the given header
*
* @param string $offset The key for the item to unset.
*/
#[ReturnTypeWillChange]
public function offsetUnset($offset) {
unset($this->cookies[$offset]);
}
/**
* Get an iterator for the data
*
* @return \ArrayIterator
*/
#[ReturnTypeWillChange]
public function getIterator() {
return new ArrayIterator($this->cookies);
}
/**
* Register the cookie handler with the request's hooking system
*
* @param \WpOrg\Requests\HookManager $hooks Hooking system
*/
public function register(HookManager $hooks) {
$hooks->register('requests.before_request', [$this, 'before_request']);
$hooks->register('requests.before_redirect_check', [$this, 'before_redirect_check']);
}
/**
* Add Cookie header to a request if we have any
*
* As per RFC 6265, cookies are separated by '; '
*
* @param string $url
* @param array $headers
* @param array $data
* @param string $type
* @param array $options
*/
public function before_request($url, &$headers, &$data, &$type, &$options) {
if (!$url instanceof Iri) {
$url = new Iri($url);
}
if (!empty($this->cookies)) {
$cookies = [];
foreach ($this->cookies as $key => $cookie) {
$cookie = $this->normalize_cookie($cookie, $key);
// Skip expired cookies
if ($cookie->is_expired()) {
continue;
}
if ($cookie->domain_matches($url->host)) {
$cookies[] = $cookie->format_for_header();
}
}
$headers['Cookie'] = implode('; ', $cookies);
}
}
/**
* Parse all cookies from a response and attach them to the response
*
* @param \WpOrg\Requests\Response $response Response as received.
*/
public function before_redirect_check(Response $response) {
$url = $response->url;
if (!$url instanceof Iri) {
$url = new Iri($url);
}
$cookies = Cookie::parse_from_headers($response->headers, $url);
$this->cookies = array_merge($this->cookies, $cookies);
$response->cookies = $this;
}
}
Transport.php 0000604 00000003010 15133277060 0007243 0 ustar 00 <?php
/**
* Base HTTP transport
*
* @package Requests\Transport
*/
namespace WpOrg\Requests;
/**
* Base HTTP transport
*
* @package Requests\Transport
*/
interface Transport {
/**
* Perform a request
*
* @param string $url URL to request
* @param array $headers Associative array of request headers
* @param string|array $data Data to send either as the POST body, or as parameters in the URL for a GET/HEAD
* @param array $options Request options, see {@see \WpOrg\Requests\Requests::response()} for documentation
* @return string Raw HTTP result
*/
public function request($url, $headers = [], $data = [], $options = []);
/**
* Send multiple requests simultaneously
*
* @param array $requests Request data (array of 'url', 'headers', 'data', 'options') as per {@see \WpOrg\Requests\Transport::request()}
* @param array $options Global options, see {@see \WpOrg\Requests\Requests::response()} for documentation
* @return array Array of \WpOrg\Requests\Response objects (may contain \WpOrg\Requests\Exception or string responses as well)
*/
public function request_multiple($requests, $options);
/**
* Self-test whether the transport can be used.
*
* The available capabilities to test for can be found in {@see \WpOrg\Requests\Capability}.
*
* @param array<string, bool> $capabilities Optional. Associative array of capabilities to test against, i.e. `['<capability>' => true]`.
* @return bool Whether the transport can be used.
*/
public static function test($capabilities = []);
}
Autoload.php 0000604 00000022167 15133277060 0007035 0 ustar 00 <?php
/**
* Autoloader for Requests for PHP.
*
* Include this file if you'd like to avoid having to create your own autoloader.
*
* @package Requests
* @since 2.0.0
*
* @codeCoverageIgnore
*/
namespace WpOrg\Requests;
/*
* Ensure the autoloader is only declared once.
* This safeguard is in place as this is the typical entry point for this library
* and this file being required unconditionally could easily cause
* fatal "Class already declared" errors.
*/
if (class_exists('WpOrg\Requests\Autoload') === false) {
/**
* Autoloader for Requests for PHP.
*
* This autoloader supports the PSR-4 based Requests 2.0.0 classes in a case-sensitive manner
* as the most common server OS-es are case-sensitive and the file names are in mixed case.
*
* For the PSR-0 Requests 1.x BC-layer, requested classes will be treated case-insensitively.
*
* @package Requests
*/
final class Autoload {
/**
* List of the old PSR-0 class names in lowercase as keys with their PSR-4 case-sensitive name as a value.
*
* @var array
*/
private static $deprecated_classes = [
// Interfaces.
'requests_auth' => '\WpOrg\Requests\Auth',
'requests_hooker' => '\WpOrg\Requests\HookManager',
'requests_proxy' => '\WpOrg\Requests\Proxy',
'requests_transport' => '\WpOrg\Requests\Transport',
// Classes.
'requests_cookie' => '\WpOrg\Requests\Cookie',
'requests_exception' => '\WpOrg\Requests\Exception',
'requests_hooks' => '\WpOrg\Requests\Hooks',
'requests_idnaencoder' => '\WpOrg\Requests\IdnaEncoder',
'requests_ipv6' => '\WpOrg\Requests\Ipv6',
'requests_iri' => '\WpOrg\Requests\Iri',
'requests_response' => '\WpOrg\Requests\Response',
'requests_session' => '\WpOrg\Requests\Session',
'requests_ssl' => '\WpOrg\Requests\Ssl',
'requests_auth_basic' => '\WpOrg\Requests\Auth\Basic',
'requests_cookie_jar' => '\WpOrg\Requests\Cookie\Jar',
'requests_proxy_http' => '\WpOrg\Requests\Proxy\Http',
'requests_response_headers' => '\WpOrg\Requests\Response\Headers',
'requests_transport_curl' => '\WpOrg\Requests\Transport\Curl',
'requests_transport_fsockopen' => '\WpOrg\Requests\Transport\Fsockopen',
'requests_utility_caseinsensitivedictionary' => '\WpOrg\Requests\Utility\CaseInsensitiveDictionary',
'requests_utility_filterediterator' => '\WpOrg\Requests\Utility\FilteredIterator',
'requests_exception_http' => '\WpOrg\Requests\Exception\Http',
'requests_exception_transport' => '\WpOrg\Requests\Exception\Transport',
'requests_exception_transport_curl' => '\WpOrg\Requests\Exception\Transport\Curl',
'requests_exception_http_304' => '\WpOrg\Requests\Exception\Http\Status304',
'requests_exception_http_305' => '\WpOrg\Requests\Exception\Http\Status305',
'requests_exception_http_306' => '\WpOrg\Requests\Exception\Http\Status306',
'requests_exception_http_400' => '\WpOrg\Requests\Exception\Http\Status400',
'requests_exception_http_401' => '\WpOrg\Requests\Exception\Http\Status401',
'requests_exception_http_402' => '\WpOrg\Requests\Exception\Http\Status402',
'requests_exception_http_403' => '\WpOrg\Requests\Exception\Http\Status403',
'requests_exception_http_404' => '\WpOrg\Requests\Exception\Http\Status404',
'requests_exception_http_405' => '\WpOrg\Requests\Exception\Http\Status405',
'requests_exception_http_406' => '\WpOrg\Requests\Exception\Http\Status406',
'requests_exception_http_407' => '\WpOrg\Requests\Exception\Http\Status407',
'requests_exception_http_408' => '\WpOrg\Requests\Exception\Http\Status408',
'requests_exception_http_409' => '\WpOrg\Requests\Exception\Http\Status409',
'requests_exception_http_410' => '\WpOrg\Requests\Exception\Http\Status410',
'requests_exception_http_411' => '\WpOrg\Requests\Exception\Http\Status411',
'requests_exception_http_412' => '\WpOrg\Requests\Exception\Http\Status412',
'requests_exception_http_413' => '\WpOrg\Requests\Exception\Http\Status413',
'requests_exception_http_414' => '\WpOrg\Requests\Exception\Http\Status414',
'requests_exception_http_415' => '\WpOrg\Requests\Exception\Http\Status415',
'requests_exception_http_416' => '\WpOrg\Requests\Exception\Http\Status416',
'requests_exception_http_417' => '\WpOrg\Requests\Exception\Http\Status417',
'requests_exception_http_418' => '\WpOrg\Requests\Exception\Http\Status418',
'requests_exception_http_428' => '\WpOrg\Requests\Exception\Http\Status428',
'requests_exception_http_429' => '\WpOrg\Requests\Exception\Http\Status429',
'requests_exception_http_431' => '\WpOrg\Requests\Exception\Http\Status431',
'requests_exception_http_500' => '\WpOrg\Requests\Exception\Http\Status500',
'requests_exception_http_501' => '\WpOrg\Requests\Exception\Http\Status501',
'requests_exception_http_502' => '\WpOrg\Requests\Exception\Http\Status502',
'requests_exception_http_503' => '\WpOrg\Requests\Exception\Http\Status503',
'requests_exception_http_504' => '\WpOrg\Requests\Exception\Http\Status504',
'requests_exception_http_505' => '\WpOrg\Requests\Exception\Http\Status505',
'requests_exception_http_511' => '\WpOrg\Requests\Exception\Http\Status511',
'requests_exception_http_unknown' => '\WpOrg\Requests\Exception\Http\StatusUnknown',
];
/**
* Register the autoloader.
*
* Note: the autoloader is *prepended* in the autoload queue.
* This is done to ensure that the Requests 2.0 autoloader takes precedence
* over a potentially (dependency-registered) Requests 1.x autoloader.
*
* @internal This method contains a safeguard against the autoloader being
* registered multiple times. This safeguard uses a global constant to
* (hopefully/in most cases) still function correctly, even if the
* class would be renamed.
*
* @return void
*/
public static function register() {
if (defined('REQUESTS_AUTOLOAD_REGISTERED') === false) {
spl_autoload_register([self::class, 'load'], true);
define('REQUESTS_AUTOLOAD_REGISTERED', true);
}
}
/**
* Autoloader.
*
* @param string $class_name Name of the class name to load.
*
* @return bool Whether a class was loaded or not.
*/
public static function load($class_name) {
// Check that the class starts with "Requests" (PSR-0) or "WpOrg\Requests" (PSR-4).
$psr_4_prefix_pos = strpos($class_name, 'WpOrg\\Requests\\');
if (stripos($class_name, 'Requests') !== 0 && $psr_4_prefix_pos !== 0) {
return false;
}
$class_lower = strtolower($class_name);
if ($class_lower === 'requests') {
// Reference to the original PSR-0 Requests class.
$file = dirname(__DIR__) . '/library/Requests.php';
} elseif ($psr_4_prefix_pos === 0) {
// PSR-4 classname.
$file = __DIR__ . '/' . strtr(substr($class_name, 15), '\\', '/') . '.php';
}
if (isset($file) && file_exists($file)) {
include $file;
return true;
}
/*
* Okay, so the class starts with "Requests", but we couldn't find the file.
* If this is one of the deprecated/renamed PSR-0 classes being requested,
* let's alias it to the new name and throw a deprecation notice.
*/
if (isset(self::$deprecated_classes[$class_lower])) {
/*
* Integrators who cannot yet upgrade to the PSR-4 class names can silence deprecations
* by defining a `REQUESTS_SILENCE_PSR0_DEPRECATIONS` constant and setting it to `true`.
* The constant needs to be defined before the first deprecated class is requested
* via this autoloader.
*/
if (!defined('REQUESTS_SILENCE_PSR0_DEPRECATIONS') || REQUESTS_SILENCE_PSR0_DEPRECATIONS !== true) {
// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error
trigger_error(
'The PSR-0 `Requests_...` class names in the Requests library are deprecated.'
. ' Switch to the PSR-4 `WpOrg\Requests\...` class names at your earliest convenience.',
E_USER_DEPRECATED
);
// Prevent the deprecation notice from being thrown twice.
if (!defined('REQUESTS_SILENCE_PSR0_DEPRECATIONS')) {
define('REQUESTS_SILENCE_PSR0_DEPRECATIONS', true);
}
}
// Create an alias and let the autoloader recursively kick in to load the PSR-4 class.
return class_alias(self::$deprecated_classes[$class_lower], $class_name, true);
}
return false;
}
}
}
Proxy.php 0000604 00000001543 15133277060 0006401 0 ustar 00 <?php
/**
* Proxy connection interface
*
* @package Requests\Proxy
* @since 1.6
*/
namespace WpOrg\Requests;
use WpOrg\Requests\Hooks;
/**
* Proxy connection interface
*
* Implement this interface to handle proxy settings and authentication
*
* Parameters should be passed via the constructor where possible, as this
* makes it much easier for users to use your provider.
*
* @see \WpOrg\Requests\Hooks
*
* @package Requests\Proxy
* @since 1.6
*/
interface Proxy {
/**
* Register hooks as needed
*
* This method is called in {@see \WpOrg\Requests\Requests::request()} when the user
* has set an instance as the 'auth' option. Use this callback to register all the
* hooks you'll need.
*
* @see \WpOrg\Requests\Hooks::register()
* @param \WpOrg\Requests\Hooks $hooks Hook system
*/
public function register(Hooks $hooks);
}
IdnaEncoder.php 0000604 00000030223 15133277060 0007430 0 ustar 00 <?php
namespace WpOrg\Requests;
use WpOrg\Requests\Exception;
use WpOrg\Requests\Exception\InvalidArgument;
use WpOrg\Requests\Utility\InputValidator;
/**
* IDNA URL encoder
*
* Note: Not fully compliant, as nameprep does nothing yet.
*
* @package Requests\Utilities
*
* @link https://tools.ietf.org/html/rfc3490 IDNA specification
* @link https://tools.ietf.org/html/rfc3492 Punycode/Bootstrap specification
*/
class IdnaEncoder {
/**
* ACE prefix used for IDNA
*
* @link https://tools.ietf.org/html/rfc3490#section-5
* @var string
*/
const ACE_PREFIX = 'xn--';
/**
* Maximum length of a IDNA URL in ASCII.
*
* @see \WpOrg\Requests\IdnaEncoder::to_ascii()
*
* @since 2.0.0
*
* @var int
*/
const MAX_LENGTH = 64;
/**#@+
* Bootstrap constant for Punycode
*
* @link https://tools.ietf.org/html/rfc3492#section-5
* @var int
*/
const BOOTSTRAP_BASE = 36;
const BOOTSTRAP_TMIN = 1;
const BOOTSTRAP_TMAX = 26;
const BOOTSTRAP_SKEW = 38;
const BOOTSTRAP_DAMP = 700;
const BOOTSTRAP_INITIAL_BIAS = 72;
const BOOTSTRAP_INITIAL_N = 128;
/**#@-*/
/**
* Encode a hostname using Punycode
*
* @param string|Stringable $hostname Hostname
* @return string Punycode-encoded hostname
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed argument is not a string or a stringable object.
*/
public static function encode($hostname) {
if (InputValidator::is_string_or_stringable($hostname) === false) {
throw InvalidArgument::create(1, '$hostname', 'string|Stringable', gettype($hostname));
}
$parts = explode('.', $hostname);
foreach ($parts as &$part) {
$part = self::to_ascii($part);
}
return implode('.', $parts);
}
/**
* Convert a UTF-8 text string to an ASCII string using Punycode
*
* @param string $text ASCII or UTF-8 string (max length 64 characters)
* @return string ASCII string
*
* @throws \WpOrg\Requests\Exception Provided string longer than 64 ASCII characters (`idna.provided_too_long`)
* @throws \WpOrg\Requests\Exception Prepared string longer than 64 ASCII characters (`idna.prepared_too_long`)
* @throws \WpOrg\Requests\Exception Provided string already begins with xn-- (`idna.provided_is_prefixed`)
* @throws \WpOrg\Requests\Exception Encoded string longer than 64 ASCII characters (`idna.encoded_too_long`)
*/
public static function to_ascii($text) {
// Step 1: Check if the text is already ASCII
if (self::is_ascii($text)) {
// Skip to step 7
if (strlen($text) < self::MAX_LENGTH) {
return $text;
}
throw new Exception('Provided string is too long', 'idna.provided_too_long', $text);
}
// Step 2: nameprep
$text = self::nameprep($text);
// Step 3: UseSTD3ASCIIRules is false, continue
// Step 4: Check if it's ASCII now
if (self::is_ascii($text)) {
// Skip to step 7
/*
* As the `nameprep()` method returns the original string, this code will never be reached until
* that method is properly implemented.
*/
// @codeCoverageIgnoreStart
if (strlen($text) < self::MAX_LENGTH) {
return $text;
}
throw new Exception('Prepared string is too long', 'idna.prepared_too_long', $text);
// @codeCoverageIgnoreEnd
}
// Step 5: Check ACE prefix
if (strpos($text, self::ACE_PREFIX) === 0) {
throw new Exception('Provided string begins with ACE prefix', 'idna.provided_is_prefixed', $text);
}
// Step 6: Encode with Punycode
$text = self::punycode_encode($text);
// Step 7: Prepend ACE prefix
$text = self::ACE_PREFIX . $text;
// Step 8: Check size
if (strlen($text) < self::MAX_LENGTH) {
return $text;
}
throw new Exception('Encoded string is too long', 'idna.encoded_too_long', $text);
}
/**
* Check whether a given text string contains only ASCII characters
*
* @internal (Testing found regex was the fastest implementation)
*
* @param string $text Text to examine.
* @return bool Is the text string ASCII-only?
*/
protected static function is_ascii($text) {
return (preg_match('/(?:[^\x00-\x7F])/', $text) !== 1);
}
/**
* Prepare a text string for use as an IDNA name
*
* @todo Implement this based on RFC 3491 and the newer 5891
* @param string $text Text to prepare.
* @return string Prepared string
*/
protected static function nameprep($text) {
return $text;
}
/**
* Convert a UTF-8 string to a UCS-4 codepoint array
*
* Based on \WpOrg\Requests\Iri::replace_invalid_with_pct_encoding()
*
* @param string $input Text to convert.
* @return array Unicode code points
*
* @throws \WpOrg\Requests\Exception Invalid UTF-8 codepoint (`idna.invalidcodepoint`)
*/
protected static function utf8_to_codepoints($input) {
$codepoints = [];
// Get number of bytes
$strlen = strlen($input);
// phpcs:ignore Generic.CodeAnalysis.JumbledIncrementer -- This is a deliberate choice.
for ($position = 0; $position < $strlen; $position++) {
$value = ord($input[$position]);
if ((~$value & 0x80) === 0x80) { // One byte sequence:
$character = $value;
$length = 1;
$remaining = 0;
} elseif (($value & 0xE0) === 0xC0) { // Two byte sequence:
$character = ($value & 0x1F) << 6;
$length = 2;
$remaining = 1;
} elseif (($value & 0xF0) === 0xE0) { // Three byte sequence:
$character = ($value & 0x0F) << 12;
$length = 3;
$remaining = 2;
} elseif (($value & 0xF8) === 0xF0) { // Four byte sequence:
$character = ($value & 0x07) << 18;
$length = 4;
$remaining = 3;
} else { // Invalid byte:
throw new Exception('Invalid Unicode codepoint', 'idna.invalidcodepoint', $value);
}
if ($remaining > 0) {
if ($position + $length > $strlen) {
throw new Exception('Invalid Unicode codepoint', 'idna.invalidcodepoint', $character);
}
for ($position++; $remaining > 0; $position++) {
$value = ord($input[$position]);
// If it is invalid, count the sequence as invalid and reprocess the current byte:
if (($value & 0xC0) !== 0x80) {
throw new Exception('Invalid Unicode codepoint', 'idna.invalidcodepoint', $character);
}
--$remaining;
$character |= ($value & 0x3F) << ($remaining * 6);
}
$position--;
}
if (// Non-shortest form sequences are invalid
$length > 1 && $character <= 0x7F
|| $length > 2 && $character <= 0x7FF
|| $length > 3 && $character <= 0xFFFF
// Outside of range of ucschar codepoints
// Noncharacters
|| ($character & 0xFFFE) === 0xFFFE
|| $character >= 0xFDD0 && $character <= 0xFDEF
|| (
// Everything else not in ucschar
$character > 0xD7FF && $character < 0xF900
|| $character < 0x20
|| $character > 0x7E && $character < 0xA0
|| $character > 0xEFFFD
)
) {
throw new Exception('Invalid Unicode codepoint', 'idna.invalidcodepoint', $character);
}
$codepoints[] = $character;
}
return $codepoints;
}
/**
* RFC3492-compliant encoder
*
* @internal Pseudo-code from Section 6.3 is commented with "#" next to relevant code
*
* @param string $input UTF-8 encoded string to encode
* @return string Punycode-encoded string
*
* @throws \WpOrg\Requests\Exception On character outside of the domain (never happens with Punycode) (`idna.character_outside_domain`)
*/
public static function punycode_encode($input) {
$output = '';
// let n = initial_n
$n = self::BOOTSTRAP_INITIAL_N;
// let delta = 0
$delta = 0;
// let bias = initial_bias
$bias = self::BOOTSTRAP_INITIAL_BIAS;
// let h = b = the number of basic code points in the input
$h = 0;
$b = 0; // see loop
// copy them to the output in order
$codepoints = self::utf8_to_codepoints($input);
$extended = [];
foreach ($codepoints as $char) {
if ($char < 128) {
// Character is valid ASCII
// TODO: this should also check if it's valid for a URL
$output .= chr($char);
$h++;
// Check if the character is non-ASCII, but below initial n
// This never occurs for Punycode, so ignore in coverage
// @codeCoverageIgnoreStart
} elseif ($char < $n) {
throw new Exception('Invalid character', 'idna.character_outside_domain', $char);
// @codeCoverageIgnoreEnd
} else {
$extended[$char] = true;
}
}
$extended = array_keys($extended);
sort($extended);
$b = $h;
// [copy them] followed by a delimiter if b > 0
if (strlen($output) > 0) {
$output .= '-';
}
// {if the input contains a non-basic code point < n then fail}
// while h < length(input) do begin
$codepointcount = count($codepoints);
while ($h < $codepointcount) {
// let m = the minimum code point >= n in the input
$m = array_shift($extended);
//printf('next code point to insert is %s' . PHP_EOL, dechex($m));
// let delta = delta + (m - n) * (h + 1), fail on overflow
$delta += ($m - $n) * ($h + 1);
// let n = m
$n = $m;
// for each code point c in the input (in order) do begin
for ($num = 0; $num < $codepointcount; $num++) {
$c = $codepoints[$num];
// if c < n then increment delta, fail on overflow
if ($c < $n) {
$delta++;
} elseif ($c === $n) { // if c == n then begin
// let q = delta
$q = $delta;
// for k = base to infinity in steps of base do begin
for ($k = self::BOOTSTRAP_BASE; ; $k += self::BOOTSTRAP_BASE) {
// let t = tmin if k <= bias {+ tmin}, or
// tmax if k >= bias + tmax, or k - bias otherwise
if ($k <= ($bias + self::BOOTSTRAP_TMIN)) {
$t = self::BOOTSTRAP_TMIN;
} elseif ($k >= ($bias + self::BOOTSTRAP_TMAX)) {
$t = self::BOOTSTRAP_TMAX;
} else {
$t = $k - $bias;
}
// if q < t then break
if ($q < $t) {
break;
}
// output the code point for digit t + ((q - t) mod (base - t))
$digit = (int) ($t + (($q - $t) % (self::BOOTSTRAP_BASE - $t)));
$output .= self::digit_to_char($digit);
// let q = (q - t) div (base - t)
$q = (int) floor(($q - $t) / (self::BOOTSTRAP_BASE - $t));
} // end
// output the code point for digit q
$output .= self::digit_to_char($q);
// let bias = adapt(delta, h + 1, test h equals b?)
$bias = self::adapt($delta, $h + 1, $h === $b);
// let delta = 0
$delta = 0;
// increment h
$h++;
} // end
} // end
// increment delta and n
$delta++;
$n++;
} // end
return $output;
}
/**
* Convert a digit to its respective character
*
* @link https://tools.ietf.org/html/rfc3492#section-5
*
* @param int $digit Digit in the range 0-35
* @return string Single character corresponding to digit
*
* @throws \WpOrg\Requests\Exception On invalid digit (`idna.invalid_digit`)
*/
protected static function digit_to_char($digit) {
// @codeCoverageIgnoreStart
// As far as I know, this never happens, but still good to be sure.
if ($digit < 0 || $digit > 35) {
throw new Exception(sprintf('Invalid digit %d', $digit), 'idna.invalid_digit', $digit);
}
// @codeCoverageIgnoreEnd
$digits = 'abcdefghijklmnopqrstuvwxyz0123456789';
return substr($digits, $digit, 1);
}
/**
* Adapt the bias
*
* @link https://tools.ietf.org/html/rfc3492#section-6.1
* @param int $delta
* @param int $numpoints
* @param bool $firsttime
* @return int|float New bias
*
* function adapt(delta,numpoints,firsttime):
*/
protected static function adapt($delta, $numpoints, $firsttime) {
// if firsttime then let delta = delta div damp
if ($firsttime) {
$delta = floor($delta / self::BOOTSTRAP_DAMP);
} else {
// else let delta = delta div 2
$delta = floor($delta / 2);
}
// let delta = delta + (delta div numpoints)
$delta += floor($delta / $numpoints);
// let k = 0
$k = 0;
// while delta > ((base - tmin) * tmax) div 2 do begin
$max = floor(((self::BOOTSTRAP_BASE - self::BOOTSTRAP_TMIN) * self::BOOTSTRAP_TMAX) / 2);
while ($delta > $max) {
// let delta = delta div (base - tmin)
$delta = floor($delta / (self::BOOTSTRAP_BASE - self::BOOTSTRAP_TMIN));
// let k = k + base
$k += self::BOOTSTRAP_BASE;
} // end
// return k + (((base - tmin + 1) * delta) div (delta + skew))
return $k + floor(((self::BOOTSTRAP_BASE - self::BOOTSTRAP_TMIN + 1) * $delta) / ($delta + self::BOOTSTRAP_SKEW));
}
}
HookManager.php 0000604 00000001305 15133277060 0007447 0 ustar 00 <?php
/**
* Event dispatcher
*
* @package Requests\EventDispatcher
*/
namespace WpOrg\Requests;
/**
* Event dispatcher
*
* @package Requests\EventDispatcher
*/
interface HookManager {
/**
* Register a callback for a hook
*
* @param string $hook Hook name
* @param callable $callback Function/method to call on event
* @param int $priority Priority number. <0 is executed earlier, >0 is executed later
*/
public function register($hook, $callback, $priority = 0);
/**
* Dispatch a message
*
* @param string $hook Hook name
* @param array $parameters Parameters to pass to callbacks
* @return boolean Successfulness
*/
public function dispatch($hook, $parameters = []);
}
Ssl.php 0000604 00000012461 15133277060 0006022 0 ustar 00 <?php
/**
* SSL utilities for Requests
*
* @package Requests\Utilities
*/
namespace WpOrg\Requests;
use WpOrg\Requests\Exception\InvalidArgument;
use WpOrg\Requests\Utility\InputValidator;
/**
* SSL utilities for Requests
*
* Collection of utilities for working with and verifying SSL certificates.
*
* @package Requests\Utilities
*/
final class Ssl {
/**
* Verify the certificate against common name and subject alternative names
*
* Unfortunately, PHP doesn't check the certificate against the alternative
* names, leading things like 'https://www.github.com/' to be invalid.
*
* @link https://tools.ietf.org/html/rfc2818#section-3.1 RFC2818, Section 3.1
*
* @param string|Stringable $host Host name to verify against
* @param array $cert Certificate data from openssl_x509_parse()
* @return bool
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $host argument is not a string or a stringable object.
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $cert argument is not an array or array accessible.
*/
public static function verify_certificate($host, $cert) {
if (InputValidator::is_string_or_stringable($host) === false) {
throw InvalidArgument::create(1, '$host', 'string|Stringable', gettype($host));
}
if (InputValidator::has_array_access($cert) === false) {
throw InvalidArgument::create(2, '$cert', 'array|ArrayAccess', gettype($cert));
}
$has_dns_alt = false;
// Check the subjectAltName
if (!empty($cert['extensions']['subjectAltName'])) {
$altnames = explode(',', $cert['extensions']['subjectAltName']);
foreach ($altnames as $altname) {
$altname = trim($altname);
if (strpos($altname, 'DNS:') !== 0) {
continue;
}
$has_dns_alt = true;
// Strip the 'DNS:' prefix and trim whitespace
$altname = trim(substr($altname, 4));
// Check for a match
if (self::match_domain($host, $altname) === true) {
return true;
}
}
if ($has_dns_alt === true) {
return false;
}
}
// Fall back to checking the common name if we didn't get any dNSName
// alt names, as per RFC2818
if (!empty($cert['subject']['CN'])) {
// Check for a match
return (self::match_domain($host, $cert['subject']['CN']) === true);
}
return false;
}
/**
* Verify that a reference name is valid
*
* Verifies a dNSName for HTTPS usage, (almost) as per Firefox's rules:
* - Wildcards can only occur in a name with more than 3 components
* - Wildcards can only occur as the last character in the first
* component
* - Wildcards may be preceded by additional characters
*
* We modify these rules to be a bit stricter and only allow the wildcard
* character to be the full first component; that is, with the exclusion of
* the third rule.
*
* @param string|Stringable $reference Reference dNSName
* @return boolean Is the name valid?
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed argument is not a string or a stringable object.
*/
public static function verify_reference_name($reference) {
if (InputValidator::is_string_or_stringable($reference) === false) {
throw InvalidArgument::create(1, '$reference', 'string|Stringable', gettype($reference));
}
if ($reference === '') {
return false;
}
if (preg_match('`\s`', $reference) > 0) {
// Whitespace detected. This can never be a dNSName.
return false;
}
$parts = explode('.', $reference);
if ($parts !== array_filter($parts)) {
// DNSName cannot contain two dots next to each other.
return false;
}
// Check the first part of the name
$first = array_shift($parts);
if (strpos($first, '*') !== false) {
// Check that the wildcard is the full part
if ($first !== '*') {
return false;
}
// Check that we have at least 3 components (including first)
if (count($parts) < 2) {
return false;
}
}
// Check the remaining parts
foreach ($parts as $part) {
if (strpos($part, '*') !== false) {
return false;
}
}
// Nothing found, verified!
return true;
}
/**
* Match a hostname against a dNSName reference
*
* @param string|Stringable $host Requested host
* @param string|Stringable $reference dNSName to match against
* @return boolean Does the domain match?
* @throws \WpOrg\Requests\Exception\InvalidArgument When either of the passed arguments is not a string or a stringable object.
*/
public static function match_domain($host, $reference) {
if (InputValidator::is_string_or_stringable($host) === false) {
throw InvalidArgument::create(1, '$host', 'string|Stringable', gettype($host));
}
// Check if the reference is blocklisted first
if (self::verify_reference_name($reference) !== true) {
return false;
}
// Check for a direct match
if ((string) $host === (string) $reference) {
return true;
}
// Calculate the valid wildcard match if the host is not an IP address
// Also validates that the host has 3 parts or more, as per Firefox's ruleset,
// as a wildcard reference is only allowed with 3 parts or more, so the
// comparison will never match if host doesn't contain 3 parts or more as well.
if (ip2long($host) === false) {
$parts = explode('.', $host);
$parts[0] = '*';
$wildcard = implode('.', $parts);
if ($wildcard === (string) $reference) {
return true;
}
}
return false;
}
}
Auth/Basic.php 0000604 00000004755 15133277060 0007212 0 ustar 00 <?php
/**
* Basic Authentication provider
*
* @package Requests\Authentication
*/
namespace WpOrg\Requests\Auth;
use WpOrg\Requests\Auth;
use WpOrg\Requests\Exception\ArgumentCount;
use WpOrg\Requests\Exception\InvalidArgument;
use WpOrg\Requests\Hooks;
/**
* Basic Authentication provider
*
* Provides a handler for Basic HTTP authentication via the Authorization
* header.
*
* @package Requests\Authentication
*/
class Basic implements Auth {
/**
* Username
*
* @var string
*/
public $user;
/**
* Password
*
* @var string
*/
public $pass;
/**
* Constructor
*
* @since 2.0 Throws an `InvalidArgument` exception.
* @since 2.0 Throws an `ArgumentCount` exception instead of the Requests base `Exception.
*
* @param array|null $args Array of user and password. Must have exactly two elements
*
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed argument is not an array or null.
* @throws \WpOrg\Requests\Exception\ArgumentCount On incorrect number of array elements (`authbasicbadargs`).
*/
public function __construct($args = null) {
if (is_array($args)) {
if (count($args) !== 2) {
throw ArgumentCount::create('an array with exactly two elements', count($args), 'authbasicbadargs');
}
list($this->user, $this->pass) = $args;
return;
}
if ($args !== null) {
throw InvalidArgument::create(1, '$args', 'array|null', gettype($args));
}
}
/**
* Register the necessary callbacks
*
* @see \WpOrg\Requests\Auth\Basic::curl_before_send()
* @see \WpOrg\Requests\Auth\Basic::fsockopen_header()
* @param \WpOrg\Requests\Hooks $hooks Hook system
*/
public function register(Hooks $hooks) {
$hooks->register('curl.before_send', [$this, 'curl_before_send']);
$hooks->register('fsockopen.after_headers', [$this, 'fsockopen_header']);
}
/**
* Set cURL parameters before the data is sent
*
* @param resource|\CurlHandle $handle cURL handle
*/
public function curl_before_send(&$handle) {
curl_setopt($handle, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_setopt($handle, CURLOPT_USERPWD, $this->getAuthString());
}
/**
* Add extra headers to the request before sending
*
* @param string $out HTTP header string
*/
public function fsockopen_header(&$out) {
$out .= sprintf("Authorization: Basic %s\r\n", base64_encode($this->getAuthString()));
}
/**
* Get the authentication string (user:pass)
*
* @return string
*/
public function getAuthString() {
return $this->user . ':' . $this->pass;
}
}
Requests.php 0000604 00000102321 15133277060 0007067 0 ustar 00 <?php
/**
* Requests for PHP
*
* Inspired by Requests for Python.
*
* Based on concepts from SimplePie_File, RequestCore and WP_Http.
*
* @package Requests
*/
namespace WpOrg\Requests;
use WpOrg\Requests\Auth\Basic;
use WpOrg\Requests\Capability;
use WpOrg\Requests\Cookie\Jar;
use WpOrg\Requests\Exception;
use WpOrg\Requests\Exception\InvalidArgument;
use WpOrg\Requests\Hooks;
use WpOrg\Requests\IdnaEncoder;
use WpOrg\Requests\Iri;
use WpOrg\Requests\Proxy\Http;
use WpOrg\Requests\Response;
use WpOrg\Requests\Transport\Curl;
use WpOrg\Requests\Transport\Fsockopen;
use WpOrg\Requests\Utility\InputValidator;
/**
* Requests for PHP
*
* Inspired by Requests for Python.
*
* Based on concepts from SimplePie_File, RequestCore and WP_Http.
*
* @package Requests
*/
class Requests {
/**
* POST method
*
* @var string
*/
const POST = 'POST';
/**
* PUT method
*
* @var string
*/
const PUT = 'PUT';
/**
* GET method
*
* @var string
*/
const GET = 'GET';
/**
* HEAD method
*
* @var string
*/
const HEAD = 'HEAD';
/**
* DELETE method
*
* @var string
*/
const DELETE = 'DELETE';
/**
* OPTIONS method
*
* @var string
*/
const OPTIONS = 'OPTIONS';
/**
* TRACE method
*
* @var string
*/
const TRACE = 'TRACE';
/**
* PATCH method
*
* @link https://tools.ietf.org/html/rfc5789
* @var string
*/
const PATCH = 'PATCH';
/**
* Default size of buffer size to read streams
*
* @var integer
*/
const BUFFER_SIZE = 1160;
/**
* Option defaults.
*
* @see \WpOrg\Requests\Requests::get_default_options()
* @see \WpOrg\Requests\Requests::request() for values returned by this method
*
* @since 2.0.0
*
* @var array
*/
const OPTION_DEFAULTS = [
'timeout' => 10,
'connect_timeout' => 10,
'useragent' => 'php-requests/' . self::VERSION,
'protocol_version' => 1.1,
'redirected' => 0,
'redirects' => 10,
'follow_redirects' => true,
'blocking' => true,
'type' => self::GET,
'filename' => false,
'auth' => false,
'proxy' => false,
'cookies' => false,
'max_bytes' => false,
'idn' => true,
'hooks' => null,
'transport' => null,
'verify' => null,
'verifyname' => true,
];
/**
* Default supported Transport classes.
*
* @since 2.0.0
*
* @var array
*/
const DEFAULT_TRANSPORTS = [
Curl::class => Curl::class,
Fsockopen::class => Fsockopen::class,
];
/**
* Current version of Requests
*
* @var string
*/
const VERSION = '2.0.11';
/**
* Selected transport name
*
* Use {@see \WpOrg\Requests\Requests::get_transport()} instead
*
* @var array
*/
public static $transport = [];
/**
* Registered transport classes
*
* @var array
*/
protected static $transports = [];
/**
* Default certificate path.
*
* @see \WpOrg\Requests\Requests::get_certificate_path()
* @see \WpOrg\Requests\Requests::set_certificate_path()
*
* @var string
*/
protected static $certificate_path = __DIR__ . '/../certificates/cacert.pem';
/**
* All (known) valid deflate, gzip header magic markers.
*
* These markers relate to different compression levels.
*
* @link https://stackoverflow.com/a/43170354/482864 Marker source.
*
* @since 2.0.0
*
* @var array
*/
private static $magic_compression_headers = [
"\x1f\x8b" => true, // Gzip marker.
"\x78\x01" => true, // Zlib marker - level 1.
"\x78\x5e" => true, // Zlib marker - level 2 to 5.
"\x78\x9c" => true, // Zlib marker - level 6.
"\x78\xda" => true, // Zlib marker - level 7 to 9.
];
/**
* This is a static class, do not instantiate it
*
* @codeCoverageIgnore
*/
private function __construct() {}
/**
* Register a transport
*
* @param string $transport Transport class to add, must support the \WpOrg\Requests\Transport interface
*/
public static function add_transport($transport) {
if (empty(self::$transports)) {
self::$transports = self::DEFAULT_TRANSPORTS;
}
self::$transports[$transport] = $transport;
}
/**
* Get the fully qualified class name (FQCN) for a working transport.
*
* @param array<string, bool> $capabilities Optional. Associative array of capabilities to test against, i.e. `['<capability>' => true]`.
* @return string FQCN of the transport to use, or an empty string if no transport was
* found which provided the requested capabilities.
*/
protected static function get_transport_class(array $capabilities = []) {
// Caching code, don't bother testing coverage.
// @codeCoverageIgnoreStart
// Array of capabilities as a string to be used as an array key.
ksort($capabilities);
$cap_string = serialize($capabilities);
// Don't search for a transport if it's already been done for these $capabilities.
if (isset(self::$transport[$cap_string])) {
return self::$transport[$cap_string];
}
// Ensure we will not run this same check again later on.
self::$transport[$cap_string] = '';
// @codeCoverageIgnoreEnd
if (empty(self::$transports)) {
self::$transports = self::DEFAULT_TRANSPORTS;
}
// Find us a working transport.
foreach (self::$transports as $class) {
if (!class_exists($class)) {
continue;
}
$result = $class::test($capabilities);
if ($result === true) {
self::$transport[$cap_string] = $class;
break;
}
}
return self::$transport[$cap_string];
}
/**
* Get a working transport.
*
* @param array<string, bool> $capabilities Optional. Associative array of capabilities to test against, i.e. `['<capability>' => true]`.
* @return \WpOrg\Requests\Transport
* @throws \WpOrg\Requests\Exception If no valid transport is found (`notransport`).
*/
protected static function get_transport(array $capabilities = []) {
$class = self::get_transport_class($capabilities);
if ($class === '') {
throw new Exception('No working transports found', 'notransport', self::$transports);
}
return new $class();
}
/**
* Checks to see if we have a transport for the capabilities requested.
*
* Supported capabilities can be found in the {@see \WpOrg\Requests\Capability}
* interface as constants.
*
* Example usage:
* `Requests::has_capabilities([Capability::SSL => true])`.
*
* @param array<string, bool> $capabilities Optional. Associative array of capabilities to test against, i.e. `['<capability>' => true]`.
* @return bool Whether the transport has the requested capabilities.
*/
public static function has_capabilities(array $capabilities = []) {
return self::get_transport_class($capabilities) !== '';
}
/**#@+
* @see \WpOrg\Requests\Requests::request()
* @param string $url
* @param array $headers
* @param array $options
* @return \WpOrg\Requests\Response
*/
/**
* Send a GET request
*/
public static function get($url, $headers = [], $options = []) {
return self::request($url, $headers, null, self::GET, $options);
}
/**
* Send a HEAD request
*/
public static function head($url, $headers = [], $options = []) {
return self::request($url, $headers, null, self::HEAD, $options);
}
/**
* Send a DELETE request
*/
public static function delete($url, $headers = [], $options = []) {
return self::request($url, $headers, null, self::DELETE, $options);
}
/**
* Send a TRACE request
*/
public static function trace($url, $headers = [], $options = []) {
return self::request($url, $headers, null, self::TRACE, $options);
}
/**#@-*/
/**#@+
* @see \WpOrg\Requests\Requests::request()
* @param string $url
* @param array $headers
* @param array $data
* @param array $options
* @return \WpOrg\Requests\Response
*/
/**
* Send a POST request
*/
public static function post($url, $headers = [], $data = [], $options = []) {
return self::request($url, $headers, $data, self::POST, $options);
}
/**
* Send a PUT request
*/
public static function put($url, $headers = [], $data = [], $options = []) {
return self::request($url, $headers, $data, self::PUT, $options);
}
/**
* Send an OPTIONS request
*/
public static function options($url, $headers = [], $data = [], $options = []) {
return self::request($url, $headers, $data, self::OPTIONS, $options);
}
/**
* Send a PATCH request
*
* Note: Unlike {@see \WpOrg\Requests\Requests::post()} and {@see \WpOrg\Requests\Requests::put()},
* `$headers` is required, as the specification recommends that should send an ETag
*
* @link https://tools.ietf.org/html/rfc5789
*/
public static function patch($url, $headers, $data = [], $options = []) {
return self::request($url, $headers, $data, self::PATCH, $options);
}
/**#@-*/
/**
* Main interface for HTTP requests
*
* This method initiates a request and sends it via a transport before
* parsing.
*
* The `$options` parameter takes an associative array with the following
* options:
*
* - `timeout`: How long should we wait for a response?
* Note: for cURL, a minimum of 1 second applies, as DNS resolution
* operates at second-resolution only.
* (float, seconds with a millisecond precision, default: 10, example: 0.01)
* - `connect_timeout`: How long should we wait while trying to connect?
* (float, seconds with a millisecond precision, default: 10, example: 0.01)
* - `useragent`: Useragent to send to the server
* (string, default: php-requests/$version)
* - `follow_redirects`: Should we follow 3xx redirects?
* (boolean, default: true)
* - `redirects`: How many times should we redirect before erroring?
* (integer, default: 10)
* - `blocking`: Should we block processing on this request?
* (boolean, default: true)
* - `filename`: File to stream the body to instead.
* (string|boolean, default: false)
* - `auth`: Authentication handler or array of user/password details to use
* for Basic authentication
* (\WpOrg\Requests\Auth|array|boolean, default: false)
* - `proxy`: Proxy details to use for proxy by-passing and authentication
* (\WpOrg\Requests\Proxy|array|string|boolean, default: false)
* - `max_bytes`: Limit for the response body size.
* (integer|boolean, default: false)
* - `idn`: Enable IDN parsing
* (boolean, default: true)
* - `transport`: Custom transport. Either a class name, or a
* transport object. Defaults to the first working transport from
* {@see \WpOrg\Requests\Requests::getTransport()}
* (string|\WpOrg\Requests\Transport, default: {@see \WpOrg\Requests\Requests::getTransport()})
* - `hooks`: Hooks handler.
* (\WpOrg\Requests\HookManager, default: new WpOrg\Requests\Hooks())
* - `verify`: Should we verify SSL certificates? Allows passing in a custom
* certificate file as a string. (Using true uses the system-wide root
* certificate store instead, but this may have different behaviour
* across transports.)
* (string|boolean, default: certificates/cacert.pem)
* - `verifyname`: Should we verify the common name in the SSL certificate?
* (boolean, default: true)
* - `data_format`: How should we send the `$data` parameter?
* (string, one of 'query' or 'body', default: 'query' for
* HEAD/GET/DELETE, 'body' for POST/PUT/OPTIONS/PATCH)
*
* @param string|Stringable $url URL to request
* @param array $headers Extra headers to send with the request
* @param array|null $data Data to send either as a query string for GET/HEAD requests, or in the body for POST requests
* @param string $type HTTP request type (use Requests constants)
* @param array $options Options for the request (see description for more information)
* @return \WpOrg\Requests\Response
*
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $url argument is not a string or Stringable.
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $type argument is not a string.
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $options argument is not an array.
* @throws \WpOrg\Requests\Exception On invalid URLs (`nonhttp`)
*/
public static function request($url, $headers = [], $data = [], $type = self::GET, $options = []) {
if (InputValidator::is_string_or_stringable($url) === false) {
throw InvalidArgument::create(1, '$url', 'string|Stringable', gettype($url));
}
if (is_string($type) === false) {
throw InvalidArgument::create(4, '$type', 'string', gettype($type));
}
if (is_array($options) === false) {
throw InvalidArgument::create(5, '$options', 'array', gettype($options));
}
if (empty($options['type'])) {
$options['type'] = $type;
}
$options = array_merge(self::get_default_options(), $options);
self::set_defaults($url, $headers, $data, $type, $options);
$options['hooks']->dispatch('requests.before_request', [&$url, &$headers, &$data, &$type, &$options]);
if (!empty($options['transport'])) {
$transport = $options['transport'];
if (is_string($options['transport'])) {
$transport = new $transport();
}
} else {
$need_ssl = (stripos($url, 'https://') === 0);
$capabilities = [Capability::SSL => $need_ssl];
$transport = self::get_transport($capabilities);
}
$response = $transport->request($url, $headers, $data, $options);
$options['hooks']->dispatch('requests.before_parse', [&$response, $url, $headers, $data, $type, $options]);
return self::parse_response($response, $url, $headers, $data, $options);
}
/**
* Send multiple HTTP requests simultaneously
*
* The `$requests` parameter takes an associative or indexed array of
* request fields. The key of each request can be used to match up the
* request with the returned data, or with the request passed into your
* `multiple.request.complete` callback.
*
* The request fields value is an associative array with the following keys:
*
* - `url`: Request URL Same as the `$url` parameter to
* {@see \WpOrg\Requests\Requests::request()}
* (string, required)
* - `headers`: Associative array of header fields. Same as the `$headers`
* parameter to {@see \WpOrg\Requests\Requests::request()}
* (array, default: `array()`)
* - `data`: Associative array of data fields or a string. Same as the
* `$data` parameter to {@see \WpOrg\Requests\Requests::request()}
* (array|string, default: `array()`)
* - `type`: HTTP request type (use \WpOrg\Requests\Requests constants). Same as the `$type`
* parameter to {@see \WpOrg\Requests\Requests::request()}
* (string, default: `\WpOrg\Requests\Requests::GET`)
* - `cookies`: Associative array of cookie name to value, or cookie jar.
* (array|\WpOrg\Requests\Cookie\Jar)
*
* If the `$options` parameter is specified, individual requests will
* inherit options from it. This can be used to use a single hooking system,
* or set all the types to `\WpOrg\Requests\Requests::POST`, for example.
*
* In addition, the `$options` parameter takes the following global options:
*
* - `complete`: A callback for when a request is complete. Takes two
* parameters, a \WpOrg\Requests\Response/\WpOrg\Requests\Exception reference, and the
* ID from the request array (Note: this can also be overridden on a
* per-request basis, although that's a little silly)
* (callback)
*
* @param array $requests Requests data (see description for more information)
* @param array $options Global and default options (see {@see \WpOrg\Requests\Requests::request()})
* @return array Responses (either \WpOrg\Requests\Response or a \WpOrg\Requests\Exception object)
*
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $requests argument is not an array or iterable object with array access.
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $options argument is not an array.
*/
public static function request_multiple($requests, $options = []) {
if (InputValidator::has_array_access($requests) === false || InputValidator::is_iterable($requests) === false) {
throw InvalidArgument::create(1, '$requests', 'array|ArrayAccess&Traversable', gettype($requests));
}
if (is_array($options) === false) {
throw InvalidArgument::create(2, '$options', 'array', gettype($options));
}
$options = array_merge(self::get_default_options(true), $options);
if (!empty($options['hooks'])) {
$options['hooks']->register('transport.internal.parse_response', [static::class, 'parse_multiple']);
if (!empty($options['complete'])) {
$options['hooks']->register('multiple.request.complete', $options['complete']);
}
}
foreach ($requests as $id => &$request) {
if (!isset($request['headers'])) {
$request['headers'] = [];
}
if (!isset($request['data'])) {
$request['data'] = [];
}
if (!isset($request['type'])) {
$request['type'] = self::GET;
}
if (!isset($request['options'])) {
$request['options'] = $options;
$request['options']['type'] = $request['type'];
} else {
if (empty($request['options']['type'])) {
$request['options']['type'] = $request['type'];
}
$request['options'] = array_merge($options, $request['options']);
}
self::set_defaults($request['url'], $request['headers'], $request['data'], $request['type'], $request['options']);
// Ensure we only hook in once
if ($request['options']['hooks'] !== $options['hooks']) {
$request['options']['hooks']->register('transport.internal.parse_response', [static::class, 'parse_multiple']);
if (!empty($request['options']['complete'])) {
$request['options']['hooks']->register('multiple.request.complete', $request['options']['complete']);
}
}
}
unset($request);
if (!empty($options['transport'])) {
$transport = $options['transport'];
if (is_string($options['transport'])) {
$transport = new $transport();
}
} else {
$transport = self::get_transport();
}
$responses = $transport->request_multiple($requests, $options);
foreach ($responses as $id => &$response) {
// If our hook got messed with somehow, ensure we end up with the
// correct response
if (is_string($response)) {
$request = $requests[$id];
self::parse_multiple($response, $request);
$request['options']['hooks']->dispatch('multiple.request.complete', [&$response, $id]);
}
}
return $responses;
}
/**
* Get the default options
*
* @see \WpOrg\Requests\Requests::request() for values returned by this method
* @param boolean $multirequest Is this a multirequest?
* @return array Default option values
*/
protected static function get_default_options($multirequest = false) {
$defaults = static::OPTION_DEFAULTS;
$defaults['verify'] = self::$certificate_path;
if ($multirequest !== false) {
$defaults['complete'] = null;
}
return $defaults;
}
/**
* Get default certificate path.
*
* @return string Default certificate path.
*/
public static function get_certificate_path() {
return self::$certificate_path;
}
/**
* Set default certificate path.
*
* @param string|Stringable|bool $path Certificate path, pointing to a PEM file.
*
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $url argument is not a string, Stringable or boolean.
*/
public static function set_certificate_path($path) {
if (InputValidator::is_string_or_stringable($path) === false && is_bool($path) === false) {
throw InvalidArgument::create(1, '$path', 'string|Stringable|bool', gettype($path));
}
self::$certificate_path = $path;
}
/**
* Set the default values
*
* The $options parameter is updated with the results.
*
* @param string $url URL to request
* @param array $headers Extra headers to send with the request
* @param array|null $data Data to send either as a query string for GET/HEAD requests, or in the body for POST requests
* @param string $type HTTP request type
* @param array $options Options for the request
* @return void
*
* @throws \WpOrg\Requests\Exception When the $url is not an http(s) URL.
*/
protected static function set_defaults(&$url, &$headers, &$data, &$type, &$options) {
if (!preg_match('/^http(s)?:\/\//i', $url, $matches)) {
throw new Exception('Only HTTP(S) requests are handled.', 'nonhttp', $url);
}
if (empty($options['hooks'])) {
$options['hooks'] = new Hooks();
}
if (is_array($options['auth'])) {
$options['auth'] = new Basic($options['auth']);
}
if ($options['auth'] !== false) {
$options['auth']->register($options['hooks']);
}
if (is_string($options['proxy']) || is_array($options['proxy'])) {
$options['proxy'] = new Http($options['proxy']);
}
if ($options['proxy'] !== false) {
$options['proxy']->register($options['hooks']);
}
if (is_array($options['cookies'])) {
$options['cookies'] = new Jar($options['cookies']);
} elseif (empty($options['cookies'])) {
$options['cookies'] = new Jar();
}
if ($options['cookies'] !== false) {
$options['cookies']->register($options['hooks']);
}
if ($options['idn'] !== false) {
$iri = new Iri($url);
$iri->host = IdnaEncoder::encode($iri->ihost);
$url = $iri->uri;
}
// Massage the type to ensure we support it.
$type = strtoupper($type);
if (!isset($options['data_format'])) {
if (in_array($type, [self::HEAD, self::GET, self::DELETE], true)) {
$options['data_format'] = 'query';
} else {
$options['data_format'] = 'body';
}
}
}
/**
* HTTP response parser
*
* @param string $headers Full response text including headers and body
* @param string $url Original request URL
* @param array $req_headers Original $headers array passed to {@link request()}, in case we need to follow redirects
* @param array $req_data Original $data array passed to {@link request()}, in case we need to follow redirects
* @param array $options Original $options array passed to {@link request()}, in case we need to follow redirects
* @return \WpOrg\Requests\Response
*
* @throws \WpOrg\Requests\Exception On missing head/body separator (`requests.no_crlf_separator`)
* @throws \WpOrg\Requests\Exception On missing head/body separator (`noversion`)
* @throws \WpOrg\Requests\Exception On missing head/body separator (`toomanyredirects`)
*/
protected static function parse_response($headers, $url, $req_headers, $req_data, $options) {
$return = new Response();
if (!$options['blocking']) {
return $return;
}
$return->raw = $headers;
$return->url = (string) $url;
$return->body = '';
if (!$options['filename']) {
$pos = strpos($headers, "\r\n\r\n");
if ($pos === false) {
// Crap!
throw new Exception('Missing header/body separator', 'requests.no_crlf_separator');
}
$headers = substr($return->raw, 0, $pos);
// Headers will always be separated from the body by two new lines - `\n\r\n\r`.
$body = substr($return->raw, $pos + 4);
if (!empty($body)) {
$return->body = $body;
}
}
// Pretend CRLF = LF for compatibility (RFC 2616, section 19.3)
$headers = str_replace("\r\n", "\n", $headers);
// Unfold headers (replace [CRLF] 1*( SP | HT ) with SP) as per RFC 2616 (section 2.2)
$headers = preg_replace('/\n[ \t]/', ' ', $headers);
$headers = explode("\n", $headers);
preg_match('#^HTTP/(1\.\d)[ \t]+(\d+)#i', array_shift($headers), $matches);
if (empty($matches)) {
throw new Exception('Response could not be parsed', 'noversion', $headers);
}
$return->protocol_version = (float) $matches[1];
$return->status_code = (int) $matches[2];
if ($return->status_code >= 200 && $return->status_code < 300) {
$return->success = true;
}
foreach ($headers as $header) {
list($key, $value) = explode(':', $header, 2);
$value = trim($value);
preg_replace('#(\s+)#i', ' ', $value);
$return->headers[$key] = $value;
}
if (isset($return->headers['transfer-encoding'])) {
$return->body = self::decode_chunked($return->body);
unset($return->headers['transfer-encoding']);
}
if (isset($return->headers['content-encoding'])) {
$return->body = self::decompress($return->body);
}
//fsockopen and cURL compatibility
if (isset($return->headers['connection'])) {
unset($return->headers['connection']);
}
$options['hooks']->dispatch('requests.before_redirect_check', [&$return, $req_headers, $req_data, $options]);
if ($return->is_redirect() && $options['follow_redirects'] === true) {
if (isset($return->headers['location']) && $options['redirected'] < $options['redirects']) {
if ($return->status_code === 303) {
$options['type'] = self::GET;
}
$options['redirected']++;
$location = $return->headers['location'];
if (strpos($location, 'http://') !== 0 && strpos($location, 'https://') !== 0) {
// relative redirect, for compatibility make it absolute
$location = Iri::absolutize($url, $location);
$location = $location->uri;
}
$hook_args = [
&$location,
&$req_headers,
&$req_data,
&$options,
$return,
];
$options['hooks']->dispatch('requests.before_redirect', $hook_args);
$redirected = self::request($location, $req_headers, $req_data, $options['type'], $options);
$redirected->history[] = $return;
return $redirected;
} elseif ($options['redirected'] >= $options['redirects']) {
throw new Exception('Too many redirects', 'toomanyredirects', $return);
}
}
$return->redirects = $options['redirected'];
$options['hooks']->dispatch('requests.after_request', [&$return, $req_headers, $req_data, $options]);
return $return;
}
/**
* Callback for `transport.internal.parse_response`
*
* Internal use only. Converts a raw HTTP response to a \WpOrg\Requests\Response
* while still executing a multiple request.
*
* `$response` is either set to a \WpOrg\Requests\Response instance, or a \WpOrg\Requests\Exception object
*
* @param string $response Full response text including headers and body (will be overwritten with Response instance)
* @param array $request Request data as passed into {@see \WpOrg\Requests\Requests::request_multiple()}
* @return void
*/
public static function parse_multiple(&$response, $request) {
try {
$url = $request['url'];
$headers = $request['headers'];
$data = $request['data'];
$options = $request['options'];
$response = self::parse_response($response, $url, $headers, $data, $options);
} catch (Exception $e) {
$response = $e;
}
}
/**
* Decoded a chunked body as per RFC 2616
*
* @link https://tools.ietf.org/html/rfc2616#section-3.6.1
* @param string $data Chunked body
* @return string Decoded body
*/
protected static function decode_chunked($data) {
if (!preg_match('/^([0-9a-f]+)(?:;(?:[\w-]*)(?:=(?:(?:[\w-]*)*|"(?:[^\r\n])*"))?)*\r\n/i', trim($data))) {
return $data;
}
$decoded = '';
$encoded = $data;
while (true) {
$is_chunked = (bool) preg_match('/^([0-9a-f]+)(?:;(?:[\w-]*)(?:=(?:(?:[\w-]*)*|"(?:[^\r\n])*"))?)*\r\n/i', $encoded, $matches);
if (!$is_chunked) {
// Looks like it's not chunked after all
return $data;
}
$length = hexdec(trim($matches[1]));
if ($length === 0) {
// Ignore trailer headers
return $decoded;
}
$chunk_length = strlen($matches[0]);
$decoded .= substr($encoded, $chunk_length, $length);
$encoded = substr($encoded, $chunk_length + $length + 2);
if (trim($encoded) === '0' || empty($encoded)) {
return $decoded;
}
}
// We'll never actually get down here
// @codeCoverageIgnoreStart
}
// @codeCoverageIgnoreEnd
/**
* Convert a key => value array to a 'key: value' array for headers
*
* @param iterable $dictionary Dictionary of header values
* @return array List of headers
*
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed argument is not iterable.
*/
public static function flatten($dictionary) {
if (InputValidator::is_iterable($dictionary) === false) {
throw InvalidArgument::create(1, '$dictionary', 'iterable', gettype($dictionary));
}
$return = [];
foreach ($dictionary as $key => $value) {
$return[] = sprintf('%s: %s', $key, $value);
}
return $return;
}
/**
* Decompress an encoded body
*
* Implements gzip, compress and deflate. Guesses which it is by attempting
* to decode.
*
* @param string $data Compressed data in one of the above formats
* @return string Decompressed string
*
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed argument is not a string.
*/
public static function decompress($data) {
if (is_string($data) === false) {
throw InvalidArgument::create(1, '$data', 'string', gettype($data));
}
if (trim($data) === '') {
// Empty body does not need further processing.
return $data;
}
$marker = substr($data, 0, 2);
if (!isset(self::$magic_compression_headers[$marker])) {
// Not actually compressed. Probably cURL ruining this for us.
return $data;
}
if (function_exists('gzdecode')) {
$decoded = @gzdecode($data);
if ($decoded !== false) {
return $decoded;
}
}
if (function_exists('gzinflate')) {
$decoded = @gzinflate($data);
if ($decoded !== false) {
return $decoded;
}
}
$decoded = self::compatible_gzinflate($data);
if ($decoded !== false) {
return $decoded;
}
if (function_exists('gzuncompress')) {
$decoded = @gzuncompress($data);
if ($decoded !== false) {
return $decoded;
}
}
return $data;
}
/**
* Decompression of deflated string while staying compatible with the majority of servers.
*
* Certain Servers will return deflated data with headers which PHP's gzinflate()
* function cannot handle out of the box. The following function has been created from
* various snippets on the gzinflate() PHP documentation.
*
* Warning: Magic numbers within. Due to the potential different formats that the compressed
* data may be returned in, some "magic offsets" are needed to ensure proper decompression
* takes place. For a simple progmatic way to determine the magic offset in use, see:
* https://core.trac.wordpress.org/ticket/18273
*
* @since 1.6.0
* @link https://core.trac.wordpress.org/ticket/18273
* @link https://www.php.net/gzinflate#70875
* @link https://www.php.net/gzinflate#77336
*
* @param string $gz_data String to decompress.
* @return string|bool False on failure.
*
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed argument is not a string.
*/
public static function compatible_gzinflate($gz_data) {
if (is_string($gz_data) === false) {
throw InvalidArgument::create(1, '$gz_data', 'string', gettype($gz_data));
}
if (trim($gz_data) === '') {
return false;
}
// Compressed data might contain a full zlib header, if so strip it for
// gzinflate()
if (substr($gz_data, 0, 3) === "\x1f\x8b\x08") {
$i = 10;
$flg = ord(substr($gz_data, 3, 1));
if ($flg > 0) {
if ($flg & 4) {
list($xlen) = unpack('v', substr($gz_data, $i, 2));
$i += 2 + $xlen;
}
if ($flg & 8) {
$i = strpos($gz_data, "\0", $i) + 1;
}
if ($flg & 16) {
$i = strpos($gz_data, "\0", $i) + 1;
}
if ($flg & 2) {
$i += 2;
}
}
$decompressed = self::compatible_gzinflate(substr($gz_data, $i));
if ($decompressed !== false) {
return $decompressed;
}
}
// If the data is Huffman Encoded, we must first strip the leading 2
// byte Huffman marker for gzinflate()
// The response is Huffman coded by many compressors such as
// java.util.zip.Deflater, Ruby's Zlib::Deflate, and .NET's
// System.IO.Compression.DeflateStream.
//
// See https://decompres.blogspot.com/ for a quick explanation of this
// data type
$huffman_encoded = false;
// low nibble of first byte should be 0x08
list(, $first_nibble) = unpack('h', $gz_data);
// First 2 bytes should be divisible by 0x1F
list(, $first_two_bytes) = unpack('n', $gz_data);
if ($first_nibble === 0x08 && ($first_two_bytes % 0x1F) === 0) {
$huffman_encoded = true;
}
if ($huffman_encoded) {
$decompressed = @gzinflate(substr($gz_data, 2));
if ($decompressed !== false) {
return $decompressed;
}
}
if (substr($gz_data, 0, 4) === "\x50\x4b\x03\x04") {
// ZIP file format header
// Offset 6: 2 bytes, General-purpose field
// Offset 26: 2 bytes, filename length
// Offset 28: 2 bytes, optional field length
// Offset 30: Filename field, followed by optional field, followed
// immediately by data
list(, $general_purpose_flag) = unpack('v', substr($gz_data, 6, 2));
// If the file has been compressed on the fly, 0x08 bit is set of
// the general purpose field. We can use this to differentiate
// between a compressed document, and a ZIP file
$zip_compressed_on_the_fly = ((0x08 & $general_purpose_flag) === 0x08);
if (!$zip_compressed_on_the_fly) {
// Don't attempt to decode a compressed zip file
return $gz_data;
}
// Determine the first byte of data, based on the above ZIP header
// offsets:
$first_file_start = array_sum(unpack('v2', substr($gz_data, 26, 4)));
$decompressed = @gzinflate(substr($gz_data, 30 + $first_file_start));
if ($decompressed !== false) {
return $decompressed;
}
return false;
}
// Finally fall back to straight gzinflate
$decompressed = @gzinflate($gz_data);
if ($decompressed !== false) {
return $decompressed;
}
// Fallback for all above failing, not expected, but included for
// debugging and preventing regressions and to track stats
$decompressed = @gzinflate(substr($gz_data, 2));
if ($decompressed !== false) {
return $decompressed;
}
return false;
}
}
Port.php 0000604 00000002741 15133277060 0006205 0 ustar 00 <?php
/**
* Port utilities for Requests
*
* @package Requests\Utilities
* @since 2.0.0
*/
namespace WpOrg\Requests;
use WpOrg\Requests\Exception;
use WpOrg\Requests\Exception\InvalidArgument;
/**
* Find the correct port depending on the Request type.
*
* @package Requests\Utilities
* @since 2.0.0
*/
final class Port {
/**
* Port to use with Acap requests.
*
* @var int
*/
const ACAP = 674;
/**
* Port to use with Dictionary requests.
*
* @var int
*/
const DICT = 2628;
/**
* Port to use with HTTP requests.
*
* @var int
*/
const HTTP = 80;
/**
* Port to use with HTTP over SSL requests.
*
* @var int
*/
const HTTPS = 443;
/**
* Retrieve the port number to use.
*
* @param string $type Request type.
* The following requests types are supported:
* 'acap', 'dict', 'http' and 'https'.
*
* @return int
*
* @throws \WpOrg\Requests\Exception\InvalidArgument When a non-string input has been passed.
* @throws \WpOrg\Requests\Exception When a non-supported port is requested ('portnotsupported').
*/
public static function get($type) {
if (!is_string($type)) {
throw InvalidArgument::create(1, '$type', 'string', gettype($type));
}
$type = strtoupper($type);
if (!defined("self::{$type}")) {
$message = sprintf('Invalid port type (%s) passed', $type);
throw new Exception($message, 'portnotsupported');
}
return constant("self::{$type}");
}
}
Proxy/Http.php 0000604 00000010171 15133277060 0007315 0 ustar 00 <?php
/**
* HTTP Proxy connection interface
*
* @package Requests\Proxy
* @since 1.6
*/
namespace WpOrg\Requests\Proxy;
use WpOrg\Requests\Exception\ArgumentCount;
use WpOrg\Requests\Exception\InvalidArgument;
use WpOrg\Requests\Hooks;
use WpOrg\Requests\Proxy;
/**
* HTTP Proxy connection interface
*
* Provides a handler for connection via an HTTP proxy
*
* @package Requests\Proxy
* @since 1.6
*/
final class Http implements Proxy {
/**
* Proxy host and port
*
* Notation: "host:port" (eg 127.0.0.1:8080 or someproxy.com:3128)
*
* @var string
*/
public $proxy;
/**
* Username
*
* @var string
*/
public $user;
/**
* Password
*
* @var string
*/
public $pass;
/**
* Do we need to authenticate? (ie username & password have been provided)
*
* @var boolean
*/
public $use_authentication;
/**
* Constructor
*
* @since 1.6
*
* @param array|string|null $args Proxy as a string or an array of proxy, user and password.
* When passed as an array, must have exactly one (proxy)
* or three elements (proxy, user, password).
*
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed argument is not an array, a string or null.
* @throws \WpOrg\Requests\Exception\ArgumentCount On incorrect number of arguments (`proxyhttpbadargs`)
*/
public function __construct($args = null) {
if (is_string($args)) {
$this->proxy = $args;
} elseif (is_array($args)) {
if (count($args) === 1) {
list($this->proxy) = $args;
} elseif (count($args) === 3) {
list($this->proxy, $this->user, $this->pass) = $args;
$this->use_authentication = true;
} else {
throw ArgumentCount::create(
'an array with exactly one element or exactly three elements',
count($args),
'proxyhttpbadargs'
);
}
} elseif ($args !== null) {
throw InvalidArgument::create(1, '$args', 'array|string|null', gettype($args));
}
}
/**
* Register the necessary callbacks
*
* @since 1.6
* @see \WpOrg\Requests\Proxy\Http::curl_before_send()
* @see \WpOrg\Requests\Proxy\Http::fsockopen_remote_socket()
* @see \WpOrg\Requests\Proxy\Http::fsockopen_remote_host_path()
* @see \WpOrg\Requests\Proxy\Http::fsockopen_header()
* @param \WpOrg\Requests\Hooks $hooks Hook system
*/
public function register(Hooks $hooks) {
$hooks->register('curl.before_send', [$this, 'curl_before_send']);
$hooks->register('fsockopen.remote_socket', [$this, 'fsockopen_remote_socket']);
$hooks->register('fsockopen.remote_host_path', [$this, 'fsockopen_remote_host_path']);
if ($this->use_authentication) {
$hooks->register('fsockopen.after_headers', [$this, 'fsockopen_header']);
}
}
/**
* Set cURL parameters before the data is sent
*
* @since 1.6
* @param resource|\CurlHandle $handle cURL handle
*/
public function curl_before_send(&$handle) {
curl_setopt($handle, CURLOPT_PROXYTYPE, CURLPROXY_HTTP);
curl_setopt($handle, CURLOPT_PROXY, $this->proxy);
if ($this->use_authentication) {
curl_setopt($handle, CURLOPT_PROXYAUTH, CURLAUTH_ANY);
curl_setopt($handle, CURLOPT_PROXYUSERPWD, $this->get_auth_string());
}
}
/**
* Alter remote socket information before opening socket connection
*
* @since 1.6
* @param string $remote_socket Socket connection string
*/
public function fsockopen_remote_socket(&$remote_socket) {
$remote_socket = $this->proxy;
}
/**
* Alter remote path before getting stream data
*
* @since 1.6
* @param string $path Path to send in HTTP request string ("GET ...")
* @param string $url Full URL we're requesting
*/
public function fsockopen_remote_host_path(&$path, $url) {
$path = $url;
}
/**
* Add extra headers to the request before sending
*
* @since 1.6
* @param string $out HTTP header string
*/
public function fsockopen_header(&$out) {
$out .= sprintf("Proxy-Authorization: Basic %s\r\n", base64_encode($this->get_auth_string()));
}
/**
* Get the authentication string (user:pass)
*
* @since 1.6
* @return string
*/
public function get_auth_string() {
return $this->user . ':' . $this->pass;
}
}
Hooks.php 0000604 00000005730 15133277060 0006345 0 ustar 00 <?php
/**
* Handles adding and dispatching events
*
* @package Requests\EventDispatcher
*/
namespace WpOrg\Requests;
use WpOrg\Requests\Exception\InvalidArgument;
use WpOrg\Requests\HookManager;
use WpOrg\Requests\Utility\InputValidator;
/**
* Handles adding and dispatching events
*
* @package Requests\EventDispatcher
*/
class Hooks implements HookManager {
/**
* Registered callbacks for each hook
*
* @var array
*/
protected $hooks = [];
/**
* Register a callback for a hook
*
* @param string $hook Hook name
* @param callable $callback Function/method to call on event
* @param int $priority Priority number. <0 is executed earlier, >0 is executed later
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $hook argument is not a string.
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $callback argument is not callable.
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $priority argument is not an integer.
*/
public function register($hook, $callback, $priority = 0) {
if (is_string($hook) === false) {
throw InvalidArgument::create(1, '$hook', 'string', gettype($hook));
}
if (is_callable($callback) === false) {
throw InvalidArgument::create(2, '$callback', 'callable', gettype($callback));
}
if (InputValidator::is_numeric_array_key($priority) === false) {
throw InvalidArgument::create(3, '$priority', 'integer', gettype($priority));
}
if (!isset($this->hooks[$hook])) {
$this->hooks[$hook] = [
$priority => [],
];
} elseif (!isset($this->hooks[$hook][$priority])) {
$this->hooks[$hook][$priority] = [];
}
$this->hooks[$hook][$priority][] = $callback;
}
/**
* Dispatch a message
*
* @param string $hook Hook name
* @param array $parameters Parameters to pass to callbacks
* @return boolean Successfulness
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $hook argument is not a string.
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $parameters argument is not an array.
*/
public function dispatch($hook, $parameters = []) {
if (is_string($hook) === false) {
throw InvalidArgument::create(1, '$hook', 'string', gettype($hook));
}
// Check strictly against array, as Array* objects don't work in combination with `call_user_func_array()`.
if (is_array($parameters) === false) {
throw InvalidArgument::create(2, '$parameters', 'array', gettype($parameters));
}
if (empty($this->hooks[$hook])) {
return false;
}
if (!empty($parameters)) {
// Strip potential keys from the array to prevent them being interpreted as parameter names in PHP 8.0.
$parameters = array_values($parameters);
}
ksort($this->hooks[$hook]);
foreach ($this->hooks[$hook] as $priority => $hooked) {
foreach ($hooked as $callback) {
$callback(...$parameters);
}
}
return true;
}
public function __wakeup() {
throw new \LogicException( __CLASS__ . ' should never be unserialized' );
}
}
Cookie.php 0000604 00000036035 15133277060 0006475 0 ustar 00 <?php
/**
* Cookie storage object
*
* @package Requests\Cookies
*/
namespace WpOrg\Requests;
use WpOrg\Requests\Exception\InvalidArgument;
use WpOrg\Requests\Iri;
use WpOrg\Requests\Response\Headers;
use WpOrg\Requests\Utility\CaseInsensitiveDictionary;
use WpOrg\Requests\Utility\InputValidator;
/**
* Cookie storage object
*
* @package Requests\Cookies
*/
class Cookie {
/**
* Cookie name.
*
* @var string
*/
public $name;
/**
* Cookie value.
*
* @var string
*/
public $value;
/**
* Cookie attributes
*
* Valid keys are `'path'`, `'domain'`, `'expires'`, `'max-age'`, `'secure'` and
* `'httponly'`.
*
* @var \WpOrg\Requests\Utility\CaseInsensitiveDictionary|array Array-like object
*/
public $attributes = [];
/**
* Cookie flags
*
* Valid keys are `'creation'`, `'last-access'`, `'persistent'` and `'host-only'`.
*
* @var array
*/
public $flags = [];
/**
* Reference time for relative calculations
*
* This is used in place of `time()` when calculating Max-Age expiration and
* checking time validity.
*
* @var int
*/
public $reference_time = 0;
/**
* Create a new cookie object
*
* @param string $name The name of the cookie.
* @param string $value The value for the cookie.
* @param array|\WpOrg\Requests\Utility\CaseInsensitiveDictionary $attributes Associative array of attribute data
* @param array $flags The flags for the cookie.
* Valid keys are `'creation'`, `'last-access'`,
* `'persistent'` and `'host-only'`.
* @param int|null $reference_time Reference time for relative calculations.
*
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $name argument is not a string.
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $value argument is not a string.
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $attributes argument is not an array or iterable object with array access.
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $flags argument is not an array.
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $reference_time argument is not an integer or null.
*/
public function __construct($name, $value, $attributes = [], $flags = [], $reference_time = null) {
if (is_string($name) === false) {
throw InvalidArgument::create(1, '$name', 'string', gettype($name));
}
if (is_string($value) === false) {
throw InvalidArgument::create(2, '$value', 'string', gettype($value));
}
if (InputValidator::has_array_access($attributes) === false || InputValidator::is_iterable($attributes) === false) {
throw InvalidArgument::create(3, '$attributes', 'array|ArrayAccess&Traversable', gettype($attributes));
}
if (is_array($flags) === false) {
throw InvalidArgument::create(4, '$flags', 'array', gettype($flags));
}
if ($reference_time !== null && is_int($reference_time) === false) {
throw InvalidArgument::create(5, '$reference_time', 'integer|null', gettype($reference_time));
}
$this->name = $name;
$this->value = $value;
$this->attributes = $attributes;
$default_flags = [
'creation' => time(),
'last-access' => time(),
'persistent' => false,
'host-only' => true,
];
$this->flags = array_merge($default_flags, $flags);
$this->reference_time = time();
if ($reference_time !== null) {
$this->reference_time = $reference_time;
}
$this->normalize();
}
/**
* Get the cookie value
*
* Attributes and other data can be accessed via methods.
*/
public function __toString() {
return $this->value;
}
/**
* Check if a cookie is expired.
*
* Checks the age against $this->reference_time to determine if the cookie
* is expired.
*
* @return boolean True if expired, false if time is valid.
*/
public function is_expired() {
// RFC6265, s. 4.1.2.2:
// If a cookie has both the Max-Age and the Expires attribute, the Max-
// Age attribute has precedence and controls the expiration date of the
// cookie.
if (isset($this->attributes['max-age'])) {
$max_age = $this->attributes['max-age'];
return $max_age < $this->reference_time;
}
if (isset($this->attributes['expires'])) {
$expires = $this->attributes['expires'];
return $expires < $this->reference_time;
}
return false;
}
/**
* Check if a cookie is valid for a given URI
*
* @param \WpOrg\Requests\Iri $uri URI to check
* @return boolean Whether the cookie is valid for the given URI
*/
public function uri_matches(Iri $uri) {
if (!$this->domain_matches($uri->host)) {
return false;
}
if (!$this->path_matches($uri->path)) {
return false;
}
return empty($this->attributes['secure']) || $uri->scheme === 'https';
}
/**
* Check if a cookie is valid for a given domain
*
* @param string $domain Domain to check
* @return boolean Whether the cookie is valid for the given domain
*/
public function domain_matches($domain) {
if (is_string($domain) === false) {
return false;
}
if (!isset($this->attributes['domain'])) {
// Cookies created manually; cookies created by Requests will set
// the domain to the requested domain
return true;
}
$cookie_domain = $this->attributes['domain'];
if ($cookie_domain === $domain) {
// The cookie domain and the passed domain are identical.
return true;
}
// If the cookie is marked as host-only and we don't have an exact
// match, reject the cookie
if ($this->flags['host-only'] === true) {
return false;
}
if (strlen($domain) <= strlen($cookie_domain)) {
// For obvious reasons, the cookie domain cannot be a suffix if the passed domain
// is shorter than the cookie domain
return false;
}
if (substr($domain, -1 * strlen($cookie_domain)) !== $cookie_domain) {
// The cookie domain should be a suffix of the passed domain.
return false;
}
$prefix = substr($domain, 0, strlen($domain) - strlen($cookie_domain));
if (substr($prefix, -1) !== '.') {
// The last character of the passed domain that is not included in the
// domain string should be a %x2E (".") character.
return false;
}
// The passed domain should be a host name (i.e., not an IP address).
return !preg_match('#^(.+\.)\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$#', $domain);
}
/**
* Check if a cookie is valid for a given path
*
* From the path-match check in RFC 6265 section 5.1.4
*
* @param string $request_path Path to check
* @return boolean Whether the cookie is valid for the given path
*/
public function path_matches($request_path) {
if (empty($request_path)) {
// Normalize empty path to root
$request_path = '/';
}
if (!isset($this->attributes['path'])) {
// Cookies created manually; cookies created by Requests will set
// the path to the requested path
return true;
}
if (is_scalar($request_path) === false) {
return false;
}
$cookie_path = $this->attributes['path'];
if ($cookie_path === $request_path) {
// The cookie-path and the request-path are identical.
return true;
}
if (strlen($request_path) > strlen($cookie_path) && substr($request_path, 0, strlen($cookie_path)) === $cookie_path) {
if (substr($cookie_path, -1) === '/') {
// The cookie-path is a prefix of the request-path, and the last
// character of the cookie-path is %x2F ("/").
return true;
}
if (substr($request_path, strlen($cookie_path), 1) === '/') {
// The cookie-path is a prefix of the request-path, and the
// first character of the request-path that is not included in
// the cookie-path is a %x2F ("/") character.
return true;
}
}
return false;
}
/**
* Normalize cookie and attributes
*
* @return boolean Whether the cookie was successfully normalized
*/
public function normalize() {
foreach ($this->attributes as $key => $value) {
$orig_value = $value;
if (is_string($key)) {
$value = $this->normalize_attribute($key, $value);
}
if ($value === null) {
unset($this->attributes[$key]);
continue;
}
if ($value !== $orig_value) {
$this->attributes[$key] = $value;
}
}
return true;
}
/**
* Parse an individual cookie attribute
*
* Handles parsing individual attributes from the cookie values.
*
* @param string $name Attribute name
* @param string|int|bool $value Attribute value (string/integer value, or true if empty/flag)
* @return mixed Value if available, or null if the attribute value is invalid (and should be skipped)
*/
protected function normalize_attribute($name, $value) {
switch (strtolower($name)) {
case 'expires':
// Expiration parsing, as per RFC 6265 section 5.2.1
if (is_int($value)) {
return $value;
}
$expiry_time = strtotime($value);
if ($expiry_time === false) {
return null;
}
return $expiry_time;
case 'max-age':
// Expiration parsing, as per RFC 6265 section 5.2.2
if (is_int($value)) {
return $value;
}
// Check that we have a valid age
if (!preg_match('/^-?\d+$/', $value)) {
return null;
}
$delta_seconds = (int) $value;
if ($delta_seconds <= 0) {
$expiry_time = 0;
} else {
$expiry_time = $this->reference_time + $delta_seconds;
}
return $expiry_time;
case 'domain':
// Domains are not required as per RFC 6265 section 5.2.3
if (empty($value)) {
return null;
}
// Domain normalization, as per RFC 6265 section 5.2.3
if ($value[0] === '.') {
$value = substr($value, 1);
}
return $value;
default:
return $value;
}
}
/**
* Format a cookie for a Cookie header
*
* This is used when sending cookies to a server.
*
* @return string Cookie formatted for Cookie header
*/
public function format_for_header() {
return sprintf('%s=%s', $this->name, $this->value);
}
/**
* Format a cookie for a Set-Cookie header
*
* This is used when sending cookies to clients. This isn't really
* applicable to client-side usage, but might be handy for debugging.
*
* @return string Cookie formatted for Set-Cookie header
*/
public function format_for_set_cookie() {
$header_value = $this->format_for_header();
if (!empty($this->attributes)) {
$parts = [];
foreach ($this->attributes as $key => $value) {
// Ignore non-associative attributes
if (is_numeric($key)) {
$parts[] = $value;
} else {
$parts[] = sprintf('%s=%s', $key, $value);
}
}
$header_value .= '; ' . implode('; ', $parts);
}
return $header_value;
}
/**
* Parse a cookie string into a cookie object
*
* Based on Mozilla's parsing code in Firefox and related projects, which
* is an intentional deviation from RFC 2109 and RFC 2616. RFC 6265
* specifies some of this handling, but not in a thorough manner.
*
* @param string $cookie_header Cookie header value (from a Set-Cookie header)
* @param string $name
* @param int|null $reference_time
* @return \WpOrg\Requests\Cookie Parsed cookie object
*
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $cookie_header argument is not a string.
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $name argument is not a string.
*/
public static function parse($cookie_header, $name = '', $reference_time = null) {
if (is_string($cookie_header) === false) {
throw InvalidArgument::create(1, '$cookie_header', 'string', gettype($cookie_header));
}
if (is_string($name) === false) {
throw InvalidArgument::create(2, '$name', 'string', gettype($name));
}
$parts = explode(';', $cookie_header);
$kvparts = array_shift($parts);
if (!empty($name)) {
$value = $cookie_header;
} elseif (strpos($kvparts, '=') === false) {
// Some sites might only have a value without the equals separator.
// Deviate from RFC 6265 and pretend it was actually a blank name
// (`=foo`)
//
// https://bugzilla.mozilla.org/show_bug.cgi?id=169091
$name = '';
$value = $kvparts;
} else {
list($name, $value) = explode('=', $kvparts, 2);
}
$name = trim($name);
$value = trim($value);
// Attribute keys are handled case-insensitively
$attributes = new CaseInsensitiveDictionary();
if (!empty($parts)) {
foreach ($parts as $part) {
if (strpos($part, '=') === false) {
$part_key = $part;
$part_value = true;
} else {
list($part_key, $part_value) = explode('=', $part, 2);
$part_value = trim($part_value);
}
$part_key = trim($part_key);
$attributes[$part_key] = $part_value;
}
}
return new static($name, $value, $attributes, [], $reference_time);
}
/**
* Parse all Set-Cookie headers from request headers
*
* @param \WpOrg\Requests\Response\Headers $headers Headers to parse from
* @param \WpOrg\Requests\Iri|null $origin URI for comparing cookie origins
* @param int|null $time Reference time for expiration calculation
* @return array
*
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $origin argument is not null or an instance of the Iri class.
*/
public static function parse_from_headers(Headers $headers, $origin = null, $time = null) {
$cookie_headers = $headers->getValues('Set-Cookie');
if (empty($cookie_headers)) {
return [];
}
if ($origin !== null && !($origin instanceof Iri)) {
throw InvalidArgument::create(2, '$origin', Iri::class . ' or null', gettype($origin));
}
$cookies = [];
foreach ($cookie_headers as $header) {
$parsed = self::parse($header, '', $time);
// Default domain/path attributes
if (empty($parsed->attributes['domain']) && !empty($origin)) {
$parsed->attributes['domain'] = $origin->host;
$parsed->flags['host-only'] = true;
} else {
$parsed->flags['host-only'] = false;
}
$path_is_valid = (!empty($parsed->attributes['path']) && $parsed->attributes['path'][0] === '/');
if (!$path_is_valid && !empty($origin)) {
$path = $origin->path;
// Default path normalization as per RFC 6265 section 5.1.4
if (substr($path, 0, 1) !== '/') {
// If the uri-path is empty or if the first character of
// the uri-path is not a %x2F ("/") character, output
// %x2F ("/") and skip the remaining steps.
$path = '/';
} elseif (substr_count($path, '/') === 1) {
// If the uri-path contains no more than one %x2F ("/")
// character, output %x2F ("/") and skip the remaining
// step.
$path = '/';
} else {
// Output the characters of the uri-path from the first
// character up to, but not including, the right-most
// %x2F ("/").
$path = substr($path, 0, strrpos($path, '/'));
}
$parsed->attributes['path'] = $path;
}
// Reject invalid cookie domains
if (!empty($origin) && !$parsed->domain_matches($origin->host)) {
continue;
}
$cookies[$parsed->name] = $parsed;
}
return $cookies;
}
}
Transport/Curl.php 0000604 00000046163 15133277060 0010170 0 ustar 00 <?php
/**
* cURL HTTP transport
*
* @package Requests\Transport
*/
namespace WpOrg\Requests\Transport;
use RecursiveArrayIterator;
use RecursiveIteratorIterator;
use WpOrg\Requests\Capability;
use WpOrg\Requests\Exception;
use WpOrg\Requests\Exception\InvalidArgument;
use WpOrg\Requests\Exception\Transport\Curl as CurlException;
use WpOrg\Requests\Requests;
use WpOrg\Requests\Transport;
use WpOrg\Requests\Utility\InputValidator;
/**
* cURL HTTP transport
*
* @package Requests\Transport
*/
final class Curl implements Transport {
const CURL_7_10_5 = 0x070A05;
const CURL_7_16_2 = 0x071002;
/**
* Raw HTTP data
*
* @var string
*/
public $headers = '';
/**
* Raw body data
*
* @var string
*/
public $response_data = '';
/**
* Information on the current request
*
* @var array cURL information array, see {@link https://www.php.net/curl_getinfo}
*/
public $info;
/**
* cURL version number
*
* @var int
*/
public $version;
/**
* cURL handle
*
* @var resource|\CurlHandle Resource in PHP < 8.0, Instance of CurlHandle in PHP >= 8.0.
*/
private $handle;
/**
* Hook dispatcher instance
*
* @var \WpOrg\Requests\Hooks
*/
private $hooks;
/**
* Have we finished the headers yet?
*
* @var boolean
*/
private $done_headers = false;
/**
* If streaming to a file, keep the file pointer
*
* @var resource
*/
private $stream_handle;
/**
* How many bytes are in the response body?
*
* @var int
*/
private $response_bytes;
/**
* What's the maximum number of bytes we should keep?
*
* @var int|bool Byte count, or false if no limit.
*/
private $response_byte_limit;
/**
* Constructor
*/
public function __construct() {
$curl = curl_version();
$this->version = $curl['version_number'];
$this->handle = curl_init();
curl_setopt($this->handle, CURLOPT_HEADER, false);
curl_setopt($this->handle, CURLOPT_RETURNTRANSFER, 1);
if ($this->version >= self::CURL_7_10_5) {
curl_setopt($this->handle, CURLOPT_ENCODING, '');
}
if (defined('CURLOPT_PROTOCOLS')) {
// phpcs:ignore PHPCompatibility.Constants.NewConstants.curlopt_protocolsFound
curl_setopt($this->handle, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
}
if (defined('CURLOPT_REDIR_PROTOCOLS')) {
// phpcs:ignore PHPCompatibility.Constants.NewConstants.curlopt_redir_protocolsFound
curl_setopt($this->handle, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
}
}
/**
* Destructor
*/
public function __destruct() {
if (is_resource($this->handle)) {
curl_close($this->handle);
}
}
/**
* Perform a request
*
* @param string|Stringable $url URL to request
* @param array $headers Associative array of request headers
* @param string|array $data Data to send either as the POST body, or as parameters in the URL for a GET/HEAD
* @param array $options Request options, see {@see \WpOrg\Requests\Requests::response()} for documentation
* @return string Raw HTTP result
*
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $url argument is not a string or Stringable.
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $headers argument is not an array.
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $data parameter is not an array or string.
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $options argument is not an array.
* @throws \WpOrg\Requests\Exception On a cURL error (`curlerror`)
*/
public function request($url, $headers = [], $data = [], $options = []) {
if (InputValidator::is_string_or_stringable($url) === false) {
throw InvalidArgument::create(1, '$url', 'string|Stringable', gettype($url));
}
if (is_array($headers) === false) {
throw InvalidArgument::create(2, '$headers', 'array', gettype($headers));
}
if (!is_array($data) && !is_string($data)) {
if ($data === null) {
$data = '';
} else {
throw InvalidArgument::create(3, '$data', 'array|string', gettype($data));
}
}
if (is_array($options) === false) {
throw InvalidArgument::create(4, '$options', 'array', gettype($options));
}
$this->hooks = $options['hooks'];
$this->setup_handle($url, $headers, $data, $options);
$options['hooks']->dispatch('curl.before_send', [&$this->handle]);
if ($options['filename'] !== false) {
// phpcs:ignore WordPress.PHP.NoSilencedErrors -- Silenced the PHP native warning in favour of throwing an exception.
$this->stream_handle = @fopen($options['filename'], 'wb');
if ($this->stream_handle === false) {
$error = error_get_last();
throw new Exception($error['message'], 'fopen');
}
}
$this->response_data = '';
$this->response_bytes = 0;
$this->response_byte_limit = false;
if ($options['max_bytes'] !== false) {
$this->response_byte_limit = $options['max_bytes'];
}
if (isset($options['verify'])) {
if ($options['verify'] === false) {
curl_setopt($this->handle, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($this->handle, CURLOPT_SSL_VERIFYPEER, 0);
} elseif (is_string($options['verify'])) {
curl_setopt($this->handle, CURLOPT_CAINFO, $options['verify']);
}
}
if (isset($options['verifyname']) && $options['verifyname'] === false) {
curl_setopt($this->handle, CURLOPT_SSL_VERIFYHOST, 0);
}
curl_exec($this->handle);
$response = $this->response_data;
$options['hooks']->dispatch('curl.after_send', []);
if (curl_errno($this->handle) === CURLE_WRITE_ERROR || curl_errno($this->handle) === CURLE_BAD_CONTENT_ENCODING) {
// Reset encoding and try again
curl_setopt($this->handle, CURLOPT_ENCODING, 'none');
$this->response_data = '';
$this->response_bytes = 0;
curl_exec($this->handle);
$response = $this->response_data;
}
$this->process_response($response, $options);
// Need to remove the $this reference from the curl handle.
// Otherwise \WpOrg\Requests\Transport\Curl won't be garbage collected and the curl_close() will never be called.
curl_setopt($this->handle, CURLOPT_HEADERFUNCTION, null);
curl_setopt($this->handle, CURLOPT_WRITEFUNCTION, null);
return $this->headers;
}
/**
* Send multiple requests simultaneously
*
* @param array $requests Request data
* @param array $options Global options
* @return array Array of \WpOrg\Requests\Response objects (may contain \WpOrg\Requests\Exception or string responses as well)
*
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $requests argument is not an array or iterable object with array access.
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $options argument is not an array.
*/
public function request_multiple($requests, $options) {
// If you're not requesting, we can't get any responses ¯\_(ツ)_/¯
if (empty($requests)) {
return [];
}
if (InputValidator::has_array_access($requests) === false || InputValidator::is_iterable($requests) === false) {
throw InvalidArgument::create(1, '$requests', 'array|ArrayAccess&Traversable', gettype($requests));
}
if (is_array($options) === false) {
throw InvalidArgument::create(2, '$options', 'array', gettype($options));
}
$multihandle = curl_multi_init();
$subrequests = [];
$subhandles = [];
$class = get_class($this);
foreach ($requests as $id => $request) {
$subrequests[$id] = new $class();
$subhandles[$id] = $subrequests[$id]->get_subrequest_handle($request['url'], $request['headers'], $request['data'], $request['options']);
$request['options']['hooks']->dispatch('curl.before_multi_add', [&$subhandles[$id]]);
curl_multi_add_handle($multihandle, $subhandles[$id]);
}
$completed = 0;
$responses = [];
$subrequestcount = count($subrequests);
$request['options']['hooks']->dispatch('curl.before_multi_exec', [&$multihandle]);
do {
$active = 0;
do {
$status = curl_multi_exec($multihandle, $active);
} while ($status === CURLM_CALL_MULTI_PERFORM);
$to_process = [];
// Read the information as needed
while ($done = curl_multi_info_read($multihandle)) {
$key = array_search($done['handle'], $subhandles, true);
if (!isset($to_process[$key])) {
$to_process[$key] = $done;
}
}
// Parse the finished requests before we start getting the new ones
foreach ($to_process as $key => $done) {
$options = $requests[$key]['options'];
if ($done['result'] !== CURLE_OK) {
//get error string for handle.
$reason = curl_error($done['handle']);
$exception = new CurlException(
$reason,
CurlException::EASY,
$done['handle'],
$done['result']
);
$responses[$key] = $exception;
$options['hooks']->dispatch('transport.internal.parse_error', [&$responses[$key], $requests[$key]]);
} else {
$responses[$key] = $subrequests[$key]->process_response($subrequests[$key]->response_data, $options);
$options['hooks']->dispatch('transport.internal.parse_response', [&$responses[$key], $requests[$key]]);
}
curl_multi_remove_handle($multihandle, $done['handle']);
curl_close($done['handle']);
if (!is_string($responses[$key])) {
$options['hooks']->dispatch('multiple.request.complete', [&$responses[$key], $key]);
}
$completed++;
}
} while ($active || $completed < $subrequestcount);
$request['options']['hooks']->dispatch('curl.after_multi_exec', [&$multihandle]);
curl_multi_close($multihandle);
return $responses;
}
/**
* Get the cURL handle for use in a multi-request
*
* @param string $url URL to request
* @param array $headers Associative array of request headers
* @param string|array $data Data to send either as the POST body, or as parameters in the URL for a GET/HEAD
* @param array $options Request options, see {@see \WpOrg\Requests\Requests::response()} for documentation
* @return resource|\CurlHandle Subrequest's cURL handle
*/
public function &get_subrequest_handle($url, $headers, $data, $options) {
$this->setup_handle($url, $headers, $data, $options);
if ($options['filename'] !== false) {
$this->stream_handle = fopen($options['filename'], 'wb');
}
$this->response_data = '';
$this->response_bytes = 0;
$this->response_byte_limit = false;
if ($options['max_bytes'] !== false) {
$this->response_byte_limit = $options['max_bytes'];
}
$this->hooks = $options['hooks'];
return $this->handle;
}
/**
* Setup the cURL handle for the given data
*
* @param string $url URL to request
* @param array $headers Associative array of request headers
* @param string|array $data Data to send either as the POST body, or as parameters in the URL for a GET/HEAD
* @param array $options Request options, see {@see \WpOrg\Requests\Requests::response()} for documentation
*/
private function setup_handle($url, $headers, $data, $options) {
$options['hooks']->dispatch('curl.before_request', [&$this->handle]);
// Force closing the connection for old versions of cURL (<7.22).
if (!isset($headers['Connection'])) {
$headers['Connection'] = 'close';
}
/**
* Add "Expect" header.
*
* By default, cURL adds a "Expect: 100-Continue" to most requests. This header can
* add as much as a second to the time it takes for cURL to perform a request. To
* prevent this, we need to set an empty "Expect" header. To match the behaviour of
* Guzzle, we'll add the empty header to requests that are smaller than 1 MB and use
* HTTP/1.1.
*
* https://curl.se/mail/lib-2017-07/0013.html
*/
if (!isset($headers['Expect']) && $options['protocol_version'] === 1.1) {
$headers['Expect'] = $this->get_expect_header($data);
}
$headers = Requests::flatten($headers);
if (!empty($data)) {
$data_format = $options['data_format'];
if ($data_format === 'query') {
$url = self::format_get($url, $data);
$data = '';
} elseif (!is_string($data)) {
$data = http_build_query($data, '', '&');
}
}
switch ($options['type']) {
case Requests::POST:
curl_setopt($this->handle, CURLOPT_POST, true);
curl_setopt($this->handle, CURLOPT_POSTFIELDS, $data);
break;
case Requests::HEAD:
curl_setopt($this->handle, CURLOPT_CUSTOMREQUEST, $options['type']);
curl_setopt($this->handle, CURLOPT_NOBODY, true);
break;
case Requests::TRACE:
curl_setopt($this->handle, CURLOPT_CUSTOMREQUEST, $options['type']);
break;
case Requests::PATCH:
case Requests::PUT:
case Requests::DELETE:
case Requests::OPTIONS:
default:
curl_setopt($this->handle, CURLOPT_CUSTOMREQUEST, $options['type']);
if (!empty($data)) {
curl_setopt($this->handle, CURLOPT_POSTFIELDS, $data);
}
}
// cURL requires a minimum timeout of 1 second when using the system
// DNS resolver, as it uses `alarm()`, which is second resolution only.
// There's no way to detect which DNS resolver is being used from our
// end, so we need to round up regardless of the supplied timeout.
//
// https://github.com/curl/curl/blob/4f45240bc84a9aa648c8f7243be7b79e9f9323a5/lib/hostip.c#L606-L609
$timeout = max($options['timeout'], 1);
if (is_int($timeout) || $this->version < self::CURL_7_16_2) {
curl_setopt($this->handle, CURLOPT_TIMEOUT, ceil($timeout));
} else {
// phpcs:ignore PHPCompatibility.Constants.NewConstants.curlopt_timeout_msFound
curl_setopt($this->handle, CURLOPT_TIMEOUT_MS, round($timeout * 1000));
}
if (is_int($options['connect_timeout']) || $this->version < self::CURL_7_16_2) {
curl_setopt($this->handle, CURLOPT_CONNECTTIMEOUT, ceil($options['connect_timeout']));
} else {
// phpcs:ignore PHPCompatibility.Constants.NewConstants.curlopt_connecttimeout_msFound
curl_setopt($this->handle, CURLOPT_CONNECTTIMEOUT_MS, round($options['connect_timeout'] * 1000));
}
curl_setopt($this->handle, CURLOPT_URL, $url);
curl_setopt($this->handle, CURLOPT_USERAGENT, $options['useragent']);
if (!empty($headers)) {
curl_setopt($this->handle, CURLOPT_HTTPHEADER, $headers);
}
if ($options['protocol_version'] === 1.1) {
curl_setopt($this->handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
} else {
curl_setopt($this->handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
}
if ($options['blocking'] === true) {
curl_setopt($this->handle, CURLOPT_HEADERFUNCTION, [$this, 'stream_headers']);
curl_setopt($this->handle, CURLOPT_WRITEFUNCTION, [$this, 'stream_body']);
curl_setopt($this->handle, CURLOPT_BUFFERSIZE, Requests::BUFFER_SIZE);
}
}
/**
* Process a response
*
* @param string $response Response data from the body
* @param array $options Request options
* @return string|false HTTP response data including headers. False if non-blocking.
* @throws \WpOrg\Requests\Exception If the request resulted in a cURL error.
*/
public function process_response($response, $options) {
if ($options['blocking'] === false) {
$fake_headers = '';
$options['hooks']->dispatch('curl.after_request', [&$fake_headers]);
return false;
}
if ($options['filename'] !== false && $this->stream_handle) {
fclose($this->stream_handle);
$this->headers = trim($this->headers);
} else {
$this->headers .= $response;
}
if (curl_errno($this->handle)) {
$error = sprintf(
'cURL error %s: %s',
curl_errno($this->handle),
curl_error($this->handle)
);
throw new Exception($error, 'curlerror', $this->handle);
}
$this->info = curl_getinfo($this->handle);
$options['hooks']->dispatch('curl.after_request', [&$this->headers, &$this->info]);
return $this->headers;
}
/**
* Collect the headers as they are received
*
* @param resource|\CurlHandle $handle cURL handle
* @param string $headers Header string
* @return integer Length of provided header
*/
public function stream_headers($handle, $headers) {
// Why do we do this? cURL will send both the final response and any
// interim responses, such as a 100 Continue. We don't need that.
// (We may want to keep this somewhere just in case)
if ($this->done_headers) {
$this->headers = '';
$this->done_headers = false;
}
$this->headers .= $headers;
if ($headers === "\r\n") {
$this->done_headers = true;
}
return strlen($headers);
}
/**
* Collect data as it's received
*
* @since 1.6.1
*
* @param resource|\CurlHandle $handle cURL handle
* @param string $data Body data
* @return integer Length of provided data
*/
public function stream_body($handle, $data) {
$this->hooks->dispatch('request.progress', [$data, $this->response_bytes, $this->response_byte_limit]);
$data_length = strlen($data);
// Are we limiting the response size?
if ($this->response_byte_limit) {
if ($this->response_bytes === $this->response_byte_limit) {
// Already at maximum, move on
return $data_length;
}
if (($this->response_bytes + $data_length) > $this->response_byte_limit) {
// Limit the length
$limited_length = ($this->response_byte_limit - $this->response_bytes);
$data = substr($data, 0, $limited_length);
}
}
if ($this->stream_handle) {
fwrite($this->stream_handle, $data);
} else {
$this->response_data .= $data;
}
$this->response_bytes += strlen($data);
return $data_length;
}
/**
* Format a URL given GET data
*
* @param string $url Original URL.
* @param array|object $data Data to build query using, see {@link https://www.php.net/http_build_query}
* @return string URL with data
*/
private static function format_get($url, $data) {
if (!empty($data)) {
$query = '';
$url_parts = parse_url($url);
if (empty($url_parts['query'])) {
$url_parts['query'] = '';
} else {
$query = $url_parts['query'];
}
$query .= '&' . http_build_query($data, '', '&');
$query = trim($query, '&');
if (empty($url_parts['query'])) {
$url .= '?' . $query;
} else {
$url = str_replace($url_parts['query'], $query, $url);
}
}
return $url;
}
/**
* Self-test whether the transport can be used.
*
* The available capabilities to test for can be found in {@see \WpOrg\Requests\Capability}.
*
* @codeCoverageIgnore
* @param array<string, bool> $capabilities Optional. Associative array of capabilities to test against, i.e. `['<capability>' => true]`.
* @return bool Whether the transport can be used.
*/
public static function test($capabilities = []) {
if (!function_exists('curl_init') || !function_exists('curl_exec')) {
return false;
}
// If needed, check that our installed curl version supports SSL
if (isset($capabilities[Capability::SSL]) && $capabilities[Capability::SSL]) {
$curl_version = curl_version();
if (!(CURL_VERSION_SSL & $curl_version['features'])) {
return false;
}
}
return true;
}
/**
* Get the correct "Expect" header for the given request data.
*
* @param string|array $data Data to send either as the POST body, or as parameters in the URL for a GET/HEAD.
* @return string The "Expect" header.
*/
private function get_expect_header($data) {
if (!is_array($data)) {
return strlen((string) $data) >= 1048576 ? '100-Continue' : '';
}
$bytesize = 0;
$iterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($data));
foreach ($iterator as $datum) {
$bytesize += strlen((string) $datum);
if ($bytesize >= 1048576) {
return '100-Continue';
}
}
return '';
}
}
Transport/Fsockopen.php 0000604 00000037033 15133277060 0011206 0 ustar 00 <?php
/**
* fsockopen HTTP transport
*
* @package Requests\Transport
*/
namespace WpOrg\Requests\Transport;
use WpOrg\Requests\Capability;
use WpOrg\Requests\Exception;
use WpOrg\Requests\Exception\InvalidArgument;
use WpOrg\Requests\Port;
use WpOrg\Requests\Requests;
use WpOrg\Requests\Ssl;
use WpOrg\Requests\Transport;
use WpOrg\Requests\Utility\CaseInsensitiveDictionary;
use WpOrg\Requests\Utility\InputValidator;
/**
* fsockopen HTTP transport
*
* @package Requests\Transport
*/
final class Fsockopen implements Transport {
/**
* Second to microsecond conversion
*
* @var integer
*/
const SECOND_IN_MICROSECONDS = 1000000;
/**
* Raw HTTP data
*
* @var string
*/
public $headers = '';
/**
* Stream metadata
*
* @var array Associative array of properties, see {@link https://www.php.net/stream_get_meta_data}
*/
public $info;
/**
* What's the maximum number of bytes we should keep?
*
* @var int|bool Byte count, or false if no limit.
*/
private $max_bytes = false;
/**
* Cache for received connection errors.
*
* @var string
*/
private $connect_error = '';
/**
* Perform a request
*
* @param string|Stringable $url URL to request
* @param array $headers Associative array of request headers
* @param string|array $data Data to send either as the POST body, or as parameters in the URL for a GET/HEAD
* @param array $options Request options, see {@see \WpOrg\Requests\Requests::response()} for documentation
* @return string Raw HTTP result
*
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $url argument is not a string or Stringable.
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $headers argument is not an array.
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $data parameter is not an array or string.
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $options argument is not an array.
* @throws \WpOrg\Requests\Exception On failure to connect to socket (`fsockopenerror`)
* @throws \WpOrg\Requests\Exception On socket timeout (`timeout`)
*/
public function request($url, $headers = [], $data = [], $options = []) {
if (InputValidator::is_string_or_stringable($url) === false) {
throw InvalidArgument::create(1, '$url', 'string|Stringable', gettype($url));
}
if (is_array($headers) === false) {
throw InvalidArgument::create(2, '$headers', 'array', gettype($headers));
}
if (!is_array($data) && !is_string($data)) {
if ($data === null) {
$data = '';
} else {
throw InvalidArgument::create(3, '$data', 'array|string', gettype($data));
}
}
if (is_array($options) === false) {
throw InvalidArgument::create(4, '$options', 'array', gettype($options));
}
$options['hooks']->dispatch('fsockopen.before_request');
$url_parts = parse_url($url);
if (empty($url_parts)) {
throw new Exception('Invalid URL.', 'invalidurl', $url);
}
$host = $url_parts['host'];
$context = stream_context_create();
$verifyname = false;
$case_insensitive_headers = new CaseInsensitiveDictionary($headers);
// HTTPS support
if (isset($url_parts['scheme']) && strtolower($url_parts['scheme']) === 'https') {
$remote_socket = 'ssl://' . $host;
if (!isset($url_parts['port'])) {
$url_parts['port'] = Port::HTTPS;
}
$context_options = [
'verify_peer' => true,
'capture_peer_cert' => true,
];
$verifyname = true;
// SNI, if enabled (OpenSSL >=0.9.8j)
// phpcs:ignore PHPCompatibility.Constants.NewConstants.openssl_tlsext_server_nameFound
if (defined('OPENSSL_TLSEXT_SERVER_NAME') && OPENSSL_TLSEXT_SERVER_NAME) {
$context_options['SNI_enabled'] = true;
if (isset($options['verifyname']) && $options['verifyname'] === false) {
$context_options['SNI_enabled'] = false;
}
}
if (isset($options['verify'])) {
if ($options['verify'] === false) {
$context_options['verify_peer'] = false;
$context_options['verify_peer_name'] = false;
$verifyname = false;
} elseif (is_string($options['verify'])) {
$context_options['cafile'] = $options['verify'];
}
}
if (isset($options['verifyname']) && $options['verifyname'] === false) {
$context_options['verify_peer_name'] = false;
$verifyname = false;
}
// Handle the PHP 8.4 deprecation (PHP 9.0 removal) of the function signature we use for stream_context_set_option().
// Ref: https://wiki.php.net/rfc/deprecate_functions_with_overloaded_signatures#stream_context_set_option
if (function_exists('stream_context_set_options')) {
// PHP 8.3+.
stream_context_set_options($context, ['ssl' => $context_options]);
} else {
// PHP < 8.3.
stream_context_set_option($context, ['ssl' => $context_options]);
}
} else {
$remote_socket = 'tcp://' . $host;
}
$this->max_bytes = $options['max_bytes'];
if (!isset($url_parts['port'])) {
$url_parts['port'] = Port::HTTP;
}
$remote_socket .= ':' . $url_parts['port'];
// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_set_error_handler
set_error_handler([$this, 'connect_error_handler'], E_WARNING | E_NOTICE);
$options['hooks']->dispatch('fsockopen.remote_socket', [&$remote_socket]);
$socket = stream_socket_client($remote_socket, $errno, $errstr, ceil($options['connect_timeout']), STREAM_CLIENT_CONNECT, $context);
restore_error_handler();
if ($verifyname && !$this->verify_certificate_from_context($host, $context)) {
throw new Exception('SSL certificate did not match the requested domain name', 'ssl.no_match');
}
if (!$socket) {
if ($errno === 0) {
// Connection issue
throw new Exception(rtrim($this->connect_error), 'fsockopen.connect_error');
}
throw new Exception($errstr, 'fsockopenerror', null, $errno);
}
$data_format = $options['data_format'];
if ($data_format === 'query') {
$path = self::format_get($url_parts, $data);
$data = '';
} else {
$path = self::format_get($url_parts, []);
}
$options['hooks']->dispatch('fsockopen.remote_host_path', [&$path, $url]);
$request_body = '';
$out = sprintf("%s %s HTTP/%.1F\r\n", $options['type'], $path, $options['protocol_version']);
if ($options['type'] !== Requests::TRACE) {
if (is_array($data)) {
$request_body = http_build_query($data, '', '&');
} else {
$request_body = $data;
}
// Always include Content-length on POST requests to prevent
// 411 errors from some servers when the body is empty.
if (!empty($data) || $options['type'] === Requests::POST) {
if (!isset($case_insensitive_headers['Content-Length'])) {
$headers['Content-Length'] = strlen($request_body);
}
if (!isset($case_insensitive_headers['Content-Type'])) {
$headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8';
}
}
}
if (!isset($case_insensitive_headers['Host'])) {
$out .= sprintf('Host: %s', $url_parts['host']);
$scheme_lower = strtolower($url_parts['scheme']);
if (($scheme_lower === 'http' && $url_parts['port'] !== Port::HTTP) || ($scheme_lower === 'https' && $url_parts['port'] !== Port::HTTPS)) {
$out .= ':' . $url_parts['port'];
}
$out .= "\r\n";
}
if (!isset($case_insensitive_headers['User-Agent'])) {
$out .= sprintf("User-Agent: %s\r\n", $options['useragent']);
}
$accept_encoding = $this->accept_encoding();
if (!isset($case_insensitive_headers['Accept-Encoding']) && !empty($accept_encoding)) {
$out .= sprintf("Accept-Encoding: %s\r\n", $accept_encoding);
}
$headers = Requests::flatten($headers);
if (!empty($headers)) {
$out .= implode("\r\n", $headers) . "\r\n";
}
$options['hooks']->dispatch('fsockopen.after_headers', [&$out]);
if (substr($out, -2) !== "\r\n") {
$out .= "\r\n";
}
if (!isset($case_insensitive_headers['Connection'])) {
$out .= "Connection: Close\r\n";
}
$out .= "\r\n" . $request_body;
$options['hooks']->dispatch('fsockopen.before_send', [&$out]);
fwrite($socket, $out);
$options['hooks']->dispatch('fsockopen.after_send', [$out]);
if (!$options['blocking']) {
fclose($socket);
$fake_headers = '';
$options['hooks']->dispatch('fsockopen.after_request', [&$fake_headers]);
return '';
}
$timeout_sec = (int) floor($options['timeout']);
if ($timeout_sec === $options['timeout']) {
$timeout_msec = 0;
} else {
$timeout_msec = self::SECOND_IN_MICROSECONDS * $options['timeout'] % self::SECOND_IN_MICROSECONDS;
}
stream_set_timeout($socket, $timeout_sec, $timeout_msec);
$response = '';
$body = '';
$headers = '';
$this->info = stream_get_meta_data($socket);
$size = 0;
$doingbody = false;
$download = false;
if ($options['filename']) {
// phpcs:ignore WordPress.PHP.NoSilencedErrors -- Silenced the PHP native warning in favour of throwing an exception.
$download = @fopen($options['filename'], 'wb');
if ($download === false) {
$error = error_get_last();
throw new Exception($error['message'], 'fopen');
}
}
while (!feof($socket)) {
$this->info = stream_get_meta_data($socket);
if ($this->info['timed_out']) {
throw new Exception('fsocket timed out', 'timeout');
}
$block = fread($socket, Requests::BUFFER_SIZE);
if (!$doingbody) {
$response .= $block;
if (strpos($response, "\r\n\r\n")) {
list($headers, $block) = explode("\r\n\r\n", $response, 2);
$doingbody = true;
}
}
// Are we in body mode now?
if ($doingbody) {
$options['hooks']->dispatch('request.progress', [$block, $size, $this->max_bytes]);
$data_length = strlen($block);
if ($this->max_bytes) {
// Have we already hit a limit?
if ($size === $this->max_bytes) {
continue;
}
if (($size + $data_length) > $this->max_bytes) {
// Limit the length
$limited_length = ($this->max_bytes - $size);
$block = substr($block, 0, $limited_length);
}
}
$size += strlen($block);
if ($download) {
fwrite($download, $block);
} else {
$body .= $block;
}
}
}
$this->headers = $headers;
if ($download) {
fclose($download);
} else {
$this->headers .= "\r\n\r\n" . $body;
}
fclose($socket);
$options['hooks']->dispatch('fsockopen.after_request', [&$this->headers, &$this->info]);
return $this->headers;
}
/**
* Send multiple requests simultaneously
*
* @param array $requests Request data (array of 'url', 'headers', 'data', 'options') as per {@see \WpOrg\Requests\Transport::request()}
* @param array $options Global options, see {@see \WpOrg\Requests\Requests::response()} for documentation
* @return array Array of \WpOrg\Requests\Response objects (may contain \WpOrg\Requests\Exception or string responses as well)
*
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $requests argument is not an array or iterable object with array access.
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $options argument is not an array.
*/
public function request_multiple($requests, $options) {
// If you're not requesting, we can't get any responses ¯\_(ツ)_/¯
if (empty($requests)) {
return [];
}
if (InputValidator::has_array_access($requests) === false || InputValidator::is_iterable($requests) === false) {
throw InvalidArgument::create(1, '$requests', 'array|ArrayAccess&Traversable', gettype($requests));
}
if (is_array($options) === false) {
throw InvalidArgument::create(2, '$options', 'array', gettype($options));
}
$responses = [];
$class = get_class($this);
foreach ($requests as $id => $request) {
try {
$handler = new $class();
$responses[$id] = $handler->request($request['url'], $request['headers'], $request['data'], $request['options']);
$request['options']['hooks']->dispatch('transport.internal.parse_response', [&$responses[$id], $request]);
} catch (Exception $e) {
$responses[$id] = $e;
}
if (!is_string($responses[$id])) {
$request['options']['hooks']->dispatch('multiple.request.complete', [&$responses[$id], $id]);
}
}
return $responses;
}
/**
* Retrieve the encodings we can accept
*
* @return string Accept-Encoding header value
*/
private static function accept_encoding() {
$type = [];
if (function_exists('gzinflate')) {
$type[] = 'deflate;q=1.0';
}
if (function_exists('gzuncompress')) {
$type[] = 'compress;q=0.5';
}
$type[] = 'gzip;q=0.5';
return implode(', ', $type);
}
/**
* Format a URL given GET data
*
* @param array $url_parts Array of URL parts as received from {@link https://www.php.net/parse_url}
* @param array|object $data Data to build query using, see {@link https://www.php.net/http_build_query}
* @return string URL with data
*/
private static function format_get($url_parts, $data) {
if (!empty($data)) {
if (empty($url_parts['query'])) {
$url_parts['query'] = '';
}
$url_parts['query'] .= '&' . http_build_query($data, '', '&');
$url_parts['query'] = trim($url_parts['query'], '&');
}
if (isset($url_parts['path'])) {
if (isset($url_parts['query'])) {
$get = $url_parts['path'] . '?' . $url_parts['query'];
} else {
$get = $url_parts['path'];
}
} else {
$get = '/';
}
return $get;
}
/**
* Error handler for stream_socket_client()
*
* @param int $errno Error number (e.g. E_WARNING)
* @param string $errstr Error message
*/
public function connect_error_handler($errno, $errstr) {
// Double-check we can handle it
if (($errno & E_WARNING) === 0 && ($errno & E_NOTICE) === 0) {
// Return false to indicate the default error handler should engage
return false;
}
$this->connect_error .= $errstr . "\n";
return true;
}
/**
* Verify the certificate against common name and subject alternative names
*
* Unfortunately, PHP doesn't check the certificate against the alternative
* names, leading things like 'https://www.github.com/' to be invalid.
* Instead
*
* @link https://tools.ietf.org/html/rfc2818#section-3.1 RFC2818, Section 3.1
*
* @param string $host Host name to verify against
* @param resource $context Stream context
* @return bool
*
* @throws \WpOrg\Requests\Exception On failure to connect via TLS (`fsockopen.ssl.connect_error`)
* @throws \WpOrg\Requests\Exception On not obtaining a match for the host (`fsockopen.ssl.no_match`)
*/
public function verify_certificate_from_context($host, $context) {
$meta = stream_context_get_options($context);
// If we don't have SSL options, then we couldn't make the connection at
// all
if (empty($meta) || empty($meta['ssl']) || empty($meta['ssl']['peer_certificate'])) {
throw new Exception(rtrim($this->connect_error), 'ssl.connect_error');
}
$cert = openssl_x509_parse($meta['ssl']['peer_certificate']);
return Ssl::verify_certificate($host, $cert);
}
/**
* Self-test whether the transport can be used.
*
* The available capabilities to test for can be found in {@see \WpOrg\Requests\Capability}.
*
* @codeCoverageIgnore
* @param array<string, bool> $capabilities Optional. Associative array of capabilities to test against, i.e. `['<capability>' => true]`.
* @return bool Whether the transport can be used.
*/
public static function test($capabilities = []) {
if (!function_exists('fsockopen')) {
return false;
}
// If needed, check that streams support SSL
if (isset($capabilities[Capability::SSL]) && $capabilities[Capability::SSL]) {
if (!extension_loaded('openssl') || !function_exists('openssl_x509_parse')) {
return false;
}
}
return true;
}
}
Utility/FilteredIterator.php 0000604 00000004155 15133277060 0012175 0 ustar 00 <?php
/**
* Iterator for arrays requiring filtered values
*
* @package Requests\Utilities
*/
namespace WpOrg\Requests\Utility;
use ArrayIterator;
use ReturnTypeWillChange;
use WpOrg\Requests\Exception\InvalidArgument;
use WpOrg\Requests\Utility\InputValidator;
/**
* Iterator for arrays requiring filtered values
*
* @package Requests\Utilities
*/
final class FilteredIterator extends ArrayIterator {
/**
* Callback to run as a filter
*
* @var callable
*/
private $callback;
/**
* Create a new iterator
*
* @param array $data The array or object to be iterated on.
* @param callable $callback Callback to be called on each value
*
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $data argument is not iterable.
*/
public function __construct($data, $callback) {
if (InputValidator::is_iterable($data) === false) {
throw InvalidArgument::create(1, '$data', 'iterable', gettype($data));
}
parent::__construct($data);
if (is_callable($callback)) {
$this->callback = $callback;
}
}
/**
* Prevent unserialization of the object for security reasons.
*
* @phpcs:disable PHPCompatibility.FunctionNameRestrictions.NewMagicMethods.__unserializeFound
*
* @param array $data Restored array of data originally serialized.
*
* @return void
*/
#[ReturnTypeWillChange]
public function __unserialize($data) {}
// phpcs:enable
/**
* Perform reinitialization tasks.
*
* Prevents a callback from being injected during unserialization of an object.
*
* @return void
*/
public function __wakeup() {
unset($this->callback);
}
/**
* Get the current item's value after filtering
*
* @return string
*/
#[ReturnTypeWillChange]
public function current() {
$value = parent::current();
if (is_callable($this->callback)) {
$value = call_user_func($this->callback, $value);
}
return $value;
}
/**
* Prevent creating a PHP value from a stored representation of the object for security reasons.
*
* @param string $data The serialized string.
*
* @return void
*/
#[ReturnTypeWillChange]
public function unserialize($data) {}
}
Utility/InputValidator.php 0000604 00000004720 15133277060 0011670 0 ustar 00 <?php
/**
* Input validation utilities.
*
* @package Requests\Utilities
*/
namespace WpOrg\Requests\Utility;
use ArrayAccess;
use CurlHandle;
use Traversable;
/**
* Input validation utilities.
*
* @package Requests\Utilities
*/
final class InputValidator {
/**
* Verify that a received input parameter is of type string or is "stringable".
*
* @param mixed $input Input parameter to verify.
*
* @return bool
*/
public static function is_string_or_stringable($input) {
return is_string($input) || self::is_stringable_object($input);
}
/**
* Verify whether a received input parameter is usable as an integer array key.
*
* @param mixed $input Input parameter to verify.
*
* @return bool
*/
public static function is_numeric_array_key($input) {
if (is_int($input)) {
return true;
}
if (!is_string($input)) {
return false;
}
return (bool) preg_match('`^-?[0-9]+$`', $input);
}
/**
* Verify whether a received input parameter is "stringable".
*
* @param mixed $input Input parameter to verify.
*
* @return bool
*/
public static function is_stringable_object($input) {
return is_object($input) && method_exists($input, '__toString');
}
/**
* Verify whether a received input parameter is _accessible as if it were an array_.
*
* @param mixed $input Input parameter to verify.
*
* @return bool
*/
public static function has_array_access($input) {
return is_array($input) || $input instanceof ArrayAccess;
}
/**
* Verify whether a received input parameter is "iterable".
*
* @internal The PHP native `is_iterable()` function was only introduced in PHP 7.1
* and this library still supports PHP 5.6.
*
* @param mixed $input Input parameter to verify.
*
* @return bool
*/
public static function is_iterable($input) {
return is_array($input) || $input instanceof Traversable;
}
/**
* Verify whether a received input parameter is a Curl handle.
*
* The PHP Curl extension worked with resources prior to PHP 8.0 and with
* an instance of the `CurlHandle` class since PHP 8.0.
* {@link https://www.php.net/manual/en/migration80.incompatible.php#migration80.incompatible.resource2object}
*
* @param mixed $input Input parameter to verify.
*
* @return bool
*/
public static function is_curl_handle($input) {
if (is_resource($input)) {
return get_resource_type($input) === 'curl';
}
if (is_object($input)) {
return $input instanceof CurlHandle;
}
return false;
}
}
Utility/CaseInsensitiveDictionary.php 0000604 00000004713 15133277060 0014047 0 ustar 00 <?php
/**
* Case-insensitive dictionary, suitable for HTTP headers
*
* @package Requests\Utilities
*/
namespace WpOrg\Requests\Utility;
use ArrayAccess;
use ArrayIterator;
use IteratorAggregate;
use ReturnTypeWillChange;
use WpOrg\Requests\Exception;
/**
* Case-insensitive dictionary, suitable for HTTP headers
*
* @package Requests\Utilities
*/
class CaseInsensitiveDictionary implements ArrayAccess, IteratorAggregate {
/**
* Actual item data
*
* @var array
*/
protected $data = [];
/**
* Creates a case insensitive dictionary.
*
* @param array $data Dictionary/map to convert to case-insensitive
*/
public function __construct(array $data = []) {
foreach ($data as $offset => $value) {
$this->offsetSet($offset, $value);
}
}
/**
* Check if the given item exists
*
* @param string $offset Item key
* @return boolean Does the item exist?
*/
#[ReturnTypeWillChange]
public function offsetExists($offset) {
if (is_string($offset)) {
$offset = strtolower($offset);
}
return isset($this->data[$offset]);
}
/**
* Get the value for the item
*
* @param string $offset Item key
* @return string|null Item value (null if the item key doesn't exist)
*/
#[ReturnTypeWillChange]
public function offsetGet($offset) {
if (is_string($offset)) {
$offset = strtolower($offset);
}
if (!isset($this->data[$offset])) {
return null;
}
return $this->data[$offset];
}
/**
* Set the given item
*
* @param string $offset Item name
* @param string $value Item value
*
* @throws \WpOrg\Requests\Exception On attempting to use dictionary as list (`invalidset`)
*/
#[ReturnTypeWillChange]
public function offsetSet($offset, $value) {
if ($offset === null) {
throw new Exception('Object is a dictionary, not a list', 'invalidset');
}
if (is_string($offset)) {
$offset = strtolower($offset);
}
$this->data[$offset] = $value;
}
/**
* Unset the given header
*
* @param string $offset The key for the item to unset.
*/
#[ReturnTypeWillChange]
public function offsetUnset($offset) {
if (is_string($offset)) {
$offset = strtolower($offset);
}
unset($this->data[$offset]);
}
/**
* Get an iterator for the data
*
* @return \ArrayIterator
*/
#[ReturnTypeWillChange]
public function getIterator() {
return new ArrayIterator($this->data);
}
/**
* Get the headers as an array
*
* @return array Header data
*/
public function getAll() {
return $this->data;
}
}
Core/Base64/Common.php 0000604 00000015027 15133553004 0010421 0 ustar 00 <?php
/**
* Class ParagonIE_Sodium_Core_Base64
*
* Copyright (c) 2016 - 2018 Paragon Initiative Enterprises.
* Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot com)
*
* We have to copy/paste the contents into the variant files because PHP 5.2
* doesn't support late static binding, and we have no better workaround
* available that won't break PHP 7+. Therefore, we're forced to duplicate code.
*/
abstract class ParagonIE_Sodium_Core_Base64_Common
{
/**
* Encode into Base64
*
* Base64 character set "[A-Z][a-z][0-9]+/"
*
* @param string $src
* @return string
* @throws TypeError
*/
public static function encode($src)
{
return self::doEncode($src, true);
}
/**
* Encode into Base64, no = padding
*
* Base64 character set "[A-Z][a-z][0-9]+/"
*
* @param string $src
* @return string
* @throws TypeError
*/
public static function encodeUnpadded($src)
{
return self::doEncode($src, false);
}
/**
* @param string $src
* @param bool $pad Include = padding?
* @return string
* @throws TypeError
*/
protected static function doEncode($src, $pad = true)
{
$dest = '';
$srcLen = ParagonIE_Sodium_Core_Util::strlen($src);
// Main loop (no padding):
for ($i = 0; $i + 3 <= $srcLen; $i += 3) {
/** @var array<int, int> $chunk */
$chunk = unpack('C*', ParagonIE_Sodium_Core_Util::substr($src, $i, 3));
$b0 = $chunk[1];
$b1 = $chunk[2];
$b2 = $chunk[3];
$dest .=
self::encode6Bits( $b0 >> 2 ) .
self::encode6Bits((($b0 << 4) | ($b1 >> 4)) & 63) .
self::encode6Bits((($b1 << 2) | ($b2 >> 6)) & 63) .
self::encode6Bits( $b2 & 63);
}
// The last chunk, which may have padding:
if ($i < $srcLen) {
/** @var array<int, int> $chunk */
$chunk = unpack('C*', ParagonIE_Sodium_Core_Util::substr($src, $i, $srcLen - $i));
$b0 = $chunk[1];
if ($i + 1 < $srcLen) {
$b1 = $chunk[2];
$dest .=
self::encode6Bits($b0 >> 2) .
self::encode6Bits((($b0 << 4) | ($b1 >> 4)) & 63) .
self::encode6Bits(($b1 << 2) & 63);
if ($pad) {
$dest .= '=';
}
} else {
$dest .=
self::encode6Bits( $b0 >> 2) .
self::encode6Bits(($b0 << 4) & 63);
if ($pad) {
$dest .= '==';
}
}
}
return $dest;
}
/**
* decode from base64 into binary
*
* Base64 character set "./[A-Z][a-z][0-9]"
*
* @param string $src
* @param bool $strictPadding
* @return string
* @throws RangeException
* @throws TypeError
* @psalm-suppress RedundantCondition
*/
public static function decode($src, $strictPadding = false)
{
// Remove padding
$srcLen = ParagonIE_Sodium_Core_Util::strlen($src);
if ($srcLen === 0) {
return '';
}
if ($strictPadding) {
if (($srcLen & 3) === 0) {
if ($src[$srcLen - 1] === '=') {
$srcLen--;
if ($src[$srcLen - 1] === '=') {
$srcLen--;
}
}
}
if (($srcLen & 3) === 1) {
throw new RangeException(
'Incorrect padding'
);
}
if ($src[$srcLen - 1] === '=') {
throw new RangeException(
'Incorrect padding'
);
}
} else {
$src = rtrim($src, '=');
$srcLen = ParagonIE_Sodium_Core_Util::strlen($src);
}
$err = 0;
$dest = '';
// Main loop (no padding):
for ($i = 0; $i + 4 <= $srcLen; $i += 4) {
/** @var array<int, int> $chunk */
$chunk = unpack('C*', ParagonIE_Sodium_Core_Util::substr($src, $i, 4));
$c0 = self::decode6Bits($chunk[1]);
$c1 = self::decode6Bits($chunk[2]);
$c2 = self::decode6Bits($chunk[3]);
$c3 = self::decode6Bits($chunk[4]);
$dest .= pack(
'CCC',
((($c0 << 2) | ($c1 >> 4)) & 0xff),
((($c1 << 4) | ($c2 >> 2)) & 0xff),
((($c2 << 6) | $c3 ) & 0xff)
);
$err |= ($c0 | $c1 | $c2 | $c3) >> 8;
}
// The last chunk, which may have padding:
if ($i < $srcLen) {
/** @var array<int, int> $chunk */
$chunk = unpack('C*', ParagonIE_Sodium_Core_Util::substr($src, $i, $srcLen - $i));
$c0 = self::decode6Bits($chunk[1]);
if ($i + 2 < $srcLen) {
$c1 = self::decode6Bits($chunk[2]);
$c2 = self::decode6Bits($chunk[3]);
$dest .= pack(
'CC',
((($c0 << 2) | ($c1 >> 4)) & 0xff),
((($c1 << 4) | ($c2 >> 2)) & 0xff)
);
$err |= ($c0 | $c1 | $c2) >> 8;
} elseif ($i + 1 < $srcLen) {
$c1 = self::decode6Bits($chunk[2]);
$dest .= pack(
'C',
((($c0 << 2) | ($c1 >> 4)) & 0xff)
);
$err |= ($c0 | $c1) >> 8;
} elseif ($i < $srcLen && $strictPadding) {
$err |= 1;
}
}
/** @var bool $check */
$check = ($err === 0);
if (!$check) {
throw new RangeException(
'Base64::decode() only expects characters in the correct base64 alphabet'
);
}
return $dest;
}
/**
* Uses bitwise operators instead of table-lookups to turn 6-bit integers
* into 8-bit integers.
*
* Base64 character set:
* [A-Z] [a-z] [0-9] + /
* 0x41-0x5a, 0x61-0x7a, 0x30-0x39, 0x2b, 0x2f
*
* @param int $src
* @return int
*/
abstract protected static function decode6Bits($src);
/**
* Uses bitwise operators instead of table-lookups to turn 8-bit integers
* into 6-bit integers.
*
* @param int $src
* @return string
*/
abstract protected static function encode6Bits($src);
}