Plugin Directory

source: updraftplus/trunk/methods/dropbox.php

Last change on this file was 3474785, checked in by DavidAnderson, 3 weeks ago

Release version 1.26.2

File size: 46.4 KB
Line 
1<?php
2/**
3 * https://www.dropbox.com/developers/apply?cont=/developers/apps
4 */
5
6if (!defined('ABSPATH')) exit;
7if (!defined('UPDRAFTPLUS_DIR')) die('No direct access allowed');
8
9// Converted to multi-options (Feb 2017-) and previous options conversion removed: Yes
10
11if (!class_exists('UpdraftPlus_BackupModule')) updraft_try_include_file('methods/backup-module.php', 'require_once');
12
13// Fix a potential problem for users who had the short-lived 1.12.35-1.12.38 free versions (see: https://wordpress.org/support/topic/1-12-37-dropbox-auth-broken/page/2/#post-8981457)
14// Can be removed after a few months
15$potential_options = UpdraftPlus_Options::get_updraft_option('updraft_dropbox');
16if (is_array($potential_options) && isset($potential_options['version']) && isset($potential_options['settings']) && array() === $potential_options['settings']) {
17        // Wipe it, which will force its re-creation in proper format
18        UpdraftPlus_Options::delete_updraft_option('updraft_dropbox');
19}
20
21class UpdraftPlus_BackupModule_dropbox extends UpdraftPlus_BackupModule {
22
23        private $current_file_hash;
24
25        private $current_file_size;
26
27        private $uploaded_offset;
28
29        private $upload_tick;
30
31        /**
32         * This callback is called as upload progress is made
33         *
34         * @param Integer                $offset   - the byte offset
35         * @param String                 $uploadid - identifier for the upload in progress
36         * @param Boolean|String $fullpath - optional full path to the file being uploaded
37         */
38        public function chunked_callback($offset, $uploadid, $fullpath = false) {
39       
40                global $updraftplus;
41
42                $storage = $this->get_storage();
43
44                // Update upload ID
45                $this->jobdata_set('upload_id_'.$this->current_file_hash, $uploadid);
46                $this->jobdata_set('upload_offset_'.$this->current_file_hash, $offset);
47
48                $time_now = microtime(true);
49               
50                $time_since_last_tick = $time_now - $this->upload_tick;
51                $data_since_last_tick = $offset - $this->uploaded_offset;
52               
53                $this->upload_tick = $time_now;
54                $this->uploaded_offset = $offset;
55               
56                // Here we use job-wide data, because we don't expect wildly different performance for different Dropbox accounts
57                $chunk_size = $updraftplus->jobdata_get('dropbox_chunk_size', 1048576);
58                // Don't go beyond 10MB, or change the chunk size after the last segment
59                if ($chunk_size < 10485760 && $this->current_file_size > 0 && $offset < $this->current_file_size) {
60                        $job_run_time = $time_now - $updraftplus->job_time_ms;
61                        if ($time_since_last_tick < 10) {
62                                $upload_rate = $data_since_last_tick / max($time_since_last_tick, 1);
63                                $upload_secs = min(floor($job_run_time), 10);
64                                if ($job_run_time < 15) $upload_secs = max(6, $job_run_time*0.6);
65                                $new_chunk = (int) max(min($upload_secs * $upload_rate * 0.9, 10485760), 1048576);
66                                $new_chunk = $new_chunk - ($new_chunk % 524288);
67                                $chunk_size = $new_chunk;
68                                $storage->setChunkSize($chunk_size);
69                                $updraftplus->jobdata_set('dropbox_chunk_size', $chunk_size);
70                        }
71                }
72               
73                if ($this->current_file_size > 0) {
74                        $percent = round(100*($offset/$this->current_file_size), 1);
75                        $updraftplus->record_uploaded_chunk($percent, "$uploadid, $offset, ".round($chunk_size/1024, 1)." KB", $fullpath);
76                } else {
77                        $this->log("Chunked Upload: $offset bytes uploaded");
78                        // This act is done by record_uploaded_chunk, and helps prevent overlapping runs
79                        if ($fullpath) touch($fullpath);
80                }
81        }
82
83        /**
84         * Supported features
85         *
86         * @return Array
87         */
88        public function get_supported_features() {
89                // This options format is handled via only accessing options via $this->get_options()
90                return array('multi_options', 'config_templates', 'multi_storage', 'conditional_logic', 'manual_authentication');
91        }
92
93        /**
94         * Default options
95         *
96         * @return Array
97         */
98        public function get_default_options() {
99                return array(
100                        'appkey' => '',
101                        'secret' => '',
102                        'folder' => '',
103                        'tk_access_token' => '',
104                );
105        }
106
107        /**
108         * Check whether options have been set up by the user, or not
109         *
110         * @param Array $opts - the potential options
111         *
112         * @return Boolean
113         */
114        public function options_exist($opts) {
115                if (is_array($opts) && !empty($opts['tk_access_token'])) return true;
116                return false;
117        }
118
119        /**
120         * Acts as a WordPress options filter
121         *
122         * @param  Array $dropbox - An array of Dropbox options
123         * @return Array - the returned array can either be the set of updated Dropbox settings or a WordPress error array
124         */
125        public function options_filter($dropbox) {
126
127                // Get the current options (and possibly update them to the new format)
128                $opts = UpdraftPlus_Storage_Methods_Interface::update_remote_storage_options_format('dropbox');
129               
130                if (is_wp_error($opts)) {
131                        if ('recursion' !== $opts->get_error_code()) {
132                                $msg = "(".$opts->get_error_code()."): ".$opts->get_error_message();
133                                $this->log($msg);
134                                error_log("UpdraftPlus: $msg");
135                        }
136                        // The saved options had a problem; so, return the new ones
137                        return $dropbox;
138                }
139               
140                // If the input is not as expected, then return the current options
141                if (!is_array($dropbox)) return $opts;
142               
143                // Remove instances that no longer exist
144                foreach ($opts['settings'] as $instance_id => $storage_options) {
145                        if (!isset($dropbox['settings'][$instance_id])) unset($opts['settings'][$instance_id]);
146                }
147               
148                // Dropbox has a special case where the settings could be empty so we should check for this before
149                if (!empty($dropbox['settings'])) {
150               
151                        foreach ($dropbox['settings'] as $instance_id => $storage_options) {
152                                if (!empty($opts['settings'][$instance_id]['tk_access_token'])) {
153                               
154                                        $current_app_key = empty($opts['settings'][$instance_id]['appkey']) ? false : $opts['settings'][$instance_id]['appkey'];
155                                        $new_app_key = empty($storage_options['appkey']) ? false : $storage_options['appkey'];
156
157                                        // If a different app key is being used, then wipe the stored token as it cannot belong to the new app
158                                        if ($current_app_key !== $new_app_key) {
159                                                unset($opts['settings'][$instance_id]['tk_access_token']);
160                                                unset($opts['settings'][$instance_id]['ownername']);
161                                                unset($opts['settings'][$instance_id]['CSRF']);
162                                        }
163                               
164                                }
165
166                                // Now loop over the new options, and replace old options with them
167                                foreach ($storage_options as $key => $value) {
168                                        if (null === $value) {
169                                                unset($opts['settings'][$instance_id][$key]);
170                                        } else {
171                                                if (!isset($opts['settings'][$instance_id])) $opts['settings'][$instance_id] = array();
172                                                $opts['settings'][$instance_id][$key] = $value;
173                                        }
174                                }
175                               
176                                if (!empty($opts['settings'][$instance_id]['folder']) && preg_match('#^https?://(www.)dropbox\.com/home/Apps/UpdraftPlus(.Com)?([^/]*)/(.*)$#i', $opts['settings'][$instance_id]['folder'], $matches)) $opts['settings'][$instance_id]['folder'] = $matches[3];
177                               
178                                // check if we have the dummy nosave option and remove it so that it doesn't get saved
179                                if (isset($opts['settings'][$instance_id]['dummy-nosave'])) unset($opts['settings'][$instance_id]['dummy-nosave']);
180                        }
181                       
182                }
183               
184                return $opts;
185        }
186       
187        public function backup($backup_array) {
188
189                global $updraftplus;
190
191                $opts = $this->get_options();
192               
193                if (empty($opts['tk_access_token'])) {
194                        $this->log('You are not authenticated with Dropbox (1)');
195                        $this->log(__('You are not authenticated with Dropbox', 'updraftplus'), 'error');
196                        return false;
197                }
198               
199                // 28 September 2017: APIv1 is gone. We'll keep the variable to make life easier if there's ever an APIv3.
200                $use_api_ver = 2;
201               
202                if (empty($opts['tk_request_token'])) {
203                        $this->log("begin cloud upload (using API version $use_api_ver with OAuth v2 token)");
204                } else {
205                        $this->log("begin cloud upload (using API version $use_api_ver with OAuth v1 token)");
206                }
207
208                $chunk_size = $updraftplus->jobdata_get('dropbox_chunk_size', 1048576);
209
210                try {
211                        $dropbox = $this->bootstrap();
212                        if (false === $dropbox) throw new Exception(__('You are not authenticated with Dropbox', 'updraftplus'));
213                        $this->log("access gained; setting chunk size to: ".round($chunk_size/1024, 1)." KB");
214                        $dropbox->setChunkSize($chunk_size);
215                } catch (Exception $e) {
216                        $this->log('error when trying to gain access: '.$e->getMessage().' (line: '.$e->getLine().', file: '.$e->getFile().')');
217                        /* translators: %s: Error message */
218                        $this->log(sprintf(__('error: %s (see log file for more)', 'updraftplus'), $e->getMessage()), 'error');
219                        return false;
220                }
221
222                $updraft_dir = $updraftplus->backups_dir_location();
223
224                foreach ($backup_array as $file) {
225
226                        $available_quota = -1;
227
228                        // If we experience any failures collecting account info, then carry on anyway
229                        try {
230
231                                /*
232                                        Quota information is no longer provided with account information a new call to quotaInfo must be made to get this information.
233                                 */
234                                $quota_info = $dropbox->quotaInfo();
235
236                                // Access token expired try to refresh and then call quota info again
237                                if ("401" == $quota_info['code']) {
238                                        $this->log('HTTP code 401 (unauthorized) code returned from Dropbox; attempting to refresh access token');
239                                        $dropbox->refreshAccessToken();
240                                        $quota_info = $dropbox->quotaInfo();
241                                }
242
243                                if ("200" != $quota_info['code']) {
244                                        $message = "account/info did not return HTTP 200; returned: ". $quota_info['code'];
245                                } elseif (!isset($quota_info['body'])) {
246                                        $message = "account/info did not return the expected data";
247                                } else {
248                                        $body = $quota_info['body'];
249                                        if (isset($body->quota_info)) {
250                                                $quota_info = $body->quota_info;
251                                                $total_quota = $quota_info->quota;
252                                                $normal_quota = $quota_info->normal;
253                                                $shared_quota = $quota_info->shared;
254                                                $available_quota = $total_quota - ($normal_quota + $shared_quota);
255                                                $message = "quota usage: normal=".round($normal_quota/1048576, 1)." MB, shared=".round($shared_quota/1048576, 1)." MB, total=".round($total_quota/1048576, 1)." MB, available=".round($available_quota/1048576, 1)." MB";
256                                        } else {
257                                                $total_quota = max($body->allocation->allocated, 1);
258                                                $used = $body->used;
259                                                /* check here to see if the account is a team account and if so use the other used value
260                                                This will give us their total usage including their individual account and team account */
261                                                if (isset($body->allocation->used)) $used = $body->allocation->used;
262                                                $available_quota = $total_quota - $used;
263                                                $message = "quota usage: used=".round($used/1048576, 1)." MB, total=".round($total_quota/1048576, 1)." MB, available=".round($available_quota/1048576, 1)." MB";
264                                        }
265                                }
266                                $this->log($message);
267                        } catch (Exception $e) {
268                                $this->log("exception (".get_class($e).") occurred whilst getting account info: ".$e->getMessage());
269                                // $this->log(sprintf(__("%s error: %s", 'updraftplus'), 'Dropbox', $e->getMessage()).' ('.$e->getCode().')', 'warning', md5($e->getMessage()));
270                        }
271
272                        $file_success = 1;
273
274                        $hash = md5($file);
275                        $this->current_file_hash = $hash;
276
277                        $filesize = filesize($updraft_dir.'/'.$file);
278                        $this->current_file_size = $filesize;
279
280                        // Into KB
281                        $filesize = $filesize/1024;
282                        $microtime = microtime(true);
283
284                        if ('None' != ($upload_id = $this->jobdata_get('upload_id_'.$hash, 'None', 'updraf_dbid_'.$hash))) {
285                                // Resume
286                                $offset = $this->jobdata_get('upload_offset_'.$hash, 0, 'updraf_dbof_'.$hash);
287                                if ($offset) $this->log("This is a resumption: $offset bytes had already been uploaded");
288                        } else {
289                                $offset = 0;
290                                $upload_id = 'None';
291                        }
292
293                        // We don't actually abort now - there's no harm in letting it try and then fail
294                        if (-1 != $available_quota && $available_quota < ($filesize-$offset)) {
295                                $this->log("File upload expected to fail: file data remaining to upload ($file) size is ".($filesize-$offset)." b (overall file size; .".($filesize*1024)." b), whereas available quota is only $available_quota b");
296// $this->log(sprintf(__("Account full: your %s account has only %d bytes left, but the file to be uploaded has %d bytes remaining (total size: %d bytes)",'updraftplus'),'Dropbox', $available_quota, $filesize-$offset, $filesize), 'warning');
297                        }
298
299                        $ufile = apply_filters('updraftplus_dropbox_modpath', $file, $this);
300
301                        $this->log("Attempt to upload: $file to: $ufile");
302
303                        $this->upload_tick = microtime(true);
304                        $this->uploaded_offset = $offset;
305
306                        try {
307                                $response = $dropbox->chunkedUpload($updraft_dir.'/'.$file, '', $ufile, true, $offset, $upload_id, array($this, 'chunked_callback'));
308                                if (empty($response['code']) || "200" != $response['code']) {
309                                        $this->log('Unexpected HTTP code returned from Dropbox: '.$response['code']." (".serialize($response).")");
310                                        if ($response['code'] >= 400) {
311                                                if (401 == $response['code']) {
312                                                        $this->log('HTTP code 401 returned from Dropbox, refreshing access token');
313                                                        $dropbox->refreshAccessToken();
314                                                }
315                                                /* translators: %s: Destination path */
316                                                $this->log(sprintf(__('error: failed to upload file to %s (see log file for more)', 'updraftplus'), $file), 'error');
317                                                $file_success = 0;
318                                        } else {
319                                                $this->log(__('did not return the expected response - check your log file for more details', 'updraftplus'), 'warning');
320                                        }
321                                }
322                        } catch (Exception $e) {
323                                $this->log("chunked upload exception (".get_class($e)."): ".$e->getMessage().' (line: '.$e->getLine().', file: '.$e->getFile().')');
324                                if (preg_match("/Submitted input out of alignment: got \[(\d+)\] expected \[(\d+)\]/i", $e->getMessage(), $matches)) {
325                                        // Try the indicated offset
326                                        $we_tried = $matches[1];
327                                        $dropbox_wanted = (int) $matches[2];
328                                        $this->log("not yet aligned: tried=$we_tried, wanted=$dropbox_wanted; will attempt recovery");
329                                        $this->uploaded_offset = $dropbox_wanted;
330                                        $upload_id = $this->jobdata_get('upload_id_'.$hash, 'None', 'updraf_dbid_'.$hash);
331                                        try {
332                                                $dropbox->chunkedUpload($updraft_dir.'/'.$file, '', $ufile, true, $dropbox_wanted, $upload_id, array($this, 'chunked_callback'));
333                                        } catch (Exception $e) {
334                                                $msg = $e->getMessage();
335                                                if (preg_match('/Upload with upload_id .* already completed/', $msg)) {
336                                                        $this->log('returned an error, but apparently indicating previous success: '.$msg);
337                                                } else {
338                                                        $this->log($msg.' (line: '.$e->getLine().', file: '.$e->getFile().')');
339                                                        /* translators: %s: Destination path */
340                                                        $this->log(sprintf(__('failed to upload file to %s (see log file for more)', 'updraftplus'), $ufile), 'error');
341                                                        $file_success = 0;
342                                                        if (strpos($msg, 'select/poll returned error') !== false && $this->upload_tick > 0 && time() - $this->upload_tick > 800) {
343                                                                UpdraftPlus_Job_Scheduler::reschedule(60);
344                                                                $this->log("Select/poll returned after a long time: scheduling a resumption and terminating for now");
345                                                                UpdraftPlus_Job_Scheduler::record_still_alive();
346                                                                die;
347                                                        }
348                                                }
349                                        }
350                                } else {
351                                        $msg = $e->getMessage();
352                                        if (preg_match('/Upload with upload_id .* already completed/', $msg)) {
353                                                $this->log('returned an error, but apparently indicating previous success: '.$msg);
354                                        } else {
355                                                /* translators: %s: Destination path */
356                                                $this->log(sprintf(__('failed to upload file to %s (see log file for more)', 'updraftplus'), $ufile), 'error');
357                                                $file_success = 0;
358                                                if (strpos($msg, 'select/poll returned error') !== false && $this->upload_tick > 0 && time() - $this->upload_tick > 800) {
359                                                        UpdraftPlus_Job_Scheduler::reschedule(60);
360                                                        $this->log("Select/poll returned after a long time: scheduling a resumption and terminating for now");
361                                                        UpdraftPlus_Job_Scheduler::record_still_alive();
362                                                        die;
363                                                }
364                                        }
365                                }
366                        }
367                        if ($file_success) {
368                                $updraftplus->uploaded_file($file);
369                                $microtime_elapsed = microtime(true)-$microtime;
370                                $speedps = ($microtime_elapsed > 0) ? $filesize/$microtime_elapsed : 0;
371                                $speed = sprintf("%.2d", $filesize)." KB in ".sprintf("%.2d", $microtime_elapsed)."s (".sprintf("%.2d", $speedps)." KB/s)";
372                                $this->log("File upload success (".$file."): $speed");
373                                $this->jobdata_delete('upload_id_'.$hash, 'updraf_dbid_'.$hash);
374                                $this->jobdata_delete('upload_offset_'.$hash, 'updraf_dbof_'.$hash);
375                        }
376
377                }
378
379                return null;
380
381        }
382
383        /**
384         * This method gets a list of files from the remote storage that match the string passed in and returns an array of backups
385         *
386         * @param  String $match a substring to require (tested via strpos() !== false)
387         * @return Array
388         */
389        public function listfiles($match = 'backup_') {
390
391                $opts = $this->get_options();
392
393                if (empty($opts['tk_access_token'])) return new WP_Error('no_settings', __('No settings were found', 'updraftplus').' (dropbox)');
394
395                try {
396                        $dropbox = $this->bootstrap();
397                } catch (Exception $e) {
398                        $this->log('access error: '.$e->getMessage().' (line: '.$e->getLine().', file: '.$e->getFile().')');
399                        return new WP_Error('access_error', $e->getMessage());
400                }
401
402                $searchpath = '/'.untrailingslashit(apply_filters('updraftplus_dropbox_modpath', '', $this));
403
404                try {
405                        /* Some users could have a large amount of backups, the max search is 1000 entries we should continue to search until there are no more entries to bring back. */
406                        $cursor = '';
407                        $matches = array();
408
409                        while (true) {
410                                $search = $dropbox->search($match, $searchpath, 1000, $cursor);
411                                /* translators: 1: Service name, 2: HTTP response code */
412                                if (empty($search['code']) || 200 != $search['code']) return new WP_Error('response_error', sprintf(__('%1$s returned an unexpected HTTP response: %2$s', 'updraftplus'), 'Dropbox', $search['code']), $search['body']);
413
414                                if (empty($search['body'])) return array();
415
416                                if (isset($search['body']->matches) && is_array($search['body']->matches)) {
417                                        $matches = array_merge($matches, $search['body']->matches);
418                                } elseif (is_array($search['body'])) {
419                                        $matches = $search['body'];
420                                } else {
421                                        break;
422                                }
423
424                                if (isset($search['body']->has_more) && true == $search['body']->has_more && isset($search['body']->cursor)) {
425                                        $cursor = $search['body']->cursor;
426                                } else {
427                                        break;
428                                }
429                        }
430
431                } catch (Exception $e) {
432                        $this->log($e->getMessage().' (line: '.$e->getLine().', file: '.$e->getFile().')');
433                        // The most likely cause of a search_error is specifying a non-existent path, which should just result in an empty result set.
434// return new WP_Error('search_error', $e->getMessage());
435                        return array();
436                }
437
438                $results = array();
439
440                foreach ($matches as $item) {
441                        $item = $item->metadata;
442                        if (!is_object($item)) continue;
443                        if (isset($item->metadata)) $item = $item->metadata; // 2/files/search_v2 has a slightly different output structure compared to 2/files/search model
444
445                        if ((!isset($item->size) || $item->size > 0) && 'folder' != $item->{'.tag'} && !empty($item->path_display) && 0 === strpos($item->path_display, $searchpath)) {
446
447                                $path = substr($item->path_display, strlen($searchpath));
448                                if ('/' == substr($path, 0, 1)) $path = substr($path, 1);
449
450                                // Ones in subfolders are not wanted
451                                if (false !== strpos($path, '/')) continue;
452
453                                $result = array('name' => $path);
454                                if (!empty($item->size)) $result['size'] = $item->size;
455
456                                $results[] = $result;
457                        }
458                }
459
460                return $results;
461        }
462
463        /**
464         * Identification of Dropbox app
465         *
466         * @return Array
467         */
468        private function defaults() {
469                return apply_filters('updraftplus_dropbox_defaults', array('Z3Q3ZmkwbnplNHA0Zzlx', 'bTY0bm9iNmY4eWhjODRt'));
470        }
471
472        /**
473         * Delete files from the service using the Dropbox API
474         *
475         * @param Array $files    - array of filenames to delete
476         * @param Array $data     - unused here
477         * @param Array $sizeinfo - unused here
478         * @return Boolean|String - either a boolean true or an error code string
479         */
480        public function delete($files, $data = null, $sizeinfo = array()) {// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- $data and $sizeinfo unused
481
482                if (is_string($files)) $files = array($files);
483
484                $opts = $this->get_options();
485
486                if (empty($opts['tk_access_token'])) {
487                        $this->log('You are not authenticated with Dropbox (3)');
488                        /* translators: %s: Service name */
489                        $this->log(sprintf(__('You are not authenticated with %s (whilst deleting)', 'updraftplus'), 'Dropbox'), 'warning');
490                        return 'authentication_fail';
491                }
492
493                try {
494                        $dropbox = $this->bootstrap();
495                } catch (Exception $e) {
496                        $this->log($e->getMessage().' (line: '.$e->getLine().', file: '.$e->getFile().')');
497                        /* translators: %s: Service name */
498                        $this->log(sprintf(__('Failed to access %s when deleting (see log file for more)', 'updraftplus'), 'Dropbox'), 'warning');
499                        return 'service_unavailable';
500                }
501                if (false === $dropbox) return false;
502
503                $any_failures = false;
504               
505                foreach ($files as $file) {
506                        $ufile = apply_filters('updraftplus_dropbox_modpath', $file, $this);
507                        $this->log("request deletion: $ufile");
508
509                        try {
510                                $dropbox->delete($ufile);
511                                $file_success = 1;
512                        } catch (Exception $e) {
513                                $this->log($e->getMessage().' (line: '.$e->getLine().', file: '.$e->getFile().')');
514                        }
515
516                        if (isset($file_success)) {
517                                $this->log('deletion succeeded');
518                        } else {
519                                $this->log('deletion failed');
520                                $any_failures = true;
521                        }
522                }
523               
524                return $any_failures ? 'file_delete_error' : true;
525
526        }
527
528        public function download($file) {
529
530                global $updraftplus;
531
532                $opts = $this->get_options();
533
534                if (empty($opts['tk_access_token'])) {
535                        $this->log('You are not authenticated with Dropbox (4)');
536                        /* translators: %s: Service name */
537                        $this->log(sprintf(__('You are not authenticated with %s', 'updraftplus'), 'Dropbox'), 'error');
538                        return false;
539                }
540
541                try {
542                        $dropbox = $this->bootstrap();
543                } catch (Exception $e) {
544                        $this->log($e->getMessage().' (line: '.$e->getLine().', file: '.$e->getFile().')');
545                        $this->log($e->getMessage().' (line: '.$e->getLine().', file: '.$e->getFile().')', 'error');
546                        return false;
547                }
548                if (false === $dropbox) return false;
549               
550                $remote_files = $this->listfiles($file);
551               
552                foreach ($remote_files as $file_info) {
553                        if ($file_info['name'] == $file) {
554                                return $updraftplus->chunked_download($file, $this, $file_info['size'], apply_filters('updraftplus_dropbox_downloads_manually_break_up', false), null, 2*1048576);
555                        }
556                }
557
558                $this->log("$file: file not found in listing of remote directory");
559               
560                return false;
561        }
562
563        /**
564         * Callback used by by chunked downloading API
565         *
566         * @param String   $file        - the file (basename) to be downloaded
567         * @param Array    $headers - supplied headers
568         * @param Mixed    $data    - pass-back from our call to the API (which we don't use)
569         * @param resource $fh      - the local file handle
570         *
571         * @return String - the data downloaded
572         */
573        public function chunked_download($file, $headers, $data, $fh) {// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found -- Unused parameter is present because the caller from UpdraftPlus class uses 4 arguments.
574
575                $opts = $this->get_options();// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- filter use
576                $storage = $this->get_storage();
577
578                $try_the_other_one = false;// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- filter use
579
580                $ufile = apply_filters('updraftplus_dropbox_modpath', $file, $this);
581
582                $options = array();
583               
584                if (!empty($headers)) $options['headers'] = $headers;
585
586                try {
587                        $get = $storage->download($ufile, $fh, $options);
588                } catch (Exception $e) {
589                        $this->log($e);
590                        $this->log($e->getMessage(), 'error');
591                        $get = false;
592                }
593               
594                return $get;
595        }
596
597        /**
598         * 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
599         * NOTE: Please sanitise all strings that are required to be shown as HTML content on the frontend side (i.e. wp_kses())
600         *
601         * @return Array an associative array keyed by names that describe themselves as they are
602         */
603        public function get_template_properties() {
604                global $updraftplus, $updraftplus_admin;
605                $partial_templates = $this->get_partial_templates();
606                $properties = array(
607                        'storage_image_url' => UPDRAFTPLUS_URL.'/images/dropbox-logo.png',
608                        /* translators: %s: Service name */
609                        'storage_image_description' => sprintf(__('%s logo', 'updraftplus'), 'Dropbox'),
610                        'curl_existence_label' => wp_kses($updraftplus_admin->curl_check($updraftplus->backup_methods[$this->get_id()], true, $this->get_id().' hidden-in-updraftcentral', false), $this->allowed_html_for_content_sanitisation()),
611                        'app_authorisation_policy_label' => wp_kses(sprintf(
612                                /* translators: 1: Link to privacy policy, 2: Service name */
613                                __('Please read %1$s for use of our %2$s authorization app (none of your backup data is sent to us).', 'updraftplus'),
614                                '<a target="_blank" href="https://teamupdraft.com/documentation/updraftplus/topics/cloud-storage/dropbox/faqs/what-is-your-privacy-policy-for-the-use-of-your-dropbox-app?utm_source=udp-plugin&utm_medium=referral&utm_campaign=paac&utm_content=privacy-policy&utm_creative_format=text">'.
615                                __('this privacy policy', 'updraftplus').'</a>',
616                                'Dropbox'
617                        ), $this->allowed_html_for_content_sanitisation()),
618                        'sub_folders_instruction_label1' => __('Need to use sub-folders?', 'updraftplus'),
619                        /* translators: %s: Backup folder path */
620                        'sub_folders_instruction_label2' => sprintf(__('Backups are saved in %s.', 'updraftplus'), 'apps/UpdraftPlus'),
621                        /* translators: 1: Opening link tag, 2: Closing link tag */
622                        'sub_folders_instruction_label3' => wp_kses(sprintf(__('If you backup several sites into the same Dropbox and want to organize with sub-folders, then %1$scheck out Premium%2$s', 'updraftplus'), '<a href="'.esc_url($updraftplus->get_url('premium_dropbox')).'" target="_blank">', '</a>'), $this->allowed_html_for_content_sanitisation()),
623                        /* translators: %s: Service name */
624                        'input_authenticate_with_label' => sprintf(
625                                __('Authenticate with %s', 'updraftplus'),
626                        __('Dropbox', 'updraftplus')),
627                        'already_authenticated_label' => __('(You are already authenticated).', 'updraftplus'),
628                        /* translators: %s: Service name */
629                        'authentication_link_text' => wp_kses(sprintf(__("<strong>After</strong> you have saved your settings (by clicking 'Save Changes' below), then come back here and follow this link to complete authentication with %s.", 'updraftplus'), $updraftplus->backup_methods[$this->get_id()]), $this->allowed_html_for_content_sanitisation()),
630                        /* translators: %s: Service name */
631                        'deauthentication_link_text' => sprintf(__("Follow this link to remove these settings for %s.", 'updraftplus'), $updraftplus->backup_methods[$this->get_id()]),
632                        'authentication_label' => __('Ensure you are logged into the correct account before continuing.', 'updraftplus'),
633                        'authorised_redirect_uri_label' => __('You must add the following as the authorised redirect URI in your Dropbox console (under "API Settings") when asked', 'updraftplus'),
634                        'input_app_key_label' => __('Your Dropbox App Key', 'updraftplus'),
635                        'input_app_secret_label' => __('Your Dropbox App Secret', 'updraftplus'),
636                        'partial_templates_contain_input_element' => isset($partial_templates['dropbox_additional_configuration_top']) && preg_match('/<input(?:>|[^>]+>)/i', $partial_templates['dropbox_additional_configuration_top']),
637                        'deauthentication_nonce' => wp_create_nonce($this->get_id().'_deauth_nonce'),
638                );
639                return wp_parse_args(apply_filters('updraft_'.$this->get_id().'_template_properties', array()), wp_parse_args($properties, $this->get_persistent_variables_and_methods()));
640        }
641
642        /**
643         * Get the pre configuration template
644         *
645         * @return String - the template
646         */
647        public function get_pre_configuration_template() {
648                ?>
649                        <tr class="{{get_template_css_classes false}} {{method_id}}_pre_config_container">
650                                <td colspan="2">
651                                        <img alt="{{storage_image_description}}" src="{{storage_image_url}}">
652                                        <br>
653                                        <p>
654                                        {{{curl_existence_label}}}
655                                        </p>
656                                        <p>
657                                        {{{app_authorisation_policy_label}}}
658                                        </p>
659                                </td>
660                        </tr>
661                <?php
662        }
663
664        /**
665         * Get remote storage partial templates, the partial template is recognised by its name. To find out a name of partial template, look for the partial call syntax in the template, it's enclosed by double curly braces (i.e. {{> partial_template_name }})
666         *
667         * @return Array an associative array keyed by name of the partial templates
668         */
669        public function get_partial_templates() {
670                $partial_templates = array();
671                ob_start();
672                ?>
673                        <tr class="{{get_template_css_classes true}}">
674                                <td colspan="2"><strong>{{sub_folders_instruction_label1}}</strong> {{sub_folders_instruction_label2}} {{{sub_folders_instruction_label3}}}</td>
675                        </tr>
676                <?php
677                $partial_templates['dropbox_additional_configuration_top'] = ob_get_clean();
678                return wp_parse_args(apply_filters('updraft_'.$this->get_id().'_partial_templates', $partial_templates), parent::get_partial_templates());
679        }
680
681        /**
682         * Get the configuration template
683         *
684         * @return String - the template, ready for substitutions to be carried out
685         */
686        public function get_configuration_template() {
687                ob_start();
688                ?>
689                        {{#> dropbox_additional_configuration_top}}
690                        {{/dropbox_additional_configuration_top}}
691                        <tr class="{{get_template_css_classes true}}">
692                                <th>{{input_authenticate_with_label}}:</th>
693                                <td>
694                                        {{#if is_authenticated}}
695                                        <p>
696                                                <strong>{{already_authenticated_label}}</strong>
697                                                <a class="updraft_deauthlink" href="{{admin_page_url}}?action=updraftmethod-{{method_id}}-auth&page=updraftplus&updraftplus_{{method_id}}auth=deauth&nonce={{deauthentication_nonce}}&updraftplus_instance={{instance_id}}" data-instance_id="{{instance_id}}" data-remote_method="{{method_id}}">{{deauthentication_link_text}}</a>
698                                        </p>
699                                        {{/if}}
700                                        {{#if ownername_sentence}}
701                                                <br/>
702                                                {{ownername_sentence}}
703                                        {{/if}}
704                                        <p>
705                                        {{authentication_label}} <a class="updraft_authlink" href="{{admin_page_url}}?&action=updraftmethod-{{method_id}}-auth&page=updraftplus&updraftplus_{{method_id}}auth=doit&nonce={{storage_auth_nonce}}&updraftplus_instance={{instance_id}}" data-instance_id="{{instance_id}}" data-remote_method="{{method_id}}">{{{authentication_link_text}}}</a>
706                                        </p>
707                                </td>
708                        </tr>
709                        {{!-- Legacy: only show this next setting to old users who had a setting stored --}}
710                        {{#if old_user_settings}}
711                                <tr class="{{get_template_css_classes true}}">
712                                        <th></th>
713                                        <td>
714                                                <p>{{authorised_redirect_uri_label}}: <kbd>{{admin_page_url}}?page=updraftplus&action=updraftmethod-dropbox-auth</kbd></p>
715                                        </td>
716                                </tr>
717                                <tr class="{{get_template_css_classes true}}">
718                                        <th>{{input_app_key_label}}:</th>
719                                        <td><input type="text" autocomplete="off" style="width:332px" id="{{get_template_input_attribute_value "id" "appkey"}}" name="{{get_template_input_attribute_value "name" "appkey"}}" value="{{appkey}}" /></td>
720                                </tr>
721                                <tr class="{{get_template_css_classes true}}">
722                                        <th>{{input_app_secret_label}}:</th>
723                                        <td><input type="text" style="width:332px" id="{{get_template_input_attribute_value "id" "secret"}}" name="{{get_template_input_attribute_value "name" "secret"}}" value="{{secret}}" /></td>
724                                </tr>
725                        {{else}}
726                                {{#unless partial_templates_contain_input_element}}
727                                {{!-- We need to make sure that it is not the case that the module has no settings whatsoever - this can result in the module being effectively invisible. --}}
728                                <input type="hidden" id="{{get_template_input_attribute_value "id" "dummy-nosave"}}" name="{{get_template_input_attribute_value "name" "dummy-nosave"}}" value="0">
729                                {{/unless}}
730                        {{/if}}
731                <?php
732                return ob_get_clean();
733        }
734
735        /**
736         * Generates ownername with email
737         *
738         * @param array $opts
739         * @return String - Ownername with email
740         */
741        private function generate_ownername_with_email($opts) {
742                $ownername_with_email = '';
743               
744                if (!empty($opts['ownername'])) {
745                        $ownername_with_email = $opts['ownername'];
746                }
747               
748                if (!empty($opts['email'])) {
749                        if (!empty($ownername_with_email)) {
750                                $ownername_with_email = $ownername_with_email.' ('.$opts['email'].')';
751                        } else {
752                                $ownername_with_email = $opts['email'];
753                        }
754                }
755
756           return $ownername_with_email;
757        }
758       
759        /**
760         * Modifies handerbar template options
761         *
762         * @param array $opts
763         * @return Array - Modified handerbar template options
764         */
765        public function transform_options_for_template($opts) {
766                if (!empty($opts['tk_access_token'])) {
767                        $opts['ownername'] = empty($opts['ownername']) ? '' : $opts['ownername'];
768                        $opts['email'] = empty($opts['email']) ? '' : $opts['email'];
769                        $ownername_with_email = $this->generate_ownername_with_email($opts);
770                       
771                        if ($ownername_with_email) {
772                                /* translators: %s: Account holder's name with email */
773                                $opts['ownername_sentence']     = sprintf(__("Account holder's name: %s.", 'updraftplus'), $ownername_with_email).' ';
774                        }
775                        $opts['is_authenticated'] = true;
776                }
777                $opts['old_user_settings'] = (!empty($opts['appkey']) || (defined('UPDRAFTPLUS_CUSTOM_DROPBOX_APP') && UPDRAFTPLUS_CUSTOM_DROPBOX_APP));
778                if ($opts['old_user_settings']) {
779                        $opts['appkey'] = empty($opts['appkey']) ? '' : $opts['appkey'];
780                        $opts['secret'] = empty($opts['secret']) ? '' : $opts['secret'];
781                }
782                $opts = apply_filters("updraftplus_options_dropbox_options", $opts);
783                return $opts;
784        }
785       
786        /**
787         * Gives settings keys which values should not passed to handlebarsjs context.
788         * The settings stored in UD in the database sometimes also include internal information that it would be best not to send to the front-end (so that it can't be stolen by a man-in-the-middle attacker)
789         *
790         * @return Array - Settings array keys which should be filtered
791         */
792        public function filter_frontend_settings_keys() {
793                return array(
794                        'CSRF',
795                        'code',
796                        'ownername',
797                        'tk_access_token',
798                );
799        }
800
801        /**
802         * Over-rides the parent to allow this method to output extra information about using the correct account for OAuth authentication
803         *
804         * @return [boolean] - return false so that no extra information is output
805         */
806        public function output_account_warning() {
807                return true;
808        }
809
810        /**
811         * Handles various URL actions, as indicated by the updraftplus_dropboxauth URL parameter
812         *
813         * @return null
814         */
815        public function action_auth() {
816                if (isset($_GET['updraftplus_dropboxauth'])) {
817                        if ('doit' == $_GET['updraftplus_dropboxauth']) {
818                                $this->action_authenticate_storage();
819                                return;
820                        } elseif ('deauth' == $_GET['updraftplus_dropboxauth']) {
821                                $this->action_deauthenticate_storage();
822                                return;
823                        }
824                } elseif (isset($_REQUEST['state'])) {
825                        if (isset($_SERVER['REQUEST_METHOD']) && 'POST' == $_SERVER['REQUEST_METHOD']) {
826                                $auth_state = UpdraftPlus_Manipulation_Functions::fetch_superglobal('post', 'state', false, null, null, '');
827                                $raw_state = urldecode($auth_state);
828                                $raw_code = urldecode(UpdraftPlus_Manipulation_Functions::fetch_superglobal('post', 'code', false, null, null, ''));
829                        } else {
830                                $raw_state = UpdraftPlus_Manipulation_Functions::fetch_superglobal('get', 'state', false, null, null, '');
831                                $raw_code = UpdraftPlus_Manipulation_Functions::fetch_superglobal('get', 'code', false, null, null, '');
832                        }
833
834                        if (!empty($raw_code)) $this->do_complete_authentication($raw_state, $raw_code);
835                }
836                try {
837                        $this->auth_request();
838                } catch (Exception $e) {
839                        $this->log(
840                                sprintf(
841                                        /* translators: 1: Service name, 2: Error message */
842                                        __('%1$s error: %2$s', 'updraftplus'),
843                                        /* translators: %s: Service name */
844                                        sprintf(__('%s authentication', 'updraftplus'), 'Dropbox'),
845                                        $e->getMessage()
846                                ),
847                        'error');
848                }
849        }
850
851        /**
852         * This function will complete the oAuth flow, if return_instead_of_echo is true then add the action to display the authed admin notice, otherwise echo this notice to page.
853         *
854         * @param string  $raw_state              - the state
855         * @param string  $raw_code               - the oauth code
856         * @param boolean $return_instead_of_echo - a boolean to indicate if we should return the result or echo it
857         *
858         * @return void|string - returns the authentication message if return_instead_of_echo is true
859         */
860        public function do_complete_authentication($raw_state, $raw_code, $return_instead_of_echo = false) {
861                // Get the CSRF from setting and check it matches the one returned if it does no CSRF attack has happened
862                $opts = $this->get_options();
863                $csrf = $opts['CSRF'];
864                $state = stripslashes($raw_state);
865                // Check the state to see if an instance_id has been attached and if it has then extract the state
866                $parts = explode(':', $state);
867                $state = $parts[0];
868
869                if (strcmp($csrf, $state) == 0) {
870                        $opts['CSRF'] = '';
871                        if (isset($raw_code)) {
872                                // set code so it can be accessed in the next authentication step
873                                $opts['code'] = stripslashes($raw_code);
874                                // remove our flag so we know this authentication is complete
875                                if (isset($opts['auth_in_progress'])) unset($opts['auth_in_progress']);
876                                $this->set_options($opts, true);
877                                $auth_result = $this->auth_token($return_instead_of_echo);
878                                if ($return_instead_of_echo) return $auth_result;
879                        }
880                } else {
881                        error_log("UpdraftPlus: CSRF comparison failure: $csrf != $state");
882                }
883        }
884
885        /**
886         * This method will reset any saved options and start the bootstrap process for an authentication
887         *
888         * @param  String $instance_id - the instance id of the settings we want to authenticate
889         */
890        public function do_authenticate_storage($instance_id) {
891                try {
892                        // Clear out the existing credentials
893                        $opts = $this->get_options();
894                        $opts['tk_access_token'] = '';
895                        unset($opts['tk_request_token']);
896                        $opts['ownername'] = '';
897                        // Set a flag so we know this authentication is in progress
898                        $opts['auth_in_progress'] = true;
899                        $this->set_options($opts, true);
900
901                        $this->set_instance_id($instance_id);
902                        $this->bootstrap(false);
903                } catch (Exception $e) {
904                        $this->log(
905                                sprintf(
906                                        /* translators: 1: Service name, 2: Error message */
907                                        __('%1$s error: %2$s', 'updraftplus'),
908                                        /* translators: %s: Service name */
909                                        sprintf(__('%s authentication', 'updraftplus'),
910                                        'Dropbox'),
911                                        $e->getMessage()
912                                ),
913                        'error');
914                }
915        }
916       
917        /**
918         * This method will start the bootstrap process for a de-authentication
919         *
920         * @param  String $instance_id - the instance id of the settings we want to de-authenticate
921         */
922        public function do_deauthenticate_storage($instance_id) {
923                try {
924                        $this->set_instance_id($instance_id);
925                        $this->bootstrap(true);
926                } catch (Exception $e) {
927                        $this->log(
928                                sprintf(
929                                        /* translators: 1: Service name, 2: Error message */
930                                        __('%1$s error: %2$s', 'updraftplus'),
931                                        /* translators: %s: Service name */
932                                        sprintf(__('%s de-authentication', 'updraftplus'), 'Dropbox'),
933                                        $e->getMessage()
934                                ),
935                        'error');
936                }
937        }
938
939        /**
940         * This method will setup the authenticated admin warning, it can either return this or echo it
941         *
942         * @param boolean $return_instead_of_echo - a boolean to indicate if we should return the result or echo it
943         *
944         * @return void|string - returns the authentication message if return_instead_of_echo is true
945         */
946        public function show_authed_admin_warning($return_instead_of_echo) {
947                global $updraftplus_admin;
948
949                $dropbox = $this->bootstrap();
950                if (false === $dropbox) return false;
951
952                try {
953                        $account_info = $dropbox->accountInfo();
954                } catch (Exception $e) {
955                        /* translators: 1: Service name, 2: Error message */
956                        $accountinfo_err = sprintf(__('%1$s error: %2$s', 'updraftplus'), 'Dropbox', $e->getMessage()).' ('.$e->getCode().')';
957                }
958
959                $message = "<strong>".__('Success:', 'updraftplus').'</strong> '.
960                        /* translators: %s: Service name */
961                        sprintf(__('you have authenticated your %s account', 'updraftplus'), 'Dropbox');
962                // We log, because otherwise people get confused by the most recent log message of 'Parameter not found: oauth_token' and raise support requests
963                $this->log(
964                        __('Success:', 'updraftplus').' '.
965                        /* translators: %s: Service name */
966                        sprintf(__('you have authenticated your %s account', 'updraftplus'), 'Dropbox')
967                );
968
969                if (empty($account_info['code']) || "200" != $account_info['code']) {
970                        $message .= " (".__('though part of the returned information was not as expected - whether this indicates a real problem cannot be determined', 'updraftplus').") ". $account_info['code'];
971                        if (!empty($accountinfo_err)) $message .= "<br>".htmlspecialchars($accountinfo_err);
972                } else {
973                        $body = $account_info['body'];
974                        $name = '';
975                        $email = '';
976                        if (isset($body->display_name)) {
977                                $name = $body->display_name;
978                        } else {
979                                $name = $body->name->display_name;
980                        }
981                        if (isset($body->email)) {
982                                $email = $body->email;
983                        }
984
985                        $opts = $this->get_options();
986                        $opts['ownername'] = $name;
987                        $opts['email'] = $email;
988                        $ownername_with_email = $this->generate_ownername_with_email($opts);
989                        /* translators: 1: Service name, 2: Account holder's name with email */
990                        $message .= ". <br>".sprintf(__('Your %1$s account name: %2$s', 'updraftplus'), 'Dropbox', htmlspecialchars($ownername_with_email));
991                        $this->set_options($opts, true);
992
993                        try {
994                                /**
995                                 * Quota information is no longer provided with account information a new call to qoutaInfo must be made to get this information. The timeout is because we've seen cases where it returned after 180 seconds (apparently a faulty outgoing proxy), and we may as well wait as cause an error leading to user confusion.
996                                 */
997                                $quota_info = $dropbox->quotaInfo(array('timeout' => 190));
998
999                                if (empty($quota_info['code']) || "200" != $quota_info['code']) {
1000                                        $message .= " (".__('though part of the returned information was not as expected - whether this indicates a real problem cannot be determined', 'updraftplus').")". $quota_info['code'];
1001                                        if (!empty($accountinfo_err)) $message .= "<br>".htmlspecialchars($accountinfo_err);
1002                                } else {
1003                                        $body = $quota_info['body'];
1004                                        if (isset($body->quota_info)) {
1005                                                $quota_info = $body->quota_info;
1006                                                $total_quota = max($quota_info->quota, 1);
1007                                                $normal_quota = $quota_info->normal;
1008                                                $shared_quota = $quota_info->shared;
1009                                                $available_quota =$total_quota - ($normal_quota + $shared_quota);
1010                                                $used_perc = round(($normal_quota + $shared_quota)*100/$total_quota, 1);
1011                                                /* translators: 1: Service name, 2: Percentage used, 3: Available space */
1012                                                $message .= ' <br>'.sprintf(__('Your %1$s quota usage: %2$s %% used, %3$s available', 'updraftplus'), 'Dropbox', $used_perc, round($available_quota/1048576, 1).' MB');
1013                                        } else {
1014                                                $total_quota = max($body->allocation->allocated, 1);
1015                                                $used = $body->used;
1016                                                /* check here to see if the account is a team account and if so use the other used value
1017                                                This will give us their total usage including their individual account and team account */
1018                                                if (isset($body->allocation->used)) $used = $body->allocation->used;
1019                                                $available_quota =$total_quota - $used;
1020                                                $used_perc = round($used*100/$total_quota, 1);
1021                                                /* translators: 1: Service name, 2: Percentage of quota used, 3: Available quota in MB */
1022                                                $message .= ' <br>'.sprintf(__('Your %1$s quota usage: %2$s%% used, %3$s available', 'updraftplus'), 'Dropbox', $used_perc, round($available_quota/1048576, 1).' MB');
1023                                        }
1024                                }
1025                        } catch (Exception $e) {
1026                                // Catch
1027                        }
1028
1029                }
1030                if ($return_instead_of_echo) {
1031                        return "<div class='updraftmessage updated'><p>{$message}</p></div>";
1032                } else {
1033                        $updraftplus_admin->show_admin_warning($message);
1034                }
1035
1036        }
1037
1038        /**
1039         * Bootstrap and check token, can also return the authentication method if return_instead_of_echo is true
1040         *
1041         * @param boolean $return_instead_of_echo - a boolean to indicate if we should return the result or echo it
1042         *
1043         * @return void|string - returns the authentication message if return_instead_of_echo is true
1044         */
1045        public function auth_token($return_instead_of_echo) {
1046                $this->bootstrap();
1047                $opts = $this->get_options();
1048                if (!empty($opts['tk_access_token'])) {
1049                        if ($return_instead_of_echo) {
1050                                return $this->show_authed_admin_warning($return_instead_of_echo);
1051                        } else {
1052                                add_action('all_admin_notices', array($this, 'show_authed_admin_warning'));
1053                        }
1054                }
1055        }
1056
1057        /**
1058         * Acquire single-use authorization code
1059         */
1060        public function auth_request() {
1061                $this->bootstrap();
1062        }
1063
1064        /**
1065         * This basically reproduces the relevant bits of bootstrap.php from the SDK
1066         *
1067         * @param  Boolean $deauthenticate indicates if we should bootstrap for a deauth or auth request
1068         * @return object
1069         */
1070        public function bootstrap($deauthenticate = false) {
1071
1072                $storage = $this->get_storage();
1073
1074                if (!empty($storage) && !is_wp_error($storage)) return $storage;
1075
1076                // Dropbox APIv1 is dead, but we'll keep the variable in case v3 is ever announced
1077                $dropbox_api = 'Dropbox2';
1078
1079                updraft_try_include_file('includes/'.$dropbox_api.'/API.php', 'include_once');
1080                updraft_try_include_file('includes/'.$dropbox_api.'/Exception.php', 'include_once');
1081                updraft_try_include_file('includes/'.$dropbox_api.'/OAuth/Consumer/ConsumerAbstract.php', 'include_once');
1082                updraft_try_include_file('includes/'.$dropbox_api.'/OAuth/Storage/StorageInterface.php', 'include_once');
1083                updraft_try_include_file('includes/'.$dropbox_api.'/OAuth/Storage/Encrypter.php', 'include_once');
1084                updraft_try_include_file('includes/'.$dropbox_api.'/OAuth/Storage/WordPress.php', 'include_once');
1085                updraft_try_include_file('includes/'.$dropbox_api.'/OAuth/Consumer/Curl.php', 'include_once');
1086                // updraft_try_include_file('includes/'.$dropbox_api.'/OAuth/Consumer/WordPress.php', 'require_once');
1087
1088                $opts = $this->get_options();
1089
1090                $key = empty($opts['secret']) ? '' : $opts['secret'];
1091                $sec = empty($opts['appkey']) ? '' : $opts['appkey'];
1092               
1093                $oauth2_id = defined('UPDRAFTPLUS_DROPBOX_CLIENT_ID') ? UPDRAFTPLUS_DROPBOX_CLIENT_ID : base64_decode('dzQxM3o0cWhqejY1Nm5l');
1094
1095                // Set the callback URL
1096                $callbackhome = UpdraftPlus_Options::admin_page_url().'?page=updraftplus&action=updraftmethod-dropbox-auth';
1097                $callback = defined('UPDRAFTPLUS_DROPBOX_AUTH_RETURN_URL') ? UPDRAFTPLUS_DROPBOX_AUTH_RETURN_URL : 'https://auth.updraftplus.com/auth/dropbox/';
1098
1099                if (defined('UPDRAFTPLUS_CUSTOM_DROPBOX_APP') && UPDRAFTPLUS_CUSTOM_DROPBOX_APP) $callback = $callbackhome;
1100               
1101                $instance_id = $this->get_instance_id();
1102                // Instantiate the Encryptor and storage objects
1103                $encrypter = new Dropbox_Encrypter('ThisOneDoesNotMatterBeyondLength');
1104
1105                // Instantiate the storage
1106                $dropbox_storage = new Dropbox_WordPress($encrypter, "tk_", 'updraft_dropbox', $this);
1107
1108                // WordPress consumer does not yet work
1109                // $oauth = new Dropbox_ConsumerWordPress($sec, $key, $dropbox_storage, $callback);
1110
1111                // Get the DropBox API access details
1112                list($d2, $d1) = $this->defaults();
1113                if (empty($sec)) {
1114                        $sec = base64_decode($d1);
1115                }
1116
1117                if (empty($key)) {
1118                        $key = base64_decode($d2);
1119                }
1120
1121                $root = 'sandbox';
1122                if ('dropbox:' == substr($sec, 0, 8)) {
1123                        $sec = substr($sec, 8);
1124                        $root = 'dropbox';
1125                }
1126               
1127                try {
1128                        $oauth = new Dropbox_Curl($sec, $oauth2_id, $key, $dropbox_storage, $callback, $callbackhome, $deauthenticate, $instance_id);
1129                } catch (Exception $e) {
1130                        $this->log("Curl error: ".$e->getMessage());
1131                        /* translators: 1: Service name, 2: Error message with exception details */
1132                        $this->log(sprintf(__('%1$s error: %2$s', 'updraftplus'), "Dropbox/Curl", $e->getMessage().' ('.get_class($e).') (line: '.$e->getLine().', file: '.$e->getFile()).')', 'error');
1133                        return false;
1134                }
1135
1136                if ($deauthenticate) return true;
1137               
1138                $storage = new UpdraftPlus_Dropbox_API($oauth, $root);
1139               
1140                $this->set_storage($storage);
1141               
1142                return $storage;
1143        }
1144}
Note: See TracBrowser for help on using the repository browser.