From 91f78ecd0f3ff7a2ae225a835b3c87b8a7224fb4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Steven=20Andr=C3=A9s?=
]*>/.test(l)&&(d=!0)}a[s]=l}return e=(e=(e=a.join("\n")).replace(/^\n+/g,"")).replace(/\n+$/g,""),t.converter._dispatch("paragraphs.after",e,r,t)}),x.subParser("runExtension",function(e,r,t,a){"use strict";return e.filter?r=e.filter(r,a.converter,t):e.regex&&((a=e.regex)instanceof RegExp||(a=new RegExp(a,"g")),r=r.replace(a,e.replace)),r}),x.subParser("spanGamut",function(e,r,t){"use strict";return e=t.converter._dispatch("spanGamut.before",e,r,t),e=x.subParser("codeSpans")(e,r,t),e=x.subParser("escapeSpecialCharsWithinTagAttributes")(e,r,t),e=x.subParser("encodeBackslashEscapes")(e,r,t),e=x.subParser("images")(e,r,t),e=x.subParser("anchors")(e,r,t),e=x.subParser("autoLinks")(e,r,t),e=x.subParser("simplifiedAutoLinks")(e,r,t),e=x.subParser("emoji")(e,r,t),e=x.subParser("underline")(e,r,t),e=x.subParser("italicsAndBold")(e,r,t),e=x.subParser("strikethrough")(e,r,t),e=x.subParser("ellipsis")(e,r,t),e=x.subParser("hashHTMLSpans")(e,r,t),e=x.subParser("encodeAmpsAndAngles")(e,r,t),r.simpleLineBreaks?/\n\n¨K/.test(e)||(e=e.replace(/\n+/g,"
\n")):e=e.replace(/ +\n/g,"
\n"),e=t.converter._dispatch("spanGamut.after",e,r,t)}),x.subParser("strikethrough",function(e,t,a){"use strict";return t.strikethrough&&(e=(e=a.converter._dispatch("strikethrough.before",e,t,a)).replace(/(?:~){2}([\s\S]+?)(?:~){2}/g,function(e,r){return r=r,""+(r=t.simplifiedAutoLink?x.subParser("simplifiedAutoLinks")(r,t,a):r)+""}),e=a.converter._dispatch("strikethrough.after",e,t,a)),e}),x.subParser("stripLinkDefinitions",function(i,l,c){"use strict";function e(e,r,t,a,n,s,o){return r=r.toLowerCase(),i.toLowerCase().split(r).length-1<2?e:(t.match(/^data:.+?\/.+?;base64,/)?c.gUrls[r]=t.replace(/\s/g,""):c.gUrls[r]=x.subParser("encodeAmpsAndAngles")(t,l,c),s?s+o:(o&&(c.gTitles[r]=o.replace(/"|'/g,""")),l.parseImgDimensions&&a&&n&&(c.gDimensions[r]={width:a,height:n}),""))}return i=(i=(i=(i+="¨0").replace(/^ {0,3}\[([^\]]+)]:[ \t]*\n?[ \t]*(data:.+?\/.+?;base64,[A-Za-z0-9+/=\n]+?)>?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*\n?[ \t]*(?:(\n*)["|'(](.+?)["|')][ \t]*)?(?:\n\n|(?=¨0)|(?=\n\[))/gm,e)).replace(/^ {0,3}\[([^\]]+)]:[ \t]*\n?[ \t]*([^>\s]+)>?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*\n?[ \t]*(?:(\n*)["|'(](.+?)["|')][ \t]*)?(?:\n+|(?=¨0))/gm,e)).replace(/¨0/,"")}),x.subParser("tables",function(e,y,P){"use strict";if(!y.tables)return e;function r(u){for(var e=u.split("\n"),r=0;r"+(a=x.subParser("spanGamut")(a,y,P))+"\n"));for(r=0;r"+x.subParser("spanGamut")(p,y,P)+"\n"));m.push(f)}for(var b=g,w=m,l="\n\n\n",k=b.length,c=0;c\n \n\n",c=0;c\n";for(var v=0;v\n"}return l+=" \n
\n"}return e=(e=(e=(e=P.converter._dispatch("tables.before",e,y,P)).replace(/\\(\|)/g,x.helper.escapeCharactersCallback)).replace(/^ {0,3}\|?.+\|.+\n {0,3}\|?[ \t]*:?[ \t]*(?:[-=]){2,}[ \t]*:?[ \t]*\|[ \t]*:?[ \t]*(?:[-=]){2,}[\s\S]+?(?:\n\n|¨0)/gm,r)).replace(/^ {0,3}\|.+\|[ \t]*\n {0,3}\|[ \t]*:?[ \t]*(?:[-=]){2,}[ \t]*:?[ \t]*\|[ \t]*\n( {0,3}\|.+\|[ \t]*\n)*(?:\n|¨0)/gm,r),e=P.converter._dispatch("tables.after",e,y,P)}),x.subParser("underline",function(e,r,t){"use strict";return r.underline?(e=t.converter._dispatch("underline.before",e,r,t),e=(e=r.literalMidWordUnderscores?(e=e.replace(/\b___(\S[\s\S]*?)___\b/g,function(e,r){return""+r+""})).replace(/\b__(\S[\s\S]*?)__\b/g,function(e,r){return""+r+""}):(e=e.replace(/___(\S[\s\S]*?)___/g,function(e,r){return/\S$/.test(r)?""+r+"":e})).replace(/__(\S[\s\S]*?)__/g,function(e,r){return/\S$/.test(r)?""+r+"":e})).replace(/(_)/g,x.helper.escapeCharactersCallback),t.converter._dispatch("underline.after",e,r,t)):e}),x.subParser("unescapeSpecialChars",function(e,r,t){"use strict";return e=(e=t.converter._dispatch("unescapeSpecialChars.before",e,r,t)).replace(/¨E(\d+)E/g,function(e,r){r=parseInt(r);return String.fromCharCode(r)}),e=t.converter._dispatch("unescapeSpecialChars.after",e,r,t)}),x.subParser("makeMarkdown.blockquote",function(e,r){"use strict";var t="";if(e.hasChildNodes())for(var a=e.childNodes,n=a.length,s=0;s ")}),x.subParser("makeMarkdown.codeBlock",function(e,r){"use strict";var t=e.getAttribute("language"),e=e.getAttribute("precodenum");return"```"+t+"\n"+r.preList[e]+"\n```"}),x.subParser("makeMarkdown.codeSpan",function(e){"use strict";return"`"+e.innerHTML+"`"}),x.subParser("makeMarkdown.emphasis",function(e,r){"use strict";var t="";if(e.hasChildNodes()){t+="*";for(var a=e.childNodes,n=a.length,s=0;s",e.hasAttribute("width")&&e.hasAttribute("height")&&(r+=" ="+e.getAttribute("width")+"x"+e.getAttribute("height")),e.hasAttribute("title")&&(r+=' "'+e.getAttribute("title")+'"'),r+=")"),r}),x.subParser("makeMarkdown.links",function(e,r){"use strict";var t="";if(e.hasChildNodes()&&e.hasAttribute("href")){for(var a=e.childNodes,n=a.length,t="[",s=0;s"),e.hasAttribute("title")&&(t+=' "'+e.getAttribute("title")+'"'),t+=")"}return t}),x.subParser("makeMarkdown.list",function(e,r,t){"use strict";var a="";if(!e.hasChildNodes())return"";for(var n=e.childNodes,s=n.length,o=e.getAttribute("start")||1,i=0;i"+r.preList[e]+""}),x.subParser("makeMarkdown.strikethrough",function(e,r){"use strict";var t="";if(e.hasChildNodes()){t+="~~";for(var a=e.childNodes,n=a.length,s=0;str>th"),s=e.querySelectorAll("tbody>tr"),o=0;o/g,"\\$1>")).replace(/^#/gm,"\\#")).replace(/^(\s*)([-=]{3,})(\s*)$/,"$1\\$2$3")).replace(/^( {0,3}\d+)\./gm,"$1\\.")).replace(/^( {0,3})([+-])/gm,"$1\\$2")).replace(/]([\s]*)\(/g,"\\]$1\\(")).replace(/^ {0,3}\[([\S \t]*?)]:/gm,"\\[$1]:")});"function"==typeof define&&define.amd?define(function(){"use strict";return x}):"undefined"!=typeof module&&module.exports?module.exports=x:this.showdown=x}.call(this);
diff --git a/tpl/bootstrap.php b/tpl/bootstrap.php
index 650504d..f714a5e 100644
--- a/tpl/bootstrap.php
+++ b/tpl/bootstrap.php
@@ -41,7 +41,7 @@ if ($SYNTAXHIGHLIGHTING) :
endif;
?>
-
+
@@ -55,7 +55,7 @@ if ($ZEROBINCOMPATIBILITY) :
endif;
?>
-
+
-
+
-
+
diff --git a/tpl/page.php b/tpl/page.php
index f54df21..b4ae12a 100644
--- a/tpl/page.php
+++ b/tpl/page.php
@@ -20,7 +20,7 @@ if ($SYNTAXHIGHLIGHTING):
endif;
endif;
?>
-
+
@@ -34,7 +34,7 @@ if ($ZEROBINCOMPATIBILITY):
endif;
?>
-
+
-
+
-
+
diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php
index 2693674..3f795c8 100644
--- a/vendor/composer/autoload_classmap.php
+++ b/vendor/composer/autoload_classmap.php
@@ -12,6 +12,7 @@ return array(
'IPLib\\Address\\IPv6' => $vendorDir . '/mlocati/ip-lib/src/Address/IPv6.php',
'IPLib\\Address\\Type' => $vendorDir . '/mlocati/ip-lib/src/Address/Type.php',
'IPLib\\Factory' => $vendorDir . '/mlocati/ip-lib/src/Factory.php',
+ 'IPLib\\ParseStringFlag' => $vendorDir . '/mlocati/ip-lib/src/ParseStringFlag.php',
'IPLib\\Range\\AbstractRange' => $vendorDir . '/mlocati/ip-lib/src/Range/AbstractRange.php',
'IPLib\\Range\\Pattern' => $vendorDir . '/mlocati/ip-lib/src/Range/Pattern.php',
'IPLib\\Range\\RangeInterface' => $vendorDir . '/mlocati/ip-lib/src/Range/RangeInterface.php',
@@ -19,7 +20,8 @@ return array(
'IPLib\\Range\\Subnet' => $vendorDir . '/mlocati/ip-lib/src/Range/Subnet.php',
'IPLib\\Range\\Type' => $vendorDir . '/mlocati/ip-lib/src/Range/Type.php',
'IPLib\\Service\\BinaryMath' => $vendorDir . '/mlocati/ip-lib/src/Service/BinaryMath.php',
- 'IPLib\\Service\\RangesFromBounradyCalculator' => $vendorDir . '/mlocati/ip-lib/src/Service/RangesFromBounradyCalculator.php',
+ 'IPLib\\Service\\RangesFromBoundaryCalculator' => $vendorDir . '/mlocati/ip-lib/src/Service/RangesFromBoundaryCalculator.php',
+ 'IPLib\\Service\\UnsignedIntegerMath' => $vendorDir . '/mlocati/ip-lib/src/Service/UnsignedIntegerMath.php',
'Identicon\\Generator\\BaseGenerator' => $vendorDir . '/yzalis/identicon/src/Identicon/Generator/BaseGenerator.php',
'Identicon\\Generator\\GdGenerator' => $vendorDir . '/yzalis/identicon/src/Identicon/Generator/GdGenerator.php',
'Identicon\\Generator\\GeneratorInterface' => $vendorDir . '/yzalis/identicon/src/Identicon/Generator/GeneratorInterface.php',
@@ -41,7 +43,6 @@ return array(
'PrivateBin\\Model\\Comment' => $baseDir . '/lib/Model/Comment.php',
'PrivateBin\\Model\\Paste' => $baseDir . '/lib/Model/Paste.php',
'PrivateBin\\Persistence\\AbstractPersistence' => $baseDir . '/lib/Persistence/AbstractPersistence.php',
- 'PrivateBin\\Persistence\\DataStore' => $baseDir . '/lib/Persistence/DataStore.php',
'PrivateBin\\Persistence\\PurgeLimiter' => $baseDir . '/lib/Persistence/PurgeLimiter.php',
'PrivateBin\\Persistence\\ServerSalt' => $baseDir . '/lib/Persistence/ServerSalt.php',
'PrivateBin\\Persistence\\TrafficLimiter' => $baseDir . '/lib/Persistence/TrafficLimiter.php',
diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php
index 58f7a58..ab1f7d3 100644
--- a/vendor/composer/autoload_static.php
+++ b/vendor/composer/autoload_static.php
@@ -44,6 +44,7 @@ class ComposerStaticInitDontChange
'IPLib\\Address\\IPv6' => __DIR__ . '/..' . '/mlocati/ip-lib/src/Address/IPv6.php',
'IPLib\\Address\\Type' => __DIR__ . '/..' . '/mlocati/ip-lib/src/Address/Type.php',
'IPLib\\Factory' => __DIR__ . '/..' . '/mlocati/ip-lib/src/Factory.php',
+ 'IPLib\\ParseStringFlag' => __DIR__ . '/..' . '/mlocati/ip-lib/src/ParseStringFlag.php',
'IPLib\\Range\\AbstractRange' => __DIR__ . '/..' . '/mlocati/ip-lib/src/Range/AbstractRange.php',
'IPLib\\Range\\Pattern' => __DIR__ . '/..' . '/mlocati/ip-lib/src/Range/Pattern.php',
'IPLib\\Range\\RangeInterface' => __DIR__ . '/..' . '/mlocati/ip-lib/src/Range/RangeInterface.php',
@@ -51,7 +52,8 @@ class ComposerStaticInitDontChange
'IPLib\\Range\\Subnet' => __DIR__ . '/..' . '/mlocati/ip-lib/src/Range/Subnet.php',
'IPLib\\Range\\Type' => __DIR__ . '/..' . '/mlocati/ip-lib/src/Range/Type.php',
'IPLib\\Service\\BinaryMath' => __DIR__ . '/..' . '/mlocati/ip-lib/src/Service/BinaryMath.php',
- 'IPLib\\Service\\RangesFromBounradyCalculator' => __DIR__ . '/..' . '/mlocati/ip-lib/src/Service/RangesFromBounradyCalculator.php',
+ 'IPLib\\Service\\RangesFromBoundaryCalculator' => __DIR__ . '/..' . '/mlocati/ip-lib/src/Service/RangesFromBoundaryCalculator.php',
+ 'IPLib\\Service\\UnsignedIntegerMath' => __DIR__ . '/..' . '/mlocati/ip-lib/src/Service/UnsignedIntegerMath.php',
'Identicon\\Generator\\BaseGenerator' => __DIR__ . '/..' . '/yzalis/identicon/src/Identicon/Generator/BaseGenerator.php',
'Identicon\\Generator\\GdGenerator' => __DIR__ . '/..' . '/yzalis/identicon/src/Identicon/Generator/GdGenerator.php',
'Identicon\\Generator\\GeneratorInterface' => __DIR__ . '/..' . '/yzalis/identicon/src/Identicon/Generator/GeneratorInterface.php',
@@ -73,7 +75,6 @@ class ComposerStaticInitDontChange
'PrivateBin\\Model\\Comment' => __DIR__ . '/../..' . '/lib/Model/Comment.php',
'PrivateBin\\Model\\Paste' => __DIR__ . '/../..' . '/lib/Model/Paste.php',
'PrivateBin\\Persistence\\AbstractPersistence' => __DIR__ . '/../..' . '/lib/Persistence/AbstractPersistence.php',
- 'PrivateBin\\Persistence\\DataStore' => __DIR__ . '/../..' . '/lib/Persistence/DataStore.php',
'PrivateBin\\Persistence\\PurgeLimiter' => __DIR__ . '/../..' . '/lib/Persistence/PurgeLimiter.php',
'PrivateBin\\Persistence\\ServerSalt' => __DIR__ . '/../..' . '/lib/Persistence/ServerSalt.php',
'PrivateBin\\Persistence\\TrafficLimiter' => __DIR__ . '/../..' . '/lib/Persistence/TrafficLimiter.php',
diff --git a/vendor/mlocati/ip-lib/src/Address/AddressInterface.php b/vendor/mlocati/ip-lib/src/Address/AddressInterface.php
index 3b254c4..f484c62 100644
--- a/vendor/mlocati/ip-lib/src/Address/AddressInterface.php
+++ b/vendor/mlocati/ip-lib/src/Address/AddressInterface.php
@@ -21,6 +21,8 @@ interface AddressInterface
*
* @return int
*
+ * @since 1.14.0
+ *
* @example 32 for IPv4
* @example 128 for IPv6
*/
@@ -51,6 +53,8 @@ interface AddressInterface
*
* @return string
*
+ * @since 1.14.0
+ *
* @example For localhost: For IPv4 you'll get '01111111000000000000000000000001' (32 digits), for IPv6 '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001' (128 digits)
*/
public function getBits();
@@ -66,6 +70,8 @@ interface AddressInterface
* Get the default RFC reserved range type.
*
* @return int One of the \IPLib\Range\Type::T_... constants
+ *
+ * @since 1.5.0
*/
public static function getDefaultReservedRangeType();
@@ -73,6 +79,8 @@ interface AddressInterface
* Get the RFC reserved ranges (except the ones of type getDefaultReservedRangeType).
*
* @return \IPLib\Address\AssignedRange[] ranges are sorted
+ *
+ * @since 1.5.0
*/
public static function getReservedRanges();
@@ -99,10 +107,28 @@ interface AddressInterface
*/
public function matches(RangeInterface $range);
+ /**
+ * Get the address at a certain distance from this address.
+ *
+ * @param int $n the distance of the address (can be negative)
+ *
+ * @return \IPLib\Address\AddressInterface|null return NULL if $n is not an integer or if the final address would be invalid
+ *
+ * @since 1.15.0
+ *
+ * @example passing 1 to the address 127.0.0.1 will result in 127.0.0.2
+ * @example passing -1 to the address 127.0.0.1 will result in 127.0.0.0
+ * @example passing -1 to the address 0.0.0.0 will result in NULL
+ */
+ public function getAddressAtOffset($n);
+
/**
* Get the address right after this IP address (if available).
*
* @return \IPLib\Address\AddressInterface|null
+ *
+ * @see \IPLib\Address\AddressInterface::getAddressAtOffset()
+ * @since 1.4.0
*/
public function getNextAddress();
@@ -110,6 +136,9 @@ interface AddressInterface
* Get the address right before this IP address (if available).
*
* @return \IPLib\Address\AddressInterface|null
+ *
+ * @see \IPLib\Address\AddressInterface::getAddressAtOffset()
+ * @since 1.4.0
*/
public function getPreviousAddress();
@@ -118,6 +147,8 @@ interface AddressInterface
*
* @return string
*
+ * @since 1.12.0
+ *
* @example for IPv4 it returns something like x.x.x.x.in-addr.arpa
* @example for IPv6 it returns something like x.x.x.x..x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.ip6.arpa
*/
diff --git a/vendor/mlocati/ip-lib/src/Address/AssignedRange.php b/vendor/mlocati/ip-lib/src/Address/AssignedRange.php
index c5db08d..3d6fa53 100644
--- a/vendor/mlocati/ip-lib/src/Address/AssignedRange.php
+++ b/vendor/mlocati/ip-lib/src/Address/AssignedRange.php
@@ -6,6 +6,8 @@ use IPLib\Range\RangeInterface;
/**
* Represents an IP address range with an assigned range type.
+ *
+ * @since 1.5.0
*/
class AssignedRange
{
diff --git a/vendor/mlocati/ip-lib/src/Address/IPv4.php b/vendor/mlocati/ip-lib/src/Address/IPv4.php
index d31338e..cd2c416 100644
--- a/vendor/mlocati/ip-lib/src/Address/IPv4.php
+++ b/vendor/mlocati/ip-lib/src/Address/IPv4.php
@@ -2,6 +2,7 @@
namespace IPLib\Address;
+use IPLib\ParseStringFlag;
use IPLib\Range\RangeInterface;
use IPLib\Range\Subnet;
use IPLib\Range\Type as RangeType;
@@ -46,7 +47,7 @@ class IPv4 implements AddressInterface
*
* @var array|null
*/
- private static $reservedRanges = null;
+ private static $reservedRanges;
/**
* Initializes the instance.
@@ -82,52 +83,94 @@ class IPv4 implements AddressInterface
}
/**
- * Parse a string and returns an IPv4 instance if the string is valid, or null otherwise.
+ * @deprecated since 1.17.0: use the parseString() method instead.
+ * For upgrading:
+ * - if $mayIncludePort is true, use the ParseStringFlag::MAY_INCLUDE_PORT flag
+ * - if $supportNonDecimalIPv4 is true, use the ParseStringFlag::IPV4_MAYBE_NON_DECIMAL flag
*
* @param string|mixed $address the address to parse
- * @param bool $mayIncludePort set to false to avoid parsing addresses with ports
- * @param bool $supportNonDecimalIPv4 set to true to support parsing non decimal (that is, octal and hexadecimal) IPv4 addresses
+ * @param bool $mayIncludePort
+ * @param bool $supportNonDecimalIPv4
*
* @return static|null
+ *
+ * @see \IPLib\Address\IPv4::parseString()
+ * @since 1.1.0 added the $mayIncludePort argument
+ * @since 1.10.0 added the $supportNonDecimalIPv4 argument
*/
public static function fromString($address, $mayIncludePort = true, $supportNonDecimalIPv4 = false)
{
- if (!is_string($address) || !strpos($address, '.')) {
+ return static::parseString($address, 0 | ($mayIncludePort ? ParseStringFlag::MAY_INCLUDE_PORT : 0) | ($supportNonDecimalIPv4 ? ParseStringFlag::IPV4_MAYBE_NON_DECIMAL : 0));
+ }
+
+ /**
+ * Parse a string and returns an IPv4 instance if the string is valid, or null otherwise.
+ *
+ * @param string|mixed $address the address to parse
+ * @param int $flags A combination or zero or more flags
+ *
+ * @return static|null
+ *
+ * @see \IPLib\ParseStringFlag
+ * @since 1.17.0
+ */
+ public static function parseString($address, $flags = 0)
+ {
+ if (!is_string($address)) {
return null;
}
- $rxChunk = '0?[0-9]{1,3}';
- if ($supportNonDecimalIPv4) {
- $rxChunk = "(?:0[Xx]0*[0-9A-Fa-f]{1,2})|(?:{$rxChunk})";
+ $flags = (int) $flags;
+ $matches = null;
+ if ($flags & ParseStringFlag::ADDRESS_MAYBE_RDNS) {
+ if (preg_match('/^([12]?[0-9]{1,2}\.[12]?[0-9]{1,2}\.[12]?[0-9]{1,2}\.[12]?[0-9]{1,2})\.in-addr\.arpa\.?$/i', $address, $matches)) {
+ $address = implode('.', array_reverse(explode('.', $matches[1])));
+ $flags = $flags & ~(ParseStringFlag::IPV4_MAYBE_NON_DECIMAL | ParseStringFlag::IPV4ADDRESS_MAYBE_NON_QUAD_DOTTED);
+ }
}
- $rx = "0*?({$rxChunk})\.0*?({$rxChunk})\.0*?({$rxChunk})\.0*?({$rxChunk})";
- if ($mayIncludePort) {
+ if ($flags & ParseStringFlag::IPV4ADDRESS_MAYBE_NON_QUAD_DOTTED) {
+ if (strpos($address, '.') === 0) {
+ return null;
+ }
+ $lengthNonHex = '{1,11}';
+ $lengthHex = '{1,8}';
+ $chunk234Optional = true;
+ } else {
+ if (!strpos($address, '.')) {
+ return null;
+ }
+ $lengthNonHex = '{1,3}';
+ $lengthHex = '{1,2}';
+ $chunk234Optional = false;
+ }
+ $rxChunk1 = "0?[0-9]{$lengthNonHex}";
+ if ($flags & ParseStringFlag::IPV4_MAYBE_NON_DECIMAL) {
+ $rxChunk1 = "(?:0[Xx]0*[0-9A-Fa-f]{$lengthHex})|(?:{$rxChunk1})";
+ $onlyDecimal = false;
+ } else {
+ $onlyDecimal = true;
+ }
+ $rxChunk1 = "0*?({$rxChunk1})";
+ $rxChunk234 = "\.{$rxChunk1}";
+ if ($chunk234Optional) {
+ $rxChunk234 = "(?:{$rxChunk234})?";
+ }
+ $rx = "{$rxChunk1}{$rxChunk234}{$rxChunk234}{$rxChunk234}";
+ if ($flags & ParseStringFlag::MAY_INCLUDE_PORT) {
$rx .= '(?::\d+)?';
}
- $matches = null;
if (!preg_match('/^' . $rx . '$/', $address, $matches)) {
return null;
}
+ $math = new \IPLib\Service\UnsignedIntegerMath();
$nums = array();
- for ($i = 1; $i <= 4; $i++) {
- $s = $matches[$i];
- if ($supportNonDecimalIPv4) {
- if (stripos($s, '0x') === 0) {
- $n = hexdec(substr($s, 2));
- } elseif ($s[0] === '0') {
- if (!preg_match('/^[0-7]+$/', $s)) {
- return null;
- }
- $n = octdec(substr($s, 1));
- } else {
- $n = (int) $s;
- }
- } else {
- $n = (int) $s;
- }
- if ($n < 0 || $n > 255) {
+ $maxChunkIndex = count($matches) - 1;
+ for ($i = 1; $i <= $maxChunkIndex; $i++) {
+ $numBytes = $i === $maxChunkIndex ? 5 - $i : 1;
+ $chunkBytes = $math->getBytes($matches[$i], $numBytes, $onlyDecimal);
+ if ($chunkBytes === null) {
return null;
}
- $nums[] = (string) $n;
+ $nums = array_merge($nums, $chunkBytes);
}
return new static(implode('.', $nums));
@@ -179,6 +222,8 @@ class IPv4 implements AddressInterface
*
* @return string
*
+ * @since 1.10.0
+ *
* @example if $long == false: if the decimal representation is '0.7.8.255': '0.7.010.0377'
* @example if $long == true: if the decimal representation is '0.7.8.255': '0000.0007.0010.0377'
*/
@@ -203,6 +248,8 @@ class IPv4 implements AddressInterface
*
* @return string
*
+ * @since 1.10.0
+ *
* @example if $long == false: if the decimal representation is '0.9.10.255': '0.9.0xa.0xff'
* @example if $long == true: if the decimal representation is '0.9.10.255': '0x00.0x09.0x0a.0xff'
*/
@@ -318,10 +365,10 @@ class IPv4 implements AddressInterface
$exceptions = array();
if (isset($data[1])) {
foreach ($data[1] as $exceptionRange => $exceptionType) {
- $exceptions[] = new AssignedRange(Subnet::fromString($exceptionRange), $exceptionType);
+ $exceptions[] = new AssignedRange(Subnet::parseString($exceptionRange), $exceptionType);
}
}
- $reservedRanges[] = new AssignedRange(Subnet::fromString($range), $data[0], $exceptions);
+ $reservedRanges[] = new AssignedRange(Subnet::parseString($range), $data[0], $exceptions);
}
self::$reservedRanges = $reservedRanges;
}
@@ -359,13 +406,15 @@ class IPv4 implements AddressInterface
{
$myBytes = $this->getBytes();
- return IPv6::fromString('2002:' . sprintf('%02x', $myBytes[0]) . sprintf('%02x', $myBytes[1]) . ':' . sprintf('%02x', $myBytes[2]) . sprintf('%02x', $myBytes[3]) . '::');
+ return IPv6::parseString('2002:' . sprintf('%02x', $myBytes[0]) . sprintf('%02x', $myBytes[1]) . ':' . sprintf('%02x', $myBytes[2]) . sprintf('%02x', $myBytes[3]) . '::');
}
/**
* Create an IPv6 representation of this address (in IPv6 IPv4-mapped notation).
*
* @return \IPLib\Address\IPv6
+ *
+ * @since 1.11.0
*/
public function toIPv6IPv4Mapped()
{
@@ -400,6 +449,37 @@ class IPv4 implements AddressInterface
return $range->contains($this);
}
+ /**
+ * {@inheritdoc}
+ *
+ * @see \IPLib\Address\AddressInterface::getAddressAtOffset()
+ */
+ public function getAddressAtOffset($n)
+ {
+ if (!is_int($n)) {
+ return null;
+ }
+
+ $boundary = 256;
+ $mod = $n;
+ $bytes = $this->getBytes();
+ for ($i = count($bytes) - 1; $i >= 0; $i--) {
+ $tmp = ($bytes[$i] + $mod) % $boundary;
+ $mod = (int) floor(($bytes[$i] + $mod) / $boundary);
+ if ($tmp < 0) {
+ $tmp += $boundary;
+ }
+
+ $bytes[$i] = $tmp;
+ }
+
+ if ($mod !== 0) {
+ return null;
+ }
+
+ return static::fromBytes($bytes);
+ }
+
/**
* {@inheritdoc}
*
@@ -407,22 +487,7 @@ class IPv4 implements AddressInterface
*/
public function getNextAddress()
{
- $overflow = false;
- $bytes = $this->getBytes();
- for ($i = count($bytes) - 1; $i >= 0; $i--) {
- if ($bytes[$i] === 255) {
- if ($i === 0) {
- $overflow = true;
- break;
- }
- $bytes[$i] = 0;
- } else {
- $bytes[$i]++;
- break;
- }
- }
-
- return $overflow ? null : static::fromBytes($bytes);
+ return $this->getAddressAtOffset(1);
}
/**
@@ -432,22 +497,7 @@ class IPv4 implements AddressInterface
*/
public function getPreviousAddress()
{
- $overflow = false;
- $bytes = $this->getBytes();
- for ($i = count($bytes) - 1; $i >= 0; $i--) {
- if ($bytes[$i] === 0) {
- if ($i === 0) {
- $overflow = true;
- break;
- }
- $bytes[$i] = 255;
- } else {
- $bytes[$i]--;
- break;
- }
- }
-
- return $overflow ? null : static::fromBytes($bytes);
+ return $this->getAddressAtOffset(-1);
}
/**
diff --git a/vendor/mlocati/ip-lib/src/Address/IPv6.php b/vendor/mlocati/ip-lib/src/Address/IPv6.php
index 3666487..e094881 100644
--- a/vendor/mlocati/ip-lib/src/Address/IPv6.php
+++ b/vendor/mlocati/ip-lib/src/Address/IPv6.php
@@ -2,6 +2,7 @@
namespace IPLib\Address;
+use IPLib\ParseStringFlag;
use IPLib\Range\RangeInterface;
use IPLib\Range\Subnet;
use IPLib\Range\Type as RangeType;
@@ -55,7 +56,7 @@ class IPv6 implements AddressInterface
*
* @var array|null
*/
- private static $reservedRanges = null;
+ private static $reservedRanges;
/**
* Initializes the instance.
@@ -92,32 +93,69 @@ class IPv6 implements AddressInterface
}
/**
- * Parse a string and returns an IPv6 instance if the string is valid, or null otherwise.
+ * @deprecated since 1.17.0: use the parseString() method instead.
+ * For upgrading:
+ * - if $mayIncludePort is true, use the ParseStringFlag::MAY_INCLUDE_PORT flag
+ * - if $mayIncludeZoneID is true, use the ParseStringFlag::MAY_INCLUDE_ZONEID flag
*
- * @param string|mixed $address the address to parse
- * @param bool $mayIncludePort set to false to avoid parsing addresses with ports
- * @param bool $mayIncludeZoneID set to false to avoid parsing addresses with zone IDs (see RFC 4007)
+ * @param string|mixed $address
+ * @param bool $mayIncludePort
+ * @param bool $mayIncludeZoneID
*
* @return static|null
+ *
+ * @see \IPLib\Address\IPv6::parseString()
+ * @since 1.1.0 added the $mayIncludePort argument
+ * @since 1.3.0 added the $mayIncludeZoneID argument
*/
public static function fromString($address, $mayIncludePort = true, $mayIncludeZoneID = true)
{
+ return static::parseString($address, 0 | ($mayIncludePort ? ParseStringFlag::MAY_INCLUDE_PORT : 0) | ($mayIncludeZoneID ? ParseStringFlag::MAY_INCLUDE_ZONEID : 0));
+ }
+
+ /**
+ * Parse a string and returns an IPv6 instance if the string is valid, or null otherwise.
+ *
+ * @param string|mixed $address the address to parse
+ * @param int $flags A combination or zero or more flags
+ *
+ * @return static|null
+ *
+ * @see \IPLib\ParseStringFlag
+ * @since 1.17.0
+ */
+ public static function parseString($address, $flags = 0)
+ {
+ if (!is_string($address)) {
+ return null;
+ }
+ $matches = null;
+ $flags = (int) $flags;
+ if ($flags & ParseStringFlag::ADDRESS_MAYBE_RDNS) {
+ if (preg_match('/^([0-9a-f](?:\.[0-9a-f]){31})\.ip6\.arpa\.?/i', $address, $matches)) {
+ $nibbles = array_reverse(explode('.', $matches[1]));
+ $quibbles = array();
+ foreach (array_chunk($nibbles, 4) as $n) {
+ $quibbles[] = implode('', $n);
+ }
+ $address = implode(':', $quibbles);
+ }
+ }
$result = null;
if (is_string($address) && strpos($address, ':') !== false && strpos($address, ':::') === false) {
- $matches = null;
- if ($mayIncludePort && $address[0] === '[' && preg_match('/^\[(.+)]:\d+$/', $address, $matches)) {
+ if ($flags & ParseStringFlag::MAY_INCLUDE_PORT && $address[0] === '[' && preg_match('/^\[(.+)]:\d+$/', $address, $matches)) {
$address = $matches[1];
}
- if ($mayIncludeZoneID) {
+ if ($flags & ParseStringFlag::MAY_INCLUDE_ZONEID) {
$percentagePos = strpos($address, '%');
if ($percentagePos > 0) {
$address = substr($address, 0, $percentagePos);
}
}
if (preg_match('/^((?:[0-9a-f]*:+)+)(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/i', $address, $matches)) {
- $address6 = static::fromString($matches[1] . '0:0', false);
+ $address6 = static::parseString($matches[1] . '0:0');
if ($address6 !== null) {
- $address4 = IPv4::fromString($matches[2], false);
+ $address4 = IPv4::parseString($matches[2]);
if ($address4 !== null) {
$bytes4 = $address4->getBytes();
$address6->longAddress = substr($address6->longAddress, 0, -9) . sprintf('%02x%02x:%02x%02x', $bytes4[0], $bytes4[1], $bytes4[2], $bytes4[3]);
@@ -401,10 +439,10 @@ class IPv6 implements AddressInterface
$exceptions = array();
if (isset($data[1])) {
foreach ($data[1] as $exceptionRange => $exceptionType) {
- $exceptions[] = new AssignedRange(Subnet::fromString($exceptionRange), $exceptionType);
+ $exceptions[] = new AssignedRange(Subnet::parseString($exceptionRange), $exceptionType);
}
}
- $reservedRanges[] = new AssignedRange(Subnet::fromString($range), $data[0], $exceptions);
+ $reservedRanges[] = new AssignedRange(Subnet::parseString($range), $data[0], $exceptions);
}
self::$reservedRanges = $reservedRanges;
}
@@ -471,6 +509,7 @@ class IPv6 implements AddressInterface
* @example '0000:0000:0000:0000:0000:0000:013.001.068.003' when $ipV6Long and $ipV4Long are true
*
* @see https://tools.ietf.org/html/rfc4291#section-2.2 point 3.
+ * @since 1.9.0
*/
public function toMixedIPv6IPv4String($ipV6Long = false, $ipV4Long = false)
{
@@ -503,6 +542,37 @@ class IPv6 implements AddressInterface
return $range->contains($this);
}
+ /**
+ * {@inheritdoc}
+ *
+ * @see \IPLib\Address\AddressInterface::getAddressAtOffset()
+ */
+ public function getAddressAtOffset($n)
+ {
+ if (!is_int($n)) {
+ return null;
+ }
+
+ $boundary = 0x10000;
+ $mod = $n;
+ $words = $this->getWords();
+ for ($i = count($words) - 1; $i >= 0; $i--) {
+ $tmp = ($words[$i] + $mod) % $boundary;
+ $mod = (int) floor(($words[$i] + $mod) / $boundary);
+ if ($tmp < 0) {
+ $tmp += $boundary;
+ }
+
+ $words[$i] = $tmp;
+ }
+
+ if ($mod !== 0) {
+ return null;
+ }
+
+ return static::fromWords($words);
+ }
+
/**
* {@inheritdoc}
*
@@ -510,22 +580,7 @@ class IPv6 implements AddressInterface
*/
public function getNextAddress()
{
- $overflow = false;
- $words = $this->getWords();
- for ($i = count($words) - 1; $i >= 0; $i--) {
- if ($words[$i] === 0xffff) {
- if ($i === 0) {
- $overflow = true;
- break;
- }
- $words[$i] = 0;
- } else {
- $words[$i]++;
- break;
- }
- }
-
- return $overflow ? null : static::fromWords($words);
+ return $this->getAddressAtOffset(1);
}
/**
@@ -535,22 +590,7 @@ class IPv6 implements AddressInterface
*/
public function getPreviousAddress()
{
- $overflow = false;
- $words = $this->getWords();
- for ($i = count($words) - 1; $i >= 0; $i--) {
- if ($words[$i] === 0) {
- if ($i === 0) {
- $overflow = true;
- break;
- }
- $words[$i] = 0xffff;
- } else {
- $words[$i]--;
- break;
- }
- }
-
- return $overflow ? null : static::fromWords($words);
+ return $this->getAddressAtOffset(-1);
}
/**
diff --git a/vendor/mlocati/ip-lib/src/Address/Type.php b/vendor/mlocati/ip-lib/src/Address/Type.php
index 06b0753..17488f3 100644
--- a/vendor/mlocati/ip-lib/src/Address/Type.php
+++ b/vendor/mlocati/ip-lib/src/Address/Type.php
@@ -27,6 +27,8 @@ class Type
* @param int $type
*
* @return string
+ *
+ * @since 1.1.0
*/
public static function getName($type)
{
diff --git a/vendor/mlocati/ip-lib/src/Factory.php b/vendor/mlocati/ip-lib/src/Factory.php
index 76b6e1e..31b21d4 100644
--- a/vendor/mlocati/ip-lib/src/Factory.php
+++ b/vendor/mlocati/ip-lib/src/Factory.php
@@ -4,7 +4,7 @@ namespace IPLib;
use IPLib\Address\AddressInterface;
use IPLib\Range\Subnet;
-use IPLib\Service\RangesFromBounradyCalculator;
+use IPLib\Service\RangesFromBoundaryCalculator;
/**
* Factory methods to build class instances.
@@ -12,23 +12,48 @@ use IPLib\Service\RangesFromBounradyCalculator;
class Factory
{
/**
- * Parse an IP address string.
+ * @deprecated since 1.17.0: use the parseAddressString() method instead.
+ * For upgrading:
+ * - if $mayIncludePort is true, use the ParseStringFlag::MAY_INCLUDE_PORT flag
+ * - if $mayIncludeZoneID is true, use the ParseStringFlag::MAY_INCLUDE_ZONEID flag
+ * - if $supportNonDecimalIPv4 is true, use the ParseStringFlag::IPV4_MAYBE_NON_DECIMAL flag
*
- * @param string $address the address to parse
- * @param bool $mayIncludePort set to false to avoid parsing addresses with ports
- * @param bool $mayIncludeZoneID set to false to avoid parsing IPv6 addresses with zone IDs (see RFC 4007)
- * @param bool $supportNonDecimalIPv4 set to true to support parsing non decimal (that is, octal and hexadecimal) IPv4 addresses
+ * @param string|mixed $address
+ * @param bool $mayIncludePort
+ * @param bool $mayIncludeZoneID
+ * @param bool $supportNonDecimalIPv4
*
* @return \IPLib\Address\AddressInterface|null
+ *
+ * @see \IPLib\Factory::parseAddressString()
+ * @since 1.1.0 added the $mayIncludePort argument
+ * @since 1.3.0 added the $mayIncludeZoneID argument
+ * @since 1.10.0 added the $supportNonDecimalIPv4 argument
*/
public static function addressFromString($address, $mayIncludePort = true, $mayIncludeZoneID = true, $supportNonDecimalIPv4 = false)
+ {
+ return static::parseAddressString($address, 0 + ($mayIncludePort ? ParseStringFlag::MAY_INCLUDE_PORT : 0) + ($mayIncludeZoneID ? ParseStringFlag::MAY_INCLUDE_ZONEID : 0) + ($supportNonDecimalIPv4 ? ParseStringFlag::IPV4_MAYBE_NON_DECIMAL : 0));
+ }
+
+ /**
+ * Parse an IP address string.
+ *
+ * @param string|mixed $address the address to parse
+ * @param int $flags A combination or zero or more flags
+ *
+ * @return \IPLib\Address\AddressInterface|null
+ *
+ * @see \IPLib\ParseStringFlag
+ * @since 1.17.0
+ */
+ public static function parseAddressString($address, $flags = 0)
{
$result = null;
if ($result === null) {
- $result = Address\IPv4::fromString($address, $mayIncludePort, $supportNonDecimalIPv4);
+ $result = Address\IPv4::parseString($address, $flags);
}
if ($result === null) {
- $result = Address\IPv6::fromString($address, $mayIncludePort, $mayIncludeZoneID);
+ $result = Address\IPv6::parseString($address, $flags);
}
return $result;
@@ -54,59 +79,125 @@ class Factory
return $result;
}
+ /**
+ * @deprecated since 1.17.0: use the parseRangeString() method instead.
+ * For upgrading:
+ * - if $supportNonDecimalIPv4 is true, use the ParseStringFlag::IPV4_MAYBE_NON_DECIMAL flag
+ *
+ * @param string|mixed $address
+ * @param bool $supportNonDecimalIPv4
+ *
+ * @return \IPLib\Address\AddressInterface|null
+ *
+ * @see \IPLib\Factory::parseRangeString()
+ * @since 1.10.0 added the $supportNonDecimalIPv4 argument
+ */
+ public static function rangeFromString($address, $supportNonDecimalIPv4 = false)
+ {
+ return static::parseRangeString($address, $supportNonDecimalIPv4 ? ParseStringFlag::IPV4_MAYBE_NON_DECIMAL : 0);
+ }
+
/**
* Parse an IP range string.
*
* @param string $range
- * @param bool $supportNonDecimalIPv4 set to true to support parsing non decimal (that is, octal and hexadecimal) IPv4 addresses
+ * @param int $flags A combination or zero or more flags
*
* @return \IPLib\Range\RangeInterface|null
+ *
+ * @see \IPLib\ParseStringFlag
+ * @since 1.17.0
*/
- public static function rangeFromString($range, $supportNonDecimalIPv4 = false)
+ public static function parseRangeString($range, $flags = 0)
{
$result = null;
if ($result === null) {
- $result = Range\Subnet::fromString($range, $supportNonDecimalIPv4);
+ $result = Range\Subnet::parseString($range, $flags);
}
if ($result === null) {
- $result = Range\Pattern::fromString($range, $supportNonDecimalIPv4);
+ $result = Range\Pattern::parseString($range, $flags);
}
if ($result === null) {
- $result = Range\Single::fromString($range, $supportNonDecimalIPv4);
+ $result = Range\Single::parseString($range, $flags);
}
return $result;
}
/**
- * Create the smallest address range that comprises two addresses.
+ * @deprecated since 1.17.0: use the getRangeFromBoundaries() method instead.
+ * For upgrading:
+ * - if $supportNonDecimalIPv4 is true, use the ParseStringFlag::IPV4_MAYBE_NON_DECIMAL flag
*
- * @param string|\IPLib\Address\AddressInterface $from
- * @param string|\IPLib\Address\AddressInterface $to
- * @param bool $supportNonDecimalIPv4 set to true to support parsing non decimal (that is, octal and hexadecimal) IPv4 addresses
+ * @param string|\IPLib\Address\AddressInterface|mixed $from
+ * @param string|\IPLib\Address\AddressInterface|mixed $to
+ * @param bool $supportNonDecimalIPv4
*
- * @return \IPLib\Range\RangeInterface|null return NULL if $from and/or $to are invalid addresses, or if both are NULL or empty strings, or if they are addresses of different types
+ * @return \IPLib\Address\AddressInterface|null
+ *
+ * @see \IPLib\Factory::getRangeFromBoundaries()
+ * @since 1.2.0
+ * @since 1.10.0 added the $supportNonDecimalIPv4 argument
*/
public static function rangeFromBoundaries($from, $to, $supportNonDecimalIPv4 = false)
{
- list($from, $to) = self::parseBoundaries($from, $to, $supportNonDecimalIPv4);
+ return static::getRangeFromBoundaries($from, $to, ParseStringFlag::MAY_INCLUDE_PORT | ParseStringFlag::MAY_INCLUDE_ZONEID | ($supportNonDecimalIPv4 ? ParseStringFlag::IPV4_MAYBE_NON_DECIMAL : 0));
+ }
+
+ /**
+ * Create the smallest address range that comprises two addresses.
+ *
+ * @param string|\IPLib\Address\AddressInterface|mixed $from
+ * @param string|\IPLib\Address\AddressInterface|mixed $to
+ * @param int $flags A combination or zero or more flags
+ *
+ * @return \IPLib\Range\RangeInterface|null return NULL if $from and/or $to are invalid addresses, or if both are NULL or empty strings, or if they are addresses of different types
+ *
+ * @see \IPLib\ParseStringFlag
+ * @since 1.17.0
+ */
+ public static function getRangeFromBoundaries($from, $to, $flags = 0)
+ {
+ list($from, $to) = self::parseBoundaries($from, $to, $flags);
return $from === false || $to === false ? null : static::rangeFromBoundaryAddresses($from, $to);
}
+ /**
+ * @deprecated since 1.17.0: use the getRangesFromBoundaries() method instead.
+ * For upgrading:
+ * - if $supportNonDecimalIPv4 is true, use the ParseStringFlag::IPV4_MAYBE_NON_DECIMAL flag
+ *
+ * @param string|\IPLib\Address\AddressInterface|mixed $from
+ * @param string|\IPLib\Address\AddressInterface|mixed $to
+ * @param bool $supportNonDecimalIPv4
+ *
+ * @return \IPLib\Range\Subnet[]|null
+ *
+ * @see \IPLib\Factory::getRangesFromBoundaries()
+ * @since 1.14.0
+ */
+ public static function rangesFromBoundaries($from, $to, $supportNonDecimalIPv4 = false)
+ {
+ return static::getRangesFromBoundaries($from, $to, ParseStringFlag::MAY_INCLUDE_PORT | ParseStringFlag::MAY_INCLUDE_ZONEID | ($supportNonDecimalIPv4 ? ParseStringFlag::IPV4_MAYBE_NON_DECIMAL : 0));
+ }
+
/**
* Create a list of Range instances that exactly describes all the addresses between the two provided addresses.
*
* @param string|\IPLib\Address\AddressInterface $from
* @param string|\IPLib\Address\AddressInterface $to
- * @param bool $supportNonDecimalIPv4 set to true to support parsing non decimal (that is, octal and hexadecimal) IPv4 addresses
+ * @param int $flags A combination or zero or more flags
*
* @return \IPLib\Range\Subnet[]|null return NULL if $from and/or $to are invalid addresses, or if both are NULL or empty strings, or if they are addresses of different types
+ *
+ * @see \IPLib\ParseStringFlag
+ * @since 1.17.0
*/
- public static function rangesFromBoundaries($from, $to, $supportNonDecimalIPv4 = false)
+ public static function getRangesFromBoundaries($from, $to, $flags = 0)
{
- list($from, $to) = self::parseBoundaries($from, $to, $supportNonDecimalIPv4);
- if (($from === false || $to === false) || ($from === null && $to === null)) {
+ list($from, $to) = self::parseBoundaries($from, $to, $flags);
+ if ($from === false || $to === false || ($from === null && $to === null)) {
return null;
}
if ($from === null || $to === null) {
@@ -118,7 +209,7 @@ class Factory
if ($to->getNumberOfBits() !== $numberOfBits) {
return null;
}
- $calculator = new RangesFromBounradyCalculator($numberOfBits);
+ $calculator = new RangesFromBoundaryCalculator($numberOfBits);
return $calculator->getRanges($from, $to);
}
@@ -128,6 +219,8 @@ class Factory
* @param \IPLib\Address\AddressInterface $to
*
* @return \IPLib\Range\RangeInterface|null
+ *
+ * @since 1.2.0
*/
protected static function rangeFromBoundaryAddresses(AddressInterface $from = null, AddressInterface $to = null)
{
@@ -163,7 +256,7 @@ class Factory
break;
}
}
- $result = static::rangeFromString($from->toString(true) . '/' . (string) $sameBits);
+ $result = static::parseRangeString($from->toString() . '/' . (string) $sameBits);
}
}
}
@@ -174,11 +267,11 @@ class Factory
/**
* @param string|\IPLib\Address\AddressInterface $from
* @param string|\IPLib\Address\AddressInterface $to
- * @param bool $supportNonDecimalIPv4
+ * @param int $flags
*
* @return \IPLib\Address\AddressInterface[]|null[]|false[]
*/
- private static function parseBoundaries($from, $to, $supportNonDecimalIPv4 = false)
+ private static function parseBoundaries($from, $to, $flags = 0)
{
$result = array();
foreach (array('from', 'to') as $param) {
@@ -188,7 +281,7 @@ class Factory
if ($value === '') {
$value = null;
} else {
- $value = static::addressFromString($value, true, true, $supportNonDecimalIPv4);
+ $value = static::parseAddressString($value, $flags);
if ($value === null) {
$value = false;
}
diff --git a/vendor/mlocati/ip-lib/src/ParseStringFlag.php b/vendor/mlocati/ip-lib/src/ParseStringFlag.php
new file mode 100644
index 0000000..860256e
--- /dev/null
+++ b/vendor/mlocati/ip-lib/src/ParseStringFlag.php
@@ -0,0 +1,79 @@
+ 5.0.0.1
+ * @example 5.256 => 5.0.1.0
+ * @example 5.0.256 => 5.0.1.0
+ * @example 123456789 => 7.91.205.21
+ */
+ const IPV4_MAYBE_NON_DECIMAL = 4;
+
+ /**
+ * Use this flag if IPv4 subnet ranges may be in compact form.
+ *
+ * @example 127/24 => 127.0.0.0/24
+ * @example 10/8 => 10.0.0.0/8
+ * @example 10/24 => 10.0.0.0/24
+ * @example 10.10.10/24 => 10.10.10.0/24
+ *
+ * @var int
+ */
+ const IPV4SUBNET_MAYBE_COMPACT = 8;
+
+ /**
+ * Use this flag if IPv4 addresses may be in non quad-dotted notation.
+ * This notation is accepted by the implementation of inet_aton and inet_addr of the libc implementation of GNU, Windows and Mac (but not Musl), but not by inet_pton and ip2long.
+ *
+ * @var int
+ *
+ * @example 5.1 => 5.0.0.1
+ * @example 5.256 => 5.0.1.0
+ * @example 5.0.256 => 5.0.1.0
+ * @example 123456789 => 7.91.205.21
+ *
+ * @see https://man7.org/linux/man-pages/man3/inet_addr.3.html#DESCRIPTION
+ * @see https://www.freebsd.org/cgi/man.cgi?query=inet_net&sektion=3&apropos=0&manpath=FreeBSD+12.2-RELEASE+and+Ports#end
+ * @see http://git.musl-libc.org/cgit/musl/tree/src/network/inet_aton.c?h=v1.2.2
+ */
+ const IPV4ADDRESS_MAYBE_NON_QUAD_DOTTED = 16;
+
+ /**
+ * Use this flag if you want to accept parsing IPv4/IPv6 addresses in Reverse DNS Lookup Address format.
+ *
+ * @var int
+ *
+ * @since 1.18.0
+ *
+ * @example 140.13.12.10.in-addr.arpa => 10.12.13.140
+ * @example b.a.9.8.7.6.5.0.4.0.0.0.3.0.0.0.2.0.0.0.1.0.0.0.0.0.0.0.1.2.3.4.ip6.arpa => 4321:0:1:2:3:4:567:89ab
+ */
+ const ADDRESS_MAYBE_RDNS = 32;
+}
diff --git a/vendor/mlocati/ip-lib/src/Range/AbstractRange.php b/vendor/mlocati/ip-lib/src/Range/AbstractRange.php
index 1da94d0..1220435 100644
--- a/vendor/mlocati/ip-lib/src/Range/AbstractRange.php
+++ b/vendor/mlocati/ip-lib/src/Range/AbstractRange.php
@@ -8,6 +8,9 @@ use IPLib\Address\IPv6;
use IPLib\Address\Type as AddressType;
use IPLib\Factory;
+/**
+ * Base class for range classes.
+ */
abstract class AbstractRange implements RangeInterface
{
/**
@@ -20,7 +23,7 @@ abstract class AbstractRange implements RangeInterface
if ($this->rangeType === null) {
$addressType = $this->getAddressType();
if ($addressType === AddressType::T_IPv6 && Subnet::get6to4()->containsRange($this)) {
- $this->rangeType = Factory::rangeFromBoundaries($this->fromAddress->toIPv4(), $this->toAddress->toIPv4())->getRangeType();
+ $this->rangeType = Factory::getRangeFromBoundaries($this->fromAddress->toIPv4(), $this->toAddress->toIPv4())->getRangeType();
} else {
switch ($addressType) {
case AddressType::T_IPv4:
@@ -48,6 +51,33 @@ abstract class AbstractRange implements RangeInterface
return $this->rangeType === false ? null : $this->rangeType;
}
+ /**
+ * {@inheritdoc}
+ *
+ * @see \IPLib\Range\RangeInterface::getAddressAtOffset()
+ */
+ public function getAddressAtOffset($n)
+ {
+ if (!is_int($n)) {
+ return null;
+ }
+
+ $address = null;
+ if ($n >= 0) {
+ $start = Factory::parseAddressString($this->getComparableStartString());
+ $address = $start->getAddressAtOffset($n);
+ } else {
+ $end = Factory::parseAddressString($this->getComparableEndString());
+ $address = $end->getAddressAtOffset($n + 1);
+ }
+
+ if ($address === null) {
+ return null;
+ }
+
+ return $this->contains($address) ? $address : null;
+ }
+
/**
* {@inheritdoc}
*
diff --git a/vendor/mlocati/ip-lib/src/Range/Pattern.php b/vendor/mlocati/ip-lib/src/Range/Pattern.php
index fc19d1e..1e180ea 100644
--- a/vendor/mlocati/ip-lib/src/Range/Pattern.php
+++ b/vendor/mlocati/ip-lib/src/Range/Pattern.php
@@ -6,6 +6,7 @@ use IPLib\Address\AddressInterface;
use IPLib\Address\IPv4;
use IPLib\Address\IPv6;
use IPLib\Address\Type as AddressType;
+use IPLib\ParseStringFlag;
/**
* Represents an address range in pattern format (only ending asterisks are supported).
@@ -40,6 +41,8 @@ class Pattern extends AbstractRange
* The type of the range of this IP range.
*
* @var int|false|null false if this range crosses multiple range types, null if yet to be determined
+ *
+ * @since 1.5.0
*/
protected $rangeType;
@@ -67,24 +70,45 @@ class Pattern extends AbstractRange
return $this->toString();
}
+ /**
+ * @deprecated since 1.17.0: use the parseString() method instead.
+ * For upgrading:
+ * - if $supportNonDecimalIPv4 is true, use the ParseStringFlag::IPV4_MAYBE_NON_DECIMAL flag
+ *
+ * @param string|mixed $range
+ * @param bool $supportNonDecimalIPv4
+ *
+ * @return static|null
+ *
+ * @see \IPLib\Range\Pattern::parseString()
+ * @since 1.10.0 added the $supportNonDecimalIPv4 argument
+ */
+ public static function fromString($range, $supportNonDecimalIPv4 = false)
+ {
+ return static::parseString($range, ParseStringFlag::MAY_INCLUDE_PORT | ParseStringFlag::MAY_INCLUDE_ZONEID | ($supportNonDecimalIPv4 ? ParseStringFlag::IPV4_MAYBE_NON_DECIMAL : 0));
+ }
+
/**
* Try get the range instance starting from its string representation.
*
* @param string|mixed $range
- * @param bool $supportNonDecimalIPv4 set to true to support parsing non decimal (that is, octal and hexadecimal) IPv4 addresses
+ * @param int $flags A combination or zero or more flags
*
* @return static|null
+ *
+ * @since 1.17.0
+ * @see \IPLib\ParseStringFlag
*/
- public static function fromString($range, $supportNonDecimalIPv4 = false)
+ public static function parseString($range, $flags = 0)
{
if (!is_string($range) || strpos($range, '*') === false) {
return null;
}
if ($range === '*.*.*.*') {
- return new static(IPv4::fromString('0.0.0.0'), IPv4::fromString('255.255.255.255'), 4);
+ return new static(IPv4::parseString('0.0.0.0'), IPv4::parseString('255.255.255.255'), 4);
}
if ($range === '*:*:*:*:*:*:*:*') {
- return new static(IPv6::fromString('::'), IPv6::fromString('ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff'), 8);
+ return new static(IPv6::parseString('::'), IPv6::parseString('ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff'), 8);
}
$matches = null;
if (strpos($range, '.') !== false && preg_match('/^[^*]+((?:\.\*)+)$/', $range, $matches)) {
@@ -96,7 +120,7 @@ class Pattern extends AbstractRange
$asterisksCount += $missingDots;
}
}
- $fromAddress = IPv4::fromString(str_replace('*', '0', $range), true, $supportNonDecimalIPv4);
+ $fromAddress = IPv4::parseString(str_replace('*', '0', $range), $flags);
if ($fromAddress === null) {
return null;
}
@@ -108,7 +132,7 @@ class Pattern extends AbstractRange
}
if (strpos($range, ':') !== false && preg_match('/^[^*]+((?::\*)+)$/', $range, $matches)) {
$asterisksCount = strlen($matches[1]) >> 1;
- $fromAddress = IPv6::fromString(str_replace('*', '0', $range));
+ $fromAddress = IPv6::parseString(str_replace('*', '0', $range));
if ($fromAddress === null) {
return null;
}
@@ -217,15 +241,11 @@ class Pattern extends AbstractRange
* {@inheritdoc}
*
* @see \IPLib\Range\RangeInterface::asSubnet()
+ * @since 1.8.0
*/
public function asSubnet()
{
- switch ($this->getAddressType()) {
- case AddressType::T_IPv4:
- return new Subnet($this->getStartAddress(), $this->getEndAddress(), 8 * (4 - $this->asterisksCount));
- case AddressType::T_IPv6:
- return new Subnet($this->getStartAddress(), $this->getEndAddress(), 16 * (8 - $this->asterisksCount));
- }
+ return new Subnet($this->getStartAddress(), $this->getEndAddress(), $this->getNetworkPrefix());
}
/**
@@ -272,4 +292,31 @@ class Pattern extends AbstractRange
{
return $this->asterisksCount === 0 ? array($this->getStartAddress()->getReverseDNSLookupName()) : $this->asSubnet()->getReverseDNSLookupName();
}
+
+ /**
+ * {@inheritdoc}
+ *
+ * @see \IPLib\Range\RangeInterface::getSize()
+ */
+ public function getSize()
+ {
+ $fromAddress = $this->fromAddress;
+ $maxPrefix = $fromAddress::getNumberOfBits();
+ $prefix = $this->getNetworkPrefix();
+
+ return pow(2, ($maxPrefix - $prefix));
+ }
+
+ /**
+ * @return float|int
+ */
+ private function getNetworkPrefix()
+ {
+ switch ($this->getAddressType()) {
+ case AddressType::T_IPv4:
+ return 8 * (4 - $this->asterisksCount);
+ case AddressType::T_IPv6:
+ return 16 * (8 - $this->asterisksCount);
+ }
+ }
}
diff --git a/vendor/mlocati/ip-lib/src/Range/RangeInterface.php b/vendor/mlocati/ip-lib/src/Range/RangeInterface.php
index 468df3e..4d4d834 100644
--- a/vendor/mlocati/ip-lib/src/Range/RangeInterface.php
+++ b/vendor/mlocati/ip-lib/src/Range/RangeInterface.php
@@ -38,9 +38,26 @@ interface RangeInterface
* Get the type of range of the IP address.
*
* @return int One of the \IPLib\Range\Type::T_... constants
+ *
+ * @since 1.5.0
*/
public function getRangeType();
+ /**
+ * Get the address at a certain offset of this range.
+ *
+ * @param int $n the offset of the address (support negative offset)
+ *
+ * @return \IPLib\Address\AddressInterface|null return NULL if $n is not an integer or if the offset out of range
+ *
+ * @since 1.15.0
+ *
+ * @example passing 256 to the range 127.0.0.0/16 will result in 127.0.1.0
+ * @example passing -1 to the range 127.0.1.0/16 will result in 127.0.255.255
+ * @example passing 256 to the range 127.0.0.0/24 will result in NULL
+ */
+ public function getAddressAtOffset($n);
+
/**
* Check if this range contains an IP address.
*
@@ -56,6 +73,8 @@ interface RangeInterface
* @param \IPLib\Range\RangeInterface $range
*
* @return bool
+ *
+ * @since 1.5.0
*/
public function containsRange(RangeInterface $range);
@@ -63,6 +82,8 @@ interface RangeInterface
* Get the initial address contained in this range.
*
* @return \IPLib\Address\AddressInterface
+ *
+ * @since 1.4.0
*/
public function getStartAddress();
@@ -70,6 +91,8 @@ interface RangeInterface
* Get the final address contained in this range.
*
* @return \IPLib\Address\AddressInterface
+ *
+ * @since 1.4.0
*/
public function getEndAddress();
@@ -91,6 +114,8 @@ interface RangeInterface
* Get the subnet mask representing this range (only for IPv4 ranges).
*
* @return \IPLib\Address\IPv4|null return NULL if the range is an IPv6 range, the subnet mask otherwise
+ *
+ * @since 1.8.0
*/
public function getSubnetMask();
@@ -98,6 +123,8 @@ interface RangeInterface
* Get the subnet/CIDR representation of this range.
*
* @return \IPLib\Range\Subnet
+ *
+ * @since 1.13.0
*/
public function asSubnet();
@@ -105,6 +132,8 @@ interface RangeInterface
* Get the pattern/asterisk representation (if applicable) of this range.
*
* @return \IPLib\Range\Pattern|null return NULL if this range can't be represented by a pattern notation
+ *
+ * @since 1.13.0
*/
public function asPattern();
@@ -113,8 +142,19 @@ interface RangeInterface
*
* @return string[]
*
+ * @since 1.13.0
+ *
* @example for IPv4 it returns something like array('x.x.x.x.in-addr.arpa', 'x.x.x.x.in-addr.arpa') (where the number of 'x.' ranges from 1 to 4)
* @example for IPv6 it returns something like array('x.x.x.x..x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.ip6.arpa', 'x.x.x.x..x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.ip6.arpa') (where the number of 'x.' ranges from 1 to 32)
*/
public function getReverseDNSLookupName();
+
+ /**
+ * Get the count of addresses this IP range contains.
+ *
+ * @return int|float Return float as for huge IPv6 networks, int is not enough
+ *
+ * @since 1.16.0
+ */
+ public function getSize();
}
diff --git a/vendor/mlocati/ip-lib/src/Range/Single.php b/vendor/mlocati/ip-lib/src/Range/Single.php
index 6f08850..ec3531f 100644
--- a/vendor/mlocati/ip-lib/src/Range/Single.php
+++ b/vendor/mlocati/ip-lib/src/Range/Single.php
@@ -6,6 +6,7 @@ use IPLib\Address\AddressInterface;
use IPLib\Address\IPv4;
use IPLib\Address\Type as AddressType;
use IPLib\Factory;
+use IPLib\ParseStringFlag;
/**
* Represents a single address (eg a range that contains just one address).
@@ -41,17 +42,39 @@ class Single extends AbstractRange
}
/**
- * Try get the range instance starting from its string representation.
+ * @deprecated since 1.17.0: use the parseString() method instead.
+ * For upgrading:
+ * - if $supportNonDecimalIPv4 is true, use the ParseStringFlag::IPV4_MAYBE_NON_DECIMAL flag
*
* @param string|mixed $range
- * @param bool $supportNonDecimalIPv4 set to true to support parsing non decimal (that is, octal and hexadecimal) IPv4 addresses
+ * @param bool $supportNonDecimalIPv4
*
* @return static|null
+ *
+ * @see \IPLib\Range\Single::parseString()
+ * @since 1.10.0 added the $supportNonDecimalIPv4 argument
*/
public static function fromString($range, $supportNonDecimalIPv4 = false)
+ {
+ return static::parseString($range, ParseStringFlag::MAY_INCLUDE_PORT | ParseStringFlag::MAY_INCLUDE_ZONEID | ($supportNonDecimalIPv4 ? ParseStringFlag::IPV4_MAYBE_NON_DECIMAL : 0));
+ }
+
+ /**
+ * Try get the range instance starting from its string representation.
+ *
+ * @param string|mixed $range
+ * @param int $flags A combination or zero or more flags
+ *
+ * @return static|null
+ *
+ * @see \IPLib\ParseStringFlag
+ * @since 1.17.0
+ */
+ public static function parseString($range, $flags = 0)
{
$result = null;
- $address = Factory::addressFromString($range, true, true, $supportNonDecimalIPv4);
+ $flags = (int) $flags;
+ $address = Factory::parseAddressString($range, $flags);
if ($address !== null) {
$result = new static($address);
}
@@ -65,6 +88,8 @@ class Single extends AbstractRange
* @param \IPLib\Address\AddressInterface $address
*
* @return static
+ *
+ * @since 1.2.0
*/
public static function fromAddress(AddressInterface $address)
{
@@ -118,23 +143,6 @@ class Single extends AbstractRange
return $result;
}
- /**
- * {@inheritdoc}
- *
- * @see \IPLib\Range\RangeInterface::containsRange()
- */
- public function containsRange(RangeInterface $range)
- {
- $result = false;
- if ($range->getAddressType() === $this->getAddressType()) {
- if ($range->toString(false) === $this->toString(false)) {
- $result = true;
- }
- }
-
- return $result;
- }
-
/**
* {@inheritdoc}
*
@@ -223,4 +231,14 @@ class Single extends AbstractRange
{
return array($this->getStartAddress()->getReverseDNSLookupName());
}
+
+ /**
+ * {@inheritdoc}
+ *
+ * @see \IPLib\Range\RangeInterface::getSize()
+ */
+ public function getSize()
+ {
+ return 1;
+ }
}
diff --git a/vendor/mlocati/ip-lib/src/Range/Subnet.php b/vendor/mlocati/ip-lib/src/Range/Subnet.php
index 2ce166b..ea39762 100644
--- a/vendor/mlocati/ip-lib/src/Range/Subnet.php
+++ b/vendor/mlocati/ip-lib/src/Range/Subnet.php
@@ -4,9 +4,9 @@ namespace IPLib\Range;
use IPLib\Address\AddressInterface;
use IPLib\Address\IPv4;
-use IPLib\Address\IPv6;
use IPLib\Address\Type as AddressType;
use IPLib\Factory;
+use IPLib\ParseStringFlag;
/**
* Represents an address range in subnet format (eg CIDR).
@@ -41,6 +41,8 @@ class Subnet extends AbstractRange
* The type of the range of this IP range.
*
* @var int|null
+ *
+ * @since 1.5.0
*/
protected $rangeType;
@@ -77,15 +79,36 @@ class Subnet extends AbstractRange
return $this->toString();
}
+ /**
+ * @deprecated since 1.17.0: use the parseString() method instead.
+ * For upgrading:
+ * - if $supportNonDecimalIPv4 is true, use the ParseStringFlag::IPV4_MAYBE_NON_DECIMAL flag
+ *
+ * @param string|mixed $range
+ * @param bool $supportNonDecimalIPv4
+ *
+ * @return static|null
+ *
+ * @see \IPLib\Range\Subnet::parseString()
+ * @since 1.10.0 added the $supportNonDecimalIPv4 argument
+ */
+ public static function fromString($range, $supportNonDecimalIPv4 = false)
+ {
+ return static::parseString($range, ParseStringFlag::MAY_INCLUDE_PORT | ParseStringFlag::MAY_INCLUDE_ZONEID | ($supportNonDecimalIPv4 ? ParseStringFlag::IPV4_MAYBE_NON_DECIMAL : 0));
+ }
+
/**
* Try get the range instance starting from its string representation.
*
* @param string|mixed $range
- * @param bool $supportNonDecimalIPv4 set to true to support parsing non decimal (that is, octal and hexadecimal) IPv4 addresses
+ * @param int $flags A combination or zero or more flags
*
* @return static|null
+ *
+ * @see \IPLib\ParseStringFlag
+ * @since 1.17.0
*/
- public static function fromString($range, $supportNonDecimalIPv4 = false)
+ public static function parseString($range, $flags = 0)
{
if (!is_string($range)) {
return null;
@@ -94,7 +117,14 @@ class Subnet extends AbstractRange
if (count($parts) !== 2) {
return null;
}
- $address = Factory::addressFromString($parts[0], true, true, $supportNonDecimalIPv4);
+ $flags = (int) $flags;
+ if (strpos($parts[0], ':') === false && $flags & ParseStringFlag::IPV4SUBNET_MAYBE_COMPACT) {
+ $missingDots = 3 - substr_count($parts[0], '.');
+ if ($missingDots > 0) {
+ $parts[0] .= str_repeat('.0', $missingDots);
+ }
+ }
+ $address = Factory::parseAddressString($parts[0], $flags);
if ($address === null) {
return null;
}
@@ -197,9 +227,10 @@ class Subnet extends AbstractRange
}
/**
- * Get the pattern (asterisk) representation (if applicable) of this range.
+ * {@inheritdoc}
*
- * @return \IPLib\Range\Pattern|null return NULL if this range can't be represented by a pattern notation
+ * @see \IPLib\Range\RangeInterface::asPattern()
+ * @since 1.8.0
*/
public function asPattern()
{
@@ -217,11 +248,13 @@ class Subnet extends AbstractRange
* Get the 6to4 address IPv6 address range.
*
* @return self
+ *
+ * @since 1.5.0
*/
public static function get6to4()
{
if (self::$sixToFour === null) {
- self::$sixToFour = self::fromString('2002::/16');
+ self::$sixToFour = self::parseString('2002::/16');
}
return self::$sixToFour;
@@ -231,6 +264,8 @@ class Subnet extends AbstractRange
* Get subnet prefix.
*
* @return int
+ *
+ * @since 1.7.0
*/
public function getNetworkPrefix()
{
@@ -302,4 +337,18 @@ class Subnet extends AbstractRange
return $result;
}
+
+ /**
+ * {@inheritdoc}
+ *
+ * @see \IPLib\Range\RangeInterface::getSize()
+ */
+ public function getSize()
+ {
+ $fromAddress = $this->fromAddress;
+ $maxPrefix = $fromAddress::getNumberOfBits();
+ $prefix = $this->getNetworkPrefix();
+
+ return pow(2, ($maxPrefix - $prefix));
+ }
}
diff --git a/vendor/mlocati/ip-lib/src/Range/Type.php b/vendor/mlocati/ip-lib/src/Range/Type.php
index cfc268b..b2ba8eb 100644
--- a/vendor/mlocati/ip-lib/src/Range/Type.php
+++ b/vendor/mlocati/ip-lib/src/Range/Type.php
@@ -102,6 +102,8 @@ class Type
* Carrier-grade NAT address.
*
* @var int
+ *
+ * @since 1.10.0
*/
const T_CGNAT = 14;
diff --git a/vendor/mlocati/ip-lib/src/Service/BinaryMath.php b/vendor/mlocati/ip-lib/src/Service/BinaryMath.php
index ec73c8a..727fecf 100644
--- a/vendor/mlocati/ip-lib/src/Service/BinaryMath.php
+++ b/vendor/mlocati/ip-lib/src/Service/BinaryMath.php
@@ -4,6 +4,8 @@ namespace IPLib\Service;
/**
* Helper class to work with unsigned binary integers.
+ *
+ * @internal
*/
class BinaryMath
{
diff --git a/vendor/mlocati/ip-lib/src/Service/RangesFromBounradyCalculator.php b/vendor/mlocati/ip-lib/src/Service/RangesFromBoundaryCalculator.php
similarity index 89%
rename from vendor/mlocati/ip-lib/src/Service/RangesFromBounradyCalculator.php
rename to vendor/mlocati/ip-lib/src/Service/RangesFromBoundaryCalculator.php
index c1d29d6..7a127e7 100644
--- a/vendor/mlocati/ip-lib/src/Service/RangesFromBounradyCalculator.php
+++ b/vendor/mlocati/ip-lib/src/Service/RangesFromBoundaryCalculator.php
@@ -7,12 +7,14 @@ use IPLib\Factory;
use IPLib\Range\Subnet;
/**
- * Helper class to calculate the subnets describing all (and only all) the addresses between two bouundaries.
+ * Helper class to calculate the subnets describing all (and only all) the addresses between two boundaries.
+ *
+ * @internal
*/
-class RangesFromBounradyCalculator
+class RangesFromBoundaryCalculator
{
/**
- * The BinaryMath instance to be used to perform bitwise poerations.
+ * The BinaryMath instance to be used to perform bitwise operations.
*
* @var \IPLib\Service\BinaryMath
*/
@@ -53,7 +55,7 @@ class RangesFromBounradyCalculator
}
/**
- * Calculate the subnets describing all (and only all) the addresses between two bouundaries.
+ * Calculate the subnets describing all (and only all) the addresses between two boundaries.
*
* @param \IPLib\Address\AddressInterface $from
* @param \IPLib\Address\AddressInterface $to
@@ -154,8 +156,13 @@ class RangesFromBounradyCalculator
*/
private function subnetFromBits($bits, $networkPrefix)
{
- $address = $this->addressFromBits($bits);
+ $startAddress = $this->addressFromBits($bits);
+ $numOnes = $this->numBits - $networkPrefix;
+ if ($numOnes === 0) {
+ return new Subnet($startAddress, $startAddress, $networkPrefix);
+ }
+ $endAddress = $this->addressFromBits(substr($bits, 0, -$numOnes) . str_repeat('1', $numOnes));
- return new Subnet($address, $address, $networkPrefix);
+ return new Subnet($startAddress, $endAddress, $networkPrefix);
}
}
diff --git a/vendor/mlocati/ip-lib/src/Service/UnsignedIntegerMath.php b/vendor/mlocati/ip-lib/src/Service/UnsignedIntegerMath.php
new file mode 100644
index 0000000..3179f74
--- /dev/null
+++ b/vendor/mlocati/ip-lib/src/Service/UnsignedIntegerMath.php
@@ -0,0 +1,171 @@
+getBytesFromDecimal($m[1], $numBytes);
+ }
+ } else {
+ if (preg_match('/^0[Xx]0*([0-9A-Fa-f]+)$/', $value, $m)) {
+ return $this->getBytesFromHexadecimal($m[1], $numBytes);
+ }
+ if (preg_match('/^0+([0-7]*)$/', $value, $m)) {
+ return $this->getBytesFromOctal($m[1], $numBytes);
+ }
+ if (preg_match('/^[1-9][0-9]*$/', $value)) {
+ return $this->getBytesFromDecimal($value, $numBytes);
+ }
+ }
+
+ // Not a valid number
+ return null;
+ }
+
+ /**
+ * @return int
+ */
+ protected function getMaxSignedInt()
+ {
+ return PHP_INT_MAX;
+ }
+
+ /**
+ * @param string $value never zero-length, never extra leading zeroes
+ * @param int $numBytes
+ *
+ * @return int[]|null
+ */
+ private function getBytesFromBits($value, $numBytes)
+ {
+ $valueLength = strlen($value);
+ if ($valueLength > $numBytes << 3) {
+ // overflow
+ return null;
+ }
+ $remainderBits = $valueLength % 8;
+ if ($remainderBits !== 0) {
+ $value = str_pad($value, $valueLength + 8 - $remainderBits, '0', STR_PAD_LEFT);
+ }
+ $bytes = array_map('bindec', str_split($value, 8));
+
+ return array_pad($bytes, -$numBytes, 0);
+ }
+
+ /**
+ * @param string $value may be zero-length, never extra leading zeroes
+ * @param int $numBytes
+ *
+ * @return int[]|null
+ */
+ private function getBytesFromOctal($value, $numBytes)
+ {
+ if ($value === '') {
+ return array_fill(0, $numBytes, 0);
+ }
+ $bits = implode(
+ '',
+ array_map(
+ function ($octalDigit) {
+ return str_pad(decbin(octdec($octalDigit)), 3, '0', STR_PAD_LEFT);
+ },
+ str_split($value, 1)
+ )
+ );
+ $bits = ltrim($bits, '0');
+
+ return $bits === '' ? array_fill(0, $numBytes, 0) : static::getBytesFromBits($bits, $numBytes);
+ }
+
+ /**
+ * @param string $value never zero-length, never extra leading zeroes
+ * @param int $numBytes
+ *
+ * @return int[]|null
+ */
+ private function getBytesFromDecimal($value, $numBytes)
+ {
+ $valueLength = strlen($value);
+ $maxSignedIntLength = strlen((string) $this->getMaxSignedInt());
+ if ($valueLength < $maxSignedIntLength) {
+ return $this->getBytesFromBits(decbin((int) $value), $numBytes);
+ }
+ // Divide by two, so that we have 1 less bit
+ $carry = 0;
+ $halfValue = ltrim(
+ implode(
+ '',
+ array_map(
+ function ($digit) use (&$carry) {
+ $number = $carry + (int) $digit;
+ $carry = ($number % 2) * 10;
+
+ return (string) $number >> 1;
+ },
+ str_split($value, 1)
+ )
+ ),
+ '0'
+ );
+ $halfValueBytes = $this->getBytesFromDecimal($halfValue, $numBytes);
+ if ($halfValueBytes === null) {
+ return null;
+ }
+ $carry = $carry === 0 ? 0 : 1;
+ $result = array_fill(0, $numBytes, 0);
+ for ($index = $numBytes - 1; $index >= 0; $index--) {
+ $byte = $carry + ($halfValueBytes[$index] << 1);
+ if ($byte <= 0xFF) {
+ $carry = 0;
+ } else {
+ $carry = ($byte & ~0xFF) >> 8;
+ $byte -= 0x100;
+ }
+ $result[$index] = $byte;
+ }
+ if ($carry !== 0) {
+ // Overflow
+ return null;
+ }
+
+ return $result;
+ }
+
+ /**
+ * @param string $value never zero-length, never extra leading zeroes
+ * @param int $numBytes
+ *
+ * @return int[]|null
+ */
+ private function getBytesFromHexadecimal($value, $numBytes)
+ {
+ $valueLength = strlen($value);
+ if ($valueLength > $numBytes << 1) {
+ // overflow
+ return null;
+ }
+ $value = str_pad($value, $valueLength + $valueLength % 2, '0', STR_PAD_LEFT);
+ $bytes = array_map('hexdec', str_split($value, 2));
+
+ return array_pad($bytes, -$numBytes, 0);
+ }
+}
diff --git a/vendor/paragonie/random_compat/lib/random.php b/vendor/paragonie/random_compat/lib/random.php
index 6df1cb0..9429ec3 100644
--- a/vendor/paragonie/random_compat/lib/random.php
+++ b/vendor/paragonie/random_compat/lib/random.php
@@ -183,7 +183,7 @@ if (!is_callable('random_bytes')) {
try {
$RandomCompatCOMtest = new COM('CAPICOM.Utilities.1');
/** @psalm-suppress TypeDoesNotContainType */
- if (method_exists($RandomCompatCOMtest, 'GetRandom')) {
+ if (is_callable(array($RandomCompatCOMtest, 'GetRandom'))) {
// See random_bytes_com_dotnet.php
require_once $RandomCompatDIR.DIRECTORY_SEPARATOR.'random_bytes_com_dotnet.php';
}
From 91041d8c59d81e5c2675d8c3c5021e471da0cfcb Mon Sep 17 00:00:00 2001
From: El RIDO
Date: Sun, 20 Feb 2022 09:09:20 +0100
Subject: [PATCH 247/478] simplify/unify naming & wording of the two types of
IP lists for the traffic limiter
---
cfg/conf.sample.php | 17 ++++++++++-------
lib/Configuration.php | 8 ++++----
lib/Controller.php | 2 +-
lib/Persistence/TrafficLimiter.php | 20 ++++++++++----------
tst/Persistence/TrafficLimiterTest.php | 4 ++--
5 files changed, 27 insertions(+), 24 deletions(-)
diff --git a/cfg/conf.sample.php b/cfg/conf.sample.php
index d7e21f8..3bc4ec6 100644
--- a/cfg/conf.sample.php
+++ b/cfg/conf.sample.php
@@ -135,14 +135,17 @@ markdown = "Markdown"
; Set this to 0 to disable rate limiting.
limit = 10
-; Set ips (v4|v6) which should be exempted for the rate-limit. CIDR also supported. Needed to be comma separated.
-; Unset for enabling and invalid values will be ignored
-; eg: exemptedIp = '1.2.3.4,10.10.10/24'
+; (optional) Set IPs adresses (v4 or v6) or subnets (CIDR) which are exempted
+; from the rate-limit. Invalid IPs will be ignored. If multiple values are to
+; be exempted, the list needs to be comma separated. Leave unset to disable
+; exemptions.
+; exempted = "1.2.3.4,10.10.10/24"
-; (optional) if you only want some source IP addresses to create pastes
-; enter their IPv4 address(es) here, separated by commas. This does not
-; currently support CIDR notation, only individual IPv4 addresses.
-; whitelist_paste_creation = "12.34.56.78,99.88.77.66"
+; (optional) If you want only some source IP addresses (v4 or v6) or subnets
+; (CIDR) to be allowed to create pastes, set these here. Invalid IPs will be
+; ignored. If multiple values are to be exempted, the list needs to be comma
+; separated. Leave unset to allow anyone to create pastes.
+; creators = "1.2.3.4,10.10.10/24"
; (optional) if your website runs behind a reverse proxy or load balancer,
; set the HTTP header containing the visitors IP address, i.e. X_FORWARDED_FOR
diff --git a/lib/Configuration.php b/lib/Configuration.php
index c3a6fa2..130eecf 100644
--- a/lib/Configuration.php
+++ b/lib/Configuration.php
@@ -78,10 +78,10 @@ class Configuration
'markdown' => 'Markdown',
),
'traffic' => array(
- 'limit' => 10,
- 'header' => null,
- 'exemptedIp' => null,
- 'whitelist' => null,
+ 'limit' => 10,
+ 'header' => '',
+ 'exempted' => '',
+ 'creators' => '',
),
'purge' => array(
'limit' => 300,
diff --git a/lib/Controller.php b/lib/Controller.php
index b150691..8ead884 100644
--- a/lib/Controller.php
+++ b/lib/Controller.php
@@ -196,7 +196,7 @@ class Controller
private function _create()
{
// Check if whitelist feature is enabled
- if (($option = $this->_conf->getKey('whitelist_paste_creation', 'traffic')) !== null) {
+ if (($option = $this->_conf->getKey('creators', 'traffic')) !== '') {
// Parse whitelist into array
$whitelist = explode(',', $option);
// Check for source IP in HTTP header
diff --git a/lib/Persistence/TrafficLimiter.php b/lib/Persistence/TrafficLimiter.php
index 9e896c1..168b46c 100644
--- a/lib/Persistence/TrafficLimiter.php
+++ b/lib/Persistence/TrafficLimiter.php
@@ -33,13 +33,13 @@ class TrafficLimiter extends AbstractPersistence
private static $_limit = 10;
/**
- * listed ips are exempted from limits, defaults to null
+ * listed IPs are exempted from limits, defaults to null
*
* @access private
* @static
* @var string|null
*/
- private static $_exemptedIp = null;
+ private static $_exempted = null;
/**
* key to fetch IP address
@@ -63,15 +63,15 @@ class TrafficLimiter extends AbstractPersistence
}
/**
- * set a list of ip(ranges) as string
+ * set a list of IP(-ranges) as string
*
* @access public
* @static
- * @param string $exemptedIps
+ * @param string $exempted
*/
- public static function setExemptedIp($exemptedIp)
+ public static function setExempted($exempted)
{
- self::$_exemptedIp = $exemptedIp;
+ self::$_exempted = $exempted;
}
/**
@@ -84,9 +84,9 @@ class TrafficLimiter extends AbstractPersistence
public static function setConfiguration(Configuration $conf)
{
self::setLimit($conf->getKey('limit', 'traffic'));
- self::setExemptedIp($conf->getKey('exemptedIp', 'traffic'));
+ self::setExempted($conf->getKey('exempted', 'traffic'));
- if (($option = $conf->getKey('header', 'traffic')) !== null) {
+ if (($option = $conf->getKey('header', 'traffic')) !== '') {
$httpHeader = 'HTTP_' . $option;
if (array_key_exists($httpHeader, $_SERVER) && !empty($_SERVER[$httpHeader])) {
self::$_ipKey = $httpHeader;
@@ -152,8 +152,8 @@ class TrafficLimiter extends AbstractPersistence
}
// Check if $_ipKey is exempted from ratelimiting
- if (!is_null(self::$_exemptedIp)) {
- $exIp_array = explode(',', self::$_exemptedIp);
+ if (!empty(self::$_exempted)) {
+ $exIp_array = explode(',', self::$_exempted);
foreach ($exIp_array as $ipRange) {
if (self::matchIp($ipRange) === true) {
return true;
diff --git a/tst/Persistence/TrafficLimiterTest.php b/tst/Persistence/TrafficLimiterTest.php
index aedbf88..8c83f0b 100644
--- a/tst/Persistence/TrafficLimiterTest.php
+++ b/tst/Persistence/TrafficLimiterTest.php
@@ -47,7 +47,7 @@ class TrafficLimiterTest extends PHPUnit_Framework_TestCase
$this->assertFalse(TrafficLimiter::canPass(), 'fifth request is to fast, may not pass');
// exempted IPs configuration
- TrafficLimiter::setExemptedIp('1.2.3.4,10.10.10.0/24,2001:1620:2057::/48');
+ TrafficLimiter::setExempted('1.2.3.4,10.10.10.0/24,2001:1620:2057::/48');
$this->assertFalse(TrafficLimiter::canPass(), 'still too fast and not exempted');
$_SERVER['REMOTE_ADDR'] = '10.10.10.10';
$this->assertTrue(TrafficLimiter::canPass(), 'IPv4 in exempted range');
@@ -55,7 +55,7 @@ class TrafficLimiterTest extends PHPUnit_Framework_TestCase
$_SERVER['REMOTE_ADDR'] = '2001:1620:2057:dead:beef::cafe:babe';
$this->assertTrue(TrafficLimiter::canPass(), 'IPv6 in exempted range');
$this->assertTrue(TrafficLimiter::canPass(), 'request is to fast, but IPv6 in exempted range');
- TrafficLimiter::setExemptedIp('127.*,foobar');
+ TrafficLimiter::setExempted('127.*,foobar');
$this->assertFalse(TrafficLimiter::canPass(), 'request is to fast, invalid range');
$_SERVER['REMOTE_ADDR'] = 'foobar';
$this->assertTrue(TrafficLimiter::canPass(), 'non-IP address');
From 190a35a53bdd29aedbf0331fc300fe89819a8465 Mon Sep 17 00:00:00 2001
From: El RIDO
Date: Sun, 20 Feb 2022 09:30:41 +0100
Subject: [PATCH 248/478] small unit test refactoring, comment wording
---
lib/Persistence/TrafficLimiter.php | 2 +-
tst/Persistence/TrafficLimiterTest.php | 9 +++++++--
2 files changed, 8 insertions(+), 3 deletions(-)
diff --git a/lib/Persistence/TrafficLimiter.php b/lib/Persistence/TrafficLimiter.php
index 168b46c..f5f8e7a 100644
--- a/lib/Persistence/TrafficLimiter.php
+++ b/lib/Persistence/TrafficLimiter.php
@@ -161,7 +161,7 @@ class TrafficLimiter extends AbstractPersistence
}
}
- // this hash is used as an array key, hence a shorter algo is used
+ // used as array key, which are limited in length, hence using algo with shorter range
$hash = self::getHash('sha256');
$now = time();
$tl = (int) self::$_store->getValue('traffic_limiter', $hash);
diff --git a/tst/Persistence/TrafficLimiterTest.php b/tst/Persistence/TrafficLimiterTest.php
index 8c83f0b..289598e 100644
--- a/tst/Persistence/TrafficLimiterTest.php
+++ b/tst/Persistence/TrafficLimiterTest.php
@@ -45,10 +45,14 @@ class TrafficLimiterTest extends PHPUnit_Framework_TestCase
$this->assertTrue(TrafficLimiter::canPass(), 'fourth request has different ip and may pass');
$_SERVER['REMOTE_ADDR'] = '127.0.0.1';
$this->assertFalse(TrafficLimiter::canPass(), 'fifth request is to fast, may not pass');
+ }
- // exempted IPs configuration
+ public function testTrafficLimitExempted()
+ {
TrafficLimiter::setExempted('1.2.3.4,10.10.10.0/24,2001:1620:2057::/48');
- $this->assertFalse(TrafficLimiter::canPass(), 'still too fast and not exempted');
+ $_SERVER['REMOTE_ADDR'] = '127.0.0.1';
+ $this->assertTrue(TrafficLimiter::canPass(), 'first request may pass');
+ $this->assertFalse(TrafficLimiter::canPass(), 'not exempted');
$_SERVER['REMOTE_ADDR'] = '10.10.10.10';
$this->assertTrue(TrafficLimiter::canPass(), 'IPv4 in exempted range');
$this->assertTrue(TrafficLimiter::canPass(), 'request is to fast, but IPv4 in exempted range');
@@ -56,6 +60,7 @@ class TrafficLimiterTest extends PHPUnit_Framework_TestCase
$this->assertTrue(TrafficLimiter::canPass(), 'IPv6 in exempted range');
$this->assertTrue(TrafficLimiter::canPass(), 'request is to fast, but IPv6 in exempted range');
TrafficLimiter::setExempted('127.*,foobar');
+ $this->assertTrue(TrafficLimiter::canPass(), 'first cached request may pass');
$this->assertFalse(TrafficLimiter::canPass(), 'request is to fast, invalid range');
$_SERVER['REMOTE_ADDR'] = 'foobar';
$this->assertTrue(TrafficLimiter::canPass(), 'non-IP address');
From dbe8debe306c2453ed00ea381269ce044c889e44 Mon Sep 17 00:00:00 2001
From: El RIDO
Date: Sun, 20 Feb 2022 09:35:05 +0100
Subject: [PATCH 249/478] add creator unit tests for refactoring target,
currently failing
---
tst/Persistence/TrafficLimiterTest.php | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/tst/Persistence/TrafficLimiterTest.php b/tst/Persistence/TrafficLimiterTest.php
index 289598e..4e56b8a 100644
--- a/tst/Persistence/TrafficLimiterTest.php
+++ b/tst/Persistence/TrafficLimiterTest.php
@@ -66,4 +66,23 @@ class TrafficLimiterTest extends PHPUnit_Framework_TestCase
$this->assertTrue(TrafficLimiter::canPass(), 'non-IP address');
$this->assertTrue(TrafficLimiter::canPass(), 'request is to fast, but non-IP address matches exempted range');
}
+
+ public function testTrafficLimitCreators()
+ {
+ TrafficLimiter::setCreators('1.2.3.4,10.10.10.0/24,2001:1620:2057::/48');
+ $_SERVER['REMOTE_ADDR'] = '127.0.0.1';
+ $this->assertFalse(TrafficLimiter::canPass(), 'not a creator');
+ $_SERVER['REMOTE_ADDR'] = '10.10.10.10';
+ $this->assertTrue(TrafficLimiter::canPass(), 'IPv4 in creator range');
+ $this->assertTrue(TrafficLimiter::canPass(), 'request is to fast, but IPv4 in creator range');
+ $_SERVER['REMOTE_ADDR'] = '2001:1620:2057:dead:beef::cafe:babe';
+ $this->assertTrue(TrafficLimiter::canPass(), 'IPv6 in creator range');
+ $this->assertTrue(TrafficLimiter::canPass(), 'request is to fast, but IPv6 in creator range');
+ TrafficLimiter::setExempted('127.*,foobar');
+ $this->assertTrue(TrafficLimiter::canPass(), 'first cached request may pass');
+ $this->assertFalse(TrafficLimiter::canPass(), 'request is to fast, not a creator');
+ $_SERVER['REMOTE_ADDR'] = 'foobar';
+ $this->assertTrue(TrafficLimiter::canPass(), 'non-IP address');
+ $this->assertTrue(TrafficLimiter::canPass(), 'request is to fast, but non-IP address matches creator');
+ }
}
From 1034d4038e6c59b5b4d69b3b9e8a9b0b35a6fc18 Mon Sep 17 00:00:00 2001
From: El RIDO
Date: Sun, 20 Feb 2022 11:25:19 +0100
Subject: [PATCH 250/478] unify IP-related logic into traffic limiter
---
lib/Controller.php | 29 ++------
lib/Persistence/TrafficLimiter.php | 93 ++++++++++++++++++--------
tst/Persistence/TrafficLimiterTest.php | 39 ++++++++---
3 files changed, 101 insertions(+), 60 deletions(-)
diff --git a/lib/Controller.php b/lib/Controller.php
index 8ead884..56f424c 100644
--- a/lib/Controller.php
+++ b/lib/Controller.php
@@ -195,35 +195,14 @@ class Controller
*/
private function _create()
{
- // Check if whitelist feature is enabled
- if (($option = $this->_conf->getKey('creators', 'traffic')) !== '') {
- // Parse whitelist into array
- $whitelist = explode(',', $option);
- // Check for source IP in HTTP header
- if (($option = $this->_conf->getKey('header', 'traffic')) !== null) {
- $httpHeader = 'HTTP_' . $option;
- // Grab source IP from HTTP header (if it exists)
- if (array_key_exists($httpHeader, $_SERVER) && !empty($_SERVER[$httpHeader])) {
- // Check if source IP reported from HTTP header is in whitelist array
- if (!in_array($_SERVER[$httpHeader], $whitelist)) {
- $this->_return_message(1, I18n::_('Your IP is not authorized to create pastes.'));
- return;
- }
- }
- }
- }
-
// Ensure last paste from visitors IP address was more than configured amount of seconds ago.
ServerSalt::setStore($this->_model->getStore());
TrafficLimiter::setConfiguration($this->_conf);
TrafficLimiter::setStore($this->_model->getStore());
- if (!TrafficLimiter::canPass()) {
- $this->_return_message(
- 1, I18n::_(
- 'Please wait %d seconds between each post.',
- $this->_conf->getKey('limit', 'traffic')
- )
- );
+ try {
+ TrafficLimiter::canPass();
+ } catch (Exception $e) {
+ $this->_return_message(1, $e->getMessage());
return;
}
diff --git a/lib/Persistence/TrafficLimiter.php b/lib/Persistence/TrafficLimiter.php
index f5f8e7a..fab33c4 100644
--- a/lib/Persistence/TrafficLimiter.php
+++ b/lib/Persistence/TrafficLimiter.php
@@ -13,8 +13,10 @@
namespace PrivateBin\Persistence;
+use Exception;
use IPLib\Factory;
use PrivateBin\Configuration;
+use PrivateBin\I18n;
/**
* TrafficLimiter
@@ -24,13 +26,13 @@ use PrivateBin\Configuration;
class TrafficLimiter extends AbstractPersistence
{
/**
- * time limit in seconds, defaults to 10s
+ * listed IPs are the only ones allowed to create, defaults to null
*
* @access private
* @static
- * @var int
+ * @var string|null
*/
- private static $_limit = 10;
+ private static $_creators = null;
/**
* listed IPs are exempted from limits, defaults to null
@@ -51,19 +53,49 @@ class TrafficLimiter extends AbstractPersistence
private static $_ipKey = 'REMOTE_ADDR';
/**
- * set the time limit in seconds
+ * time limit in seconds, defaults to 10s
+ *
+ * @access private
+ * @static
+ * @var int
+ */
+ private static $_limit = 10;
+
+ /**
+ * set configuration options of the traffic limiter
*
* @access public
* @static
- * @param int $limit
+ * @param Configuration $conf
*/
- public static function setLimit($limit)
+ public static function setConfiguration(Configuration $conf)
{
- self::$_limit = $limit;
+ self::setCreators($conf->getKey('creators', 'traffic'));
+ self::setExempted($conf->getKey('exempted', 'traffic'));
+ self::setLimit($conf->getKey('limit', 'traffic'));
+
+ if (($option = $conf->getKey('header', 'traffic')) !== '') {
+ $httpHeader = 'HTTP_' . $option;
+ if (array_key_exists($httpHeader, $_SERVER) && !empty($_SERVER[$httpHeader])) {
+ self::$_ipKey = $httpHeader;
+ }
+ }
}
/**
- * set a list of IP(-ranges) as string
+ * set a list of creator IP(-ranges) as string
+ *
+ * @access public
+ * @static
+ * @param string $creators
+ */
+ public static function setCreators($creators)
+ {
+ self::$_creators = $creators;
+ }
+
+ /**
+ * set a list of exempted IP(-ranges) as string
*
* @access public
* @static
@@ -75,23 +107,15 @@ class TrafficLimiter extends AbstractPersistence
}
/**
- * set configuration options of the traffic limiter
+ * set the time limit in seconds
*
* @access public
* @static
- * @param Configuration $conf
+ * @param int $limit
*/
- public static function setConfiguration(Configuration $conf)
+ public static function setLimit($limit)
{
- self::setLimit($conf->getKey('limit', 'traffic'));
- self::setExempted($conf->getKey('exempted', 'traffic'));
-
- if (($option = $conf->getKey('header', 'traffic')) !== '') {
- $httpHeader = 'HTTP_' . $option;
- if (array_key_exists($httpHeader, $_SERVER) && !empty($_SERVER[$httpHeader])) {
- self::$_ipKey = $httpHeader;
- }
- }
+ self::$_limit = $limit;
}
/**
@@ -108,7 +132,7 @@ class TrafficLimiter extends AbstractPersistence
}
/**
- * Validate $_ipKey against configured ipranges. If matched we will ignore the ip
+ * validate $_ipKey against configured ipranges. If matched we will ignore the ip
*
* @access private
* @static
@@ -136,22 +160,33 @@ class TrafficLimiter extends AbstractPersistence
}
/**
- * traffic limiter
- *
- * Make sure the IP address makes at most 1 request every 10 seconds.
+ * make sure the IP address is allowed to perfom a request
*
* @access public
* @static
- * @return bool
+ * @throws Exception
+ * @return true
*/
public static function canPass()
{
+ // if creators are defined, the traffic limiter will only allow creation
+ // for these, with no limits, and skip any other rules
+ if (!empty(self::$_creators)) {
+ $creatorIps = explode(',', self::$_creators);
+ foreach ($creatorIps as $ipRange) {
+ if (self::matchIp($ipRange) === true) {
+ return true;
+ }
+ }
+ throw new Exception(I18n::_('Your IP is not authorized to create pastes.'));
+ }
+
// disable limits if set to less then 1
if (self::$_limit < 1) {
return true;
}
- // Check if $_ipKey is exempted from ratelimiting
+ // check if $_ipKey is exempted from ratelimiting
if (!empty(self::$_exempted)) {
$exIp_array = explode(',', self::$_exempted);
foreach ($exIp_array as $ipRange) {
@@ -175,6 +210,10 @@ class TrafficLimiter extends AbstractPersistence
if (!self::$_store->setValue((string) $tl, 'traffic_limiter', $hash)) {
error_log('failed to store the traffic limiter, it probably contains outdated information');
}
- return $result;
+ if ($result) return true;
+ throw new Exception(I18n::_(
+ 'Please wait %d seconds between each post.',
+ self::$_limit
+ ));
}
}
diff --git a/tst/Persistence/TrafficLimiterTest.php b/tst/Persistence/TrafficLimiterTest.php
index 4e56b8a..e5256c7 100644
--- a/tst/Persistence/TrafficLimiterTest.php
+++ b/tst/Persistence/TrafficLimiterTest.php
@@ -38,13 +38,21 @@ class TrafficLimiterTest extends PHPUnit_Framework_TestCase
$_SERVER['REMOTE_ADDR'] = '127.0.0.1';
$this->assertTrue(TrafficLimiter::canPass(), 'first request may pass');
sleep(1);
- $this->assertFalse(TrafficLimiter::canPass(), 'second request is to fast, may not pass');
+ try {
+ $this->assertFalse(TrafficLimiter::canPass(), 'expected an exception');
+ } catch (Exception $e) {
+ $this->assertEquals($e->getMessage(), 'Please wait 4 seconds between each post.', 'second request is to fast, may not pass');
+ }
sleep(4);
$this->assertTrue(TrafficLimiter::canPass(), 'third request waited long enough and may pass');
$_SERVER['REMOTE_ADDR'] = '2001:1620:2057:dead:beef::cafe:babe';
$this->assertTrue(TrafficLimiter::canPass(), 'fourth request has different ip and may pass');
$_SERVER['REMOTE_ADDR'] = '127.0.0.1';
- $this->assertFalse(TrafficLimiter::canPass(), 'fifth request is to fast, may not pass');
+ try {
+ $this->assertFalse(TrafficLimiter::canPass(), 'expected an exception');
+ } catch (Exception $e) {
+ $this->assertEquals($e->getMessage(), 'Please wait 4 seconds between each post.', 'fifth request is to fast, may not pass');
+ }
}
public function testTrafficLimitExempted()
@@ -52,7 +60,11 @@ class TrafficLimiterTest extends PHPUnit_Framework_TestCase
TrafficLimiter::setExempted('1.2.3.4,10.10.10.0/24,2001:1620:2057::/48');
$_SERVER['REMOTE_ADDR'] = '127.0.0.1';
$this->assertTrue(TrafficLimiter::canPass(), 'first request may pass');
- $this->assertFalse(TrafficLimiter::canPass(), 'not exempted');
+ try {
+ $this->assertFalse(TrafficLimiter::canPass(), 'expected an exception');
+ } catch (Exception $e) {
+ $this->assertEquals($e->getMessage(), 'Please wait 4 seconds between each post.', 'not exempted');
+ }
$_SERVER['REMOTE_ADDR'] = '10.10.10.10';
$this->assertTrue(TrafficLimiter::canPass(), 'IPv4 in exempted range');
$this->assertTrue(TrafficLimiter::canPass(), 'request is to fast, but IPv4 in exempted range');
@@ -61,7 +73,11 @@ class TrafficLimiterTest extends PHPUnit_Framework_TestCase
$this->assertTrue(TrafficLimiter::canPass(), 'request is to fast, but IPv6 in exempted range');
TrafficLimiter::setExempted('127.*,foobar');
$this->assertTrue(TrafficLimiter::canPass(), 'first cached request may pass');
- $this->assertFalse(TrafficLimiter::canPass(), 'request is to fast, invalid range');
+ try {
+ $this->assertFalse(TrafficLimiter::canPass(), 'expected an exception');
+ } catch (Exception $e) {
+ $this->assertEquals($e->getMessage(), 'Please wait 4 seconds between each post.', 'request is to fast, invalid range');
+ }
$_SERVER['REMOTE_ADDR'] = 'foobar';
$this->assertTrue(TrafficLimiter::canPass(), 'non-IP address');
$this->assertTrue(TrafficLimiter::canPass(), 'request is to fast, but non-IP address matches exempted range');
@@ -71,16 +87,23 @@ class TrafficLimiterTest extends PHPUnit_Framework_TestCase
{
TrafficLimiter::setCreators('1.2.3.4,10.10.10.0/24,2001:1620:2057::/48');
$_SERVER['REMOTE_ADDR'] = '127.0.0.1';
- $this->assertFalse(TrafficLimiter::canPass(), 'not a creator');
+ try {
+ $this->assertFalse(TrafficLimiter::canPass(), 'expected an exception');
+ } catch (Exception $e) {
+ $this->assertEquals($e->getMessage(), 'Your IP is not authorized to create pastes.', 'not a creator');
+ }
$_SERVER['REMOTE_ADDR'] = '10.10.10.10';
$this->assertTrue(TrafficLimiter::canPass(), 'IPv4 in creator range');
$this->assertTrue(TrafficLimiter::canPass(), 'request is to fast, but IPv4 in creator range');
$_SERVER['REMOTE_ADDR'] = '2001:1620:2057:dead:beef::cafe:babe';
$this->assertTrue(TrafficLimiter::canPass(), 'IPv6 in creator range');
$this->assertTrue(TrafficLimiter::canPass(), 'request is to fast, but IPv6 in creator range');
- TrafficLimiter::setExempted('127.*,foobar');
- $this->assertTrue(TrafficLimiter::canPass(), 'first cached request may pass');
- $this->assertFalse(TrafficLimiter::canPass(), 'request is to fast, not a creator');
+ TrafficLimiter::setCreators('127.*,foobar');
+ try {
+ $this->assertFalse(TrafficLimiter::canPass(), 'expected an exception');
+ } catch (Exception $e) {
+ $this->assertEquals($e->getMessage(), 'Your IP is not authorized to create pastes.', 'request is to fast, not a creator');
+ }
$_SERVER['REMOTE_ADDR'] = 'foobar';
$this->assertTrue(TrafficLimiter::canPass(), 'non-IP address');
$this->assertTrue(TrafficLimiter::canPass(), 'request is to fast, but non-IP address matches creator');
From 6b59d4f3806e27b483ae0d4521e7dd46073fe0af Mon Sep 17 00:00:00 2001
From: El RIDO
Date: Sun, 20 Feb 2022 11:51:41 +0100
Subject: [PATCH 251/478] document change
---
CHANGELOG.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7c3f804..f861baa 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,7 @@
* ADDED: Configuration option to exempt IPs from the rate-limiter (#787)
* ADDED: Google Cloud Storage backend support (#795)
* ADDED: Oracle database support (#868)
+ * ADDED: Configuration option to limit paste creation and commenting to certain IPs (#883)
* CHANGED: Language selection cookie only transmitted over HTTPS (#472)
* CHANGED: Upgrading libraries to: random_compat 2.0.20
* CHANGED: Removed automatic `.ini` configuration file migration (#808)
From 1054319313610579f773016c12e184b5c62bd12d Mon Sep 17 00:00:00 2001
From: El RIDO
Date: Sun, 20 Feb 2022 12:22:34 +0100
Subject: [PATCH 252/478] add new translation string
---
i18n/ar.json | 3 ++-
i18n/bg.json | 3 ++-
i18n/ca.json | 3 ++-
i18n/cs.json | 3 ++-
i18n/de.json | 3 ++-
i18n/el.json | 3 ++-
i18n/en.json | 3 ++-
i18n/es.json | 3 ++-
i18n/et.json | 3 ++-
i18n/fi.json | 3 ++-
i18n/fr.json | 3 ++-
i18n/he.json | 3 ++-
i18n/hi.json | 3 ++-
i18n/hu.json | 3 ++-
i18n/id.json | 3 ++-
i18n/it.json | 3 ++-
i18n/ja.json | 3 ++-
i18n/jbo.json | 3 ++-
i18n/ku.json | 3 ++-
i18n/la.json | 3 ++-
i18n/lt.json | 3 ++-
i18n/nl.json | 3 ++-
i18n/no.json | 3 ++-
i18n/oc.json | 3 ++-
i18n/pl.json | 3 ++-
i18n/pt.json | 3 ++-
i18n/ru.json | 3 ++-
i18n/sl.json | 3 ++-
i18n/sv.json | 3 ++-
i18n/tr.json | 3 ++-
i18n/uk.json | 3 ++-
i18n/zh.json | 3 ++-
32 files changed, 64 insertions(+), 32 deletions(-)
diff --git a/i18n/ar.json b/i18n/ar.json
index ddca0d6..1827ddf 100644
--- a/i18n/ar.json
+++ b/i18n/ar.json
@@ -185,5 +185,6 @@
"Encrypted note on PrivateBin": "Encrypted note on PrivateBin",
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.",
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL.",
- "Save paste": "Save paste"
+ "Save paste": "Save paste",
+ "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes."
}
diff --git a/i18n/bg.json b/i18n/bg.json
index 4a02538..717c5f1 100644
--- a/i18n/bg.json
+++ b/i18n/bg.json
@@ -185,5 +185,6 @@
"Encrypted note on PrivateBin": "Encrypted note on PrivateBin",
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.",
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL.",
- "Save paste": "Save paste"
+ "Save paste": "Save paste",
+ "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes."
}
diff --git a/i18n/ca.json b/i18n/ca.json
index 02b48d7..245b884 100644
--- a/i18n/ca.json
+++ b/i18n/ca.json
@@ -185,5 +185,6 @@
"Encrypted note on PrivateBin": "Encrypted note on PrivateBin",
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.",
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL.",
- "Save paste": "Save paste"
+ "Save paste": "Save paste",
+ "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes."
}
diff --git a/i18n/cs.json b/i18n/cs.json
index e6eff88..886e9c3 100644
--- a/i18n/cs.json
+++ b/i18n/cs.json
@@ -185,5 +185,6 @@
"Encrypted note on PrivateBin": "Šifrovaná poznámka ve službě PrivateBin",
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Navštivte tento odkaz pro zobrazení poznámky. Přeposláním URL umožníte také jiným lidem přístup.",
"URL shortener may expose your decrypt key in URL.": "Zkracovač URL může odhalit váš dešifrovací klíč v URL.",
- "Save paste": "Uložit příspěvek"
+ "Save paste": "Uložit příspěvek",
+ "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes."
}
diff --git a/i18n/de.json b/i18n/de.json
index 0c4cd70..c60c156 100644
--- a/i18n/de.json
+++ b/i18n/de.json
@@ -185,5 +185,6 @@
"Encrypted note on PrivateBin": "Verschlüsselte Notiz auf PrivateBin",
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Besuche diesen Link um das Dokument zu sehen. Wird die URL an eine andere Person gegeben, so kann diese Person ebenfalls auf dieses Dokument zugreifen.",
"URL shortener may expose your decrypt key in URL.": "Der URL-Verkürzer kann den Schlüssel in der URL enthüllen.",
- "Save paste": "Text speichern"
+ "Save paste": "Text speichern",
+ "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes."
}
diff --git a/i18n/el.json b/i18n/el.json
index 6e33978..acfca8f 100644
--- a/i18n/el.json
+++ b/i18n/el.json
@@ -185,5 +185,6 @@
"Encrypted note on PrivateBin": "Encrypted note on PrivateBin",
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.",
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL.",
- "Save paste": "Save paste"
+ "Save paste": "Save paste",
+ "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes."
}
diff --git a/i18n/en.json b/i18n/en.json
index a96bab5..2ba6068 100644
--- a/i18n/en.json
+++ b/i18n/en.json
@@ -185,5 +185,6 @@
"Encrypted note on PrivateBin": "Encrypted note on PrivateBin",
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.",
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL.",
- "Save paste": "Save paste"
+ "Save paste": "Save paste",
+ "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes."
}
diff --git a/i18n/es.json b/i18n/es.json
index 5aa2b38..909b32c 100644
--- a/i18n/es.json
+++ b/i18n/es.json
@@ -185,5 +185,6 @@
"Encrypted note on PrivateBin": "Nota cifrada en PrivateBin",
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visite este enlace para ver la nota. Dar la URL a cualquier persona también les permite acceder a la nota.",
"URL shortener may expose your decrypt key in URL.": "El acortador de URL puede exponer su clave de descifrado en el URL.",
- "Save paste": "Guardar \"paste\""
+ "Save paste": "Guardar \"paste\"",
+ "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes."
}
diff --git a/i18n/et.json b/i18n/et.json
index 9ab2840..f02cefb 100644
--- a/i18n/et.json
+++ b/i18n/et.json
@@ -185,5 +185,6 @@
"Encrypted note on PrivateBin": "Krüpteeritud kiri PrivateBin-is",
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Kirja nägemiseks külasta seda linki. Teistele URL-i andmine lubab ka neil ligi pääseda kirjale.",
"URL shortener may expose your decrypt key in URL.": "URL-i lühendaja võib paljastada sinu dekrüpteerimisvõtme URL-is.",
- "Save paste": "Salvesta kleebe"
+ "Save paste": "Salvesta kleebe",
+ "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes."
}
diff --git a/i18n/fi.json b/i18n/fi.json
index aea2b63..0ad8c41 100644
--- a/i18n/fi.json
+++ b/i18n/fi.json
@@ -185,5 +185,6 @@
"Encrypted note on PrivateBin": "Salattu viesti PrivateBinissä",
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Käy tässä linkissä nähdäksesi viestin. URL:n antaminen kenellekään antaa heidänkin päästä katsomeen viestiä. ",
"URL shortener may expose your decrypt key in URL.": "URL-lyhentäjä voi paljastaa purkuavaimesi URL:ssä.",
- "Save paste": "Tallenna paste"
+ "Save paste": "Tallenna paste",
+ "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes."
}
diff --git a/i18n/fr.json b/i18n/fr.json
index 0e14d79..b5767f7 100644
--- a/i18n/fr.json
+++ b/i18n/fr.json
@@ -185,5 +185,6 @@
"Encrypted note on PrivateBin": "Message chiffré sur PrivateBin",
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visiter ce lien pour voir la note. Donner l'URL à une autre personne lui permet également d'accéder à la note.",
"URL shortener may expose your decrypt key in URL.": "Raccourcir l'URL peut exposer votre clé de déchiffrement dans l'URL.",
- "Save paste": "Sauver le paste"
+ "Save paste": "Sauver le paste",
+ "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes."
}
diff --git a/i18n/he.json b/i18n/he.json
index 046874e..503dbc1 100644
--- a/i18n/he.json
+++ b/i18n/he.json
@@ -185,5 +185,6 @@
"Encrypted note on PrivateBin": "הערה מוצפנת ב־PrivateBin",
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "נא לבקר בקישור כדי לצפות בהערה. מסירת הקישור לאנשים כלשהם תאפשר גם להם לגשת להערה.",
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL.",
- "Save paste": "Save paste"
+ "Save paste": "Save paste",
+ "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes."
}
diff --git a/i18n/hi.json b/i18n/hi.json
index 0cd2787..4df6aa7 100644
--- a/i18n/hi.json
+++ b/i18n/hi.json
@@ -185,5 +185,6 @@
"Encrypted note on PrivateBin": "Encrypted note on PrivateBin",
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.",
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL.",
- "Save paste": "Save paste"
+ "Save paste": "Save paste",
+ "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes."
}
diff --git a/i18n/hu.json b/i18n/hu.json
index 1cff1a6..c9acb04 100644
--- a/i18n/hu.json
+++ b/i18n/hu.json
@@ -185,5 +185,6 @@
"Encrypted note on PrivateBin": "Titkosított jegyzet a PrivateBinen",
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Látogasd meg ezt a hivatkozást a bejegyzés megtekintéséhez. Ha mások számára is megadod ezt a linket, azzal hozzáférnek ők is.",
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL.",
- "Save paste": "Save paste"
+ "Save paste": "Save paste",
+ "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes."
}
diff --git a/i18n/id.json b/i18n/id.json
index e58900a..1edeb99 100644
--- a/i18n/id.json
+++ b/i18n/id.json
@@ -185,5 +185,6 @@
"Encrypted note on PrivateBin": "Catatan ter-ekrip di PrivateBin",
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Kunjungi tautan ini untuk melihat catatan. Memberikan alamat URL pada siapapun juga, akan mengizinkan mereka untuk mengakses catatan, so pasti gitu loh Kaka.",
"URL shortener may expose your decrypt key in URL.": "Pemendek URL mungkin akan menampakkan kunci dekrip Anda dalam URL.",
- "Save paste": "Simpan paste"
+ "Save paste": "Simpan paste",
+ "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes."
}
diff --git a/i18n/it.json b/i18n/it.json
index 659816e..8b9f391 100644
--- a/i18n/it.json
+++ b/i18n/it.json
@@ -185,5 +185,6 @@
"Encrypted note on PrivateBin": "Nota crittografata su PrivateBin",
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visita questo collegamento per vedere la nota. Dare l'URL a chiunque consente anche a loro di accedere alla nota.",
"URL shortener may expose your decrypt key in URL.": "URL shortener può esporre la tua chiave decrittografata nell'URL.",
- "Save paste": "Salva il messagio"
+ "Save paste": "Salva il messagio",
+ "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes."
}
diff --git a/i18n/ja.json b/i18n/ja.json
index 807a38c..330032f 100644
--- a/i18n/ja.json
+++ b/i18n/ja.json
@@ -185,5 +185,6 @@
"Encrypted note on PrivateBin": "Encrypted note on PrivateBin",
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.",
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL.",
- "Save paste": "Save paste"
+ "Save paste": "Save paste",
+ "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes."
}
diff --git a/i18n/jbo.json b/i18n/jbo.json
index 10925d0..82cea90 100644
--- a/i18n/jbo.json
+++ b/i18n/jbo.json
@@ -185,5 +185,6 @@
"Encrypted note on PrivateBin": ".i lo lo notci ku mifra cu zvati sivlolnitvanku'a",
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.",
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL.",
- "Save paste": "rejgau fukpi"
+ "Save paste": "rejgau fukpi",
+ "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes."
}
diff --git a/i18n/ku.json b/i18n/ku.json
index ae72ebd..bca4347 100644
--- a/i18n/ku.json
+++ b/i18n/ku.json
@@ -185,5 +185,6 @@
"Encrypted note on PrivateBin": "Encrypted note on PrivateBin",
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.",
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL.",
- "Save paste": "Save paste"
+ "Save paste": "Save paste",
+ "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes."
}
diff --git a/i18n/la.json b/i18n/la.json
index 2098982..fe6c400 100644
--- a/i18n/la.json
+++ b/i18n/la.json
@@ -185,5 +185,6 @@
"Encrypted note on PrivateBin": "Encrypted note on PrivateBin",
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.",
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL.",
- "Save paste": "Save paste"
+ "Save paste": "Save paste",
+ "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes."
}
diff --git a/i18n/lt.json b/i18n/lt.json
index 10b62d8..195fab0 100644
--- a/i18n/lt.json
+++ b/i18n/lt.json
@@ -185,5 +185,6 @@
"Encrypted note on PrivateBin": "Šifruoti užrašai ties PrivateBin",
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Norėdami matyti užrašus, aplankykite šį tinklalapį. Pasidalinus šiuo URL adresu su kitais žmonėmis, jiems taip pat bus leidžiama prieiga prie šių užrašų.",
"URL shortener may expose your decrypt key in URL.": "URL trumpinimo įrankis gali atskleisti URL adrese jūsų iššifravimo raktą.",
- "Save paste": "Įrašyti įdėjimą"
+ "Save paste": "Įrašyti įdėjimą",
+ "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes."
}
diff --git a/i18n/nl.json b/i18n/nl.json
index 65c4e37..b20416a 100644
--- a/i18n/nl.json
+++ b/i18n/nl.json
@@ -185,5 +185,6 @@
"Encrypted note on PrivateBin": "Encrypted note on PrivateBin",
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.",
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL.",
- "Save paste": "Save paste"
+ "Save paste": "Save paste",
+ "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes."
}
diff --git a/i18n/no.json b/i18n/no.json
index b0ca95a..eda1e4c 100644
--- a/i18n/no.json
+++ b/i18n/no.json
@@ -185,5 +185,6 @@
"Encrypted note on PrivateBin": "Kryptert notat på PrivateBin",
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Besøk denne lenken for å se notatet. Hvis lenken deles med andre, vil de også kunne se notatet.",
"URL shortener may expose your decrypt key in URL.": "URL forkorter kan avsløre dekrypteringsnøkkelen.",
- "Save paste": "Lagre utklipp"
+ "Save paste": "Lagre utklipp",
+ "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes."
}
diff --git a/i18n/oc.json b/i18n/oc.json
index 6a0a69c..1bb4de6 100644
--- a/i18n/oc.json
+++ b/i18n/oc.json
@@ -185,5 +185,6 @@
"Encrypted note on PrivateBin": "Nòtas chifradas sus PrivateBin",
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visitatz aqueste ligam per veire la nòta. Fornir lo ligam a qualqu’un mai li permet tanben d’accedir a la nòta.",
"URL shortener may expose your decrypt key in URL.": "Los espleches d’acorchiment d’URL pòdon expausar la clau de deschiframent dins l’URL.",
- "Save paste": "Enregistrar lo tèxt"
+ "Save paste": "Enregistrar lo tèxt",
+ "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes."
}
diff --git a/i18n/pl.json b/i18n/pl.json
index 4b4a67b..b6127ca 100644
--- a/i18n/pl.json
+++ b/i18n/pl.json
@@ -185,5 +185,6 @@
"Encrypted note on PrivateBin": "Encrypted note on PrivateBin",
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.",
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL.",
- "Save paste": "Save paste"
+ "Save paste": "Save paste",
+ "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes."
}
diff --git a/i18n/pt.json b/i18n/pt.json
index 0f6c133..72d0250 100644
--- a/i18n/pt.json
+++ b/i18n/pt.json
@@ -185,5 +185,6 @@
"Encrypted note on PrivateBin": "Nota criptografada no PrivateBin",
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visite esse link para ver a nota. Dar a URL para qualquer um permite que eles também acessem a nota.",
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL.",
- "Save paste": "Save paste"
+ "Save paste": "Save paste",
+ "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes."
}
diff --git a/i18n/ru.json b/i18n/ru.json
index a911233..2d3edb9 100644
--- a/i18n/ru.json
+++ b/i18n/ru.json
@@ -185,5 +185,6 @@
"Encrypted note on PrivateBin": "Зашифрованная запись на PrivateBin",
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Посетите эту ссылку чтобы просмотреть запись. Передача ссылки кому либо позволит им получить доступ к записи тоже.",
"URL shortener may expose your decrypt key in URL.": "Сервис сокращения ссылок может получить ваш ключ расшифровки из ссылки.",
- "Save paste": "Сохранить запись"
+ "Save paste": "Сохранить запись",
+ "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes."
}
diff --git a/i18n/sl.json b/i18n/sl.json
index c4bb0cb..27c4361 100644
--- a/i18n/sl.json
+++ b/i18n/sl.json
@@ -185,5 +185,6 @@
"Encrypted note on PrivateBin": "Encrypted note on PrivateBin",
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.",
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL.",
- "Save paste": "Save paste"
+ "Save paste": "Save paste",
+ "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes."
}
diff --git a/i18n/sv.json b/i18n/sv.json
index f2286e0..bb61329 100644
--- a/i18n/sv.json
+++ b/i18n/sv.json
@@ -185,5 +185,6 @@
"Encrypted note on PrivateBin": "Encrypted note on PrivateBin",
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.",
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL.",
- "Save paste": "Save paste"
+ "Save paste": "Save paste",
+ "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes."
}
diff --git a/i18n/tr.json b/i18n/tr.json
index 0abbc37..c2c380e 100644
--- a/i18n/tr.json
+++ b/i18n/tr.json
@@ -185,5 +185,6 @@
"Encrypted note on PrivateBin": "Encrypted note on PrivateBin",
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.",
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL.",
- "Save paste": "Save paste"
+ "Save paste": "Save paste",
+ "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes."
}
diff --git a/i18n/uk.json b/i18n/uk.json
index 1916a24..52e9723 100644
--- a/i18n/uk.json
+++ b/i18n/uk.json
@@ -185,5 +185,6 @@
"Encrypted note on PrivateBin": "Encrypted note on PrivateBin",
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.",
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL.",
- "Save paste": "Save paste"
+ "Save paste": "Save paste",
+ "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes."
}
diff --git a/i18n/zh.json b/i18n/zh.json
index 2ab63bf..e90627f 100644
--- a/i18n/zh.json
+++ b/i18n/zh.json
@@ -185,5 +185,6 @@
"Encrypted note on PrivateBin": "PrivateBin 上的加密笔记",
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "访问此链接来查看该笔记。将此 URL 发送给任何人即可允许其访问该笔记。",
"URL shortener may expose your decrypt key in URL.": "短链接服务可能会暴露您在 URL 中的解密密钥。",
- "Save paste": "保存内容"
+ "Save paste": "保存内容",
+ "Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes."
}
From f987e96d4b1edc336009062a7e31549886f540ef Mon Sep 17 00:00:00 2001
From: El RIDO
Date: Sun, 20 Feb 2022 12:25:55 +0100
Subject: [PATCH 253/478] apply StyleCI recommendation
---
lib/Persistence/TrafficLimiter.php | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/lib/Persistence/TrafficLimiter.php b/lib/Persistence/TrafficLimiter.php
index fab33c4..d0b82be 100644
--- a/lib/Persistence/TrafficLimiter.php
+++ b/lib/Persistence/TrafficLimiter.php
@@ -210,7 +210,9 @@ class TrafficLimiter extends AbstractPersistence
if (!self::$_store->setValue((string) $tl, 'traffic_limiter', $hash)) {
error_log('failed to store the traffic limiter, it probably contains outdated information');
}
- if ($result) return true;
+ if ($result) {
+ return true;
+ }
throw new Exception(I18n::_(
'Please wait %d seconds between each post.',
self::$_limit
From 0e3a7196f9374cd983565c47d83307f93e2081cd Mon Sep 17 00:00:00 2001
From: El RIDO
Date: Sun, 20 Feb 2022 15:21:47 +0100
Subject: [PATCH 254/478] set frame-ancestors to none
disables embedding the site in any frames, which can bypass some of the security mechanisms reg. cross site scripting
---
cfg/conf.sample.php | 2 +-
lib/Configuration.php | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/cfg/conf.sample.php b/cfg/conf.sample.php
index 7194ee5..c1292ed 100644
--- a/cfg/conf.sample.php
+++ b/cfg/conf.sample.php
@@ -87,7 +87,7 @@ languageselection = false
; async functions and display an error if not and for Chrome to enable
; webassembly support (used for zlib compression). You can remove it if Chrome
; doesn't need to be supported and old browsers don't need to be warned.
-; cspheader = "default-src 'none'; base-uri 'self'; form-action 'none'; manifest-src 'self'; connect-src * blob:; script-src 'self' 'unsafe-eval' resource:; style-src 'self'; font-src 'self'; img-src 'self' data: blob:; media-src blob:; object-src blob:; sandbox allow-same-origin allow-scripts allow-forms allow-popups allow-modals allow-downloads"
+; cspheader = "default-src 'none'; base-uri 'self'; form-action 'none'; manifest-src 'self'; connect-src * blob:; script-src 'self' 'unsafe-eval' resource:; style-src 'self'; font-src 'self'; frame-ancestors 'none'; img-src 'self' data: blob:; media-src blob:; object-src blob:; sandbox allow-same-origin allow-scripts allow-forms allow-popups allow-modals allow-downloads"
; stay compatible with PrivateBin Alpha 0.19, less secure
; if enabled will use base64.js version 1.7 instead of 2.1.9 and sha1 instead of
diff --git a/lib/Configuration.php b/lib/Configuration.php
index c56ec8e..cb29bc1 100644
--- a/lib/Configuration.php
+++ b/lib/Configuration.php
@@ -54,7 +54,7 @@ class Configuration
'urlshortener' => '',
'qrcode' => true,
'icon' => 'identicon',
- 'cspheader' => 'default-src \'none\'; base-uri \'self\'; form-action \'none\'; manifest-src \'self\'; connect-src * blob:; script-src \'self\' \'unsafe-eval\' resource:; style-src \'self\'; font-src \'self\'; img-src \'self\' data: blob:; media-src blob:; object-src blob:; sandbox allow-same-origin allow-scripts allow-forms allow-popups allow-modals allow-downloads',
+ 'cspheader' => 'default-src \'none\'; base-uri \'self\'; form-action \'none\'; manifest-src \'self\'; connect-src * blob:; script-src \'self\' \'unsafe-eval\' resource:; style-src \'self\'; font-src \'self\'; frame-ancestors \'none\'; img-src \'self\' data: blob:; media-src blob:; object-src blob:; sandbox allow-same-origin allow-scripts allow-forms allow-popups allow-modals allow-downloads',
'zerobincompatibility' => false,
'httpwarning' => true,
'compression' => 'zlib',
From fbf0eae5137c9d7008ecd5c72d007da24b27419d Mon Sep 17 00:00:00 2001
From: El RIDO
Date: Sun, 20 Feb 2022 16:13:34 +0100
Subject: [PATCH 255/478] update bootstrap JS library to 3.4.1
note that this fails one of our unit tests
---
CHANGELOG.md | 2 +-
js/bootstrap-3.3.7.js | 7 -------
js/bootstrap-3.4.1.js | 6 ++++++
js/common.js | 3 +--
tpl/bootstrap.php | 2 +-
5 files changed, 9 insertions(+), 11 deletions(-)
delete mode 100644 js/bootstrap-3.3.7.js
create mode 100644 js/bootstrap-3.4.1.js
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a877e8d..de1de13 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,7 +9,7 @@
* ADDED: Google Cloud Storage backend support (#795)
* ADDED: Oracle database support (#868)
* CHANGED: Language selection cookie only transmitted over HTTPS (#472)
- * CHANGED: Upgrading libraries to: base-x 4.0.0, DOMpurify 2.3.6, ip-lib 1.18.0, jQuery 3.6.0, random_compat 2.0.21 & Showdown 2.0.0
+ * CHANGED: Upgrading libraries to: base-x 4.0.0, bootstrap 3.4.1, DOMpurify 2.3.6, ip-lib 1.18.0, jQuery 3.6.0, random_compat 2.0.21 & Showdown 2.0.0
* CHANGED: Removed automatic `.ini` configuration file migration (#808)
* CHANGED: Removed configurable `dir` for `traffic` & `purge` limiters (#419)
* CHANGED: Server salt, traffic and purge limiter now stored in the storage backend (#419)
diff --git a/js/bootstrap-3.3.7.js b/js/bootstrap-3.3.7.js
deleted file mode 100644
index 9bcd2fc..0000000
--- a/js/bootstrap-3.3.7.js
+++ /dev/null
@@ -1,7 +0,0 @@
-/*!
- * Bootstrap v3.3.7 (http://getbootstrap.com)
- * Copyright 2011-2016 Twitter, Inc.
- * Licensed under the MIT license
- */
-if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";var b=a.fn.jquery.split(" ")[0].split(".");if(b[0]<2&&b[1]<9||1==b[0]&&9==b[1]&&b[2]<1||b[0]>3)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher, but lower than version 4")}(jQuery),+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){if(a(b.target).is(this))return b.handleObj.handler.apply(this,arguments)}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.3.7",d.TRANSITION_DURATION=150,d.prototype.close=function(b){function c(){g.detach().trigger("closed.bs.alert").remove()}var e=a(this),f=e.attr("data-target");f||(f=e.attr("href"),f=f&&f.replace(/.*(?=#[^\s]*$)/,""));var g=a("#"===f?[]:f);b&&b.preventDefault(),g.length||(g=e.closest(".alert")),g.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(g.removeClass("in"),a.support.transition&&g.hasClass("fade")?g.one("bsTransitionEnd",c).emulateTransitionEnd(d.TRANSITION_DURATION):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.3.7",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),setTimeout(a.proxy(function(){d[e](null==f[b]?this.options[b]:f[b]),"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c).prop(c,!0)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c).prop(c,!1))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")?(c.prop("checked")&&(a=!1),b.find(".active").removeClass("active"),this.$element.addClass("active")):"checkbox"==c.prop("type")&&(c.prop("checked")!==this.$element.hasClass("active")&&(a=!1),this.$element.toggleClass("active")),c.prop("checked",this.$element.hasClass("active")),a&&c.trigger("change")}else this.$element.attr("aria-pressed",!this.$element.hasClass("active")),this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target).closest(".btn");b.call(d,"toggle"),a(c.target).is('input[type="radio"], input[type="checkbox"]')||(c.preventDefault(),d.is("input,button")?d.trigger("focus"):d.find("input:visible,button:visible").first().trigger("focus"))}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(b){a(b.target).closest(".btn").toggleClass("focus",/^focus(in)?$/.test(b.type))})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=null,this.sliding=null,this.interval=null,this.$active=null,this.$items=null,this.options.keyboard&&this.$element.on("keydown.bs.carousel",a.proxy(this.keydown,this)),"hover"==this.options.pause&&!("ontouchstart"in document.documentElement)&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.3.7",c.TRANSITION_DURATION=600,c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0,keyboard:!0},c.prototype.keydown=function(a){if(!/input|textarea/i.test(a.target.tagName)){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()}},c.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},c.prototype.getItemIndex=function(a){return this.$items=a.parent().children(".item"),this.$items.index(a||this.$active)},c.prototype.getItemForDirection=function(a,b){var c=this.getItemIndex(b),d="prev"==a&&0===c||"next"==a&&c==this.$items.length-1;if(d&&!this.options.wrap)return b;var e="prev"==a?-1:1,f=(c+e)%this.$items.length;return this.$items.eq(f)},c.prototype.to=function(a){var b=this,c=this.getItemIndex(this.$active=this.$element.find(".item.active"));if(!(a>this.$items.length-1||a<0))return this.sliding?this.$element.one("slid.bs.carousel",function(){b.to(a)}):c==a?this.pause().cycle():this.slide(a>c?"next":"prev",this.$items.eq(a))},c.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){if(!this.sliding)return this.slide("next")},c.prototype.prev=function(){if(!this.sliding)return this.slide("prev")},c.prototype.slide=function(b,d){var e=this.$element.find(".item.active"),f=d||this.getItemForDirection(b,e),g=this.interval,h="next"==b?"left":"right",i=this;if(f.hasClass("active"))return this.sliding=!1;var j=f[0],k=a.Event("slide.bs.carousel",{relatedTarget:j,direction:h});if(this.$element.trigger(k),!k.isDefaultPrevented()){if(this.sliding=!0,g&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var l=a(this.$indicators.children()[this.getItemIndex(f)]);l&&l.addClass("active")}var m=a.Event("slid.bs.carousel",{relatedTarget:j,direction:h});return a.support.transition&&this.$element.hasClass("slide")?(f.addClass(b),f[0].offsetWidth,e.addClass(h),f.addClass(h),e.one("bsTransitionEnd",function(){f.removeClass([b,h].join(" ")).addClass("active"),e.removeClass(["active",h].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger(m)},0)}).emulateTransitionEnd(c.TRANSITION_DURATION)):(e.removeClass("active"),f.addClass("active"),this.sliding=!1,this.$element.trigger(m)),g&&this.cycle(),this}};var d=a.fn.carousel;a.fn.carousel=b,a.fn.carousel.Constructor=c,a.fn.carousel.noConflict=function(){return a.fn.carousel=d,this};var e=function(c){var d,e=a(this),f=a(e.attr("data-target")||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""));if(f.hasClass("carousel")){var g=a.extend({},f.data(),e.data()),h=e.attr("data-slide-to");h&&(g.interval=!1),b.call(f,g),h&&f.data("bs.carousel").to(h),c.preventDefault()}};a(document).on("click.bs.carousel.data-api","[data-slide]",e).on("click.bs.carousel.data-api","[data-slide-to]",e),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var c=a(this);b.call(c,c.data())})})}(jQuery),+function(a){"use strict";function b(b){var c,d=b.attr("data-target")||(c=b.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"");return a(d)}function c(b){return this.each(function(){var c=a(this),e=c.data("bs.collapse"),f=a.extend({},d.DEFAULTS,c.data(),"object"==typeof b&&b);!e&&f.toggle&&/show|hide/.test(b)&&(f.toggle=!1),e||c.data("bs.collapse",e=new d(this,f)),"string"==typeof b&&e[b]()})}var d=function(b,c){this.$element=a(b),this.options=a.extend({},d.DEFAULTS,c),this.$trigger=a('[data-toggle="collapse"][href="#'+b.id+'"],[data-toggle="collapse"][data-target="#'+b.id+'"]'),this.transitioning=null,this.options.parent?this.$parent=this.getParent():this.addAriaAndCollapsedClass(this.$element,this.$trigger),this.options.toggle&&this.toggle()};d.VERSION="3.3.7",d.TRANSITION_DURATION=350,d.DEFAULTS={toggle:!0},d.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},d.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b,e=this.$parent&&this.$parent.children(".panel").children(".in, .collapsing");if(!(e&&e.length&&(b=e.data("bs.collapse"),b&&b.transitioning))){var f=a.Event("show.bs.collapse");if(this.$element.trigger(f),!f.isDefaultPrevented()){e&&e.length&&(c.call(e,"hide"),b||e.data("bs.collapse",null));var g=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[g](0).attr("aria-expanded",!0),this.$trigger.removeClass("collapsed").attr("aria-expanded",!0),this.transitioning=1;var h=function(){this.$element.removeClass("collapsing").addClass("collapse in")[g](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return h.call(this);var i=a.camelCase(["scroll",g].join("-"));this.$element.one("bsTransitionEnd",a.proxy(h,this)).emulateTransitionEnd(d.TRANSITION_DURATION)[g](this.$element[0][i])}}}},d.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded",!1),this.$trigger.addClass("collapsed").attr("aria-expanded",!1),this.transitioning=1;var e=function(){this.transitioning=0,this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")};return a.support.transition?void this.$element[c](0).one("bsTransitionEnd",a.proxy(e,this)).emulateTransitionEnd(d.TRANSITION_DURATION):e.call(this)}}},d.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()},d.prototype.getParent=function(){return a(this.options.parent).find('[data-toggle="collapse"][data-parent="'+this.options.parent+'"]').each(a.proxy(function(c,d){var e=a(d);this.addAriaAndCollapsedClass(b(e),e)},this)).end()},d.prototype.addAriaAndCollapsedClass=function(a,b){var c=a.hasClass("in");a.attr("aria-expanded",c),b.toggleClass("collapsed",!c).attr("aria-expanded",c)};var e=a.fn.collapse;a.fn.collapse=c,a.fn.collapse.Constructor=d,a.fn.collapse.noConflict=function(){return a.fn.collapse=e,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(d){var e=a(this);e.attr("data-target")||d.preventDefault();var f=b(e),g=f.data("bs.collapse"),h=g?"toggle":e.data();c.call(f,h)})}(jQuery),+function(a){"use strict";function b(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}function c(c){c&&3===c.which||(a(e).remove(),a(f).each(function(){var d=a(this),e=b(d),f={relatedTarget:this};e.hasClass("open")&&(c&&"click"==c.type&&/input|textarea/i.test(c.target.tagName)&&a.contains(e[0],c.target)||(e.trigger(c=a.Event("hide.bs.dropdown",f)),c.isDefaultPrevented()||(d.attr("aria-expanded","false"),e.removeClass("open").trigger(a.Event("hidden.bs.dropdown",f)))))}))}function d(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new g(this)),"string"==typeof b&&d[b].call(c)})}var e=".dropdown-backdrop",f='[data-toggle="dropdown"]',g=function(b){a(b).on("click.bs.dropdown",this.toggle)};g.VERSION="3.3.7",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=b(e),g=f.hasClass("open");if(c(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a(document.createElement("div")).addClass("dropdown-backdrop").insertAfter(a(this)).on("click",c);var h={relatedTarget:this};if(f.trigger(d=a.Event("show.bs.dropdown",h)),d.isDefaultPrevented())return;e.trigger("focus").attr("aria-expanded","true"),f.toggleClass("open").trigger(a.Event("shown.bs.dropdown",h))}return!1}},g.prototype.keydown=function(c){if(/(38|40|27|32)/.test(c.which)&&!/input|textarea/i.test(c.target.tagName)){var d=a(this);if(c.preventDefault(),c.stopPropagation(),!d.is(".disabled, :disabled")){var e=b(d),g=e.hasClass("open");if(!g&&27!=c.which||g&&27==c.which)return 27==c.which&&e.find(f).trigger("focus"),d.trigger("click");var h=" li:not(.disabled):visible a",i=e.find(".dropdown-menu"+h);if(i.length){var j=i.index(c.target);38==c.which&&j>0&&j--,40==c.which&&jdocument.documentElement.clientHeight;this.$element.css({paddingLeft:!this.bodyIsOverflowing&&a?this.scrollbarWidth:"",paddingRight:this.bodyIsOverflowing&&!a?this.scrollbarWidth:""})},c.prototype.resetAdjustments=function(){this.$element.css({paddingLeft:"",paddingRight:""})},c.prototype.checkScrollbar=function(){var a=window.innerWidth;if(!a){var b=document.documentElement.getBoundingClientRect();a=b.right-Math.abs(b.left)}this.bodyIsOverflowing=document.body.clientWidth',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0}},c.prototype.init=function(b,c,d){if(this.enabled=!0,this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.$viewport=this.options.viewport&&a(a.isFunction(this.options.viewport)?this.options.viewport.call(this,this.$element):this.options.viewport.selector||this.options.viewport),this.inState={click:!1,hover:!1,focus:!1},this.$element[0]instanceof document.constructor&&!this.options.selector)throw new Error("`selector` option must be specified when initializing "+this.type+" on the window.document object!");for(var e=this.options.trigger.split(" "),f=e.length;f--;){var g=e[f];if("click"==g)this.$element.on("click."+this.type,this.options.selector,a.proxy(this.toggle,this));else if("manual"!=g){var h="hover"==g?"mouseenter":"focusin",i="hover"==g?"mouseleave":"focusout";this.$element.on(h+"."+this.type,this.options.selector,a.proxy(this.enter,this)),this.$element.on(i+"."+this.type,this.options.selector,a.proxy(this.leave,this))}}this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.getOptions=function(b){return b=a.extend({},this.getDefaults(),this.$element.data(),b),b.delay&&"number"==typeof b.delay&&(b.delay={show:b.delay,hide:b.delay}),b},c.prototype.getDelegateOptions=function(){var b={},c=this.getDefaults();return this._options&&a.each(this._options,function(a,d){c[a]!=d&&(b[a]=d)}),b},c.prototype.enter=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusin"==b.type?"focus":"hover"]=!0),c.tip().hasClass("in")||"in"==c.hoverState?void(c.hoverState="in"):(clearTimeout(c.timeout),c.hoverState="in",c.options.delay&&c.options.delay.show?void(c.timeout=setTimeout(function(){"in"==c.hoverState&&c.show()},c.options.delay.show)):c.show())},c.prototype.isInStateTrue=function(){for(var a in this.inState)if(this.inState[a])return!0;return!1},c.prototype.leave=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);if(c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusout"==b.type?"focus":"hover"]=!1),!c.isInStateTrue())return clearTimeout(c.timeout),c.hoverState="out",c.options.delay&&c.options.delay.hide?void(c.timeout=setTimeout(function(){"out"==c.hoverState&&c.hide()},c.options.delay.hide)):c.hide()},c.prototype.show=function(){var b=a.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(b);var d=a.contains(this.$element[0].ownerDocument.documentElement,this.$element[0]);if(b.isDefaultPrevented()||!d)return;var e=this,f=this.tip(),g=this.getUID(this.type);this.setContent(),f.attr("id",g),this.$element.attr("aria-describedby",g),this.options.animation&&f.addClass("fade");var h="function"==typeof this.options.placement?this.options.placement.call(this,f[0],this.$element[0]):this.options.placement,i=/\s?auto?\s?/i,j=i.test(h);j&&(h=h.replace(i,"")||"top"),f.detach().css({top:0,left:0,display:"block"}).addClass(h).data("bs."+this.type,this),this.options.container?f.appendTo(this.options.container):f.insertAfter(this.$element),this.$element.trigger("inserted.bs."+this.type);var k=this.getPosition(),l=f[0].offsetWidth,m=f[0].offsetHeight;if(j){var n=h,o=this.getPosition(this.$viewport);h="bottom"==h&&k.bottom+m>o.bottom?"top":"top"==h&&k.top-mo.width?"left":"left"==h&&k.left-lg.top+g.height&&(e.top=g.top+g.height-i)}else{var j=b.left-f,k=b.left+f+c;jg.right&&(e.left=g.left+g.width-k)}return e},c.prototype.getTitle=function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||("function"==typeof c.title?c.title.call(b[0]):c.title)},c.prototype.getUID=function(a){do a+=~~(1e6*Math.random());while(document.getElementById(a));return a},c.prototype.tip=function(){if(!this.$tip&&(this.$tip=a(this.options.template),1!=this.$tip.length))throw new Error(this.type+" `template` option must consist of exactly 1 top-level element!");return this.$tip},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},c.prototype.enable=function(){this.enabled=!0},c.prototype.disable=function(){this.enabled=!1},c.prototype.toggleEnabled=function(){this.enabled=!this.enabled},c.prototype.toggle=function(b){var c=this;b&&(c=a(b.currentTarget).data("bs."+this.type),c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c))),b?(c.inState.click=!c.inState.click,c.isInStateTrue()?c.enter(c):c.leave(c)):c.tip().hasClass("in")?c.leave(c):c.enter(c)},c.prototype.destroy=function(){var a=this;clearTimeout(this.timeout),this.hide(function(){a.$element.off("."+a.type).removeData("bs."+a.type),a.$tip&&a.$tip.detach(),a.$tip=null,a.$arrow=null,a.$viewport=null,a.$element=null})};var d=a.fn.tooltip;a.fn.tooltip=b,a.fn.tooltip.Constructor=c,a.fn.tooltip.noConflict=function(){return a.fn.tooltip=d,this}}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof b&&b;!e&&/destroy|hide/.test(b)||(e||d.data("bs.popover",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.init("popover",a,b)};if(!a.fn.tooltip)throw new Error("Popover requires tooltip.js");c.VERSION="3.3.7",c.DEFAULTS=a.extend({},a.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:''}),c.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),c.prototype.constructor=c,c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content").children().detach().end()[this.options.html?"string"==typeof c?"html":"append":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},c.prototype.hasContent=function(){return this.getTitle()||this.getContent()},c.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")};var d=a.fn.popover;a.fn.popover=b,a.fn.popover.Constructor=c,a.fn.popover.noConflict=function(){return a.fn.popover=d,this}}(jQuery),+function(a){"use strict";function b(c,d){this.$body=a(document.body),this.$scrollElement=a(a(c).is(document.body)?window:c),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",a.proxy(this.process,this)),this.refresh(),this.process()}function c(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})}b.VERSION="3.3.7",b.DEFAULTS={offset:10},b.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},b.prototype.refresh=function(){var b=this,c="offset",d=0;this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight(),a.isWindow(this.$scrollElement[0])||(c="position",d=this.$scrollElement.scrollTop()),this.$body.find(this.selector).map(function(){var b=a(this),e=b.data("target")||b.attr("href"),f=/^#./.test(e)&&a(e);return f&&f.length&&f.is(":visible")&&[[f[c]().top+d,e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){b.offsets.push(this[0]),b.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.getScrollHeight(),d=this.options.offset+c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(this.scrollHeight!=c&&this.refresh(),b>=d)return g!=(a=f[f.length-1])&&this.activate(a);if(g&&b=e[a]&&(void 0===e[a+1]||b .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!1),b.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded",!0),h?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu").length&&b.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!0),e&&e()}var g=d.find("> .active"),h=e&&a.support.transition&&(g.length&&g.hasClass("fade")||!!d.find("> .fade").length);g.length&&h?g.one("bsTransitionEnd",f).emulateTransitionEnd(c.TRANSITION_DURATION):f(),g.removeClass("in")};var d=a.fn.tab;a.fn.tab=b,a.fn.tab.Constructor=c,a.fn.tab.noConflict=function(){return a.fn.tab=d,this};var e=function(c){c.preventDefault(),b.call(a(this),"show")};a(document).on("click.bs.tab.data-api",'[data-toggle="tab"]',e).on("click.bs.tab.data-api",'[data-toggle="pill"]',e)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof b&&b;e||d.data("bs.affix",e=new c(this,f)),"string"==typeof b&&e[b]()})}var c=function(b,d){this.options=a.extend({},c.DEFAULTS,d),this.$target=a(this.options.target).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(b),this.affixed=null,this.unpin=null,this.pinnedOffset=null,this.checkPosition()};c.VERSION="3.3.7",c.RESET="affix affix-top affix-bottom",c.DEFAULTS={offset:0,target:window},c.prototype.getState=function(a,b,c,d){var e=this.$target.scrollTop(),f=this.$element.offset(),g=this.$target.height();if(null!=c&&"top"==this.affixed)return e=a-d&&"bottom"},c.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(c.RESET).addClass("affix");var a=this.$target.scrollTop(),b=this.$element.offset();return this.pinnedOffset=b.top-a},c.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},c.prototype.checkPosition=function(){if(this.$element.is(":visible")){var b=this.$element.height(),d=this.options.offset,e=d.top,f=d.bottom,g=Math.max(a(document).height(),a(document.body).height());"object"!=typeof d&&(f=e=d),"function"==typeof e&&(e=d.top(this.$element)),"function"==typeof f&&(f=d.bottom(this.$element));var h=this.getState(g,b,e,f);if(this.affixed!=h){null!=this.unpin&&this.$element.css("top","");var i="affix"+(h?"-"+h:""),j=a.Event(i+".bs.affix");if(this.$element.trigger(j),j.isDefaultPrevented())return;this.affixed=h,this.unpin="bottom"==h?this.getPinnedOffset():null,this.$element.removeClass(c.RESET).addClass(i).trigger(i.replace("affix","affixed")+".bs.affix")}"bottom"==h&&this.$element.offset({top:g-b-f})}};var d=a.fn.affix;a.fn.affix=b,a.fn.affix.Constructor=c,a.fn.affix.noConflict=function(){return a.fn.affix=d,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var c=a(this),d=c.data();d.offset=d.offset||{},null!=d.offsetBottom&&(d.offset.bottom=d.offsetBottom),null!=d.offsetTop&&(d.offset.top=d.offsetTop),b.call(c,d)})})}(jQuery);
\ No newline at end of file
diff --git a/js/bootstrap-3.4.1.js b/js/bootstrap-3.4.1.js
new file mode 100644
index 0000000..eb0a8b4
--- /dev/null
+++ b/js/bootstrap-3.4.1.js
@@ -0,0 +1,6 @@
+/*!
+ * Bootstrap v3.4.1 (https://getbootstrap.com/)
+ * Copyright 2011-2019 Twitter, Inc.
+ * Licensed under the MIT license
+ */
+if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");!function(t){"use strict";var e=jQuery.fn.jquery.split(" ")[0].split(".");if(e[0]<2&&e[1]<9||1==e[0]&&9==e[1]&&e[2]<1||3this.$items.length-1||t<0))return this.sliding?this.$element.one("slid.bs.carousel",function(){e.to(t)}):i==t?this.pause().cycle():this.slide(idocument.documentElement.clientHeight;this.$element.css({paddingLeft:!this.bodyIsOverflowing&&t?this.scrollbarWidth:"",paddingRight:this.bodyIsOverflowing&&!t?this.scrollbarWidth:""})},s.prototype.resetAdjustments=function(){this.$element.css({paddingLeft:"",paddingRight:""})},s.prototype.checkScrollbar=function(){var t=window.innerWidth;if(!t){var e=document.documentElement.getBoundingClientRect();t=e.right-Math.abs(e.left)}this.bodyIsOverflowing=document.body.clientWidth',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0},sanitize:!0,sanitizeFn:null,whiteList:t},m.prototype.init=function(t,e,i){if(this.enabled=!0,this.type=t,this.$element=g(e),this.options=this.getOptions(i),this.$viewport=this.options.viewport&&g(document).find(g.isFunction(this.options.viewport)?this.options.viewport.call(this,this.$element):this.options.viewport.selector||this.options.viewport),this.inState={click:!1,hover:!1,focus:!1},this.$element[0]instanceof document.constructor&&!this.options.selector)throw new Error("`selector` option must be specified when initializing "+this.type+" on the window.document object!");for(var o=this.options.trigger.split(" "),n=o.length;n--;){var s=o[n];if("click"==s)this.$element.on("click."+this.type,this.options.selector,g.proxy(this.toggle,this));else if("manual"!=s){var a="hover"==s?"mouseenter":"focusin",r="hover"==s?"mouseleave":"focusout";this.$element.on(a+"."+this.type,this.options.selector,g.proxy(this.enter,this)),this.$element.on(r+"."+this.type,this.options.selector,g.proxy(this.leave,this))}}this.options.selector?this._options=g.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},m.prototype.getDefaults=function(){return m.DEFAULTS},m.prototype.getOptions=function(t){var e=this.$element.data();for(var i in e)e.hasOwnProperty(i)&&-1!==g.inArray(i,o)&&delete e[i];return(t=g.extend({},this.getDefaults(),e,t)).delay&&"number"==typeof t.delay&&(t.delay={show:t.delay,hide:t.delay}),t.sanitize&&(t.template=n(t.template,t.whiteList,t.sanitizeFn)),t},m.prototype.getDelegateOptions=function(){var i={},o=this.getDefaults();return this._options&&g.each(this._options,function(t,e){o[t]!=e&&(i[t]=e)}),i},m.prototype.enter=function(t){var e=t instanceof this.constructor?t:g(t.currentTarget).data("bs."+this.type);if(e||(e=new this.constructor(t.currentTarget,this.getDelegateOptions()),g(t.currentTarget).data("bs."+this.type,e)),t instanceof g.Event&&(e.inState["focusin"==t.type?"focus":"hover"]=!0),e.tip().hasClass("in")||"in"==e.hoverState)e.hoverState="in";else{if(clearTimeout(e.timeout),e.hoverState="in",!e.options.delay||!e.options.delay.show)return e.show();e.timeout=setTimeout(function(){"in"==e.hoverState&&e.show()},e.options.delay.show)}},m.prototype.isInStateTrue=function(){for(var t in this.inState)if(this.inState[t])return!0;return!1},m.prototype.leave=function(t){var e=t instanceof this.constructor?t:g(t.currentTarget).data("bs."+this.type);if(e||(e=new this.constructor(t.currentTarget,this.getDelegateOptions()),g(t.currentTarget).data("bs."+this.type,e)),t instanceof g.Event&&(e.inState["focusout"==t.type?"focus":"hover"]=!1),!e.isInStateTrue()){if(clearTimeout(e.timeout),e.hoverState="out",!e.options.delay||!e.options.delay.hide)return e.hide();e.timeout=setTimeout(function(){"out"==e.hoverState&&e.hide()},e.options.delay.hide)}},m.prototype.show=function(){var t=g.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(t);var e=g.contains(this.$element[0].ownerDocument.documentElement,this.$element[0]);if(t.isDefaultPrevented()||!e)return;var i=this,o=this.tip(),n=this.getUID(this.type);this.setContent(),o.attr("id",n),this.$element.attr("aria-describedby",n),this.options.animation&&o.addClass("fade");var s="function"==typeof this.options.placement?this.options.placement.call(this,o[0],this.$element[0]):this.options.placement,a=/\s?auto?\s?/i,r=a.test(s);r&&(s=s.replace(a,"")||"top"),o.detach().css({top:0,left:0,display:"block"}).addClass(s).data("bs."+this.type,this),this.options.container?o.appendTo(g(document).find(this.options.container)):o.insertAfter(this.$element),this.$element.trigger("inserted.bs."+this.type);var l=this.getPosition(),h=o[0].offsetWidth,d=o[0].offsetHeight;if(r){var p=s,c=this.getPosition(this.$viewport);s="bottom"==s&&l.bottom+d>c.bottom?"top":"top"==s&&l.top-dc.width?"left":"left"==s&&l.left-ha.top+a.height&&(n.top=a.top+a.height-l)}else{var h=e.left-s,d=e.left+s+i;ha.right&&(n.left=a.left+a.width-d)}return n},m.prototype.getTitle=function(){var t=this.$element,e=this.options;return t.attr("data-original-title")||("function"==typeof e.title?e.title.call(t[0]):e.title)},m.prototype.getUID=function(t){for(;t+=~~(1e6*Math.random()),document.getElementById(t););return t},m.prototype.tip=function(){if(!this.$tip&&(this.$tip=g(this.options.template),1!=this.$tip.length))throw new Error(this.type+" `template` option must consist of exactly 1 top-level element!");return this.$tip},m.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},m.prototype.enable=function(){this.enabled=!0},m.prototype.disable=function(){this.enabled=!1},m.prototype.toggleEnabled=function(){this.enabled=!this.enabled},m.prototype.toggle=function(t){var e=this;t&&((e=g(t.currentTarget).data("bs."+this.type))||(e=new this.constructor(t.currentTarget,this.getDelegateOptions()),g(t.currentTarget).data("bs."+this.type,e))),t?(e.inState.click=!e.inState.click,e.isInStateTrue()?e.enter(e):e.leave(e)):e.tip().hasClass("in")?e.leave(e):e.enter(e)},m.prototype.destroy=function(){var t=this;clearTimeout(this.timeout),this.hide(function(){t.$element.off("."+t.type).removeData("bs."+t.type),t.$tip&&t.$tip.detach(),t.$tip=null,t.$arrow=null,t.$viewport=null,t.$element=null})},m.prototype.sanitizeHtml=function(t){return n(t,this.options.whiteList,this.options.sanitizeFn)};var e=g.fn.tooltip;g.fn.tooltip=function i(o){return this.each(function(){var t=g(this),e=t.data("bs.tooltip"),i="object"==typeof o&&o;!e&&/destroy|hide/.test(o)||(e||t.data("bs.tooltip",e=new m(this,i)),"string"==typeof o&&e[o]())})},g.fn.tooltip.Constructor=m,g.fn.tooltip.noConflict=function(){return g.fn.tooltip=e,this}}(jQuery),function(n){"use strict";var s=function(t,e){this.init("popover",t,e)};if(!n.fn.tooltip)throw new Error("Popover requires tooltip.js");s.VERSION="3.4.1",s.DEFAULTS=n.extend({},n.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:''}),((s.prototype=n.extend({},n.fn.tooltip.Constructor.prototype)).constructor=s).prototype.getDefaults=function(){return s.DEFAULTS},s.prototype.setContent=function(){var t=this.tip(),e=this.getTitle(),i=this.getContent();if(this.options.html){var o=typeof i;this.options.sanitize&&(e=this.sanitizeHtml(e),"string"===o&&(i=this.sanitizeHtml(i))),t.find(".popover-title").html(e),t.find(".popover-content").children().detach().end()["string"===o?"html":"append"](i)}else t.find(".popover-title").text(e),t.find(".popover-content").children().detach().end().text(i);t.removeClass("fade top bottom left right in"),t.find(".popover-title").html()||t.find(".popover-title").hide()},s.prototype.hasContent=function(){return this.getTitle()||this.getContent()},s.prototype.getContent=function(){var t=this.$element,e=this.options;return t.attr("data-content")||("function"==typeof e.content?e.content.call(t[0]):e.content)},s.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")};var t=n.fn.popover;n.fn.popover=function e(o){return this.each(function(){var t=n(this),e=t.data("bs.popover"),i="object"==typeof o&&o;!e&&/destroy|hide/.test(o)||(e||t.data("bs.popover",e=new s(this,i)),"string"==typeof o&&e[o]())})},n.fn.popover.Constructor=s,n.fn.popover.noConflict=function(){return n.fn.popover=t,this}}(jQuery),function(s){"use strict";function n(t,e){this.$body=s(document.body),this.$scrollElement=s(t).is(document.body)?s(window):s(t),this.options=s.extend({},n.DEFAULTS,e),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",s.proxy(this.process,this)),this.refresh(),this.process()}function e(o){return this.each(function(){var t=s(this),e=t.data("bs.scrollspy"),i="object"==typeof o&&o;e||t.data("bs.scrollspy",e=new n(this,i)),"string"==typeof o&&e[o]()})}n.VERSION="3.4.1",n.DEFAULTS={offset:10},n.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},n.prototype.refresh=function(){var t=this,o="offset",n=0;this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight(),s.isWindow(this.$scrollElement[0])||(o="position",n=this.$scrollElement.scrollTop()),this.$body.find(this.selector).map(function(){var t=s(this),e=t.data("target")||t.attr("href"),i=/^#./.test(e)&&s(e);return i&&i.length&&i.is(":visible")&&[[i[o]().top+n,e]]||null}).sort(function(t,e){return t[0]-e[0]}).each(function(){t.offsets.push(this[0]),t.targets.push(this[1])})},n.prototype.process=function(){var t,e=this.$scrollElement.scrollTop()+this.options.offset,i=this.getScrollHeight(),o=this.options.offset+i-this.$scrollElement.height(),n=this.offsets,s=this.targets,a=this.activeTarget;if(this.scrollHeight!=i&&this.refresh(),o<=e)return a!=(t=s[s.length-1])&&this.activate(t);if(a&&e=n[t]&&(n[t+1]===undefined||e .active"),n=i&&r.support.transition&&(o.length&&o.hasClass("fade")||!!e.find("> .fade").length);function s(){o.removeClass("active").find("> .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!1),t.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded",!0),n?(t[0].offsetWidth,t.addClass("in")):t.removeClass("fade"),t.parent(".dropdown-menu").length&&t.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!0),i&&i()}o.length&&n?o.one("bsTransitionEnd",s).emulateTransitionEnd(a.TRANSITION_DURATION):s(),o.removeClass("in")};var t=r.fn.tab;r.fn.tab=e,r.fn.tab.Constructor=a,r.fn.tab.noConflict=function(){return r.fn.tab=t,this};var i=function(t){t.preventDefault(),e.call(r(this),"show")};r(document).on("click.bs.tab.data-api",'[data-toggle="tab"]',i).on("click.bs.tab.data-api",'[data-toggle="pill"]',i)}(jQuery),function(l){"use strict";var h=function(t,e){this.options=l.extend({},h.DEFAULTS,e);var i=this.options.target===h.DEFAULTS.target?l(this.options.target):l(document).find(this.options.target);this.$target=i.on("scroll.bs.affix.data-api",l.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",l.proxy(this.checkPositionWithEventLoop,this)),this.$element=l(t),this.affixed=null,this.unpin=null,this.pinnedOffset=null,this.checkPosition()};function i(o){return this.each(function(){var t=l(this),e=t.data("bs.affix"),i="object"==typeof o&&o;e||t.data("bs.affix",e=new h(this,i)),"string"==typeof o&&e[o]()})}h.VERSION="3.4.1",h.RESET="affix affix-top affix-bottom",h.DEFAULTS={offset:0,target:window},h.prototype.getState=function(t,e,i,o){var n=this.$target.scrollTop(),s=this.$element.offset(),a=this.$target.height();if(null!=i&&"top"==this.affixed)return n
-
+
From a0f8a667aeeffa8ac795f6ec2d5c55436d033d59 Mon Sep 17 00:00:00 2001
From: El RIDO
Date: Sun, 20 Feb 2022 21:07:04 +0100
Subject: [PATCH 256/478] deprecated functions, fix test partially
---
js/test/DiscussionViewer.js | 1 -
js/test/Editor.js | 5 ++---
js/test/TopNav.js | 8 ++++----
3 files changed, 6 insertions(+), 8 deletions(-)
diff --git a/js/test/DiscussionViewer.js b/js/test/DiscussionViewer.js
index 09697f3..f38596c 100644
--- a/js/test/DiscussionViewer.js
+++ b/js/test/DiscussionViewer.js
@@ -110,4 +110,3 @@ describe('DiscussionViewer', function () {
);
});
});
-
diff --git a/js/test/Editor.js b/js/test/Editor.js
index 3f86257..4115aa4 100644
--- a/js/test/Editor.js
+++ b/js/test/Editor.js
@@ -52,12 +52,12 @@ describe('Editor', function () {
!$.PrivateBin.Editor.isPreview() &&
!$('#message').hasClass('hidden')
);
- $('#messagepreview').click();
+ $('#messagepreview').trigger('click');
results.push(
$.PrivateBin.Editor.isPreview() &&
$('#message').hasClass('hidden')
);
- $('#messageedit').click();
+ $('#messageedit').trigger('click');
results.push(
!$.PrivateBin.Editor.isPreview() &&
!$('#message').hasClass('hidden')
@@ -68,4 +68,3 @@ describe('Editor', function () {
);
});
});
-
diff --git a/js/test/TopNav.js b/js/test/TopNav.js
index 2963964..12a8ccb 100644
--- a/js/test/TopNav.js
+++ b/js/test/TopNav.js
@@ -280,7 +280,8 @@ describe('TopNav', function () {
it(
'collapses the navigation when displayed on a small screen',
function () {
- var results = [];
+ var clean = jsdom(),
+ results = [];
$('body').html(
'