PluginProbe
UpdraftPlus: WP Backup & Migration Plugin / 1.3.12
UpdraftPlus: WP Backup & Migration Plugin v1.3.12
1.26.7 1.26.6 1.26.5 1.26.4 1.26.3 1.9.19 1.9.25 1.9.26 1.9.30 1.9.31 1.9.32 1.9.4 1.9.40 1.9.41 1.9.42 1.9.43 1.9.44 1.9.45 1.9.46 1.9.5 1.9.50 1.9.51 1.9.60 1.9.62 1.9.63 All 371 releases
updraftplus / methods / googledrive.php

googledrive.php in UpdraftPlus: WP Backup & Migration Plugin 1.3.12, at methods/googledrive.php

351 lines 16.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 class UpdraftPlus_BackupModule_googledrive {
4
5 var $gdocs;
6 var $gdocs_access_token;
7
8 function action_auth() {
9 if ( isset( $_GET['state'] ) ) {
10 if ( $_GET['state'] == 'token' )
11 self::gdrive_auth_token();
12 elseif ( $_GET['state'] == 'revoke' )
13 self::gdrive_auth_revoke();
14 } elseif (isset($_GET['updraftplus_googleauth'])) {
15 self::gdrive_auth_request();
16 }
17 }
18
19 // Get a Google account access token using the refresh token
20 function access_token( $token, $client_id, $client_secret ) {
21
22 global $updraftplus;
23 $updraftplus->log("Google Drive: requesting access token: client_id=$client_id");
24
25 $query_body = array( 'refresh_token' => $token, 'client_id' => $client_id, 'client_secret' => $client_secret, 'grant_type' => 'refresh_token' );
26 $result = wp_remote_post('https://accounts.google.com/o/oauth2/token', array('timeout' => '15', 'method' => 'POST', 'body' => $query_body) );
27
28 if (is_wp_error($result)) {
29 $updraftplus->log("Google Drive error when requesting access token");
30 foreach ($result->get_error_messages() as $msg) { $updraftplus->log("Error message: $msg"); }
31 return false;
32 } else {
33 $json_values = json_decode( $result['body'], true );
34 if ( isset( $json_values['access_token'] ) ) {
35 $updraftplus->log("Google Drive: successfully obtained access token");
36 return $json_values['access_token'];
37 } else {
38 $updraftplus->log("Google Drive error when requesting access token: response does not contain access_token");
39 return false;
40 }
41 }
42 }
43
44 // Acquire single-use authorization code from Google OAuth 2.0
45 function gdrive_auth_request() {
46 $params = array(
47 'response_type' => 'code',
48 'client_id' => UpdraftPlus_Options::get_updraft_option('updraft_googledrive_clientid'),
49 'redirect_uri' => admin_url('options-general.php?page=updraftplus&action=updraftmethod-googledrive-auth'),
50 'scope' => 'https://www.googleapis.com/auth/drive.file https://docs.google.com/feeds/ https://docs.googleusercontent.com/ https://spreadsheets.google.com/feeds/',
51 'state' => 'token',
52 'access_type' => 'offline',
53 'approval_prompt' => 'auto'
54 );
55 header('Location: https://accounts.google.com/o/oauth2/auth?'.http_build_query($params));
56 }
57
58 // Revoke a Google account refresh token
59 function gdrive_auth_revoke() {
60 $ignore = wp_remote_get('https://accounts.google.com/o/oauth2/revoke?token='.UpdraftPlus_Options::get_updraft_option('updraft_googledrive_token'));
61 UpdraftPlus_Options::update_updraft_option('updraft_googledrive_token','');
62 header('Location: '.admin_url( 'options-general.php?page=updraftplus&message=Authorisation revoked'));
63 }
64
65 // Get a Google account refresh token using the code received from gdrive_auth_request
66 function gdrive_auth_token() {
67 if( isset( $_GET['code'] ) ) {
68 $post_vars = array(
69 'code' => $_GET['code'],
70 'client_id' => UpdraftPlus_Options::get_updraft_option('updraft_googledrive_clientid'),
71 'client_secret' => UpdraftPlus_Options::get_updraft_option('updraft_googledrive_secret'),
72 'redirect_uri' => admin_url('options-general.php?page=updraftplus&action=updraftmethod-googledrive-auth'),
73 'grant_type' => 'authorization_code'
74 );
75
76 $result = wp_remote_post('https://accounts.google.com/o/oauth2/token', array('timeout' => 30, 'method' => 'POST', 'body' => $post_vars) );
77
78 if (is_wp_error($result)) {
79 header('Location: '.admin_url('options-general.php?page=updraftplus&error=' . __( 'Bad response!', 'backup' ) ) );
80 } else {
81 $json_values = json_decode( $result['body'], true );
82 if ( isset( $json_values['refresh_token'] ) ) {
83 UpdraftPlus_Options::update_updraft_option('updraft_googledrive_token', $json_values['refresh_token']); // Save token
84 header('Location: '.admin_url('options-general.php?page=updraftplus&message=' . __( 'Google Drive authorisation was successful.', 'updraftplus' ) ) );
85 }
86 else {
87 header('Location: '.admin_url('options-general.php?page=updraftplus&error=' . __( 'No refresh token was received from Google. This often means that you entered your client secret wrongly, or that you have not yet re-authenticated (below) since correcting it. Re-check it, then follow the link to authenticate again. Finally, if that does not work, then use expert mode to wipe all your settings, create a new Google client ID/secret, and start again.', 'updraftplus' ) ) );
88 }
89 }
90 }
91 else {
92 header('Location: '.admin_url('options-general.php?page=updraftplus&error=' . __( 'Authorization failed!', 'updraftplus' ) ) );
93 }
94 }
95
96 // This function just does the formalities, and off-loads the main work to upload_file
97 function backup($backup_array) {
98
99 global $updraftplus;
100
101 if( !class_exists('UpdraftPlus_GDocs')) require_once(UPDRAFTPLUS_DIR.'/includes/class-gdocs.php');
102
103 // Do we have an access token?
104 if ( !$access_token = $this->access_token( UpdraftPlus_Options::get_updraft_option('updraft_googledrive_token'), UpdraftPlus_Options::get_updraft_option('updraft_googledrive_clientid'), UpdraftPlus_Options::get_updraft_option('updraft_googledrive_secret') )) {
105 $updraftplus->log('ERROR: Have not yet obtained an access token from Google (has the user authorised?)');
106 return new WP_Error( "no_access_token", "Have not yet obtained an access token from Google (has the user authorised?");
107 }
108
109 $this->gdocs_access_token = $access_token;
110
111 foreach ($backup_array as $file) {
112 $file_path = trailingslashit(UpdraftPlus_Options::get_updraft_option('updraft_dir')).$file;
113 $file_name = basename($file_path);
114 $updraftplus->log("$file_name: Attempting to upload to Google Drive");
115 $timer_start = microtime(true);
116 if ( $id = $this->upload_file( $file_path, $file_name, UpdraftPlus_Options::get_updraft_option('updraft_googledrive_remotepath')) ) {
117 $updraftplus->log('OK: Archive ' . $file_name . ' uploaded to Google Drive in ' . ( round(microtime( true ) - $timer_start,2) ) . ' seconds (id: '.$id.')' );
118 $updraftplus->uploaded_file($file, $id);
119 } else {
120 $updraftplus->log("ERROR: $file_name: Failed to upload to Google Drive" );
121 $updraftplus->error("$file_name: Failed to upload to Google Drive" );
122 }
123 }
124 $updraftplus->prune_retained_backups("googledrive", $this, null);
125 }
126
127 function delete($file) {
128 global $updraftplus;
129 $ids = UpdraftPlus_Options::get_updraft_option('updraft_file_ids', array());
130 if (!isset($ids[$file])) {
131 $updraftplus->log("Could not delete: could not find a record of the Google Drive file ID for this file");
132 return;
133 } else {
134 $del == $this->gdocs->delete_resource($ids[$file]);
135 if (is_wp_error($del)) {
136 foreach ($del->get_error_messages() as $msg) $updraftplus->log("Deletion failed: $msg");
137 } else {
138 $updraftplus->log("Deletion successful");
139 unset($ids[$file]);
140 UpdraftPlus_Options::update_updraft_option('updraft_file_ids', $ids);
141 }
142 }
143 return;
144 }
145
146 // Returns:
147 // true = already uploaded
148 // false = failure
149 // otherwise, the file ID
150 function upload_file( $file, $title, $parent = '') {
151
152 global $updraftplus;
153
154 // Make sure $this->gdocs is a UpdraftPlus_GDocs object, or give an error
155 if ( is_wp_error( $e = $this->need_gdocs() ) ) return false;
156 $gdocs_object = $this->gdocs;
157
158 $hash = md5($file);
159 $transkey = 'upd_'.$hash.'_gloc';
160 // This is unset upon completion, so if it is set then we are resuming
161 $possible_location = get_transient($transkey);
162
163 if ( empty( $possible_location ) ) {
164 $updraftplus->log("$file: Attempting to upload file to Google Drive.");
165 $location = $gdocs_object->prepare_upload( $file, $title, $parent );
166 } else {
167 $updraftplus->log("$file: Attempting to resume upload.");
168 $location = $gdocs_object->resume_upload( $file, $possible_location );
169 }
170
171 if ( is_wp_error( $location ) ) {
172 $updraftplus->log("GoogleDrive upload: an error occurred");
173 foreach ($location->get_error_messages() as $msg) {
174 $updraftplus->log("Error details: ".$msg);
175 $updraftplus->error($msg);
176 }
177 return false;
178 }
179
180 if (!is_string($location) && true == $location) {
181 $updraftplus->log("$file: this file is already uploaded");
182 return true;
183 }
184
185 if ( is_string( $location ) ) {
186 $res = $location;
187 $updraftplus->log("Uploading file with title ".$title);
188 $d = 0;
189 // This counter is only used for when deciding what to log
190 $counter = 0;
191 do {
192 $log_string = ($counter == 0) ? "Upload %: $d, URL: $res" : "Upload %: $d";
193 $counter++; if ($counter >= 20) $counter=0;
194 $updraftplus->log($log_string);
195
196 $res = $gdocs_object->upload_chunk();
197 if (is_string($res)) set_transient($transkey, $res, 3600*3);
198 $p = $gdocs_object->get_upload_percentage();
199 if ( $p - $d >= 1 ) {
200 $b = intval( $p - $d );
201 // echo '<span style="width:' . $b . '%"></span>';
202 $d += $b;
203 }
204 // $this->options['backup_list'][$id]['speed'] = $this->gdocs->get_upload_speed();
205 } while ( is_string( $res ) );
206 // echo '</div>';
207
208 if ( is_wp_error( $res ) || $res !== true) {
209 $updraftplus->log( "An error occurred during GoogleDrive upload (2)" );
210 $updraftplus->error( "An error occurred during GoogleDrive upload (see log for more details" );
211 if (is_wp_error( $res )) {
212 foreach ($res->get_error_messages() as $msg) $updraftplus->log($msg);
213 }
214 return false;
215 }
216
217 $updraftplus->log("The file was successfully uploaded to Google Drive in ".number_format_i18n( $gdocs_object->time_taken(), 3)." seconds at an upload speed of ".size_format( $gdocs_object->get_upload_speed() )."/s.");
218
219 delete_transient($transkey);
220 // unset( $this->options['backup_list'][$id]['location'], $this->options['backup_list'][$id]['attempt'] );
221 }
222
223 return $gdocs_object->get_file_id();
224
225 // $this->update_quota();
226 // Google's "user info" service
227 // if ( empty( $this->options['user_info'] ) ) $this->set_user_info();
228
229 }
230
231 function download($file) {
232
233 global $updraftplus;
234
235 if( !class_exists('UpdraftPlus_GDocs')) require_once(UPDRAFTPLUS_DIR.'/includes/class-gdocs.php');
236
237 // Do we have an access token?
238 if ( !$access_token = $this->access_token( UpdraftPlus_Options::get_updraft_option('updraft_googledrive_token'), UpdraftPlus_Options::get_updraft_option('updraft_googledrive_clientid'), UpdraftPlus_Options::get_updraft_option('updraft_googledrive_secret') )) {
239 $updraftplus->error('Have not yet obtained an access token from Google (has the user authorised?)');
240 return false;
241 }
242
243 $this->gdocs_access_token = $access_token;
244
245 // Make sure $this->gdocs is a UpdraftPlus_GDocs object, or give an error
246 if ( is_wp_error( $e = $this->need_gdocs() ) ) return false;
247 $gdocs_object = $this->gdocs;
248
249 $ids = UpdraftPlus_Options::get_updraft_option('updraft_file_ids', array());
250 if (!isset($ids[$file])) {
251 $updraftplus->error("Google Drive error: $file: could not download: could not find a record of the Google Drive file ID for this file");
252 return;
253 } else {
254 $content_link = $gdocs_object->get_content_link( $ids[$file], $file );
255 if (is_wp_error($content_link)) {
256 $updraftplus->error("Could not find $file in order to download it (id: ".$ids[$file].")");
257 foreach ($content_link->get_error_messages() as $msg) $updraftplus->error($msg);
258 return false;
259 }
260 // Actually download the thing
261 $download_to = trailingslashit(UpdraftPlus_Options::get_updraft_option('updraft_dir')).$file;
262 $gdocs_object->download_data($content_link, $download_to);
263
264 if (filesize($download_to) >0) {
265 return true;
266 } else {
267 $updraftplus->error("Google Drive error: zero-size file was downloaded");
268 return false;
269 }
270
271 }
272
273 return;
274
275 }
276
277 // This function modified from wordpress.org/extend/plugins/backup, by Sorin Iclanzan, under the GPLv3 or later at your choice
278 function need_gdocs() {
279
280 global $updraftplus;
281
282 if ( ! $this->is_gdocs($this->gdocs) ) {
283 if ( UpdraftPlus_Options::get_updraft_option('updraft_googledrive_token') == "" || UpdraftPlus_Options::get_updraft_option('updraft_googledrive_clientid') == "" || UpdraftPlus_Options::get_updraft_option('updraft_googledrive_secret') == "" ) {
284 $updraftplus->log("GoogleDrive: this account is not authorised");
285 return new WP_Error( "not_authorized", "Account is not authorized." );
286 }
287
288 if ( is_wp_error( $this->gdocs_access_token ) ) return $access_token;
289
290 $this->gdocs = new UpdraftPlus_GDocs( $this->gdocs_access_token );
291 // We need to be able to upload at least one chunk within the timeout (at least, we have seen an error report where the failure to do this seemed to be the cause)
292 // If we assume a user has at least 16kb/s (we saw one user with as low as 22kb/s), and that their provider may allow them only 15s, then we have the following settings
293 $this->gdocs->set_option( 'chunk_size', 0.2 ); # 0.2Mb; change from default of 512Kb
294 $this->gdocs->set_option( 'request_timeout', 15 ); # Change from default of 5s
295 $this->gdocs->set_option( 'max_resume_attempts', 36 ); # Doesn't look like GDocs class actually uses this anyway
296 }
297 return true;
298 }
299
300 // This function taken from wordpress.org/extend/plugins/backup, by Sorin Iclanzan, under the GPLv3 or later at your choice
301 function is_gdocs( $thing ) {
302 if ( is_object( $thing ) && is_a( $thing, 'UpdraftPlus_GDocs' ) ) return true;
303 return false;
304 }
305
306 function config_print() {
307 ?>
308 <tr class="updraftplusmethod googledrive">
309 <td>Google Drive:</td>
310 <td>
311 <img src="https://developers.google.com/drive/images/drive_logo.png" alt="Google Drive">
312 <p><em>Google Drive is a great choice, because UpdraftPlus supports chunked uploads - no matter how big your blog is, UpdraftPlus can upload it a little at a time, and not get thwarted by timeouts.</em></p>
313 </td>
314 </tr>
315 <tr class="updraftplusmethod googledrive">
316 <th>Google Drive:</th>
317 <td>
318 <p><a href="http://david.dw-perspective.org.uk/da/index.php/computer-resources/updraftplus-googledrive-authorisation/"><strong>For longer help, including screenshots, follow this link. The description below is sufficient for more expert users.</strong></a></p>
319 <p><a href="https://code.google.com/apis/console/">Follow this link to your Google API Console</a>, and there create a Client ID in the API Access section. Select 'Web Application' as the application type.</p><p>You must add <kbd><?php echo admin_url('options-general.php?page=updraftplus&action=updraftmethod-googledrive-auth'); ?></kbd> as the authorised redirect URI (under &quot;More Options&quot;) when asked. N.B. If you install UpdraftPlus on several WordPress sites, then you cannot re-use your client ID; you must create a new one from your Google API console for each blog.
320
321 <?php
322 if (!class_exists('SimpleXMLElement')) { echo " <b>WARNING:</b> You do not have the SimpleXMLElement installed. Google Drive backups will <b>not</b> work until you do."; }
323 ?>
324 </p>
325 </td>
326 </tr>
327
328 <tr class="updraftplusmethod googledrive">
329 <th>Google Drive Client ID:</th>
330 <td><input type="text" autocomplete="off" style="width:332px" name="updraft_googledrive_clientid" value="<?php echo htmlspecialchars(UpdraftPlus_Options::get_updraft_option('updraft_googledrive_clientid')) ?>" /><br><em>If Google later shows you the message &quot;invalid_client&quot;, then you did not enter a valid client ID here.</em></td>
331 </tr>
332 <tr class="updraftplusmethod googledrive">
333 <th>Google Drive Client Secret:</th>
334 <td><input type="text" style="width:332px" name="updraft_googledrive_secret" value="<?php echo htmlspecialchars(UpdraftPlus_Options::get_updraft_option('updraft_googledrive_secret')); ?>" /></td>
335 </tr>
336 <tr class="updraftplusmethod googledrive">
337 <th>Google Drive Folder ID:</th>
338 <td><input type="text" style="width:332px" name="updraft_googledrive_remotepath" value="<?php echo htmlspecialchars(UpdraftPlus_Options::get_updraft_option('updraft_googledrive_remotepath')); ?>" /> <em><strong>This is NOT a folder name</strong>. To get a folder's ID navigate to that folder in Google Drive in your web browser and copy the ID from your browser's address bar. It is the part that comes after <kbd>#folders/.</kbd> Leave empty to use your root folder)</em></td>
339 </tr>
340 <tr class="updraftplusmethod googledrive">
341 <th>Authenticate with Google:</th>
342 <td><p><?php if (UpdraftPlus_Options::get_updraft_option('updraft_googledrive_token','xyz') != 'xyz') echo "<strong>(You appear to be already authenticated).</strong>"; ?> <a href="?page=updraftplus&action=updraftmethod-googledrive-auth&updraftplus_googleauth=doit"><strong>After</strong> you have saved your settings (by clicking &quot;Save Changes&quot; below), then come back here once and click this link to complete authentication with Google.</a>
343 </p>
344 </td>
345 </tr>
346 <?php
347 }
348
349 }
350
351 ?>