PluginProbe
InfiniteWP Client / trunk
InfiniteWP Client vtrunk
1.13.10 1.13.7 trunk 0.1.4 0.1.5 1.0.0 1.0.1 1.0.2 1.0.3 1.0.4 1.1.0 1.1.1 1.1.10 1.1.2 1.1.3 1.1.4 1.1.5 1.1.6 1.1.7 1.1.8 1.1.9 1.11.0 1.11.1 1.12.1 1.12.3 All 92 releases
iwp-client / backup / googledrive.php

googledrive.php in InfiniteWP Client trunk, at backup/googledrive.php

799 lines 30.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 if ( ! defined('ABSPATH') )
4 die();
5
6 if (!class_exists('IWP_MMB_UploadModule')) require_once($GLOBALS['iwp_mmb_plugin_dir'].'/backup/backup.upload.php');
7
8 class IWP_MMB_UploadModule_googledrive extends IWP_MMB_UploadModule {
9
10 private $service;
11 private $client;
12 private $ids_from_paths;
13 public $root_id;
14
15 public function get_supported_features() {
16 // This options format is handled via only accessing options via $this->get_options()
17 return array('multi_options');
18 }
19
20 public function get_default_options() {
21 # parentid is deprecated since April 2014; it should not be in the default options (its presence is used to detect an upgraded-from-previous-SDK situation). For the same reason, 'folder' is also unset; which enables us to know whether new-style settings have ever been set.
22 return array(
23 'clientid' => '',
24 'secret' => '',
25 'token' => '',
26 );
27 }
28
29 private function root_id() {
30 if (empty($this->root_id)) $this->root_id = $this->service->about->get()->getRootFolderId();
31 return $this->root_id;
32 }
33
34 public function id_from_path($path, $retry = true) {
35 global $iwp_backup_core;
36
37 try {
38 while ('/' == substr($path, 0, 1)) { $path = substr($path, 1); }
39
40 $cache_key = (empty($path)) ? '/' : $path;
41 if (!empty($this->ids_from_paths) && isset($this->ids_from_paths[$cache_key])) return $this->ids_from_paths[$cache_key];
42
43 $current_parent = $this->root_id();
44 $current_path = '/';
45
46 if (!empty($path)) {
47 foreach (explode('/', $path) as $element) {
48 $found = false;
49 $sub_items = $this->get_subitems($current_parent, 'dir', $element);
50
51 foreach ($sub_items as $item) {
52 try {
53 if ($item->getTitle() == $element) {
54 $found = true;
55 $current_path .= $element.'/';
56 $current_parent = $item->getId();
57 break;
58 }
59 } catch (Exception $e) {
60 $iwp_backup_core->log("Google Drive id_from_path: exception: ".$e->getMessage().' (line: '.$e->getLine().', file: '.$e->getFile().')');
61 }
62 }
63
64 if (!$found) {
65 $ref = new Google_Service_Drive_ParentReference;
66 $ref->setId($current_parent);
67 $dir = new Google_Service_Drive_DriveFile();
68 $dir->setMimeType('application/vnd.google-apps.folder');
69 $dir->setParents(array($ref));
70 $dir->setTitle($element);
71 $iwp_backup_core->log("Google Drive: creating path: ".$current_path.$element);
72 $dir = $this->service->files->insert(
73 $dir,
74 array('mimeType' => 'application/vnd.google-apps.folder')
75 );
76 $current_path .= $element.'/';
77 $current_parent = $dir->getId();
78 }
79 }
80 }
81
82 if (empty($this->ids_from_paths)) $this->ids_from_paths = array();
83 $this->ids_from_paths[$cache_key] = $current_parent;
84
85 return $current_parent;
86
87 } catch (Exception $e) {
88 $msg = $e->getMessage();
89 $iwp_backup_core->log("Google Drive id_from_path failure: exception (".get_class($e)."): ".$msg.' (line: '.$e->getLine().', file: '.$e->getFile().')');
90 if (is_a($e, 'Google_Service_Exception') && false !== strpos($msg, 'Invalid json in service response') && function_exists('mb_strpos')) {
91 // Aug 2015: saw a case where the gzip-encoding was not removed from the result
92 // https://stackoverflow.com/questions/10975775/how-to-determine-if-a-string-was-compressed
93 $is_gzip = false !== mb_strpos($msg , "\x1f" . "\x8b" . "\x08");
94 if ($is_gzip) $iwp_backup_core->log("Error: Response appears to be gzip-encoded still; something is broken in the client HTTP stack, and you should define IWP_GOOGLEDRIVE_DISABLEGZIP as true in your wp-config.php to overcome this.");
95 }
96 # One retry
97 return ($retry) ? $this->id_from_path($path, false) : false;
98 }
99 }
100
101 private function get_parent_id($opts) {
102 $filtered = apply_filters('IWP_googledrive_parent_id', false, $opts, $this->service, $this);
103 if (!empty($filtered)) return $filtered;
104 if (isset($opts['parentid'])) {
105 if (empty($opts['parentid'])) {
106 return $this->root_id();
107 } else {
108 $parent = (is_array($opts['parentid'])) ? $opts['parentid']['id'] : $opts['parentid'];
109 }
110 } else {
111 $path = 'infinitewp';
112 if (!empty($opts['gdrive_site_folder'])) {
113 $site_name = iwp_getSiteName();
114 $path = trailingslashit($path);
115 $path.= $site_name;
116 }
117 $parent = $this->id_from_path($path);
118 }
119 return (empty($parent)) ? $this->root_id() : $parent;
120 }
121
122 public function listfiles($match = 'backup_') {
123
124 $opts = $this->get_options();
125
126 if (empty($opts['secret']) || empty($opts['clientid']) || empty($opts['clientid'])) return new WP_Error('no_settings', sprintf(__('No %s settings were found', 'InfiniteWP'), __('Google Drive','InfiniteWP')));
127
128 $service = $this->bootstrap();
129 if (is_wp_error($service) || false == $service) return $service;
130
131 global $iwp_backup_core;
132
133 try {
134 $parent_id = $this->get_parent_id($opts);
135 $sub_items = $this->get_subitems($parent_id, 'file');
136 } catch (Exception $e) {
137 return new WP_Error(__('Google Drive list files: failed to access parent folder', 'InfiniteWP').": ".$e->getMessage().' (line: '.$e->getLine().', file: '.$e->getFile().')');
138 }
139
140 $results = array();
141
142 foreach ($sub_items as $item) {
143 $title = "(unknown)";
144 try {
145 $title = $item->getTitle();
146 if (0 === strpos($title, $match)) {
147 $results[] = array('name' => $title, 'size' => $item->getFileSize());
148 }
149 } catch (Exception $e) {
150 $iwp_backup_core->log("Google Drive delete: exception: ".$e->getMessage().' (line: '.$e->getLine().', file: '.$e->getFile().')');
151 $ret = false;
152 continue;
153 }
154 }
155
156 return $results;
157 }
158
159 // Get a Google account access token using the refresh token
160 private function access_token($refresh_token, $client_id, $client_secret) {
161
162 global $iwp_backup_core;
163 $iwp_backup_core->log("Google Drive: requesting access token: client_id=$client_id");
164
165 $query_body = array(
166 'refresh_token' => $refresh_token,
167 'client_id' => $client_id,
168 'client_secret' => $client_secret,
169 'grant_type' => 'refresh_token'
170 );
171
172 $result = wp_remote_post('https://accounts.google.com/o/oauth2/token',
173 array(
174 'timeout' => '20',
175 'method' => 'POST',
176 'body' => $query_body
177 )
178 );
179
180 if (is_wp_error($result)) {
181 $iwp_backup_core->log("Google Drive error when requesting access token");
182 foreach ($result->get_error_messages() as $msg) $iwp_backup_core->log("Error message: $msg");
183 return false;
184 } else {
185 $json_values = json_decode(wp_remote_retrieve_body($result), true);
186 if ( isset( $json_values['access_token'] ) ) {
187 $iwp_backup_core->log("Google Drive: successfully obtained access token");
188 return $json_values['access_token'];
189 } else {
190 $response = json_decode($result['body'],true);
191 if (!empty($response['error']) && 'deleted_client' == $response['error']) {
192 $iwp_backup_core->log(__('The client has been deleted from the Google Drive API console. Please create a new Google Drive project and reconnect with iwp_backup_core.','iwp_backup_core'), 'error');
193 }
194 $error_code = empty($response['error']) ? 'no error code' : $response['error'];
195 $iwp_backup_core->log("Google Drive error ($error_code) when requesting access token: response does not contain access_token. Response: ".(is_string($result['body']) ? str_replace("\n", '', $result['body']) : json_encode($result['body'])));
196 return false;
197 }
198 }
199 }
200
201 private function redirect_uri() {
202 return '';
203 }
204
205 // Acquire single-use authorization code from Google OAuth 2.0
206 public function gdrive_auth_request() {
207 $opts = $this->get_options();
208 // First, revoke any existing token, since Google doesn't appear to like issuing new ones
209 if (!empty($opts['token'])) $this->gdrive_auth_revoke();
210
211 // We use 'force' here for the approval_prompt, not 'auto', as that deals better with messy situations where the user authenticated, then changed settings
212
213 # We require access to all Google Drive files (not just ones created by this app - scope https://www.googleapis.com/auth/drive.file) - because we need to be able to re-scan storage for backups uploaded by other installs
214 $params = array(
215 'response_type' => 'code',
216 'client_id' => $opts['clientid'],
217 'redirect_uri' => $this->redirect_uri(),
218 'scope' => 'https://www.googleapis.com/auth/drive',
219 'state' => 'token',
220 'access_type' => 'offline',
221 'approval_prompt' => 'force'
222 );
223 if(headers_sent()) {
224 global $iwp_backup_core;
225 $iwp_backup_core->log(sprintf(__('The %s authentication could not go ahead, because something else on your site is breaking it. Try disabling your other plugins and switching to a default theme. (Specifically, you are looking for the component that sends output (most likely PHP warnings/errors) before the page begins. Turning off any debugging settings may also help).', ''), 'Google Drive'), 'error');
226 } else {
227 header('Location: https://accounts.google.com/o/oauth2/auth?'.http_build_query($params, '', '&'));
228 }
229 }
230
231
232 // This function just does the formalities, and off-loads the main work to upload_file
233 public function backup($backup_array) {
234
235 global $iwp_backup_core, $IWP_backup;
236
237 $service = $this->bootstrap();
238 if (false == $service || is_wp_error($service)) return $service;
239
240 $iwp_backup_dir = trailingslashit($iwp_backup_core->backups_dir_location());
241
242 $opts = $this->get_options();
243
244 try {
245 $parent_id = $this->get_parent_id($opts);
246 } catch (Exception $e) {
247 $iwp_backup_core->log("Google Drive upload: failed to access parent folder: ".$e->getMessage().' (line: '.$e->getLine().', file: '.$e->getFile().')');
248 $iwp_backup_core->log(sprintf(__('Failed to upload to %s','InfiniteWP'),__('Google Drive','InfiniteWP')).': '.__('failed to access parent folder', 'InfiniteWP').' ('.$e->getMessage().')', 'error');
249 return false;
250 }
251
252 foreach ($backup_array as $file) {
253
254 $available_quota = -1;
255
256 try {
257 $about = $service->about->get();
258 $quota_total = max($about->getQuotaBytesTotal(), 1);
259 $quota_used = $about->getQuotaBytesUsed();
260 $available_quota = $quota_total - $quota_used;
261 $message = "Google Drive quota usage: used=".round($quota_used/1048576,1)." MB, total=".round($quota_total/1048576,1)." MB, available=".round($available_quota/1048576,1)." MB";
262 $iwp_backup_core->log($message);
263 } catch (Exception $e) {
264 $iwp_backup_core->log("Google Drive quota usage: failed to obtain this information: ".$e->getMessage());
265 }
266
267 $file_path = $iwp_backup_dir.$file;
268 $file_name = basename($file_path);
269 $iwp_backup_core->log("$file_name: Attempting to upload to Google Drive (into folder id: $parent_id)");
270
271 $filesize = filesize($file_path);
272 $already_failed = false;
273 if ($available_quota != -1) {
274 if ($filesize > $available_quota) {
275 $already_failed = true;
276 $iwp_backup_core->log("File upload expected to fail: file ($file_name) size is $filesize b, whereas available quota is only $available_quota b");
277 $iwp_backup_core->log(sprintf(__("Account full: your %s account has only %d bytes left, but the file to be uploaded is %d bytes",'InfiniteWP'),__('Google Drive', 'InfiniteWP'), $available_quota, $filesize), +'error');
278 }
279 }
280
281 if (!$already_failed && $filesize > 10737418240) {
282 # 10GB
283 $iwp_backup_core->log("File upload expected to fail: file ($file_name) size is $filesize b (".round($filesize/1073741824, 4)." GB), whereas Google Drive's limit is 10GB (1073741824 bytes)");
284 $iwp_backup_core->log(sprintf(__("Upload expected to fail: the %s limit for any single file is %s, whereas this file is %s GB (%d bytes)",'InfiniteWP'),__('Google Drive', 'InfiniteWP'), '10GB (1073741824)', round($filesize/1073741824, 4), $filesize), 'warning');
285 }
286
287 try {
288 $timer_start = microtime(true);
289 if ($this->upload_file($file_path, $parent_id)) {
290 $iwp_backup_core->log('OK: Archive ' . $file_name . ' uploaded to Google Drive in ' . ( round(microtime(true) - $timer_start, 2) ) . ' seconds');
291 $iwp_backup_core->uploaded_file($file);
292 } else {
293 $iwp_backup_core->log("ERROR: $file_name: Failed to upload to Google Drive" );
294 $iwp_backup_core->log("$file_name: ".sprintf(__('Failed to upload to %s','InfiniteWP'),__('Google Drive','InfiniteWP')), 'error');
295 }
296 } catch (Exception $e) {
297 $msg = $e->getMessage();
298 $iwp_backup_core->log("ERROR: Google Drive upload error: ".$msg.' (line: '.$e->getLine().', file: '.$e->getFile().')');
299 if (false !== ($p = strpos($msg, 'The user has exceeded their Drive storage quota'))) {
300 $iwp_backup_core->log("$file_name: ".sprintf(__('Failed to upload to %s','InfiniteWP'),__('Google Drive','InfiniteWP')).': '.substr($msg, $p), 'error');
301 } else {
302 $iwp_backup_core->log("$file_name: ".sprintf(__('Failed to upload to %s','InfiniteWP'),__('Google Drive','InfiniteWP')), 'error');
303 }
304 $this->client->setDefer(false);
305 }
306 }
307
308 return null;
309 }
310
311 public function bootstrap($access_token = false) {
312
313 global $iwp_backup_core;
314
315 if (!empty($this->service) && is_object($this->service) && is_a($this->service, 'Google_Service_Drive')) return $this->service;
316
317 $opts = $this->get_options();
318
319 if (empty($access_token)) {
320 if (empty($opts['token']) || empty($opts['clientid']) || empty($opts['secret'])) {
321 $iwp_backup_core->log('Google Drive: this account is not authorised');
322 $iwp_backup_core->log('Google Drive: '.__('Account is not authorized.', 'InfiniteWP'), 'error', 'googledrivenotauthed');
323 return new WP_Error('not_authorized', __('Account is not authorized.', 'InfiniteWP'));
324 }
325 }
326
327 $spl = spl_autoload_functions();
328 if (is_array($spl)) {
329 if (in_array('wpbgdc_autoloader', $spl)) spl_autoload_unregister('wpbgdc_autoloader');
330 // http://www.wpdownloadmanager.com/download/google-drive-explorer/ - but also others, since this is the default function name used by the Google SDK
331 if (in_array('google_api_php_client_autoload', $spl)) spl_autoload_unregister('google_api_php_client_autoload');
332 }
333
334 if ((!class_exists('Google_Config') || !class_exists('Google_Client') || !class_exists('Google_Service_Drive') || !class_exists('Google_Http_Request')) && !function_exists('google_api_php_client_autoload_iwp')) {
335 require_once($GLOBALS['iwp_mmb_plugin_dir'].'/lib/Google2/autoload.php');
336 }
337
338 if (!class_exists('IWP_MMB_Google_Http_MediaFileUpload')) {
339 require_once($GLOBALS['iwp_mmb_plugin_dir'].'/lib/google-extensions.php');
340 }
341
342 $config = new Google_Config();
343 $config->setClassConfig('Google_IO_Abstract', 'request_timeout_seconds', 60);
344 # In our testing, $service->about->get() fails if gzip is not disabled when using the stream wrapper
345 if (!function_exists('curl_version') || !function_exists('curl_exec') || (defined('IWP_GOOGLEDRIVE_DISABLEGZIP') && IWP_GOOGLEDRIVE_DISABLEGZIP)) {
346 $config->setClassConfig('Google_Http_Request', 'disable_gzip', true);
347 }
348
349 $client = new Google_Client($config);
350 $client->setClientId($opts['clientid']);
351 $client->setClientSecret($opts['secret']);
352 // $client->setUseObjects(true);
353
354 if (empty($access_token)) {
355 $access_token = $this->access_token($opts['token'], $opts['clientid'], $opts['secret']);
356 }
357
358 // Do we have an access token?
359 if (empty($access_token) || is_wp_error($access_token)) {
360 $iwp_backup_core->log('ERROR: Have not yet obtained an access token from Google (has the user authorised?)');
361 $iwp_backup_core->log(__('Have not yet obtained an access token from Google - you need to authorise or re-authorise your connection to Google Drive.','InfiniteWP'), 'error');
362 return $access_token;
363 }
364
365 $client->setAccessToken(json_encode(array(
366 'access_token' => $access_token,
367 'refresh_token' => $opts['token']
368 )));
369
370 $io = $client->getIo();
371 $setopts = array();
372
373 if (is_a($io, 'Google_IO_Curl')) {
374 $setopts[CURLOPT_SSL_VERIFYPEER] = IWP_MMB_Backup_Options::get_iwp_backup_option('IWP_ssl_disableverify') ? false : true;
375 if (!IWP_MMB_Backup_Options::get_iwp_backup_option('IWP_ssl_useservercerts')) $setopts[CURLOPT_CAINFO] = $GLOBALS['iwp_mmb_plugin_dir'].'/lib/cacert.pem';
376 // Raise the timeout from the default of 15
377 $setopts[CURLOPT_TIMEOUT] = 60;
378 $setopts[CURLOPT_CONNECTTIMEOUT] = 15;
379 if (defined('IWP_IPV4_ONLY') && IWP_IPV4_ONLY) $setopts[CURLOPT_IPRESOLVE] = CURL_IPRESOLVE_V4;
380 } elseif (is_a($io, 'Google_IO_Stream')) {
381 $setopts['timeout'] = 60;
382 # We had to modify the SDK to support this
383 # https://wiki.php.net/rfc/tls-peer-verification - before PHP 5.6, there is no default CA file
384 if (!IWP_MMB_Backup_Options::get_iwp_backup_option('IWP_ssl_useservercerts') || (version_compare(PHP_VERSION, '5.6.0', '<'))) $setopts['cafile'] = $GLOBALS['iwp_mmb_plugin_dir'].'/lib/cacert.pem';
385 if (IWP_MMB_Backup_Options::get_iwp_backup_option('IWP_ssl_disableverify')) $setopts['disable_verify_peer'] = true;
386 }
387
388 $io->setOptions($setopts);
389
390 $service = new Google_Service_Drive($client);
391 $this->client = $client;
392 $this->service = $service;
393
394 try {
395 # Get the folder name, if not previously known (this is for the legacy situation where an id, not a name, was stored)
396 if (!empty($opts['parentid']) && (!is_array($opts['parentid']) || empty($opts['parentid']['name']))) {
397 $rootid = $this->root_id();
398 $title = '';
399 $parentid = is_array($opts['parentid']) ? $opts['parentid']['id'] : $opts['parentid'];
400 while ((!empty($parentid) && $parentid != $rootid)) {
401 $resource = $service->files->get($parentid);
402 $title = ($title) ? $resource->getTitle().'/'.$title : $resource->getTitle();
403 $parents = $resource->getParents();
404 if (is_array($parents) && count($parents)>0) {
405 $parent = array_shift($parents);
406 $parentid = is_a($parent, 'Google_Service_Drive_ParentReference') ? $parent->getId() : false;
407 } else {
408 $parentid = false;
409 }
410 }
411 if (!empty($title)) {
412 $opts['parentid'] = array(
413 'id' => (is_array($opts['parentid']) ? $opts['parentid']['id'] : $opts['parentid']),
414 'name' => $title
415 );
416 $this->set_options($opts, true);
417 }
418 }
419 } catch (Exception $e) {
420 $iwp_backup_core->log("Google Drive: failed to obtain name of parent folder: ".$e->getMessage().' (line: '.$e->getLine().', file: '.$e->getFile().')');
421 }
422
423 return $this->service;
424
425 }
426
427 // Returns array of Google_Service_Drive_DriveFile objects
428 private function get_subitems($parent_id, $type = 'any', $match = 'backup_') {
429 $q = '"'.$parent_id.'" in parents and trashed = false';
430 if ('dir' == $type) {
431 $q .= ' and mimeType = "application/vnd.google-apps.folder"';
432 } elseif ('file' == $type) {
433 $q .= ' and mimeType != "application/vnd.google-apps.folder"';
434 }
435 # We used to use 'contains' in both cases, but this exposed some bug that might be in the SDK or at the Google end - a result that matched for = was not returned with contains
436 if (!empty($match)) {
437 if ('backup_' == $match) {
438 $q .= " and title contains '$match'";
439 } else {
440 $q .= " and title contains '$match'";
441 }
442 }
443
444 $result = array();
445 $pageToken = NULL;
446
447 do {
448 try {
449 // Default for maxResults is 100
450 $parameters = array('q' => $q, 'maxResults' => 200);
451 if ($pageToken) {
452 $parameters['pageToken'] = $pageToken;
453 }
454 $files = $this->service->files->listFiles($parameters);
455
456 $result = array_merge($result, $files->getItems());
457 $pageToken = $files->getNextPageToken();
458 } catch (Exception $e) {
459 global $iwp_backup_core;
460 $iwp_backup_core->log("Google Drive: get_subitems: An error occurred (will not fetch further): " . $e->getMessage());
461 $pageToken = NULL;
462 }
463 } while ($pageToken);
464
465 return $result;
466 }
467
468 public function delete($files, $data=null, $sizeinfo = array()) {
469
470 if (is_string($files)) $files=array($files);
471
472 $service = $this->bootstrap();
473 if (is_wp_error($service) || false == $service) return $service;
474
475 $opts = $this->get_options();
476
477 global $iwp_backup_core;
478
479 try {
480 $parent_id = $this->get_parent_id($opts);
481 $iwp_getSiteName = iwp_getSiteName();
482 $sub_items = $this->get_subitems($parent_id, 'file', $files[0]);
483 } catch (Exception $e) {
484 $iwp_backup_core->log("Google Drive delete: failed to access parent folder: ".$e->getMessage().' (line: '.$e->getLine().', file: '.$e->getFile().')');
485 return false;
486 }
487
488 $ret = true;
489
490 foreach ($sub_items as $item) {
491 $title = "(unknown)";
492 try {
493 $title = $item->getTitle();
494 if (in_array($title, $files)) {
495 $service->files->delete($item->getId());
496 $iwp_backup_core->log("$title: Deletion successful");
497 if(($key = array_search($title, $files)) !== false) {
498 unset($files[$key]);
499 }
500 }
501 } catch (Exception $e) {
502 $iwp_backup_core->log("Google Drive delete: exception: ".$e->getMessage().' (line: '.$e->getLine().', file: '.$e->getFile().')');
503 $ret = false;
504 continue;
505 }
506 }
507
508 foreach ($files as $file) {
509 $iwp_backup_core->log("$file: Deletion failed: file was not found");
510 }
511
512 return $ret;
513
514 }
515
516 private function upload_file($file, $parent_id, $try_again = true) {
517
518 global $iwp_backup_core;
519 $opts = $this->get_options();
520 $basename = basename($file);
521
522 $service = $this->service;
523 $client = $this->client;
524
525 # See: https://github.com/google/google-api-php-client/blob/master/examples/fileupload.php (at time of writing, only shows how to upload in chunks, not how to resume)
526
527 $client->setDefer(true);
528
529 $local_size = filesize($file);
530
531 $gdfile = new Google_Service_Drive_DriveFile();
532 $gdfile->title = $basename;
533
534 $ref = new Google_Service_Drive_ParentReference;
535 $ref->setId($parent_id);
536 $gdfile->setParents(array($ref));
537
538 $size = 0;
539 $request = $service->files->insert($gdfile);
540
541 $chunk_bytes = 1048576;
542
543 $hash = md5($file);
544 $transkey = 'gdresume_'.$hash;
545 // This is unset upon completion, so if it is set then we are resuming
546 $possible_location = $iwp_backup_core->jobdata_get($transkey);
547
548 if (is_array($possible_location)) {
549
550 $headers = array( 'content-range' => "bytes */".$local_size);
551
552 $httpRequest = new Google_Http_Request(
553 $possible_location[0],
554 'PUT',
555 $headers,
556 ''
557 );
558 $response = $this->client->getIo()->makeRequest($httpRequest);
559 $can_resume = false;
560
561 $response_http_code = $response->getResponseHttpCode();
562
563 if ($response_http_code == 200 || $response_http_code == 201) {
564 $client->setDefer(false);
565 $iwp_backup_core->jobdata_delete($transkey);
566 $iwp_backup_core->log("$basename: upload appears to be already complete (HTTP code: $response_http_code)");
567 return true;
568 }
569
570 if (308 == $response_http_code) {
571 $range = $response->getResponseHeader('range');
572 if (!empty($range) && preg_match('/bytes=0-(\d+)$/', $range, $matches)) {
573 $can_resume = true;
574 $possible_location[1] = $matches[1]+1;
575 $iwp_backup_core->log("$basename: upload already began; attempting to resume from byte ".$matches[1]);
576 }
577 }
578 if (!$can_resume) {
579 $iwp_backup_core->log("$basename: upload already began; attempt to resume did not succeed (HTTP code: ".$response_http_code.")");
580 }
581 }
582
583 $media = new IWP_MMB_Google_Http_MediaFileUpload(
584 $client,
585 $request,
586 (('.zip' == substr($basename, -4, 4)) ? 'application/zip' : 'application/octet-stream'),
587 null,
588 true,
589 $chunk_bytes
590 );
591 $media->setFileSize($local_size);
592
593 if (!empty($possible_location)) {
594 // $media->resumeUri = $possible_location[0];
595 // $media->progress = $possible_location[1];
596 $media->IWP_setResumeUri($possible_location[0]);
597 $media->IWP_setProgress($possible_location[1]);
598 $size = $possible_location[1];
599 }
600 if ($size >= $local_size) return true;
601
602 $status = false;
603 if (false == ($handle = fopen($file, 'rb'))) {
604 $iwp_backup_core->log("Google Drive: failed to open file: $basename");
605 $iwp_backup_core->log("$basename: ".sprintf(__('%s Error: Failed to open local file', 'iwp_backup_core'),'Google Drive'), 'error');
606 return false;
607 }
608 if ($size > 0 && 0 != fseek($handle, $size)) {
609 $iwp_backup_core->log("Google Drive: failed to fseek file: $basename, $size");
610 $iwp_backup_core->log("$basename (fseek): ".sprintf(__('%s Error: Failed to open local file', 'InfiniteWP'), 'Google Drive'), 'error');
611 return false;
612 }
613
614 $pointer = $size;
615
616 try {
617 while (!$status && !feof($handle)) {
618 $chunk = fread($handle, $chunk_bytes);
619 # Error handling??
620 $pointer += strlen($chunk);
621 $status = $media->nextChunk($chunk);
622 $iwp_backup_core->jobdata_set($transkey, array($media->IWP_getResumeUri(), $media->getProgress()));
623 $iwp_backup_core->record_uploaded_chunk(round(100*$pointer/$local_size, 1), $media->getProgress(), $file);
624 }
625
626 } catch (Google_Service_Exception $e) {
627 $iwp_backup_core->log("ERROR: Google Drive upload error (".get_class($e)."): ".$e->getMessage().' (line: '.$e->getLine().', file: '.$e->getFile().')');
628 $client->setDefer(false);
629 fclose($handle);
630 $iwp_backup_core->jobdata_delete($transkey);
631 if (false == $try_again) throw($e);
632 # Reset this counter to prevent the something_useful_happened condition's possibility being sent into the far future and potentially missed
633 if ($iwp_backup_core->current_resumption > 9) $iwp_backup_core->jobdata_set('uploaded_lastreset', $iwp_backup_core->current_resumption);
634 return $this->upload_file($file, $parent_id, false);
635 }
636
637 // The final value of $status will be the data from the API for the object
638 // that has been uploaded.
639 $result = false;
640 if ($status != false) $result = $status;
641
642 fclose($handle);
643 $client->setDefer(false);
644 $iwp_backup_core->jobdata_delete($transkey);
645
646 return true;
647
648 }
649
650 public function download($file) {
651
652 global $iwp_backup_core;
653
654 $service = $this->bootstrap();
655 if (false == $service || is_wp_error($service)) return false;
656
657 global $iwp_backup_core;
658 $opts = $this->get_options();
659
660 try {
661 $parent_id = $this->get_parent_id($opts);
662 #$gdparent = $service->files->get($parent_id);
663 $site_name = iwp_getSiteName();
664 $sub_items = $this->get_subitems($parent_id, 'file', $file);
665 } catch (Exception $e) {
666 $iwp_backup_core->log("Google Drive delete: failed to access parent folder: ".$e->getMessage().' (line: '.$e->getLine().', file: '.$e->getFile().')');
667 return false;
668 }
669 $found = false;
670 foreach ($sub_items as $item) {
671 if ($found) continue;
672 $title = "(unknown)";
673 try {
674 $title = $item->getTitle();
675 if ($title == $file) {
676 $gdfile = $item;
677 $found = $item->getId();
678 $size = $item->getFileSize();
679 }
680 } catch (Exception $e) {
681 $iwp_backup_core->log("Google Drive download: exception: ".$e->getMessage().' (line: '.$e->getLine().', file: '.$e->getFile().')');
682 }
683 }
684
685 if (false === $found) {
686 $iwp_backup_core->log("Google Drive download: failed: file not found");
687 $iwp_backup_core->log("$file: ".sprintf(__("%s Error",'InfiniteWP'), 'Google Drive').": ".__('File not found', 'InfiniteWP'), 'error');
688 return false;
689 }
690
691 $download_to = $iwp_backup_core->backups_dir_location().'/'.$file;
692
693 $existing_size = (file_exists($download_to)) ? filesize($download_to) : 0;
694
695 if ($existing_size >= $size) {
696 $iwp_backup_core->log('Google Drive download: was already downloaded ('.filesize($download_to)."/$size bytes)");
697 return true;
698 }
699
700 # Chunk in units of 2MB
701 // $chunk_size = 2097152;
702 $chunk_size = 1024 * ( 5* 1024 );
703
704 try {
705 while ($existing_size < $size) {
706
707 if($iwp_backup_core->restore_loop_break()){
708 return 'partial';
709 }
710
711 $end = min($existing_size + $chunk_size, $size);
712
713 if ($existing_size > 0) {
714 $put_flag = FILE_APPEND;
715 $headers = array('Range' => 'bytes='.$existing_size.'-'.$end);
716 } else {
717 $put_flag = null;
718 $headers = ($end < $size) ? array('Range' => 'bytes=0-'.$end) : array();
719 }
720
721 $pstart = round(100*$existing_size/$size,1);
722 $pend = round(100*$end/$size,1);
723 $iwp_backup_core->log("Requesting byte range: $existing_size - $end ($pstart - $pend %)");
724
725 $request = $this->client->getAuth()->sign(new Google_Http_Request($gdfile->getDownloadUrl(), 'GET', $headers, null));
726 $http_request = $this->client->getIo()->makeRequest($request);
727 $http_response = $http_request->getResponseHttpCode();
728 if (200 == $http_response || 206 == $http_response) {
729 if ($put_flag == null) {
730 file_put_contents($download_to, $http_request->getResponseBody());
731 }else{
732 file_put_contents($download_to, $http_request->getResponseBody(), $put_flag);
733 }
734 } else {
735 $iwp_backup_core->log("Google Drive download: failed: unexpected HTTP response code: ".$http_response);
736 $iwp_backup_core->log(sprintf(__("%s download: failed: file not found", 'iwp_backup_core'), 'Google Drive'), 'error');
737 return false;
738 }
739
740 clearstatcache();
741 $new_size = filesize($download_to);
742 if ($new_size > $existing_size) {
743 $existing_size = $new_size;
744 } else {
745 throw new Exception('Failed to obtain any new data at size: '.$existing_size);
746 }
747 }
748 } catch (Exception $e) {
749 $iwp_backup_core->log("Google Drive download: exception: ".$e->getMessage().' (line: '.$e->getLine().', file: '.$e->getFile().')');
750 }
751
752 return true;
753 }
754
755 public function config_print() {
756 $opts = $this->get_options();
757
758 if (isset($opts['parentid'])) {
759 $parentid = (is_array($opts['parentid'])) ? $opts['parentid']['id'] : $opts['parentid'];
760 $showparent = (is_array($opts['parentid']) && !empty($opts['parentid']['name'])) ? $opts['parentid']['name'] : $parentid;
761 }
762 }
763
764 public function get_backup_file_size($file){
765 global $iwp_backup_core;
766
767 $service = $this->bootstrap();
768 if (false == $service || is_wp_error($service)) return false;
769
770 global $iwp_backup_core;
771 $opts = $this->get_options();
772 try {
773 $parent_id = $this->get_parent_id($opts);
774 #$gdparent = $service->files->get($parent_id);
775 $site_name = iwp_getSiteName();
776 $sub_items = $this->get_subitems($parent_id, 'file', $file);
777 } catch (Exception $e) {
778 $iwp_backup_core->log("Google Drive delete: failed to access parent folder: ".$e->getMessage().' (line: '.$e->getLine().', file: '.$e->getFile().')');
779 return false;
780 }
781 $found = false;
782 foreach ($sub_items as $item) {
783 if ($found) continue;
784 $title = "(unknown)";
785 try {
786 $title = $item->getTitle();
787 if ($title == $file) {
788 $gdfile = $item;
789 $found = $item->getId();
790 $size = $item->getFileSize();
791 return $size;
792 }
793 } catch (Exception $e) {
794 $iwp_backup_core->log("Google Drive download: exception: ".$e->getMessage().' (line: '.$e->getLine().', file: '.$e->getFile().')');
795 }
796 }
797 }
798 }
799