HEX
Server: LiteSpeed
System: Linux eko108.isimtescil.net 4.18.0-477.21.1.lve.1.el8.x86_64 #1 SMP Tue Sep 5 23:08:35 UTC 2023 x86_64
User: uyarreklamcomtr (11202)
PHP: 7.4.33
Disabled: opcache_get_status
Upload Files
File: /var/www/vhosts/uyarreklam.com.tr/httpdocs/Core.tar
BLAKE2b.php000064400000000142151536240570006324 0ustar00<?php
namespace ParagonIE\Sodium\Core;

class BLAKE2b extends \ParagonIE_Sodium_Core_BLAKE2b
{

}
Base64/Common.php000064400000015027151536240570007546 0ustar00<?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);
}
Base64/Original.php000064400000017055151536240570010065 0ustar00<?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);
    }
}
Base64/UrlSafe.php000064400000017063151536240570007661 0ustar00<?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);
    }
}
ChaCha20/Ctx.php000064400000000154151536240570007274 0ustar00<?php
namespace ParagonIE\Sodium\Core\ChaCha20;

class Ctx extends \ParagonIE_Sodium_Core_ChaCha20_Ctx
{

}
ChaCha20/IetfCtx.php000064400000000164151536240570010105 0ustar00<?php
namespace ParagonIE\Sodium\Core\ChaCha20;

class IetfCtx extends \ParagonIE_Sodium_Core_ChaCha20_IetfCtx
{

}
ChaCha20.php000064400000000144151536240570006535 0ustar00<?php
namespace ParagonIE\Sodium\Core;

class ChaCha20 extends \ParagonIE_Sodium_Core_ChaCha20
{

}
Curve25519/Fe.php000064400000000156151536240570007313 0ustar00<?php
namespace ParagonIE\Sodium\Core\Curve25519;

class Fe extends \ParagonIE_Sodium_Core_Curve25519_Fe
{

}
Curve25519/Ge/Cached.php000064400000000174151536240570010463 0ustar00<?php
namespace ParagonIE\Sodium\Core\Curve25519\Ge;

class Cached extends \ParagonIE_Sodium_Core_Curve25519_Ge_Cached
{

}
Curve25519/Ge/P1p1.php000064400000000170151536240570010031 0ustar00<?php
namespace ParagonIE\Sodium\Core\Curve25519\Ge;

class P1p1 extends \ParagonIE_Sodium_Core_Curve25519_Ge_P1p1
{

}
Curve25519/Ge/P2.php000064400000000164151536240570007574 0ustar00<?php
namespace ParagonIE\Sodium\Core\Curve25519\Ge;

class P2 extends \ParagonIE_Sodium_Core_Curve25519_Ge_P2
{

}
Curve25519/Ge/P3.php000064400000000164151536240570007575 0ustar00<?php
namespace ParagonIE\Sodium\Core\Curve25519\Ge;

class P3 extends \ParagonIE_Sodium_Core_Curve25519_Ge_P3
{

}
Curve25519/Ge/Precomp.php000064400000000176151536240570010723 0ustar00<?php
namespace ParagonIE\Sodium\Core\Curve25519\Ge;

class Precomp extends \ParagonIE_Sodium_Core_Curve25519_Ge_Precomp
{

}
Curve25519/H.php000064400000000154151536240570007146 0ustar00<?php
namespace ParagonIE\Sodium\Core\Curve25519;

class H extends \ParagonIE_Sodium_Core_Curve25519_H
{

}
Curve25519/README.md000064400000000332151536240570007523 0ustar00# 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).
Curve25519.php000064400000000150151536240570006753 0ustar00<?php
namespace ParagonIE\Sodium\Core;

class Curve25519 extends \ParagonIE_Sodium_Core_Curve25519
{

}
Ed25519.php000064400000000142151536240570006220 0ustar00<?php
namespace ParagonIE\Sodium\Core;

class Ed25519 extends \ParagonIE_Sodium_Core_Ed25519
{

}
HChaCha20.php000064400000000146151536240570006647 0ustar00<?php
namespace ParagonIE\Sodium\Core;

class HChaCha20 extends \ParagonIE_Sodium_Core_HChaCha20
{

}
HSalsa20.php000064400000000144151536240570006601 0ustar00<?php
namespace ParagonIE\Sodium\Core;

class HSalsa20 extends \ParagonIE_Sodium_Core_HSalsa20
{

}
Poly1305/State.php000064400000000160151536240570007576 0ustar00<?php
namespace ParagonIE\Sodium\Core\Poly1305;

class State extends \ParagonIE_Sodium_Core_Poly1305_State
{

}
Poly1305.php000064400000000144151536240570006520 0ustar00<?php
namespace ParagonIE\Sodium\Core;

class Poly1305 extends \ParagonIE_Sodium_Core_Poly1305
{

}
Ristretto255.php000064400000052574151536240570007535 0ustar00<?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;
    }
}
Salsa20.php000064400000000142151536240570006467 0ustar00<?php
namespace ParagonIE\Sodium\Core;

class Salsa20 extends \ParagonIE_Sodium_Core_Salsa20
{

}
SecretStream/State.php000064400000007050151536240570010750 0ustar00<?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;
    }
}
SipHash.php000064400000000142151536240570006621 0ustar00<?php
namespace ParagonIE\Sodium\Core;

class SipHash extends \ParagonIE_Sodium_Core_SipHash
{

}
Util.php000064400000000134151536240570006200 0ustar00<?php
namespace ParagonIE\Sodium\Core;

class Util extends \ParagonIE_Sodium_Core_Util
{

}
X25519.php000064400000000140151536240570006075 0ustar00<?php
namespace ParagonIE\Sodium\Core;

class X25519 extends \ParagonIE_Sodium_Core_X25519
{

}
XChaCha20.php000064400000000146151536240570006667 0ustar00<?php
namespace ParagonIE\Sodium\Core;

class XChaCha20 extends \ParagonIE_Sodium_Core_XChaCha20
{

}
XSalsa20.php000064400000002533151536240570006625 0ustar00<?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
            )
        );
    }
}
Xsalsa20.php000064400000000144151537012360006654 0ustar00<?php
namespace ParagonIE\Sodium\Core;

class Xsalsa20 extends \ParagonIE_Sodium_Core_XSalsa20
{

}
Assets.php000064400000036663151540271460006542 0ustar00<?php
namespace AIOSEO\BrokenLinkChecker\Core;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Handles the loading of our file assets.
 *
 * @since 1.0.0
 */
class Assets {
	/**
	 * Whether we should load dev scripts.
	 *
	 * @since 1.0.0
	 *
	 * @var boolean|null
	 */
	private $shouldLoadDevScripts = null;

	/**
	 * The script handle to use for asset enqueuing.
	 *
	 * @since 1.0.0
	 *
	 * @var string
	 */
	private $scriptHandle = 'aioseo-broken-link-checker';

	/**
	 * Holds the location of the manifest file.
	 *
	 * @since 1.0.0
	 *
	 * @var string
	 */
	private $manifestFile = '';

	/**
	 * True if we are in a dev environment. This mirrors the global isDev.
	 *
	 * @since 1.0.0
	 *
	 * @var bool
	 */
	private $isDev = false;

	/**
	 * Asset handles that should load as regular JS and not as modern JS module.
	 *
	 * @since 1.0.0
	 *
	 * @var array An array of handles.
	 */
	private $noModuleTag = [];

	/**
	 * Core class instance.
	 *
	 * @since 1.1.0
	 *
	 * @var Core
	 */
	public $core = null;

	/**
	 * The plugin version.
	 *
	 * @since 1.1.0
	 *
	 * @var int
	 */
	public $version = 0;

	/**
	 * The domain.
	 *
	 * @since 1.1.0
	 *
	 * @var string
	 */
	public $domain = '';

	/**
	 * The port.
	 *
	 * @since 1.1.0
	 *
	 * @var int
	 */
	public $port = 0;

	/**
	 * Class constructor.
	 *
	 * @since 1.0.0
	 *
	 * @param Core $core The AIOSEO Core class.
	 */
	public function __construct( $core ) {
		$this->core         = $core;
		$this->version      = aioseoBrokenLinkChecker()->version;
		$this->manifestFile = AIOSEO_BROKEN_LINK_CHECKER_DIR . '/dist/manifest.php';
		$this->isDev        = aioseoBrokenLinkChecker()->isDev;

		if ( $this->isDev ) {
			$this->domain = getenv( 'VITE_AIOSEO_BROKEN_LINK_CHECKER_DOMAIN' );
			$this->port   = getenv( 'VITE_AIOSEO_BROKEN_LINK_CHECKER_DEV_PORT' );
		}

		add_filter( 'script_loader_tag', [ $this, 'scriptLoaderTag' ], 10, 3 );
	}

	/**
	 * The asset to load.
	 *
	 * @since 1.0.0
	 *
	 * @param  string $asset        The asset to load.
	 * @param  array  $dependencies An array of dependencies.
	 * @param  mixed  $data         Any data to be localized.
	 * @param  string $objectName   The object name to use when localizing.
	 * @return void
	 */
	public function load( $asset, $dependencies = [], $data = null, $objectName = 'aioseoBrokenLinkChecker' ) {
		$this->jsPreloadImports( $asset );
		$this->loadCss( $asset );
		$this->enqueueJs( $asset, $dependencies, $data, $objectName );
	}

	/**
	 * Filter the script loader tag if this is our script.
	 *
	 * @since 1.0.0
	 *
	 * @param  string $tag    The tag that is going to be output.
	 * @param  string $handle The handle for the script.
	 * @return string         The modified tag.
	 */
	public function scriptLoaderTag( $tag, $handle, $src ) {
		if ( $this->skipModuleTag( $handle ) ) {
			return $tag;
		}

		$tag = str_replace( $src, $this->normalizeAssetsHost( $src ), $tag );

		// Remove the type and re-add it as module.
		$tag = preg_replace( '/type=[\'"].*?[\'"]/', '', (string) $tag );
		$tag = preg_replace( '/<script/', '<script type="module"', (string) $tag );

		return $tag;
	}

	/**
	 * Preload JS imports.
	 *
	 * @since 1.0.0
	 *
	 * @param  string $asset The asset to load imports for.
	 * @return void
	 */
	private function jsPreloadImports( $asset ) {
		static $urls = []; // Prevent script from being loaded multiple times.

		$res = '';
		foreach ( $this->importsUrls( $asset ) as $url ) {
			if ( isset( $urls[ $url ] ) ) {
				continue;
			}

			$urls[ $url ] = true;

			$res .= '<link rel="modulepreload" href="' . esc_attr( $url ) . "\">\n";
		}

		$allowedHtml = [
			'link' => [
				'rel'  => [],
				'href' => []
			]
		];

		if ( ! empty( $res ) ) {
			if ( ! function_exists( 'wp_enqueue_script_module' ) ) {
				add_action( 'admin_head', function () use ( &$res, $allowedHtml ) {
					echo wp_kses( $res, $allowedHtml );
				} );
				add_action( 'wp_head', function () use ( &$res, $allowedHtml ) {
					echo wp_kses( $res, $allowedHtml );
				} );
			} else {
				add_action( 'admin_print_footer_scripts', function () use ( &$res, $allowedHtml ) {
					echo wp_kses( $res, $allowedHtml );
				}, 1000 );
			}
		}
	}

	/**
	 * Loads CSS for an asset from the manifest file.
	 *
	 * @since 1.0.0
	 *
	 * @param  string $asset The script to load CSS for.
	 * @return void
	 */
	private function loadCss( $asset ) {
		if ( $this->shouldLoadDev() ) {
			return;
		}

		foreach ( $this->getCssUrls( $asset ) as $file => $url ) {
			wp_enqueue_style( $this->cssHandle( $file ), $url, [], $this->version );
		}
	}

	/**
	 * Register a CSS asset.
	 *
	 * @since 1.0.0
	 *
	 * @param  string $asset        The script to load CSS for.
	 * @param  array  $dependencies An array of dependencies.
	 * @param  string $devPath      The file's dev path.
	 * @return void
	 */
	public function registerCss( $asset, $dependencies = [], $devPath = '' ) {
		$handle = $this->cssHandle( $asset );
		if ( wp_style_is( $handle, 'registered' ) ) {
			return;
		}

		$devPath = $devPath ?: $asset;

		$url = $this->shouldLoadDev()
			? $this->getDevUrl() . ltrim( $devPath, '/' )
			: $this->assetUrl( $asset );

		if ( ! $url ) {
			return;
		}

		wp_register_style( $handle, $url, $dependencies, $this->version );
	}

	/**
	 * Enqueue css.
	 *
	 * @since 1.0.0
	 *
	 * @param  string $asset        The css to load.
	 * @param  string $devPath      The file's dev path.
	 * @param  array  $dependencies An array of dependencies.
	 * @return void
	 */
	public function enqueueCss( $asset, $dependencies = [], $devPath = '' ) {
		$this->registerCss( $asset, $dependencies, $devPath );

		$handle = $this->cssHandle( $asset );
		if ( wp_style_is( $handle, 'enqueued' ) ) {
			return;
		}

		wp_enqueue_style( $handle );
	}

	/**
	 * Register the JS to enqueue.
	 *
	 * @since 1.0.0
	 *
	 * @param  string $asset        The script to load.
	 * @param  array  $dependencies An array of dependencies.
	 * @param  mixed  $data         Any data to be localized.
	 * @param  string $objectName   The object name to use when localizing.
	 * @return void
	 */
	public function registerJs( $asset, $dependencies = [], $data = null, $objectName = 'aioseoBrokenLinkChecker' ) {
		$handle = $this->jsHandle( $asset );
		if ( wp_script_is( $handle, 'registered' ) ) {
			return;
		}

		$url = $this->shouldLoadDev()
			? $this->getDevUrl() . ltrim( $asset, '/' )
			: $this->jsUrl( $asset );

		if ( ! $url ) {
			return;
		}

		wp_register_script( $handle, $url, $dependencies, $this->version, true );

		if ( empty( $data ) ) {
			return;
		}

		wp_localize_script(
			$handle,
			$objectName,
			$data
		);
	}

	/**
	 * Register the JS to enqueue.
	 *
	 * @since 1.0.0
	 *
	 * @param  string $asset        The script to load.
	 * @param  array  $dependencies An array of dependencies.
	 * @param  mixed  $data         Any data to be localized.
	 * @param  string $objectName   The object name to use when localizing.
	 * @return void
	 */
	public function enqueueJs( $asset, $dependencies = [], $data = null, $objectName = 'aioseoBrokenLinkChecker' ) {
		$this->registerJs( $asset, $dependencies, $data, $objectName );

		$handle = $this->jsHandle( $asset );
		if ( wp_script_is( $handle, 'enqueued' ) ) {
			return;
		}

		wp_enqueue_script( $handle );
	}

	/**
	 * Return the dev URL.
	 *
	 * @since 1.0.0
	 *
	 * @return string The dev URL.
	 */
	private function getDevUrl() {
		$protocol = is_ssl() ? 'https://' : 'http://';

		return $protocol . $this->domain . ':' . $this->port . '/';
	}

	/**
	 * Get the asset URL.
	 *
	 * @since 1.0.0
	 *
	 * @param  string $asset The asset to find the URL for.
	 * @return string        The URL for the asset.
	 */
	private function assetUrl( $asset ) {
		$assetManifest = $this->getAssetManifestItem( $asset );

		return ! empty( $assetManifest['file'] )
			? $this->basePath() . $assetManifest['file']
			: $this->basePath() . ltrim( $asset, '/' );
	}

	/**
	 * Get the JS URL.
	 *
	 * @since 1.0.0
	 *
	 * @param  string $asset The asset to find the URL for.
	 * @return string        The URL for the asset.
	 */
	public function jsUrl( $asset ) {
		$manifestAsset = $this->getManifestItem( $asset );

		return ! empty( $manifestAsset['file'] )
			? $this->basePath() . $manifestAsset['file']
			: $this->basePath() . ltrim( $asset, '/' );
	}

	/**
	 * Get an item from the manifest.
	 *
	 * @since 1.0.0
	 *
	 * @param  string $asset The asset to find.
	 * @return string        Manifest object.
	 */
	private function getManifestItem( $asset ) {
		$manifest = $this->getManifest();

		$asset = ltrim( $asset, '/' );

		return isset( $manifest[ $asset ] ) ? $manifest[ $asset ] : null;
	}

	/**
	 * Get the CSS asset handle.
	 *
	 * @since 1.0.0
	 *
	 * @param  string $asset The asset to find the handle for.
	 * @return string        The asset handle.
	 */
	public function cssHandle( $asset ) {
		return "{$this->scriptHandle}/css/$asset";
	}

	/**
	 * Get the JS asset handle.
	 *
	 * @since 1.0.0
	 *
	 * @param  string $asset The asset to find the handle for.
	 * @return string        The asset handle.
	 */
	public function jsHandle( $asset = '' ) {
		return "{$this->scriptHandle}/js/$asset";
	}

	/**
	 * Get the manifest to load assets from.
	 *
	 * @since 1.0.0
	 *
	 * @return array An array of files.
	 */
	private function getManifest() {
		static $file = null;
		if ( $file ) {
			return $file;
		}

		$manifestJson = ''; // This is set in the view.
		if ( file_exists( $this->manifestFile ) ) {
			require_once $this->manifestFile;
		}

		$file = json_decode( $manifestJson, true );

		return $file;
	}

	/**
	 * Get an item from the asset manifest.
	 *
	 * @since 1.0.0
	 *
	 * @param  string      $item An item to retrieve.
	 * @return string|null       The asset item.
	 */
	private function getAssetManifestItem( $item ) {
		$assetManifest = $this->getManifest();

		return ! empty( $assetManifest[ $item ] ) ? $assetManifest[ $item ] : null;
	}

	/**
	 * Get an asset's array of URLs to import.
	 *
	 * @since 1.0.0
	 *
	 * @param  string $asset The asset to find imports for.
	 * @return array         An array of imports.
	 */
	private function importsUrls( $asset ) {
		$urls          = [];
		$manifestAsset = $this->getManifestItem( $asset );
		if ( ! empty( $manifestAsset['imports'] ) ) {
			foreach ( $manifestAsset['imports'] as $import ) {
				$importAsset = $this->getManifestItem( $import );
				if ( ! empty( $importAsset['file'] ) ) {
					$urls[] = $this->getPublicUrlBase() . $importAsset['file'];

					// Load the import's CSS if any.
					$this->loadCss( $import );
				}
			}
		}

		return $urls;
	}

	/**
	 * Returns an asset's CSS urls.
	 *
	 * @since 1.0.0
	 *
	 * @param  string $asset The asset to find CSS URLs for.
	 * @return array         An array of CSS URLs to load.
	 */
	private function getCssUrls( $asset ) {
		$urls          = [];
		$manifestAsset = $this->getManifestItem( $asset );

		if ( ! empty( $manifestAsset['css'] ) ) {
			foreach ( $manifestAsset['css'] as $file ) {
				$urls[ $file ] = $this->getPublicUrlBase() . $file;
			}
		}

		return $urls;
	}

	/**
	 * Check if we should load the dev watcher scripts.
	 *
	 * @since 1.0.0
	 *
	 * @return boolean True if we should load the dev watcher scripts.
	 */
	private function shouldLoadDev() {
		if ( null !== $this->shouldLoadDevScripts ) {
			return $this->shouldLoadDevScripts;
		}

		if (
			! $this->isDev ||
			(
				defined( 'AIOSEO_BROKEN_LINK_CHECKER_LOAD_DEV_SCRIPTS' ) &&
				false === AIOSEO_BROKEN_LINK_CHECKER_LOAD_DEV_SCRIPTS
			)
		) {
			$this->shouldLoadDevScripts = false;

			return $this->shouldLoadDevScripts;
		}

		if ( ! $this->domain && ! $this->port ) {
			$this->shouldLoadDevScripts = false;

			return $this->shouldLoadDevScripts;
		}

		set_error_handler( function() {} ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_set_error_handler
		$connection = fsockopen( $this->domain, $this->port ); // phpcs:ignore WordPress.WP.AlternativeFunctions
		restore_error_handler();

		if ( ! $connection ) {
			$this->shouldLoadDevScripts = false;

			return $this->shouldLoadDevScripts;
		}

		$this->shouldLoadDevScripts = true;

		return $this->shouldLoadDevScripts;
	}

	/**
	 * Get the path for the assets.
	 *
	 * @since 1.0.0
	 *
	 * @param  bool   $maybeDev Whether to try and load dev scripts.
	 * @return string           The path for the assets.
	 */
	public function getAssetsPath( $maybeDev = true ) {
		return $maybeDev && $this->shouldLoadDev()
			? $this->getDevUrl()
			: $this->basePath();
	}

	/**
	 * Finds out if a handle should be loaded as regular JS and not as modern JS module.
	 *
	 * @since 1.0.0
	 *
	 * @param  string $handle The script handle.
	 * @return bool           Should the module tag be skipped.
	 */
	public function skipModuleTag( $handle ) {
		if ( ! aioseoBrokenLinkChecker()->helpers->stringContains( $handle, $this->jsHandle( '' ) ) ) {
			return true;
		}

		foreach ( $this->noModuleTag as $tag ) {
			if ( aioseoBrokenLinkChecker()->helpers->stringContains( $handle, $tag ) ) {
				return true;
			}
		}

		return false;
	}

	/**
	 * Normalize the assets host. Some sites manually set the WP_PLUGINS_URL
	 * and if that domain has www. and the site_url does not, then it will fail to load
	 * our assets. This doesn't fix the issue 100% because it will still fail on
	 * sub-domains that don't have the proper CORS headers. Those sites will need
	 * manual fixes.
	 *
	 * 4.1.10
	 *
	 * @param  string $path The path to normalize.
	 * @return string       The normalized path.
	 */
	public function normalizeAssetsHost( $path ) {
		static $paths = [];
		if ( isset( $paths[ $path ] ) ) {
			return apply_filters( 'aioseo_blc_normalize_assets_host', $paths[ $path ] );
		}

		// We need to verify the domain on the $path attribute matches
		// what's in site_url() for our assets or they won't load.
		$siteUrl        = site_url();
		$siteUrlEscaped = aioseoBrokenLinkChecker()->helpers->escapeRegex( $siteUrl );
		if ( preg_match( "/^$siteUrlEscaped/i", (string) $path ) ) {
			$paths[ $path ] = $path;

			return apply_filters( 'aioseo_blc_normalize_assets_host', $paths[ $path ] );
		}

		// We now know that the path doesn't contain the site_url().
		$newPath        = $path;
		$siteUrlParsed  = wp_parse_url( $siteUrl );
		$host           = aioseoBrokenLinkChecker()->helpers->escapeRegex( str_replace( 'www.', '', $siteUrlParsed['host'] ) );
		$scheme         = aioseoBrokenLinkChecker()->helpers->escapeRegex( $siteUrlParsed['scheme'] );

		$siteUrlHasWww = preg_match( "/^{$scheme}:\/\/www\.$host/", (string) $siteUrl );
		$pathHasWww    = preg_match( "/^{$scheme}:\/\/www\.$host/", (string) $path );

		// Check if the path contains www.
		if ( $pathHasWww && ! $siteUrlHasWww ) {
			// If the path contains www., we want to strip it out.
			$newPath = preg_replace( "/^({$scheme}:\/\/)(www\.)($host)/", '$1$3', (string) $path );
		}

		// Check if the site_url contains www.
		if ( $siteUrlHasWww && ! $pathHasWww ) {
			// If the site_url contains www., we want to add it in to the path.
			$newPath = preg_replace( "/^({$scheme}:\/\/)($host)/", '$1www.$2', (string) $path );
		}

		$paths[ $path ] = $newPath;

		return apply_filters( 'aioseo_blc_normalize_assets_host', $paths[ $path ] );
	}

	/**
	 * Returns the public URL base.
	 *
	 * @since 1.0.0
	 *
	 * @return string The URL base.
	 */
	private function getPublicUrlBase() {
		return $this->shouldLoadDev() ? $this->getDevUrl() . 'dist/assets/' : $this->basePath();
	}

	/**
	 * Returns the base path URL.
	 *
	 * @since 1.0.0
	 *
	 * @return string The base path URL.
	 */
	private function basePath() {
		return $this->normalizeAssetsHost( plugins_url( 'dist/assets/', AIOSEO_BROKEN_LINK_CHECKER_FILE ) );
	}
}Cache.php000064400000014221151540271460006265 0ustar00<?php
namespace AIOSEO\BrokenLinkChecker\Core;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Handles our cache.
 *
 * @since 1.0.0
 */
class Cache {
	/**
	 * The name of our cache table.
	 *
	 * @since 1.0.0
	 *
	 * @var string
	 */
	private $table = 'aioseo_blc_cache';

	/**
	 * Our cache.
	 *
	 * @since 1.0.0
	 *
	 * @var array
	 */
	private static $cache = [];

	/**
	 * Prefix for this cache.
	 *
	 * @since 1.0.0
	 *
	 * @var string
	 */
	protected $prefix = 'aioseo_blc_';

	/**
	 * Class constructor.
	 *
	 * @since 4.7.8
	 */
	public function __construct() {
		add_action( 'init', [ $this, 'checkIfTableExists' ] ); // This needs to run on init because the DB
		// class gets instantiated along with the cache class.
	}

	/**
	 * Checks if the cache table exists and creates it if it doesn't.
	 *
	 * @since 4.7.8
	 *
	 * @return void
	 */
	public function checkIfTableExists() {
		if ( ! aioseoBrokenLinkChecker()->core->db->tableExists( $this->table ) ) {
			aioseoBrokenLinkChecker()->preUpdates->createCacheTable();
		}
	}

	/**
	 * Returns the cache value if it exists and isn't expired.
	 *
	 * @since 1.0.0
	 *
	 * @param  string $key The key name. Use a '%' for a LIKE query.
	 * @return mixed       The value or null if the cache does not exist.
	 */
	public function get( $key ) {
		$key = $this->prepareKey( $key );
		if ( isset( self::$cache[ $key ] ) ) {
			return self::$cache[ $key ];
		}

		// Check if we're supposed to do a LIKE get.
		$isLikeGet = preg_match( '/%/', (string) $key );

		$result = aioseoBrokenLinkChecker()->core->db
			->start( $this->table )
			->select( '`key`, `value`' )
			->whereRaw( '( `expiration` IS NULL OR `expiration` > \'' . aioseoBrokenLinkChecker()->helpers->timeToMysql( time() ) . '\' )' );

		$isLikeGet ?
			$result->whereRaw( '`key` LIKE \'' . $key . '\'' ) :
			$result->where( 'key', $key );

		$result->output( ARRAY_A )->run();

		// If we have nothing in the cache, let's return null.
		$values = $result->nullSet() ? null : $result->result();

		// If we have something, let's normalize it.
		if ( $values ) {
			foreach ( $values as &$value ) {
				$value['value'] = aioseoBrokenLinkChecker()->helpers->maybeUnserialize( $value['value'] );
			}
			// Return only the single cache value.
			if ( ! $isLikeGet ) {
				$values = $values[0]['value'];
			}
		}

		// Return values without a static cache.
		// This is here because clearing the LIKE cache is not simple.
		if ( $isLikeGet ) {
			return $values;
		}

		self::$cache[ $key ] = $values;

		return self::$cache[ $key ];
	}

	/**
	 * Updates the given cache or creates it if it doesn't exist.
	 *
	 * @since 1.0.0
	 *
	 * @param  string $key        The key name.
	 * @param  mixed  $value      The value.
	 * @param  int    $expiration The expiration time in seconds. Defaults to 24 hours. 0 to no expiration.
	 * @return void
	 */
	public function update( $key, $value, $expiration = DAY_IN_SECONDS ) {
		// If the value is null we'll convert it and give it a shorter expiration.
		if ( null === $value ) {
			$value      = false;
			$expiration = 10 * MINUTE_IN_SECONDS;
		}

		$value      = serialize( $value );
		$expiration = 0 < $expiration ? aioseoBrokenLinkChecker()->helpers->timeToMysql( time() + $expiration ) : null;

		aioseoBrokenLinkChecker()->core->db->insert( $this->table )
			->set( [
				'key'        => $this->prepareKey( $key ),
				'value'      => $value,
				'expiration' => $expiration,
				'created'    => aioseoBrokenLinkChecker()->helpers->timeToMysql( time() ),
				'updated'    => aioseoBrokenLinkChecker()->helpers->timeToMysql( time() )
			] )->onDuplicate( [
				'value'      => $value,
				'expiration' => $expiration,
				'updated'    => aioseoBrokenLinkChecker()->helpers->timeToMysql( time() )
			] )
			->run();

		$this->clearStatic( $key );
	}

	/**
	 * Deletes the cache record with the given key.
	 *
	 * @since 1.0.0
	 *
	 * @param  string $key The key.
	 * @return void
	 */
	public function delete( $key ) {
		$key = $this->prepareKey( $key );

		aioseoBrokenLinkChecker()->core->db->delete( $this->table )
			->where( 'key', $key )
			->run();

		$this->clearStatic( $key );
	}

	/**
	 * Prepares the key before using the cache.
	 *
	 * @since 1.0.0
	 *
	 * @param  string $key The key to prepare.
	 * @return string      The prepared key.
	 */
	private function prepareKey( $key ) {
		$key = trim( $key );
		$key = $this->prefix && 0 !== strpos( $key, $this->prefix ) ? $this->prefix . $key : $key;

		if ( aioseoBrokenLinkChecker()->helpers->isDev() && 80 < mb_strlen( $key, 'UTF-8' ) ) {
			throw new \Exception( 'You are using a cache key that is too large, shorten your key and try again: [' . esc_html( $key ) . ']' );
		}

		return $key;
	}

	/**
	 * Clears all of our cache.
	 *
	 * @since 1.0.0
	 *
	 * @return void
	 */
	public function clear() {
		// Bust the tableExists and columnExists cache.
		aioseoBrokenLinkChecker()->internalOptions->database->installedTables = '';

		if ( $this->prefix ) {
			$this->clearPrefix( '' );

			return;
		}

		aioseoBrokenLinkChecker()->core->db->truncate( $this->table )->run();

		$this->clearStatic();
	}

	/**
	 * Clears all of our cache under a certain prefix.
	 *
	 * @since 1.0.0
	 *
	 * @param  string $prefix A prefix to clear or empty to clear everything.
	 * @return void
	 */
	public function clearPrefix( $prefix ) {
		$prefix = $this->prepareKey( $prefix );

		aioseoBrokenLinkChecker()->core->db->delete( $this->table )
			->whereRaw( "`key` LIKE '$prefix%'" )
			->run();

		$this->clearStaticPrefix( $prefix );
	}

	/**
	 * Clears all of our static in-memory cache of a prefix.
	 *
	 * @since 1.0.0
	 *
	 * @param  string $prefix The prefix to clear.
	 * @return void
	 */
	private function clearStaticPrefix( $prefix ) {
		$prefix = $this->prepareKey( $prefix );
		foreach ( array_keys( self::$cache ) as $key ) {
			if ( 0 === strpos( $key, $prefix ) ) {
				unset( self::$cache[ $key ] );
			}
		}
	}

	/**
	 * Clears all of our static in-memory cache.
	 *
	 * @since 1.0.0
	 *
	 * @param  string $key The key to clear.
	 * @return void
	 */
	private function clearStatic( $key = null ) {
		if ( empty( $key ) ) {
			self::$cache = [];

			return;
		}

		unset( self::$cache[ $this->prepareKey( $key ) ] );
	}
}Core.php000064400000002637151540271460006162 0ustar00<?php
namespace AIOSEO\BrokenLinkChecker\Core;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

use AIOSEO\BrokenLinkChecker\Options;
use AIOSEO\BrokenLinkChecker\Utils;

/**
 * Loads core classes.
 *
 * @since 1.0.0
 */
class Core {
	/**
	 * DB class instance.
	 *
	 * @since 1.1.0
	 *
	 * @var Database
	 */
	public $db = null;

	/**
	 * Filesystem class instance.
	 *
	 * @since 1.1.0
	 *
	 * @var Filesystem
	 */
	public $fs = null;

	/**
	 * Assets class instance.
	 *
	 * @since 1.1.0
	 *
	 * @var Assets
	 */
	public $assets = null;

	/**
	 * Cache class instance.
	 *
	 * @since 1.1.0
	 *
	 * @var Cache
	 */
	public $cache = null;

	/**
	 * NetworkCache class instance.
	 *
	 * @since 1.1.0
	 *
	 * @var NetworkCache
	 */
	public $networkCache = null;

	/**
	 * Options Cache class instance.
	 *
	 * @since 1.1.0
	 *
	 * @var \AIOSEO\BrokenLinkChecker\Options\Cache
	 */
	public $optionsCache = null;

	/**
	 * Uninstall class instance.
	 *
	 * @since 1.1.0
	 *
	 * @var Uninstall
	 */
	public $uninstall = null;

	/**
	 * Class constructor.
	 *
	 * @since 1.0.0
	 */
	public function __construct() {
		$this->db           = new Database();
		$this->fs           = new Filesystem( $this );
		$this->assets       = new Assets( $this );
		$this->cache        = new Cache();
		$this->networkCache = new NetworkCache();
		$this->optionsCache = new Options\Cache();
		$this->uninstall    = new Uninstall();
	}
}Database.php000064400000117531151540271460006776 0ustar00<?php
namespace AIOSEO\BrokenLinkChecker\Core;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Builds queries for the database and returns results.
 *
 * @since 1.0.0
 */
class Database {
	/**
	 * List of custom tables we support.
	 *
	 * @since 1.0.0
	 *
	 * @var array
	 */
	public $customTables = [
		'aioseo_blc_cache',
		'aioseo_blc_links',
		'aioseo_blc_link_status',
		'aioseo_blc_notifications',
		'aioseo_blc_posts'
	];

	/**
	 * Holds the global $wpdb instance.
	 *
	 * @since 1.0.0
	 *
	 * @var \wpdb
	 */
	public $db = null;

	/**
	 * Holds the $wpdb prefix.
	 *
	 * @since 1.0.0
	 *
	 * @var string
	 */
	public $prefix = '';

	/**
	 * The database table in use by this query.
	 *
	 * @since 1.0.0
	 *
	 * @var string
	 */
	public $table = '';

	/**
	 * The sql statement (SELECT, INSERT, UPDATE, DELETE, etc.).
	 *
	 * @since 1.0.0
	 *
	 * @var string
	 */
	private $statement = '';

	/**
	 * The limit clause for the sql query.
	 *
	 * @since 1.0.0
	 *
	 * @var string
	 */
	private $limit = '';

	/**
	 * The group clause for the sql query.
	 *
	 * @since 1.0.0
	 *
	 * @var array
	 */
	private $group = [];

	/**
	 * The order by clause for the sql query.
	 *
	 * @since 1.0.0
	 *
	 * @var array
	 */
	private $order = [];

	/**
	 * The select clause for the sql query.
	 *
	 * @since 1.0.0
	 *
	 * @var array
	 */
	private $select = [];

	/**
	 * The set clause for the sql query.
	 *
	 * @since 1.0.0
	 *
	 * @var array
	 */
	private $set = [];

	/**
	 * Duplicate clause for the INSERT query.
	 *
	 * @since 1.0.0
	 *
	 * @var array
	 */
	private $onDuplicate = [];

	/**
	 * Ignore clause for the INSERT query.
	 *
	 * @since 1.0.0
	 *
	 * @var array
	 */
	private $ignore = false;

	/**
	 * The where clause for the sql query.
	 *
	 * @since 1.0.0
	 *
	 * @var array
	 */
	private $where = [];

	/**
	 * The union clause for the sql query.
	 *
	 * @since 1.0.0
	 *
	 * @var array
	 */
	private $union = [];

	/**
	 * The JOIN clause for the SQL query.
	 *
	 * @since 1.1.0
	 *
	 * @var array
	 */
	private $join = [];

	/**
	 * Determines whether the select statement should be distinct.
	 *
	 * @since 1.0.0
	 *
	 * @var bool
	 */
	private $distinct = false;

	/**
	 * The order by direction for the query.
	 *
	 * @since 1.0.0
	 *
	 * @var string
	 */
	private $orderDirection = 'ASC';

	/**
	 * The query string is populated after the __toString function is run.
	 *
	 * @since 1.0.0
	 *
	 * @var string
	 */
	private $query = '';

	/**
	 * The sql query results are stored here.
	 *
	 * @since 1.0.0
	 *
	 * @var mixed
	 */
	private $result = [];

	/**
	 * The method in which $wpdb will output results.
	 *
	 * @since 1.0.0
	 *
	 * @var string
	 */
	private $output = 'OBJECT';

	/**
	 * Whether or not to strip tags.
	 *
	 * @since 1.0.0
	 *
	 * @var boolean
	 */
	private $stripTags = false;

	/**
	 * Set which option to use to escape the sql query.
	 *
	 * @since 1.0.0
	 *
	 * @var integer
	 */
	protected $escapeOptions = 0;

	/**
	 * A cache of all queries and their results.
	 *
	 * @var array
	 */
	private $cache = [];

	/**
	 * Whether or not to reset the cached results.
	 *
	 * @var boolean
	 */
	private $shouldResetCache = false;

	/**
	 * Constant for escape options.
	 *
	 * @since 1.0.0
	 *
	 * @var integer
	 */
	const ESCAPE_FORCE = 2;

	/**
	 * Constant for escape options.
	 *
	 * @since 1.0.0
	 *
	 * @var integer
	 */
	const ESCAPE_STRIP_HTML = 4;

	/**
	 * Constant for escape options.
	 *
	 * @since 1.0.0
	 *
	 * @var integer
	 */
	const ESCAPE_QUOTE = 8;

	/**
	 * List of model class instances.
	 *
	 * @since 1.1.0
	 *
	 * @var array
	 */
	private $models = [];

	/**
	 * The last query that ran, stringified.
	 *
	 * @since 1.1.0
	 */
	public $lastQuery = '';

	/**
	 * Prepares the database class for use.
	 *
	 * @since 1.0.0
	 *
	 * @global object $wpdb The WordPress database object.
	 */
	public function __construct( $escape = null ) {
		global $wpdb;
		$this->db            = $wpdb;
		$this->prefix        = $wpdb->prefix;
		$this->escapeOptions = is_null( $escape ) ? self::ESCAPE_STRIP_HTML | self::ESCAPE_QUOTE : $escape;
	}

	/**
	 * If this is a clone, lets reset all the data.
	 *
	 * @since 1.0.0
	 */
	public function __clone() {
		// We need to reset the result separetely as well since it is not in the default array.
		$this->reset( [ 'result' ] );
		$this->reset();
	}

	/**
	 * Gets all AIO installed tables.
	 *
	 * @since 1.0.0
	 *
	 * @return array An array of custom AIO tables.
	 */
	public function getInstalledTables() {
		$results = $this->db->get_results( 'SHOW TABLES', 'ARRAY_N' );

		return ! empty( $results ) ? wp_list_pluck( $results, 0 ) : [];
	}

	/**
	 * Gets all columns from a table.
	 *
	 * @since 1.0.0
	 *
	 * @param  string $table The name of the table to lookup columns for.
	 * @return array         An array of custom AIO tables.
	 */
	public function getColumns( $table ) {
		$installedTables = json_decode( aioseoBrokenLinkChecker()->internalOptions->database->installedTables, true );
		$table           = $this->prefix . $table;
		if ( isset( $installedTables[ $table ] ) ) {
			if ( empty( $installedTables[ $table ] ) ) {
				$installedTables[ $table ] = $this->db->get_col( 'SHOW COLUMNS FROM `' . $table . '`' );
				aioseoBrokenLinkChecker()->internalOptions->database->installedTables = wp_json_encode( $installedTables );
			}

			return $installedTables[ $table ];
		}

		return [];
	}

	/**
	 * Checks if a table exists.
	 *
	 * @since 1.0.0
	 *
	 * @param  string  $table The name of the table.
	 * @return boolean        Whether or not the table exists.
	 */
	public function tableExists( $table ) {
		$table           = $this->prefix . $table;
		$installedTables = json_decode( aioseoBrokenLinkChecker()->internalOptions->database->installedTables, true ) ?: [];
		if ( isset( $installedTables[ $table ] ) ) {
			return true;
		}

		$results = $this->db->get_results( "SHOW TABLES LIKE '" . $table . "'" );
		if ( ! empty( $results ) ) {
			$installedTables[ $table ] = [];
			aioseoBrokenLinkChecker()->internalOptions->database->installedTables = wp_json_encode( $installedTables );

			return true;
		}

		return false;
	}

	/**
	 * Checks if a column exists on a given table.
	 *
	 * @since 1.0.0
	 *
	 * @param  string   $table  The name of the table.
	 * @param  string   $column The name of the column.
	 * @return boolean          Whether or not the column exists.
	 */
	public function columnExists( $table, $column ) {
		if ( ! $this->tableExists( $table ) ) {
			return false;
		}

		$columns = $this->getColumns( $table );

		if ( ! in_array( $column, $columns, true ) ) {
			return false;
		}

		return true;
	}

	/**
	 * Gets the size of a table in bytes.
	 *
	 * @since 1.0.0
	 *
	 * @param  string  $table The table to check.
	 * @return integer        The size of the table in bytes.
	 */
	public function getTableSize( $table ) {
		$this->db->query( 'ANALYZE TABLE ' . $this->prefix . $table );
		$results = $this->db->get_results( '
			SELECT
				TABLE_NAME AS `table`,
				ROUND(SUM(DATA_LENGTH + INDEX_LENGTH)) AS `size`
			FROM information_schema.TABLES
			WHERE TABLE_SCHEMA = "' . $this->db->dbname . '"
			AND TABLE_NAME = "' . $this->prefix . $table . '"
			ORDER BY (DATA_LENGTH + INDEX_LENGTH) DESC;
		' );

		return empty( $results ) ? 0 : $results[0]->size;
	}

	/**
	 * The query string in all its glory.
	 *
	 * @since 1.0.0
	 *
	 * @return string The actual query string.
	 */
	public function __toString() {
		switch ( strtoupper( $this->statement ) ) {
			case 'INSERT':
				$insert = 'INSERT ';
				if ( $this->ignore ) {
					$insert .= 'IGNORE ';
				}
				$insert .= 'INTO ' . $this->table;
				$clauses   = [];
				$clauses[] = $insert;
				$clauses[] = 'SET ' . implode( ', ', $this->set );
				if ( ! empty( $this->onDuplicate ) ) {
					$clauses[] = 'ON DUPLICATE KEY UPDATE ' . implode( ', ', $this->onDuplicate );
				}

				break;
			case 'REPLACE':
				$clauses   = [];
				$clauses[] = "REPLACE INTO $this->table";
				$clauses[] = 'SET ' . implode( ', ', $this->set );

				break;
			case 'UPDATE':
				$clauses   = [];
				$clauses[] = "UPDATE $this->table";

				if ( count( $this->join ) > 0 ) {
					foreach ( (array) $this->join as $join ) {
						if ( is_array( $join[1] ) ) {
							$join_on = []; // phpcs:ignore Squiz.NamingConventions.ValidVariableName
							foreach ( (array) $join[1] as $left => $right ) {
								$join_on[] = "$this->table.`$left` = `{$join[0]}`.`$right`"; // phpcs:ignore Squiz.NamingConventions.ValidVariableName
							}
							// phpcs:disable Squiz.NamingConventions.ValidVariableName
							$clauses[] = "\t" . ( ( 'LEFT' === $join[2] || 'RIGHT' === $join[2] ) ? $join[2] . ' JOIN ' : 'JOIN ' ) . $join[0] . ' ON ' . implode( ' AND ', $join_on );
							// phpcs:enable Squiz.NamingConventions.ValidVariableName
						} else {
							$clauses[] = "\t" . ( ( 'LEFT' === $join[2] || 'RIGHT' === $join[2] ) ? $join[2] . ' JOIN ' : 'JOIN ' ) . "{$join[0]} ON {$join[1]}";
						}
					}
				}

				$clauses[] = 'SET ' . implode( ', ', $this->set );

				if ( count( $this->where ) > 0 ) {
					$clauses[] = "WHERE 1 = 1 AND\n\t" . implode( "\n\tAND ", $this->where );
				}

				if ( count( $this->order ) > 0 ) {
					$clauses[] = 'ORDER BY ' . implode( ', ', $this->order );
				}

				if ( $this->limit ) {
					$clauses[] = 'LIMIT ' . $this->limit;
				}

				break;

			case 'TRUNCATE':
				$clauses   = [];
				$clauses[] = "TRUNCATE TABLE $this->table";
				break;

			case 'DELETE':
				$clauses   = [];
				$clauses[] = "DELETE FROM $this->table";

				if ( count( $this->where ) > 0 ) {
					$clauses[] = "WHERE 1 = 1 AND\n\t" . implode( "\n\tAND ", $this->where );
				}

				if ( count( $this->order ) > 0 ) {
					$clauses[] = 'ORDER BY ' . implode( ', ', $this->order );
				}

				if ( $this->limit ) {
					$clauses[] = 'LIMIT ' . $this->limit;
				}

				break;
			case 'SELECT':
			case 'SELECT DISTINCT':
			default:
				// Select fields.
				$clauses   = [];
				$distinct  = ( $this->distinct || stripos( $this->statement, 'DISTINCT' ) !== false ) ? 'DISTINCT ' : '';
				$select    = ( count( $this->select ) > 0 ) ? implode( ",\n\t", $this->select ) : '*';
				$clauses[] = "SELECT {$distinct}\n\t{$select}";

				// Select table.
				$clauses[] = "FROM $this->table";

				// Select joins.
				if ( ! empty( $this->join ) && count( $this->join ) > 0 ) {
					foreach ( (array) $this->join as $join ) {
						if ( is_array( $join[1] ) ) {
							$join_on = []; // phpcs:ignore Squiz.NamingConventions.ValidVariableName
							foreach ( (array) $join[1] as $left => $right ) {
								$join_on[] = "$this->table.`$left` = `{$join[0]}`.`$right`"; // phpcs:ignore Squiz.NamingConventions.ValidVariableName
							}
							// phpcs:disable Squiz.NamingConventions.ValidVariableName
							$clauses[] = "\t" . ( ( 'LEFT' === $join[2] || 'RIGHT' === $join[2] ) ? $join[2] . ' JOIN ' : 'JOIN ' ) . $join[0] . ' ON ' . implode( ' AND ', $join_on );
							// phpcs:enable Squiz.NamingConventions.ValidVariableName
						} else {
							$clauses[] = "\t" . ( ( 'LEFT' === $join[2] || 'RIGHT' === $join[2] ) ? $join[2] . ' JOIN ' : 'JOIN ' ) . "{$join[0]} ON {$join[1]}";
						}
					}
				}

				// Select conditions.
				if ( count( $this->where ) > 0 ) {
					$clauses[] = "WHERE 1 = 1 AND\n\t" . implode( "\n\tAND ", $this->where );
				}

				// Union queries.
				if ( count( $this->union ) > 0 ) {
					foreach ( $this->union as $union ) {
						$keyword   = ( $union[1] ) ? 'UNION' : 'UNION ALL';
						$clauses[] = "\n$keyword\n\n$union[0]";
					}

					$clauses[] = '';
				}

				// Select groups.
				if ( count( $this->group ) > 0 ) {
					$clauses[] = 'GROUP BY ' . implode( ', ', $this->escapeColNames( $this->group ) );
				}

				// Select order.
				if ( count( $this->order ) > 0 ) {
					$orderFragments = [];
					foreach ( $this->escapeColNames( $this->order ) as $col ) {
						$orderFragments[] = ( preg_match( '/ (ASC|DESC|RAND\(\))$/i', $col ) ) ? $col : "$col $this->orderDirection";
					}

					$clauses[] = 'ORDER BY ' . implode( ', ', $orderFragments );
				}

				// Select limit.
				if ( $this->limit ) {
					$clauses[] = 'LIMIT ' . $this->limit;
				}

				break;
		}

		// @HACK for wpdb::prepare.
		$clauses[] = '/* %d = %d */';

		$this->query = str_replace( '%%d = %%d', '%d = %d', str_replace( '%', '%%', implode( "\n", $clauses ) ) );

		$this->lastQuery = $this->query;

		return $this->query;
	}

	/**
	 * Shortcut method to return the query string.
	 *
	 * @since 1.0.0
	 *
	 * @return string The query string.
	 */
	public function query() {
		return $this->__toString();
	}

	/**
	 * Start a new Database Query.
	 *
	 * @since 1.0.0
	 *
	 * @param  string  $table          The name of the table without the WordPress prefix unless includes_prefix is true.
	 * @param  boolean $includesPrefix This determines if the table name includes the WordPress prefix or not.
	 * @param  string  $statement      The MySQL statement for the query.
	 * @return Database                Returns the Database class which can then be method chained for building the query.
	 */
	public function start( $table = null, $includesPrefix = false, $statement = 'SELECT' ) {
		// Always reset everything when starting a new query.
		$this->reset();
		$this->table = $includesPrefix ? $table : $this->prefix . $table;
		$this->statement = $statement;

		return $this;
	}

	/**
	 * Shortcut method for start with INSERT as the statement.
	 *
	 * @since 1.0.0
	 *
	 * @param  string  $table          The name of the table without the WordPress prefix unless includes_prefix is true.
	 * @param  boolean $includesPrefix This determines if the table name includes the WordPress prefix or not.
	 * @return Database                Returns the Database class which can then be method chained for building the query.
	 */
	public function insert( $table = null, $includesPrefix = false ) {
		return $this->start( $table, $includesPrefix, 'INSERT' );
	}

	/**
	 * Shortcut method for start with INSERT IGNORE as the statement.
	 *
	 * @since 1.0.0
	 *
	 * @param  string  $table          The name of the table without the WordPress prefix unless includes_prefix is true.
	 * @param  boolean $includesPrefix This determines if the table name includes the WordPress prefix or not.
	 * @return Database                Returns the Database class which can then be method chained for building the query.
	 */
	public function insertIgnore( $table = null, $includesPrefix = false ) {
		$this->ignore = true;

		return $this->start( $table, $includesPrefix, 'INSERT' );
	}

	/**
	 * Shortcut method for start with UPDATE as the statement.
	 *
	 * @since 1.0.0
	 *
	 * @param  string  $table          The name of the table without the WordPress prefix unless includes_prefix is true.
	 * @param  boolean $includesPrefix This determines if the table name includes the WordPress prefix or not.
	 * @return Database                Returns the Database class which can then be method chained for building the query.
	 */
	public function update( $table = null, $includesPrefix = false ) {
		return $this->start( $table, $includesPrefix, 'UPDATE' );
	}

	/**
	 * Shortcut method for start with REPLACE as the statement.
	 *
	 * @since 1.0.0
	 *
	 * @param  string  $table          The name of the table without the WordPress prefix unless includes_prefix is true.
	 * @param  boolean $includesPrefix This determines if the table name includes the WordPress prefix or not.
	 * @return Database                Returns the Database class which can then be method chained for building the query.
	 */
	public function replace( $table = null, $includesPrefix = false ) {
		return $this->start( $table, $includesPrefix, 'REPLACE' );
	}

	/**
	 * Shortcut method for start with TRUNCATE as the statement.
	 *
	 * @since 1.0.0
	 *
	 * @param  string  $table          The name of the table without the WordPress prefix unless includes_prefix is true.
	 * @param  boolean $includesPrefix This determines if the table name includes the WordPress prefix or not.
	 * @return Database                Returns the Database class which can then be method chained for building the query.
	 */
	public function truncate( $table = null, $includesPrefix = false ) {
		return $this->start( $table, $includesPrefix, 'TRUNCATE' );
	}

	/**
	 * Shortcut method for start with DELETE as the statement.
	 *
	 * @since 1.0.0
	 *
	 * @param  string  $table          The name of the table without the WordPress prefix unless includes_prefix is true.
	 * @param  boolean $includesPrefix This determines if the table name includes the WordPress prefix or not.
	 * @return Database                Returns the Database class which can then be method chained for building the query.
	 */
	public function delete( $table = null, $includesPrefix = false ) {
		return $this->start( $table, $includesPrefix, 'DELETE' );
	}

	/**
	 * Adds a SELECT clause.
	 *
	 * @since 1.0.0
	 *
	 * @return Database Returns the Database class which can be method chained for more query building.
	 */
	public function select() {
		$args = (array) func_get_args();
		if ( count( $args ) === 1 && is_array( $args[0] ) ) {
			$args = $args[0];
		}

		$this->select = array_merge( $this->select, $this->escapeColNames( $args ) );

		return $this;
	}

	/**
	 * Adds a WHERE clause.
	 *
	 * @since 1.0.0
	 *
	 * @return Database Returns the Database class which can be method chained for more query building.
	 */
	public function where() {
		$criteria = $this->prepArgs( func_get_args() );

		foreach ( (array) $criteria as $field => $value ) {
			if ( ! preg_match( '/[\(\)<=>!]+/', $field ) && false === stripos( $field, ' IS ' ) ) {
				$operator = ( is_null( $value ) ) ? 'IS' : '=';
				$escaped  = $this->escapeColNames( $field );
				$field    = array_pop( $escaped ) . ' ' . $operator;
			}

			if ( is_null( $value ) && false !== stripos( $field, ' IS ' ) ) {
				// WHERE `field` IS NOT NULL.
				$this->where[] = "$field NULL";
			} elseif ( is_null( $value ) ) {
				// WHERE `field` IS NULL.
				$this->where[] = "$field NULL";
			} elseif ( is_array( $value ) ) {
				$wheres = [];
				foreach ( (array) $value as $val ) {
					$wheres[] = sprintf( "$field %s", $this->escape( $val, $this->getEscapeOptions() | self::ESCAPE_QUOTE ) );
				}

				$this->where[] = '(' . implode( ' OR ', $wheres ) . ')';
			} else {
				$this->where[] = sprintf( "$field %s", $this->escape( $value, $this->getEscapeOptions() | self::ESCAPE_QUOTE ) );
			}
		}

		return $this;
	}

	/**
	 * Adds a complex WHERE clause.
	 *
	 * @since 1.0.0
	 *
	 * @return Database Returns the Database class which can be method chained for more query building.
	 */
	public function whereRaw() {
		$criteria = $this->prepArgs( func_get_args() );

		foreach ( (array) $criteria as $clause ) {
			$this->where[] = $clause;
		}

		return $this;
	}

	/**
	 * Adds a WHERE clause with all arguments sent separated by OR instead of AND inside a subclause.
	 * @example [ 'a' => 1, 'b' => 2 ] becomes "AND (a = 1 OR b = 2)"
	 *
	 * @since 1.0.0
	 *
	 * @return Database Returns the Database class which can be method chained for more query building.
	 */
	public function whereOr() {
		$criteria = $this->prepArgs( func_get_args() );

		$or = [];
		foreach ( (array) $criteria as $field => $value ) {
			if ( ! preg_match( '/[\(\)<=>!]+/', $field ) && false === stripos( $field, ' IS ' ) ) {
				$operator = ( is_null( $value ) ) ? 'IS' : '=';
				$field    = $this->escapeColNames( $field );
				$field    = array_pop( $field ) . ' ' . $operator;
			}

			if ( is_null( $value ) && false !== stripos( $field, ' IS ' ) ) {
				// WHERE `field` IS NOT NULL.
				$or[] = "$field NULL";
			} elseif ( is_null( $value ) ) {
				// WHERE `field` IS NULL.
				$or[] = "$field NULL";
			} else {
				$or[] = sprintf( "$field %s", $this->escape( $value, $this->getEscapeOptions() | self::ESCAPE_QUOTE ) );
			}
		}

		// Create our subclause, and add it to the WHERE array.
		$this->where[] = '(' . implode( ' OR ', $or ) . ')';

		return $this;
	}

	/**
	 * Adds a WHERE IN() clause.
	 *
	 * @since 1.0.0
	 *
	 * @return Database Returns the Database class which can be method chained for more query building.
	 */
	public function whereIn() {
		$criteria = $this->prepArgs( func_get_args() );

		foreach ( (array) $criteria as $field => $values ) {
			if ( ! is_array( $values ) ) {
				$values = [ $values ];
			} elseif ( count( $values ) === 0 ) {
				continue;
			}

			foreach ( $values as &$value ) {
				// Note: We can no longer check for `is_numeric` because a value like `61021e6242255` returns true and breaks the query.
				if ( is_integer( $value ) || is_float( $value ) ) {
					// No change.
				} elseif ( is_null( $value ) || false !== stristr( $value, 'NULL' ) ) {
					// Change to a true NULL value.
					$value = null;
				} else {
					$value = sprintf( '%s', $this->escape( $value, $this->getEscapeOptions() | self::ESCAPE_QUOTE ) );
				}
			}

			$values = implode( ',', $values );
			$this->whereRaw( "$field IN($values)" );
		}

		return $this;
	}

	/**
	 * Adds a WHERE NOT IN() clause.
	 *
	 * @since 1.0.0
	 *
	 * @return Database Returns the Database class which can be method chained for more query building.
	 */
	public function whereNotIn() {
		$criteria = $this->prepArgs( func_get_args() );

		foreach ( (array) $criteria as $field => $values ) {
			if ( ! is_array( $values ) ) {
				$values = [ $values ];
			} elseif ( count( $values ) === 0 ) {
				continue;
			}

			foreach ( $values as &$value ) {
				if ( is_numeric( $value ) ) {
					// No change.
				} elseif ( is_null( $value ) || false !== stristr( $value, 'NULL' ) ) {
					// Change to a true NULL value.
					$value = null;
				} else {
					$value = sprintf( '%s', $this->escape( $value, $this->getEscapeOptions() | self::ESCAPE_QUOTE ) );
				}
			}

			$values = implode( ',', $values );
			$this->whereRaw( "$field NOT IN($values)" );
		}

		return $this;
	}

	/**
	 * Adds a LEFT JOIN clause.
	 *
	 * @since 1.0.0
	 *
	 * @param  string       $table          The name of the table to join to this query.
	 * @param  string|array $conditions     The conditions of the join clause.
	 * @param  boolean      $includesPrefix This determines if the table name includes the WordPress prefix or not.
	 * @return Database                     Returns the Database class which can be method chained for more query building.
	 */
	public function leftJoin( $table, $conditions, $includesPrefix = false ) {
		return $this->join( $table, $conditions, 'LEFT', $includesPrefix );
	}

	/**
	 * Adds a JOIN clause.
	 *
	 * @since 1.0.0
	 *
	 * @param  string       $table          The name of the table to join to this query.
	 * @param  string|array $conditions     The conditions of the join clause.
	 * @param  string       $direction      This can take 'LEFT' or 'RIGHT' as arguments.
	 * @param  boolean      $includesPrefix This determines if the table name includes the WordPress prefix or not.
	 * @return Database                     Returns the Database class which can be method chained for more query building.
	 */
	public function join( $table, $conditions, $direction = '', $includesPrefix = false ) {
		$this->join[] = [ $includesPrefix ? $table : $this->prefix . $table, $conditions, $direction ];

		return $this;
	}

	/**
	 * Add a UNION query.
	 *
	 * @since 1.0.0
	 *
	 * @param  Database|string $query    The query (Database object or query string) to be joined with.
	 * @param  Bool            $distinct Set whether this union should be distinct or not.
	 * @return Database                  Returns the Database class which can be method chained for more query building.
	 */
	public function union( $query, $distinct = true ) {
		$this->union[] = [ $query, $distinct ];

		return $this;
	}

	/**
	 * Adds a GROUP BY clause.
	 *
	 * @since 1.0.0
	 *
	 * @return Database Returns the Database class which can be method chained for more query building.
	 */
	public function groupBy() {
		$args = (array) func_get_args();
		if ( count( $args ) === 1 && is_array( $args[0] ) ) {
			$args = $args[0];
		}

		$this->group = array_merge( $this->group, $args );

		return $this;
	}


	/**
	 * Adds a ORDER BY clause.
	 *
	 * @since   1.0.0
	 * @version 1.2.4 Hardened against SQL injection.
	 *
	 * @return Database Returns the Database class which can be method chained for more query building.
	 */
	public function orderBy() {
		// Normalize arguments.
		$args = (array) func_get_args();
		if ( count( $args ) === 1 && is_array( $args[0] ) ) {
			$args = $args[0];
		}

		$orderBy = [];
		// Separate commas to account for multiple orders.
		foreach ( $args as $argComma ) {
			$orderBy = array_map( 'trim', array_merge( $orderBy, explode( ',', $argComma ) ) );
		}

		// Validate and sanitize column names and sort directions.]
		$sanitizedOrderBy = [];
		foreach ( $orderBy as $ordBy ) {
			$parts     = explode( ' ', $ordBy );
			$column    = str_replace( '`', '', $parts[0] ); // Strip existing ticks first.
			$column    = preg_replace( '/[^a-zA-Z0-9_.]/', '', $column ); // Strip invalid characters from the column name.
			$column    = $this->escapeColNames( $column )[0];
			$direction = isset( $parts[1] ) ? strtoupper( $parts[1] ) : 'ASC';

			// Validate the order direction.
			if ( ! in_array( $direction, [ 'ASC', 'DESC' ], true ) ) {
				$direction = 'ASC';
			}

			$sanitizedOrderBy[] = "$column $direction";
		}

		if ( ! empty( $sanitizedOrderBy ) ) {
			if ( ! empty( $args[0] ) && true !== $args[0] ) {
				$this->order = array_merge( $this->order, $sanitizedOrderBy );
			} else {
				// This allows for overwriting a preexisting order-by setting.
				array_shift( $sanitizedOrderBy );
				$this->order = $sanitizedOrderBy;
			}
		}

		return $this;
	}

	/**
	 * Adds a raw ORDER BY clause.
	 *
	 * @since 1.2.4
	 *
	 * @return Database Returns the Database class which can be method chained for more query building.
	 */
	public function orderByRaw() {
		$args = (array) func_get_args();
		if ( count( $args ) === 1 && is_array( $args[0] ) ) {
			$args = $args[0];
		}

		$this->order = array_merge( $this->order, $args );

		return $this;
	}

	/**
	 * Sets the sort direction for ORDER BY clauses.
	 *
	 * @since 1.0.0
	 *
	 * @param string    $direction This sets the direction of the order by clause, default is 'ASC'.
	 * @return Database            Returns the Database class which can be method chained for more query building.
	 */
	public function orderDirection( $direction = 'ASC' ) {
		$this->orderDirection = $direction;

		return $this;
	}

	/**
	 * Adds a LIMIT clause.
	 *
	 * @since 1.0.0
	 *
	 * @param  int      $limit  The limit for the limit clause.
	 * @param  int      $offset The offset for the limit clause.
	 * @return Database         Returns the Database class which can be method chained for more query building.
	 */
	public function limit( $limit, $offset = -1 ) {
		if ( ! is_numeric( $limit ) || $limit <= 0 ) {
			return $this;
		}

		if ( ! is_numeric( $offset ) ) {
			$offset = -1;
		}

		$this->limit = ( -1 === $offset )
			? intval( $limit )
			: intval( $offset ) . ', ' . intval( $limit );

		return $this;
	}

	/**
	 * Converts associative arrays to a SET argument.
	 *
	 * @since 1.0.0
	 *
	 * @param  array $args        The arguments.
	 * @return array $preparedSet The prepared arguments.
	 */
	private function prepareSet( $args ) {
		$args = $this->prepArgs( $args );

		$preparedSet = [];
		foreach ( (array) $args as $field => $value ) {
			if ( is_null( $value ) ) {
				$preparedSet[] = "`$field` = NULL";
			} elseif ( is_array( $value ) ) {
				throw new \Exception( 'Cannot save an unserialized array in the database. Data passed was: ' . wp_json_encode( $value ) );
			} elseif ( is_object( $value ) ) {
				throw new \Exception( 'Cannot save an unserialized object in the database. Data passed was: ' . esc_html( $value ) );
			} else {
				$preparedSet[] = sprintf( "`$field` = %s", $this->escape( $value, $this->getEscapeOptions() | self::ESCAPE_QUOTE ) );
			}
		}

		return $preparedSet;
	}

	/**
	 * Adds a SET clause.
	 *
	 * @since 1.0.0
	 *
	 * @return Database Returns the Database class which can be method chained for more query building.
	 */
	public function set() {
		$this->set = array_merge( $this->set, $this->prepareSet( func_get_args() ) );

		return $this;
	}

	/**
	 * Adds an ON DUPLICATE clause.
	 *
	 * @since 1.0.0
	 *
	 * @return Database Returns the Database class which can be method chained for more query building.
	 */
	public function onDuplicate() {
		$this->onDuplicate = array_merge( $this->onDuplicate, $this->prepareSet( func_get_args() ) );

		return $this;
	}

	/**
	 * Set the output for the query.
	 *
	 * @since 1.0.0
	 *
	 * @param  string   $output  This can be one of the following: ARRAY_A | ARRAY_N | OBJECT | OBJECT_K.
	 * @return Database          Returns the Database class which can be method chained for more query building.
	 */
	public function output( $output ) {
		$this->output = $output;

		return $this;
	}

	/**
	 * Reset the cache so we make sure the query gets to the DB.
	 *
	 * @since 1.0.0
	 *
	 * @return Database Returns the Database class which can be method chained for more query building.
	 */
	public function resetCache() {
		$this->shouldResetCache = true;

		return $this;
	}

	/**
	 * Run this query.
	 *
	 * @since 1.0.0
	 *
	 * @param  boolean  $reset  Whether to reset the results/query.
	 * @param  string   $return Determine which method to call on the $wpdb object
	 * @param  array    $params Optional extra parameters to pass to the db method call
	 * @return Database         Database query results.
	 */
	public function run( $reset = true, $return = 'results', $params = [] ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
		if ( ! in_array( $return, [ 'results', 'col', 'var' ], true ) ) {
			$return = 'results';
		}

		$prepare        = $this->db->prepare( $this->query(), 1, 1 );
		$queryHash      = sha1( $this->query() );
		$cacheTableName = $this->getCacheTableName();

		// Pull the result from the in-memory cache if everything checks out.
		if (
			! $this->shouldResetCache &&
			isset( $this->cache[ $cacheTableName ][ $queryHash ][ $return ] ) &&
			empty( $this->join )
		) {
			$this->result = $this->cache[ $cacheTableName ][ $queryHash ][ $return ];

			return $this;
		}

		switch ( $return ) {
			case 'col':
				$this->result = $this->db->get_col( $prepare );
				break;

			case 'var':
				$this->result = $this->db->get_var( $prepare );
				break;

			default:
				$this->result = $this->db->get_results( $prepare, $this->output );
		}

		if ( $reset ) {
			$this->reset();
		}

		$this->cache[ $cacheTableName ][ $queryHash ][ $return ] = $this->result;

		// Reset the cache trigger for the next run.
		$this->shouldResetCache = false;

		return $this;
	}

	/**
	 * Inject a count select statement and return the result.
	 *
	 * @since 1.0.0
	 *
	 * @param  string $countColumn The column to count with. Defaults to '*' all.
	 * @return int                 The count total.
	 */
	public function count( $countColumn = '*' ) {
		$usingGroup = ! empty( $this->group );
		$results    = $this->select( 'count(' . $countColumn . ') as count' )
			->run()
			->result();

		return 1 === $this->numRows() && ! $usingGroup ? (int) $results[0]->count : $this->numRows();
	}

	/**
	 * Returns the query results based on the output.
	 *
	 * @since 1.0.0
	 *
	 * @return mixed This could be an array or an object based on the original output method.
	 */
	public function result() {
		return $this->result;
	}

	/**
	 * Return a model model from a row.
	 *
	 * @since 1.0.0
	 *
	 * @param string  $class The class to call.
	 * @return object        The class object.
	 */
	public function model( $class ) {
		$result = $this->result();

		return ! empty( $result ) ? ( is_array( $result ) ? new $class( (array) current( $result ) ) : $result ) : new $class();
	}

	/**
	 * Return an array of model models from the result
	 *
	 * @since 1.0.0
	 *
	 * @param  string $class  The class to call.
	 * @param  string $id     The id of the index to use.
	 * @param  bool $toJson Whether to convert to json.
	 * @return array         An array of class models.
	 */
	public function models( $class, $id = null, $toJson = false ) {
		if ( empty( $this->models ) ) {
			$i      = 0;
			$models = [];
			foreach ( $this->result() as $row ) {
				$var   = ( null === $id ) ? $row : $row[ $id ];
				$class = new $class( $var );
				// Lets add the class to the array using the class ID.
				$models[ $class->id ] = $toJson ? $class->jsonSerialize() : $class;
				$i++;
			}

			$this->models = $models;
		}

		return $this->models;
	}

	/**
	 * Returns the last error reported by MySQL.
	 *
	 * @since 1.0.0
	 *
	 * @return string The last error.
	 */
	public function lastError() {
		return $this->db->last_error;
	}

	/**
	 * Return the $wpdb insert_id from the last query.
	 *
	 * @since 1.0.0
	 *
	 * @return integer The id of the most recent INSERT query.
	 */
	public function insertId() {
		return $this->db->insert_id;
	}

	/**
	 * Return the $wpdb rows_affected from the last query.
	 *
	 * @since 1.0.0
	 *
	 * @return integer The number of rows affected.
	 */
	public function rowsAffected() {
		return $this->db->rows_affected;
	}

	/**
	 * Return the $wpdb num_rows from the last query.
	 *
	 * @since 1.0.0
	 *
	 * @return integer The count for the number of rows in the last query.
	 */
	public function numRows() {
		return $this->db->num_rows;
	}

	/**
	 * Check if the last query had any rows.
	 *
	 * @since 1.0.0
	 *
	 * @return bool Whether there were any rows retrived by the last query.
	 */
	public function nullSet() {
		return ( $this->numRows() < 1 );
	}

	/**
	 * This will start a MySQL transaction. Be sure to commit or rollback!
	 *
	 * @since 1.0.0
	 */
	public function startTransaction() {
		$this->db->query( 'START TRANSACTION' );
	}

	/**
	 * This will commit a MySQL transaction. Used in conjunction with startTransaction.
	 *
	 * @since 1.0.0
	 */
	public function commit() {
		$this->db->query( 'COMMIT' );
	}

	/**
	 * This will rollback a MySQL transaction. Used in conjunction with startTransaction.
	 *
	 * @since 1.0.0
	 */
	public function rollback() {
		$this->db->query( 'ROLLBACK' );
	}

	/**
	 * Fast way to execute queries.
	 *
	 * @since 1.0.0
	 *
	 * @param  string $sql The sql query to execute.
	 * @return mixed       Could be an array or object depending on the result set.
	 */
	public function execute( $sql, $results = false ) {
		$this->lastQuery = $sql;

		if ( $results ) {
			$this->result = $this->db->get_results( $sql );

			return $this;
		}

		return $this->db->query( $sql );
	}

	/**
	 * Escape a value for safe use in SQL queries.
	 *
	 * @param string  $value   The value to be escaped.
	 * @param boolean $options Escape options.
	 * @return string          The escaped SQL value.
	 */
	public function escape( $value, $options = null ) {
		if ( is_array( $value ) ) {
			foreach ( $value as &$val ) {
				$val = $this->escape( $val, $options );
			}

			return $value;
		} else {
			$options = ( is_null( $options ) ) ? $this->getEscapeOptions() : $options;
			if ( ( $options & self::ESCAPE_STRIP_HTML ) !== 0 && isset( $this->stripTags ) && true === $this->stripTags ) {
				$value = wp_strip_all_tags( $value );
			}

			if (
				( ( $options & self::ESCAPE_FORCE ) !== 0 || php_sapi_name() === 'cli' ) ||
				( ( $options & self::ESCAPE_QUOTE ) !== 0 && ! is_integer( $value ) )
			) {
				$value = esc_sql( $value );
				if ( ! is_integer( $value ) ) {
					$value = "'$value'";
				}
			}

			return $value;
		}
	}

	/**
	 * Get the current escape options.
	 *
	 * @since 1.0.0
	 *
	 * @return integer The current escape options.
	 */
	public function getEscapeOptions() {
		return $this->escapeOptions;
	}


	/**
	 * Set the current escape options.
	 *
	 * @since 1.0.0
	 *
	 * @param integer $options
	 */
	public function setEscapeOptions( $options ) {
		$this->escapeOptions = $options;
	}

	/**
	 * Backtick-escapes an array of column and/or table names.
	 *
	 * @since 1.0.0
	 *
	 * @param array  $cols An array of column names to be escaped.
	 * @return array       An array of escaped column names.
	 */
	private function escapeColNames( $cols ) {
		if ( ! is_array( $cols ) ) {
			$cols = [ $cols ];
		}

		foreach ( $cols as &$col ) {
			if ( false === stripos( $col, '(' ) && false === stripos( $col, ' ' ) && false === stripos( $col, '*' ) ) {
				if ( stripos( $col, '.' ) ) {
					list( $table, $c ) = explode( '.', $col );
					$col = "`$table`.`$c`";
					continue;
				}

				$col = "`$col`";
			}
		}

		return $cols;
	}

	/**
	 * Gets a variable list of function arguments and reformats them as needed for many of the functions of this class.
	 *
	 * @since 1.0.0
	 *
	 * @param  mixed $values This could be anything, but if used properly its usually a string or an array.
	 * @return array         If the preparation is correct it will return an array of arguments.
	 */
	private function prepArgs( $values ) {
		$values = (array) $values;
		if ( ! is_array( $values[0] ) && count( $values ) === 2 ) {
			$values = [ $values[0] => $values[1] ];
		} elseif ( is_array( $values[0] ) && count( $values ) === 1 ) {
			$values = $values[0];
		}

		return $values;
	}

	/**
	 * Resets all the variables that make up the query.
	 *
	 * @since 1.0.0
	 *
	 * @param array     $what Set which items you want to reset, all are selected by default.
	 * @return Database       Returns the Database object.
	 */
	public function reset(
		$what = [
			'table',
			'statement',
			'limit',
			'group',
			'order',
			'select',
			'set',
			'onDuplicate',
			'ignore',
			'where',
			'union',
			'distinct',
			'orderDirection',
			'query',
			'output',
			'stripTags',
			'models',
			'join'
		]
	) {
		// If we are not running a select query, let's bust the cache for this table.
		$selectStatements = [ 'SELECT', 'SELECT DISTINCT' ];
		if (
			! empty( $this->statement ) &&
			! in_array( $this->statement, $selectStatements, true )
		) {
			$this->bustCache( $this->getCacheTableName() );
		}

		foreach ( (array) $what as $var ) {
			switch ( $var ) {
				case 'group':
				case 'order':
				case 'select':
				case 'set':
				case 'onDuplicate':
				case 'where':
				case 'union':
				case 'join':
					$this->$var = [];
					break;
				case 'orderDirection':
					$this->$var = 'ASC';
					break;
				case 'ignore':
				case 'stripTags':
					$this->$var = false;
					break;
				case 'output':
					$this->$var = 'OBJECT';
					break;
				default:
					if ( isset( $this->$var ) ) {
						$this->$var = null;
					}
					break;
			}
		}

		return $this;
	}

	/**
	 * Get the current value of one or more query properties. If only one property is specified, returns the value;
	 * if an array of values is specified, then returns an array of values.
	 *
	 * @since 1.0.0
	 *
	 * @param string|array  $what You can pass in an array of options to retrieve. By default it selects all if them.
	 * @return string|array       Returns the value of whichever variables are passed in.
	 */
	public function getQueryProperty(
		$what = [
			'table',
			'statement',
			'limit',
			'group',
			'order',
			'select',
			'set',
			'onDuplicate',
			'where',
			'union',
			'distinct',
			'orderDirection',
			'query',
			'output',
			'result'
		]
	) {
		if ( is_array( $what ) ) {
			$return = [];
			foreach ( (array) $what as $which ) {
				$return[ $which ] = $this->$which;
			}

			return $return;
		} else {
			return $this->$what;
		}
	}

	/**
	 * Get a table name for the cache key.
	 *
	 * @since 1.0.0
	 *
	 * @param  string $cacheTableName The table name to check against.
	 * @return string                 The cache key table name.
	 */
	private function getCacheTableName( $cacheTableName = null ) {
		$cacheTableName = empty( $cacheTableName ) ? $this->table : $cacheTableName;

		foreach ( $this->customTables as $tableName ) {
			if ( false !== stripos( $cacheTableName, $this->prefix . $tableName ) ) {
				$cacheTableName = $tableName;
				break;
			}
		}

		return $cacheTableName;
	}

	/**
	 * Busts the cache for the given table name.
	 *
	 * @since 1.0.0
	 *
	 * @param  string|null $tableName The table name.
	 * @return void
	 */
	public function bustCache( $tableName = null ) {
		if ( ! $tableName ) {
			// Bust all the cache.
			$this->cache = [];

			return;
		}

		unset( $this->cache[ $tableName ] );
	}

	/**
	 * In order to not have a conflict, we need to return a clone.
	 *
	 * @since 1.0.0
	 *
	 * @return Database The cloned Database object.
	 */
	public function noConflict() {
		return clone $this;
	}
}Filesystem.php000064400000013735151540271460007417 0ustar00<?php
// phpcs:disable WordPress.WP.AlternativeFunctions
namespace AIOSEO\BrokenLinkChecker\Core;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Interface for the filesystem.
 *
 * @since 1.0.0
 */
class Filesystem {
	/**
	 * Holds the WP filesystem instance.
	 *
	 * @since 1.0.0
	 *
	 * @var \WP_Filesystem_Base
	 */
	public $fs = null;

	/**
	 * Core class instance.
	 *
	 * @since 1.1.0
	 *
	 * @var Core
	 */
	public $core = null;

	/**
	 * Class constructor.
	 *
	 * @since 1.0.0
	 *
	 * @param Core  $core The Core class.
	 * @param array $args Optional arguments needed to construct the class with.
	 */
	public function __construct( $core, $args = [] ) {
		$this->core = $core;
		$this->init( $args );
	}

	/**
	 * Initialize the filesystem.
	 *
	 * @since 1.0.0
	 *
	 * @param  array $args List of arguments for the WP_Filesystem class.
	 * @return void
	 */
	public function init( $args = [] ) {
		require_once ABSPATH . 'wp-admin/includes/file.php';

		WP_Filesystem( $args );
		// phpcs:disable Squiz.NamingConventions.ValidVariableName
		global $wp_filesystem;
		if ( is_object( $wp_filesystem ) ) {
			$this->fs = $wp_filesystem;
		}
		// phpcs:enable Squiz.NamingConventions.ValidVariableName
	}

	/**
	 * Wrapper method to check if a file exists.
	 *
	 * @since 1.0.0
	 *
	 * @param  string $filename The filename to check if it exists.
	 * @return bool             Returns true if the file or dir specified exists; false otherwise.
	 */
	public function exists( $filename ) {
		if ( ! $this->isWpfsValid() ) {
			return @file_exists( $filename );
		}

		return $this->fs->exists( $filename );
	}

	/**
	 * Retrieve the contents of a file.
	 *
	 * @since 1.0.0
	 *
	 * @param  string      $filename The filename to get the contents for.
	 * @return string|bool           The function returns the read data or false on failure.
	 */
	public function getContents( $filename ) {
		if ( ! $this->exists( $filename ) ) {
			return false;
		}

		if ( ! $this->isWpfsValid() ) {
			return @file_get_contents( $filename );
		}

		return $this->fs->get_contents( $filename );
	}

	/**
	 * Reads entire file into an array.
	 *
	 * @since 1.0.0
	 *
	 * @param  string     $file Path to the file.
	 * @return array|bool       File contents in an array on success, false on failure.
	 */
	public function getContentsArray( $file ) {
		if ( ! $this->exists( $file ) ) {
			return false;
		}

		if ( ! $this->isWpfsValid() ) {
			return @file( $file );
		}

		return $this->fs->get_contents_array( $file );
	}

	/**
	 * Sets the access and modification times of a file.
	 * Note: If $file doesn't exist, it will be created.
	 *
	 * @since 1.0.0
	 *
	 * @param  string $file  Path to file.
	 * @param  int    $time  Optional. Modified time to set for file. Default 0.
	 * @param  int    $atime Optional. Access time to set for file. Default 0.
	 * @return bool          True on success, false on failure.
	 */
	public function touch( $file, $time = 0, $atime = 0 ) {
		if ( 0 === $time ) {
			$time = time();
		}

		if ( 0 === $atime ) {
			$atime = time();
		}

		if ( ! $this->isWpfsValid() ) {
			return @touch( $file, $time, $atime );
		}

		return $this->fs->touch( $file, $time, $atime );
	}

	/**
	 * Writes a string to a file.
	 *
	 * @since 1.0.0
	 *
	 * @param  string    $file     Remote path to the file where to write the data.
	 * @param  string    $contents The data to write.
	 * @param  int|false $mode     Optional. The file permissions as octal number, usually 0644. Default false.
	 * @return int|bool            True on success, false on failure.
	 */
	public function putContents( $file, $contents, $mode = false ) {
		if ( ! $this->isWpfsValid() ) {
			return @file_put_contents( $file, $contents );
		}

		return $this->fs->put_contents( $file, $contents, $mode );
	}

	/**
	 * Checks if a file or dir is writable.
	 *
	 * @since 1.0.0
	 *
	 * @param  string $file Path to file or dir.
	 * @return bool         Whether $file is writable.
	 */
	public function isWritable( $file ) {
		if ( ! $this->isWpfsValid() ) {
			return @is_writable( $file );
		}

		return $this->fs->is_writable( $file );
	}

	/**
	 * Checks if a file is readable.
	 *
	 * @since 1.0.0
	 *
	 * @param  string $file Path to file.
	 * @return bool         Whether $file is readable.
	 */
	public function isReadable( $file ) {
		if ( ! $this->isWpfsValid() ) {
			return @is_readable( $file );
		}

		return $this->fs->is_readable( $file );
	}

	/**
	 * Gets the file size (in bytes).
	 *
	 * @since 1.0.0
	 *
	 * @param  string   $file Path to file.
	 * @return int|bool       Size of the file in bytes on success, false on failure.
	 */
	public function size( $file ) {
		if ( ! $this->isWpfsValid() ) {
			return @filesize( $file );
		}

		return $this->fs->size( $file );
	}

	/**
	 * Checks if resource is a file.
	 *
	 * @since 1.0.0
	 *
	 * @param  string $file File path.
	 * @return bool         Whether $file is a file.
	 */
	public function isFile( $file ) {
		if ( ! $this->isWpfsValid() ) {
			return @is_file( $file );
		}

		return $this->fs->is_file( $file );
	}

	/**
	 * Checks if resource is a directory.
	 *
	 * @since 1.0.0
	 *
	 * @param  string $path Directory path.
	 * @return bool         Whether $path is a directory.
	 */
	public function isDir( $path ) {
		if ( ! $this->isWpfsValid() ) {
			return @is_dir( $path );
		}

		return $this->fs->is_dir( $path );
	}

	/**
	 * A simple check to ensure that the WP_Filesystem is valid.
	 *
	 * @since 1.0.0
	 *
	 * @return bool True if valid, false if not.
	 */
	public function isWpfsValid() {
		if (
			! is_a( $this->fs, 'WP_Filesystem_Base' ) ||
			(
				// Errors is a WP_Error object.
				! empty( $this->fs->errors ) &&
				// We check if the errors array is empty for compatibility with WP < 5.1.
				! empty( $this->fs->errors->errors )
			)
		) {
			return false;
		}

		return true;
	}

	/**
	 * In order to prevent conflicts, we need to return a clone.
	 *
	 * @since 1.0.0
	 *
	 * @return Filesystem The cloned Filesystem object.
	 */
	public function noConflict() {
		return clone $this;
	}
}NetworkCache.php000064400000005314151540271460007642 0ustar00<?php
namespace AIOSEO\BrokenLinkChecker\Core;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Handles our network cache.
 *
 * @since 1.0.0
 */
class NetworkCache extends Cache {
	/**
	 * Returns the cache value for a key if it exists and is not expired.
	 *
	 * @since 1.0.0
	 *
	 * @param  string $key The cache key name. Use a '%' for a like query.
	 * @return mixed       The value or null if the cache does not exist.
	 */
	public function get( $key ) {
		if ( ! is_multisite() ) {
			return parent::get( $key );
		}

		aioseoBrokenLinkChecker()->helpers->switchToBlog( aioseoBrokenLinkChecker()->helpers->getNetworkId() );
		$value = parent::get( $key );
		aioseoBrokenLinkChecker()->helpers->restoreCurrentBlog();

		return $value;
	}

	/**
	 * Updates the given cache or creates it if it doesn't exist.
	 *
	 * @since 1.0.0
	 *
	 * @param  string $key        The cache key name.
	 * @param  mixed  $value      The value.
	 * @param  int    $expiration The expiration time in seconds. Defaults to 24 hours. 0 to no expiration.
	 * @return void
	 */
	public function update( $key, $value, $expiration = DAY_IN_SECONDS ) {
		if ( ! is_multisite() ) {
			parent::update( $key, $value, $expiration );

			return;
		}

		aioseoBrokenLinkChecker()->helpers->switchToBlog( aioseoBrokenLinkChecker()->helpers->getNetworkId() );
		parent::update( $key, $value, $expiration );
		aioseoBrokenLinkChecker()->helpers->restoreCurrentBlog();
	}

	/**
	 * Deletes the given cache key.
	 *
	 * @since 1.0.0
	 *
	 * @param  string $key The cache key.
	 * @return void
	 */
	public function delete( $key ) {
		if ( ! is_multisite() ) {
			parent::delete( $key );

			return;
		}

		aioseoBrokenLinkChecker()->helpers->switchToBlog( aioseoBrokenLinkChecker()->helpers->getNetworkId() );
		parent::delete( $key );
		aioseoBrokenLinkChecker()->helpers->restoreCurrentBlog();
	}

	/**
	 * Clears all of our cache.
	 *
	 * @since 1.0.0
	 *
	 * @return void
	 */
	public function clear() {
		if ( ! is_multisite() ) {
			parent::clear();

			return;
		}

		aioseoBrokenLinkChecker()->helpers->switchToBlog( aioseoBrokenLinkChecker()->helpers->getNetworkId() );
		parent::clear();
		aioseoBrokenLinkChecker()->helpers->restoreCurrentBlog();
	}

	/**
	 * Clears all of our cache under a certain prefix.
	 *
	 * @since 1.0.0
	 *
	 * @param  string $prefix A prefix to clear or empty to clear everything.
	 * @return void
	 */
	public function clearPrefix( $prefix ) {
		if ( ! is_multisite() ) {
			parent::clearPrefix( $prefix );

			return;
		}

		aioseoBrokenLinkChecker()->helpers->switchToBlog( aioseoBrokenLinkChecker()->helpers->getNetworkId() );
		parent::clearPrefix( $prefix );
		aioseoBrokenLinkChecker()->helpers->restoreCurrentBlog();
	}
}Uninstall.php000064400000004675151540271460007247 0ustar00<?php
namespace AIOSEO\BrokenLinkChecker\Core;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

use AIOSEO\BrokenLinkChecker\Utils;

/**
 * Handles plugin deinstallation.
 *
 * @since 1.0.0
 */
class Uninstall {
	/**
	 * Removes all our tables and options.
	 *
	 * @since 1.0.0
	 *
	 * @param  bool $force Whether we should ignore the uninstall option or not. We ignore it when we reset all data via the Debug Panel.
	 * @return void
	 */
	public function dropData( $force = false ) {
		// Confirm that user has decided to remove all data, otherwise stop.
		if (
			! $force &&
			( ! aioseoBrokenLinkChecker()->options->advanced->enable || ! aioseoBrokenLinkChecker()->options->advanced->uninstall )
		) {
			return;
		}

		// Delete all our custom tables.
		global $wpdb;
		foreach ( $this->getDbTables() as $tableName ) {
			$wpdb->query( 'DROP TABLE IF EXISTS ' . $tableName ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
		}

		// Delete all the plugin settings.
		$wpdb->query( "DELETE FROM {$wpdb->options} WHERE option_name LIKE 'aioseo\_blc\_%'" );

		// Remove any transients we've left behind.
		$wpdb->query( "DELETE FROM {$wpdb->options} WHERE option_name LIKE '\_aioseo\_blc\_%'" );

		// Delete all entries from the action scheduler table.
		$wpdb->query( "DELETE FROM {$wpdb->prefix}actionscheduler_actions WHERE hook LIKE 'aioseo\_blc\_%'" );
		$wpdb->query( "DELETE FROM {$wpdb->prefix}actionscheduler_groups WHERE slug = 'aioseo\_blc'" );

		// Delete all our custom capabilities.
		$this->uninstallCapabilities();
	}

	/**
	 * Returns all the DB tables with their prefix.
	 *
	 * @since 1.0.0
	 *
	 * @return array List of tables.
	 */
	private function getDbTables() {
		global $wpdb;

		$tables = [];
		foreach ( aioseoBrokenLinkChecker()->core->db->customTables as $tableName ) {
			$tables[] = $wpdb->prefix . $tableName;
		}

		return $tables;
	}

	/**
	 * Removes all our custom capabilities.
	 *
	 * @since 1.2.4
	 *
	 * @return void
	 */
	private function uninstallCapabilities() {
		$access             = new Utils\Access();
		$customCapabilities = $access->getCapabilityList() ?? [];
		$roles              = aioseoBrokenLinkChecker()->helpers->getUserRoles();

		// Loop through roles and remove custom capabilities.
		foreach ( $roles as $roleName => $roleInfo ) {
			$role = get_role( $roleName );

			if ( $role ) {
				foreach ( $customCapabilities as $capability ) {
					$role->remove_cap( $capability );
				}
			}
		}
	}
}