UpdraftPlus WordPress Backup Plugin - Version 1.0.18

Version Description

  • 12/26/2012 =
  • First steps towards modularising cloud upload methods
Download this release

Release Info

Developer DavidAnderson
Plugin Icon 128x128 UpdraftPlus WordPress Backup Plugin
Version 1.0.18
Comparing to
See all releases

Code changes from version 1.0.16 to 1.0.18

Files changed (6) hide show
  1. methods/email.php +18 -0
  2. methods/ftp.php +44 -0
  3. methods/googledrive.php +190 -0
  4. methods/s3.php +128 -0
  5. readme.txt +4 -1
  6. updraftplus.php +50 -394
methods/email.php ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Files can easily get way too big for this method
4
+
5
+ function updraftplus_email_backup($backup_array) {
6
+
7
+ global $updraftplus;
8
+
9
+ foreach ($backup_array as $type => $file) {
10
+ $fullpath = trailingslashit(get_option('updraft_dir')).$file;
11
+ wp_mail(get_option('updraft_email'), "WordPress Backup ".date('Y-m-d H:i',$updraftplus->backup_time), "Backup is of the $type. Be wary; email backups may fail because of file size limitations on mail servers.", null, array($fullpath));
12
+ $updraftplus->uploaded_file($file);
13
+ }
14
+
15
+ $updraftplus->prune_retained_backups("local");
16
+ }
17
+
18
+ ?>
methods/ftp.php ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ function updraftplus_ftp_backup($backup_array) {
4
+
5
+ global $updraftplus;
6
+
7
+ if( !class_exists('ftp_wrapper')) require_once(UPDRAFTPLUS_DIR.'/includes/ftp.class.php');
8
+
9
+ //handle SSL and errors at some point TODO
10
+ $ftp = new ftp_wrapper(get_option('updraft_server_address'),get_option('updraft_ftp_login'),get_option('updraft_ftp_pass'));
11
+ $ftp->passive = true;
12
+ $ftp->connect();
13
+ //$ftp->make_dir(); we may need to recursively create dirs? TODO
14
+
15
+ $ftp_remote_path = trailingslashit(get_option('updraft_ftp_remote_path'));
16
+ foreach($backup_array as $file) {
17
+ $fullpath = trailingslashit(get_option('updraft_dir')).$file;
18
+ if ($ftp->put($fullpath,$ftp_remote_path.$file,FTP_BINARY)) {
19
+ $updraftplus->log("ERROR: $file_name: Successfully uploaded via FTP");
20
+ $updraftplus->uploaded_file($file);
21
+ } else {
22
+ $updraftplus->error("$file_name: Failed to upload to FTP" );
23
+ $updraftplus->log("ERROR: $file_name: Failed to upload to FTP" );
24
+ }
25
+ }
26
+
27
+ $updraftplus->prune_retained_backups("ftp",$ftp,$ftp_remote_path);
28
+ }
29
+
30
+ function updraftplus_download_ftp_backup($file) {
31
+ if( !class_exists('ftp_wrapper')) require_once(UPDRAFTPLUS_DIR.'/includes/ftp.class.php');
32
+
33
+ //handle SSL and errors at some point TODO
34
+ $ftp = new ftp_wrapper(get_option('updraft_server_address'),get_option('updraft_ftp_login'),get_option('updraft_ftp_pass'));
35
+ $ftp->passive = true;
36
+ $ftp->connect();
37
+ //$ftp->make_dir(); we may need to recursively create dirs? TODO
38
+
39
+ $ftp_remote_path = trailingslashit(get_option('updraft_ftp_remote_path'));
40
+ $fullpath = trailingslashit(get_option('updraft_dir')).$file;
41
+ $ftp->get($fullpath,$ftp_remote_path.$file,FTP_BINARY);
42
+ }
43
+
44
+ ?>
methods/googledrive.php ADDED
@@ -0,0 +1,190 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Not cleanly separated - much of the Google Drive code is still embedded in the main updraftplus.php
4
+
5
+ // This function just does the formalities, and off-loads the main work to googledrive_upload_file
6
+ function updraftplus_googledrive_backup($backup_array) {
7
+
8
+ global $updraftplus;
9
+
10
+ if( !class_exists('UpdraftPlus_GDocs')) require_once(UPDRAFTPLUS_DIR.'/includes/class-gdocs.php');
11
+
12
+ // Do we have an access token?
13
+ if ( !$access_token = $updraftplus->access_token( get_option('updraft_googledrive_token'), get_option('updraft_googledrive_clientid'), get_option('updraft_googledrive_secret') )) {
14
+ $updraftplus->log('ERROR: Have not yet obtained an access token from Google (has the user authorised?)');
15
+ return new WP_Error( "no_access_token", "Have not yet obtained an access token from Google (has the user authorised?");
16
+ }
17
+
18
+ $updraftplus->gdocs_access_token = $access_token;
19
+
20
+ foreach ($backup_array as $file) {
21
+ $file_path = trailingslashit(get_option('updraft_dir')).$file;
22
+ $file_name = basename($file_path);
23
+ $updraftplus->log("$file_name: Attempting to upload to Google Drive");
24
+ $timer_start = microtime(true);
25
+ if ( $id = updraftplus_googledrive_upload_file( $file_path, $file_name, get_option('updraft_googledrive_remotepath')) ) {
26
+ $updraftplus->log('OK: Archive ' . $file_name . ' uploaded to Google Drive in ' . ( round(microtime( true ) - $timer_start,2) ) . ' seconds (id: '.$id.')' );
27
+ $updraftplus->uploaded_file($file, $id);
28
+ } else {
29
+ $updraftplus->error("$file_name: Failed to upload to Google Drive" );
30
+ $updraftplus->log("ERROR: $file_name: Failed to upload to Google Drive" );
31
+ }
32
+ }
33
+ $updraftplus->prune_retained_backups("googledrive",$access_token,get_option('updraft_googledrive_remotepath'));
34
+ }
35
+
36
+ // Returns:
37
+ // true = already uploaded
38
+ // false = failure
39
+ // otherwise, the file ID
40
+ function updraftplus_googledrive_upload_file( $file, $title, $parent = '') {
41
+
42
+ global $updraftplus;
43
+
44
+ // Make sure $this->gdocs is a UpdraftPlus_GDocs object, or give an error
45
+ if ( is_wp_error( $e = updraftplus_need_gdocs() ) ) return false;
46
+ $gdocs_object = $updraftplus->gdocs;
47
+
48
+ $hash = md5($file);
49
+ $transkey = 'upd_'.$hash.'_gloc';
50
+ // This is unset upon completion, so if it is set then we are resuming
51
+ $possible_location = get_transient($transkey);
52
+
53
+ if ( empty( $possible_location ) ) {
54
+ $updraftplus->log("$file: Attempting to upload file to Google Drive.");
55
+ $location = $gdocs_object->prepare_upload( $file, $title, $parent );
56
+ } else {
57
+ $updraftplus->log("$file: Attempting to resume upload.");
58
+ $location = $gdocs_object->resume_upload( $file, $possible_location );
59
+ }
60
+
61
+ if ( is_wp_error( $location ) ) {
62
+ $updraftplus->log("GoogleDrive upload: an error occurred");
63
+ foreach ($location->get_error_messages() as $msg) {
64
+ $updraftplus->error($msg);
65
+ $updraftplus->log("Error details: ".$msg);
66
+ }
67
+ return false;
68
+ }
69
+
70
+ if (!is_string($location) && true == $location) {
71
+ $updraftplus->log("$file: this file is already uploaded");
72
+ return true;
73
+ }
74
+
75
+ if ( is_string( $location ) ) {
76
+ $res = $location;
77
+ $updraftplus->log("Uploading file with title ".$title);
78
+ $d = 0;
79
+ do {
80
+ $updraftplus->log("Google Drive upload: chunk d: $d, loc: $res");
81
+ $res = $gdocs_object->upload_chunk();
82
+ if (is_string($res)) set_transient($transkey, $res, 3600*3);
83
+ $p = $gdocs_object->get_upload_percentage();
84
+ if ( $p - $d >= 1 ) {
85
+ $b = intval( $p - $d );
86
+ // echo '<span style="width:' . $b . '%"></span>';
87
+ $d += $b;
88
+ }
89
+ // $this->options['backup_list'][$id]['speed'] = $this->gdocs->get_upload_speed();
90
+ } while ( is_string( $res ) );
91
+ // echo '</div>';
92
+
93
+ if ( is_wp_error( $res ) || $res !== true) {
94
+ $updraftplus->log( "An error occurred during GoogleDrive upload (2)" );
95
+ $updraftplus->error( "An error occurred during GoogleDrive upload (2)" );
96
+ if (is_wp_error( $res )) {
97
+ foreach ($res->get_error_messages() as $msg) $updraftplus->log($msg);
98
+ }
99
+ return false;
100
+ }
101
+
102
+ $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.");
103
+
104
+ delete_transient($transkey);
105
+ // unset( $this->options['backup_list'][$id]['location'], $this->options['backup_list'][$id]['attempt'] );
106
+ }
107
+
108
+ return $gdocs_object->get_file_id();
109
+
110
+ // $this->update_quota();
111
+ // Google's "user info" service
112
+ // if ( empty( $this->options['user_info'] ) ) $this->set_user_info();
113
+
114
+ }
115
+
116
+ function updraftplus_download_googledrive_backup($file) {
117
+
118
+ global $updraftplus;
119
+
120
+ if( !class_exists('UpdraftPlus_GDocs')) require_once(UPDRAFTPLUS_DIR.'/includes/class-gdocs.php');
121
+
122
+ // Do we have an access token?
123
+ if ( !$access_token = $updraftplus->access_token( get_option('updraft_googledrive_token'), get_option('updraft_googledrive_clientid'), get_option('updraft_googledrive_secret') )) {
124
+ $updraftplus->error('ERROR: Have not yet obtained an access token from Google (has the user authorised?)');
125
+ return false;
126
+ }
127
+
128
+ $updraftplus->gdocs_access_token = $access_token;
129
+
130
+ // Make sure $this->gdocs is a UpdraftPlus_GDocs object, or give an error
131
+ if ( is_wp_error( $e = updraftplus_need_gdocs() ) ) return false;
132
+ $gdocs_object = $updraftplus->gdocs;
133
+
134
+ $ids = get_option('updraft_file_ids', array());
135
+ if (!isset($ids[$file])) {
136
+ $this->error("Google Drive error: $file: could not download: could not find a record of the Google Drive file ID for this file");
137
+ return;
138
+ } else {
139
+ $content_link = $gdocs_object->get_content_link( $ids[$file], $file );
140
+ if (is_wp_error($content_link)) {
141
+ $updraftplus->error("Could not find $file in order to download it (id: ".$ids[$file].")");
142
+ foreach ($content_link->get_error_messages() as $msg) $updraftplus->error($msg);
143
+ return false;
144
+ }
145
+ // Actually download the thing
146
+ $download_to = trailingslashit(get_option('updraft_dir')).$file;
147
+ $gdocs_object->download_data($content_link, $download_to);
148
+
149
+ if (filesize($download_to) >0) {
150
+ return true;
151
+ } else {
152
+ $updraftplus->error("Google Drive error: zero-size file was downloaded");
153
+ return false;
154
+ }
155
+
156
+ }
157
+
158
+ return;
159
+
160
+ }
161
+
162
+ // This function modified from wordpress.org/extend/plugins/backup, by Sorin Iclanzan, under the GPLv3 or later at your choice
163
+ function updraftplus_need_gdocs() {
164
+
165
+ global $updraftplus;
166
+
167
+ if ( ! updraftplus_is_gdocs($updraftplus->gdocs) ) {
168
+ if ( get_option('updraft_googledrive_token') == "" || get_option('updraft_googledrive_clientid') == "" || get_option('updraft_googledrive_secret') == "" ) {
169
+ $updraftplus->log("GoogleDrive: this account is not authorised");
170
+ return new WP_Error( "not_authorized", "Account is not authorized." );
171
+ }
172
+
173
+ if ( is_wp_error( $updraftplus->gdocs_access_token ) ) return $access_token;
174
+
175
+ $updraftplus->gdocs = new UpdraftPlus_GDocs( $updraftplus->gdocs_access_token );
176
+ $updraftplus->gdocs->set_option( 'chunk_size', 1 ); # 1Mb; change from default of 512Kb
177
+ $updraftplus->gdocs->set_option( 'request_timeout', 10 ); # Change from default of 10s
178
+ $updraftplus->gdocs->set_option( 'max_resume_attempts', 36 ); # Doesn't look like GDocs class actually uses this anyway
179
+ }
180
+ return true;
181
+ }
182
+
183
+ // This function taken from wordpress.org/extend/plugins/backup, by Sorin Iclanzan, under the GPLv3 or later at your choice
184
+ function updraftplus_is_gdocs( $thing ) {
185
+ if ( is_object( $thing ) && is_a( $thing, 'UpdraftPlus_GDocs' ) ) return true;
186
+ return false;
187
+ }
188
+
189
+
190
+ ?>
methods/s3.php ADDED
@@ -0,0 +1,128 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ function updraftplus_s3_backup($backup_array) {
4
+
5
+ global $updraftplus;
6
+
7
+ if (!class_exists('S3')) require_once(UPDRAFTPLUS_DIR.'/includes/S3.php');
8
+
9
+ $s3 = new S3(get_option('updraft_s3_login'), get_option('updraft_s3_pass'));
10
+
11
+ $bucket_name = untrailingslashit(get_option('updraft_s3_remote_path'));
12
+ $bucket_path = "";
13
+ $orig_bucket_name = $bucket_name;
14
+
15
+ if (preg_match("#^([^/]+)/(.*)$#",$bucket_name,$bmatches)) {
16
+ $bucket_name = $bmatches[1];
17
+ $bucket_path = $bmatches[2]."/";
18
+ }
19
+
20
+ // See if we can detect the region (which implies the bucket exists and is ours), or if not create it
21
+ if (@$s3->getBucketLocation($bucket_name) || @$s3->putBucket($bucket_name, S3::ACL_PRIVATE)) {
22
+
23
+ foreach($backup_array as $file) {
24
+
25
+ // We upload in 5Mb chunks to allow more efficient resuming and hence uploading of larger files
26
+ $fullpath = trailingslashit(get_option('updraft_dir')).$file;
27
+ $chunks = floor(filesize($fullpath) / 5242880)+1;
28
+ $hash = md5($file);
29
+
30
+ $updraftplus->log("S3 upload: $fullpath (chunks: $chunks) -> s3://$bucket_name/$bucket_path$file");
31
+
32
+ $filepath = $bucket_path.$file;
33
+
34
+ // This is extra code for the 1-chunk case, but less overhead (no bothering with transients)
35
+ if ($chunks < 2) {
36
+ if (!$s3->putObjectFile($fullpath, $bucket_name, $filepath)) {
37
+ $updraftplus->log("S3 regular upload: failed");
38
+ $updraftplus->error("S3 Error: Failed to upload $fullpath.");
39
+ } else {
40
+ $updraftplus->log("S3 regular upload: success");
41
+ $updraftplus->uploaded_file($file);
42
+ }
43
+ } else {
44
+
45
+ // Retrieve the upload ID
46
+ $uploadId = get_transient("updraft_${hash}_uid");
47
+ if (empty($uploadId)) {
48
+ $uploadId = $s3->initiateMultipartUpload($bucket_name, $filepath);
49
+ if (empty($uploadId)) {
50
+ $updraftplus->log("S3 upload: failed: could not get uploadId for multipart upload");
51
+ continue;
52
+ } else {
53
+ $updraftplus->log("S3 chunked upload: got multipart ID: $uploadId");
54
+ set_transient("updraft_${hash}_uid", $uploadId, 3600*3);
55
+ }
56
+ } else {
57
+ $updraftplus->log("S3 chunked upload: retrieved previously obtained multipart ID: $uploadId");
58
+ }
59
+
60
+ $successes = 0;
61
+ $etags = array();
62
+ for ($i = 1 ; $i <= $chunks; $i++) {
63
+ # Shorted to upd here to avoid hitting the 45-character limit
64
+ $etag = get_transient("upd_${hash}_e$i");
65
+ if (strlen($etag) > 0) {
66
+ $updraftplus->log("S3 chunk $i: was already completed (etag: $etag)");
67
+ $successes++;
68
+ array_push($etags, $etag);
69
+ } else {
70
+ $etag = $s3->uploadPart($bucket_name, $filepath, $uploadId, $fullpath, $i);
71
+ if (is_string($etag)) {
72
+ $updraftplus->log("S3 chunk $i: uploaded (etag: $etag)");
73
+ array_push($etags, $etag);
74
+ set_transient("upd_${hash}_e$i", $etag, 3600*3);
75
+ $successes++;
76
+ } else {
77
+ $updraftplus->error("S3 chunk $i: upload failed");
78
+ $updraftplus->log("S3 chunk $i: upload failed");
79
+ }
80
+ }
81
+ }
82
+ if ($successes >= $chunks) {
83
+ $updraftplus->log("S3 upload: all chunks uploaded; will now instruct S3 to re-assemble");
84
+ if ($s3->completeMultipartUpload ($bucket_name, $filepath, $uploadId, $etags)) {
85
+ $updraftplus->log("S3 upload: re-assembly succeeded");
86
+ $updraftplus->uploaded_file($file);
87
+ } else {
88
+ $updraftplus->log("S3 upload: re-assembly failed");
89
+ $updraftplus->error("S3 upload: re-assembly failed");
90
+ }
91
+ } else {
92
+ $updraftplus->log("S3 upload: upload was not completely successful on this run");
93
+ }
94
+ }
95
+ }
96
+ $updraftplus->prune_retained_backups('s3',$s3,$orig_bucket_name);
97
+ } else {
98
+ $updraftplus->log("S3 Error: Failed to create bucket $bucket_name.");
99
+ $updraftplus->error("S3 Error: Failed to create bucket $bucket_name. Check your permissions and credentials.");
100
+ }
101
+ }
102
+
103
+ function updraftplus_download_s3_backup($file) {
104
+
105
+ global $updraftplus;
106
+ if(!class_exists('S3')) require_once(UPDRAFTPLUS_DIR.'/includes/S3.php');
107
+
108
+ $s3 = new S3(get_option('updraft_s3_login'), get_option('updraft_s3_pass'));
109
+ $bucket_name = untrailingslashit(get_option('updraft_s3_remote_path'));
110
+ $bucket_path = "";
111
+
112
+ if (preg_match("#^([^/]+)/(.*)$#",$bucket_name,$bmatches)) {
113
+ $bucket_name = $bmatches[1];
114
+ $bucket_path = $bmatches[2]."/";
115
+ }
116
+
117
+ if (@$s3->getBucketLocation($bucket_name)) {
118
+ $fullpath = trailingslashit(get_option('updraft_dir')).$file;
119
+ if (!$s3->getObject($bucket_name, $bucket_path.$file, $fullpath)) {
120
+ $updraftplus->error("S3 Error: Failed to download $fullpath. Check your permissions and credentials.");
121
+ }
122
+ } else {
123
+ $updraftplus->error("S3 Error: Failed to access bucket $bucket_name. Check your permissions and credentials.");
124
+ }
125
+
126
+ }
127
+
128
+ ?>
readme.txt CHANGED
@@ -8,7 +8,7 @@ Donate link: http://david.dw-perspective.org.uk/donate
8
  License: GPLv3 or later
9
 
10
  == Upgrade Notice ==
11
- Various bug + compatibility fixes for greater reliability
12
 
13
  == Description ==
14
 
@@ -87,6 +87,9 @@ No, there's no warranty or guarantee, etc. It's completely up to you to verify t
87
 
88
  == Changelog ==
89
 
 
 
 
90
  = 1.0.16 - 12/24/2012 =
91
  * Improve race detection and clean up already-created files when detected
92
 
8
  License: GPLv3 or later
9
 
10
  == Upgrade Notice ==
11
+ Beginning of code re-organisation to make programming new cloud methods easier
12
 
13
  == Description ==
14
 
87
 
88
  == Changelog ==
89
 
90
+ = 1.0.18 - 12/26/2012 =
91
+ * First steps towards modularising cloud upload methods
92
+
93
  = 1.0.16 - 12/24/2012 =
94
  * Improve race detection and clean up already-created files when detected
95
 
updraftplus.php CHANGED
@@ -4,7 +4,7 @@ Plugin Name: UpdraftPlus - Backup/Restore
4
  Plugin URI: http://wordpress.org/extend/plugins/updraftplus
5
  Description: Uploads, themes, plugins, and your DB can be automatically backed up to Amazon S3, Google Drive, FTP, or emailed, on separate schedules.
6
  Author: David Anderson.
7
- Version: 1.0.16
8
  Donate link: http://david.dw-perspective.org.uk/donate
9
  License: GPLv3 or later
10
  Author URI: http://wordshell.net
@@ -18,6 +18,7 @@ TODO
18
 
19
  Encrypt filesystem, if memory allows (and have option for abort if not); split up into multiple zips when needed
20
  // Does not delete old custom directories upon a restore?
 
21
  */
22
 
23
  /* Portions copyright 2010 Paul Kehrer
@@ -43,18 +44,19 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
43
  // 15 minutes
44
  @set_time_limit(900);
45
 
46
- $updraft = new UpdraftPlus();
47
 
48
- if(!$updraft->memory_check(192)) {
49
  # TODO: Better solution is to split the backup set into manageable chunks based on this limit
50
  @ini_set('memory_limit', '192M'); //up the memory limit for large backup files
51
  }
52
 
 
53
  define('UPDRAFT_DEFAULT_OTHERS_EXCLUDE','upgrade,cache,updraft,index.php');
54
 
55
  class UpdraftPlus {
56
 
57
- var $version = '1.0.16';
58
 
59
  var $dbhandle;
60
  var $errors = array();
@@ -86,7 +88,8 @@ class UpdraftPlus {
86
  // Handle Google OAuth 2.0 - ?page=updraftplus&action=auth
87
  // Also handle action=downloadlog
88
  function handle_url_actions() {
89
- if ( is_admin() && isset( $_GET['page'] ) && $_GET['page'] == 'updraftplus' && isset($_GET['action']) ) {
 
90
  if ($_GET['action'] == 'auth') {
91
  if ( isset( $_GET['state'] ) ) {
92
  if ( $_GET['state'] == 'token' )
@@ -167,9 +170,7 @@ class UpdraftPlus {
167
  return;
168
  }
169
 
170
- /**
171
- * Get a Google account refresh token using the code received from gdrive_auth_request
172
- */
173
  function gdrive_auth_token() {
174
  if( isset( $_GET['code'] ) ) {
175
  $post_vars = array(
@@ -200,9 +201,7 @@ class UpdraftPlus {
200
  }
201
  }
202
 
203
- /**
204
- * Revoke a Google account refresh token
205
- */
206
  function gdrive_auth_revoke() {
207
  $ignore = wp_remote_get('https://accounts.google.com/o/oauth2/revoke?token='.get_option('updraft_googledrive_token'));
208
  update_option('updraft_googledrive_token','');
@@ -323,7 +322,7 @@ class UpdraftPlus {
323
  if (is_array($to_delete)) {
324
  foreach ($to_delete as $key => $file) {
325
  if (is_file($bdir.'/'.$file)) {
326
- $this->log("Deleteing the file we created: ".$file);
327
  @unlink($bdir.'/'.$file);
328
  }
329
  }
@@ -433,7 +432,8 @@ class UpdraftPlus {
433
 
434
  @fclose($this->logfile_handle);
435
 
436
- if (!get_option('updraft_debug_mode')) @unlink($this->logfile_name);
 
437
 
438
  }
439
 
@@ -481,36 +481,21 @@ class UpdraftPlus {
481
 
482
  }
483
 
 
484
  function cloud_backup($backup_array) {
485
- switch(get_option('updraft_service')) {
486
- case 's3':
487
- @set_time_limit(900);
488
- $this->log("Cloud backup: S3");
489
- if (count($backup_array) >0) $this->s3_backup($backup_array);
490
- break;
491
- case 'googledrive':
492
- @set_time_limit(900);
493
- $this->log("Cloud backup: Google Drive");
494
- if (count($backup_array) >0) $this->googledrive_backup($backup_array);
495
- break;
496
- case 'ftp':
497
- @set_time_limit(900);
498
- $this->log("Cloud backup: FTP");
499
- if (count($backup_array) >0) $this->ftp_backup($backup_array);
500
- break;
501
- case 'email':
502
- @set_time_limit(900);
503
- $this->log("Cloud backup: Email");
504
- //files can easily get way too big for this...
505
- foreach($backup_array as $type=>$file) {
506
- $fullpath = trailingslashit(get_option('updraft_dir')).$file;
507
- wp_mail(get_option('updraft_email'),"WordPress Backup ".date('Y-m-d H:i',$this->backup_time),"Backup is of the $type. Be wary; email backups may fail because of file size limitations on mail servers.",null,array($fullpath));
508
- $this->uploaded_file($file);
509
- }
510
- //we don't break here so it goes and executes all the default behavior below as well. this gives us retain behavior for email
511
- default:
512
- $this->prune_retained_backups("local");
513
- break;
514
  }
515
  }
516
 
@@ -519,7 +504,7 @@ class UpdraftPlus {
519
  $this->log("Retain: beginning examination of existing backup sets");
520
  $updraft_retain = get_option('updraft_retain');
521
  // Number of backups to retain
522
- $retain = (isset($updraft_retain))?get_option('updraft_retain'):1;
523
  $this->log("Retain: user setting: number to retain = $retain");
524
  // Returns an array, most recent first, of backup sets
525
  $backup_history = $this->get_backup_history();
@@ -616,276 +601,32 @@ class UpdraftPlus {
616
  unset($backup_to_examine['themes']);
617
  unset($backup_to_examine['uploads']);
618
  unset($backup_to_examine['others']);
619
- unset($backup_to_examine['nonce']);
620
  }
621
  }
622
  // Delete backup set completely if empty, o/w just remove DB
623
- if (count($backup_to_examine)==0) {
624
  $this->log("$backup_datestamp: this backup set is now empty; will remove from history");
625
  unset($backup_history[$backup_datestamp]);
626
- } else {
627
- $this->log("$backup_datestamp: this backup set remains non-empty; will retain in history");
628
- $backup_history[$backup_datestamp] = $backup_to_examine;
629
- }
630
- }
631
- $this->log("Retain: saving new backup history (sets now: ".count($backup_history).") and finishing retain operation");
632
- update_option('updraft_backup_history',$backup_history);
633
- }
634
-
635
- function s3_backup($backup_array) {
636
-
637
- if(!class_exists('S3')) require_once(dirname(__FILE__).'/includes/S3.php');
638
-
639
- $s3 = new S3(get_option('updraft_s3_login'), get_option('updraft_s3_pass'));
640
-
641
- $bucket_name = untrailingslashit(get_option('updraft_s3_remote_path'));
642
- $bucket_path = "";
643
- $orig_bucket_name = $bucket_name;
644
-
645
- if (preg_match("#^([^/]+)/(.*)$#",$bucket_name,$bmatches)) {
646
- $bucket_name = $bmatches[1];
647
- $bucket_path = $bmatches[2]."/";
648
- }
649
-
650
- // See if we can detect the region (which implies the bucket exists and is ours), or if not create it
651
- if (@$s3->getBucketLocation($bucket_name) || @$s3->putBucket($bucket_name, S3::ACL_PRIVATE)) {
652
-
653
- foreach($backup_array as $file) {
654
-
655
- // We upload in 5Mb chunks to allow more efficient resuming and hence uploading of larger files
656
- $fullpath = trailingslashit(get_option('updraft_dir')).$file;
657
- $chunks = floor(filesize($fullpath) / 5242880)+1;
658
- $hash = md5($file);
659
-
660
- $this->log("S3 upload: $fullpath (chunks: $chunks) -> s3://$bucket_name/$bucket_path$file");
661
-
662
- $filepath = $bucket_path.$file;
663
-
664
- // This is extra code for the 1-chunk case, but less overhead (no bothering with transients)
665
- if ($chunks < 2) {
666
- if (!$s3->putObjectFile($fullpath, $bucket_name, $filepath)) {
667
- $this->log("S3 regular upload: failed");
668
- $this->error("S3 Error: Failed to upload $fullpath.");
669
  } else {
670
- $this->log("S3 regular upload: success");
671
- $this->uploaded_file($file);
672
  }
673
  } else {
674
-
675
- // Retrieve the upload ID
676
- $uploadId = get_transient("updraft_${hash}_uid");
677
- if (empty($uploadId)) {
678
- $uploadId = $s3->initiateMultipartUpload($bucket_name, $filepath);
679
- if (empty($uploadId)) {
680
- $this->log("S3 upload: failed: could not get uploadId for multipart upload");
681
- continue;
682
- } else {
683
- $this->log("S3 chunked upload: got multipart ID: $uploadId");
684
- set_transient("updraft_${hash}_uid", $uploadId, 3600*3);
685
- }
686
- } else {
687
- $this->log("S3 chunked upload: retrieved previously obtained multipart ID: $uploadId");
688
- }
689
-
690
- $successes = 0;
691
- $etags = array();
692
- for ($i = 1 ; $i <= $chunks; $i++) {
693
- # Shorted to upd here to avoid hitting the 45-character limit
694
- $etag = get_transient("upd_${hash}_e$i");
695
- if (strlen($etag) > 0) {
696
- $this->log("S3 chunk $i: was already completed (etag: $etag)");
697
- $successes++;
698
- array_push($etags, $etag);
699
- } else {
700
- $etag = $s3->uploadPart($bucket_name, $filepath, $uploadId, $fullpath, $i);
701
- if (is_string($etag)) {
702
- $this->log("S3 chunk $i: uploaded (etag: $etag)");
703
- array_push($etags, $etag);
704
- set_transient("upd_${hash}_e$i", $etag, 3600*3);
705
- $successes++;
706
- } else {
707
- $this->error("S3 chunk $i: upload failed");
708
- $this->log("S3 chunk $i: upload failed");
709
- }
710
- }
711
- }
712
- if ($successes >= $chunks) {
713
- $this->log("S3 upload: all chunks uploaded; will now instruct S3 to re-assemble");
714
- if ($s3->completeMultipartUpload ($bucket_name, $filepath, $uploadId, $etags)) {
715
- $this->log("S3 upload: re-assembly succeeded");
716
- $this->uploaded_file($file);
717
- } else {
718
- $this->log("S3 upload: re-assembly failed");
719
- $this->error("S3 upload: re-assembly failed");
720
- }
721
- } else {
722
- $this->log("S3 upload: upload was not completely successful on this run");
723
- }
724
  }
725
- }
726
- $this->prune_retained_backups('s3',$s3,$orig_bucket_name);
727
- } else {
728
- $this->log("S3 Error: Failed to create bucket $bucket_name.");
729
- $this->error("S3 Error: Failed to create bucket $bucket_name. Check your permissions and credentials.");
730
- }
731
- }
732
-
733
- // This function taken from wordpress.org/extend/plugins/backup, by Sorin Iclanzan, under the GPLv3 or later at your choice
734
- function is_gdocs( $thing ) {
735
- if ( is_object( $thing ) && is_a( $thing, 'UpdraftPlus_GDocs' ) )
736
- return true;
737
- return false;
738
- }
739
-
740
- // This function modified from wordpress.org/extend/plugins/backup, by Sorin Iclanzan, under the GPLv3 or later at your choice
741
- function need_gdocs() {
742
-
743
- if ( ! $this->is_gdocs( $this->gdocs ) ) {
744
- if ( get_option('updraft_googledrive_token') == "" || get_option('updraft_googledrive_clientid') == "" || get_option('updraft_googledrive_secret') == "" ) {
745
- $this->log("GoogleDrive: this account is not authorised");
746
- return new WP_Error( "not_authorized", "Account is not authorized." );
747
- }
748
-
749
- if ( is_wp_error( $this->gdocs_access_token ) ) return $access_token;
750
-
751
- $this->gdocs = new UpdraftPlus_GDocs( $this->gdocs_access_token );
752
- $this->gdocs->set_option( 'chunk_size', 1 ); # 1Mb; change from default of 512Kb
753
- $this->gdocs->set_option( 'request_timeout', 10 ); # Change from default of 10s
754
- $this->gdocs->set_option( 'max_resume_attempts', 36 ); # Doesn't look like GDocs class actually uses this anyway
755
- }
756
- return true;
757
- }
758
-
759
- // Returns:
760
- // true = already uploaded
761
- // false = failure
762
- // otherwise, the file ID
763
- function googledrive_upload_file( $file, $title, $parent = '') {
764
-
765
- // Make sure $this->gdocs is a UpdraftPlus_GDocs object, or give an error
766
- if ( is_wp_error( $e = $this->need_gdocs() ) ) return false;
767
-
768
- $hash = md5($file);
769
- $transkey = 'upd_'.$hash.'_gloc';
770
- // This is unset upon completion, so if it is set then we are resuming
771
- $possible_location = get_transient($transkey);
772
-
773
- if ( empty( $possible_location ) ) {
774
- $this->log("$file: Attempting to upload file to Google Drive.");
775
- $location = $this->gdocs->prepare_upload( $file, $title, $parent );
776
- } else {
777
- $this->log("$file: Attempting to resume upload.");
778
- $location = $this->gdocs->resume_upload( $file, $possible_location );
779
- }
780
-
781
- if ( is_wp_error( $location ) ) {
782
- $this->log("GoogleDrive upload: an error occurred");
783
- foreach ($location->get_error_messages() as $msg) {
784
- $this->error($msg);
785
- $this->log("Error details: ".$msg);
786
- }
787
- return false;
788
- }
789
-
790
- if (!is_string($location) && true == $location) {
791
- $this->log("$file: this file is already uploaded");
792
- return true;
793
- }
794
-
795
- if ( is_string( $location ) ) {
796
- $res = $location;
797
- $this->log("Uploading file with title ".$title);
798
- $d = 0;
799
- do {
800
- $this->log("Google Drive upload: chunk d: $d, loc: $res");
801
- $res = $this->gdocs->upload_chunk();
802
- if (is_string($res)) set_transient($transkey, $res, 3600*3);
803
- $p = $this->gdocs->get_upload_percentage();
804
- if ( $p - $d >= 1 ) {
805
- $b = intval( $p - $d );
806
- // echo '<span style="width:' . $b . '%"></span>';
807
- $d += $b;
808
- }
809
- // $this->options['backup_list'][$id]['speed'] = $this->gdocs->get_upload_speed();
810
- } while ( is_string( $res ) );
811
- // echo '</div>';
812
-
813
- if ( is_wp_error( $res ) || $res !== true) {
814
- $this->log( "An error occurred during GoogleDrive upload (2)" );
815
- $this->error( "An error occurred during GoogleDrive upload (2)" );
816
- if (is_wp_error( $res )) {
817
- foreach ($res->get_error_messages() as $msg) { $this->log($msg); }
818
- }
819
- return false;
820
- }
821
-
822
- $this->log("The file was successfully uploaded to Google Drive in ".number_format_i18n( $this->gdocs->time_taken(), 3)." seconds at an upload speed of ".size_format( $this->gdocs->get_upload_speed() )."/s.");
823
-
824
- delete_transient($transkey);
825
- // unset( $this->options['backup_list'][$id]['location'], $this->options['backup_list'][$id]['attempt'] );
826
- }
827
-
828
- return $this->gdocs->get_file_id();
829
-
830
- // $this->update_quota();
831
- // Google's "user info" service
832
- // if ( empty( $this->options['user_info'] ) ) $this->set_user_info();
833
-
834
- }
835
-
836
- // This function just does the formalities, and off-loads the main work to googledrive_upload_file
837
- function googledrive_backup($backup_array) {
838
-
839
- require_once(dirname(__FILE__).'/includes/class-gdocs.php');
840
-
841
- // Do we have an access token?
842
- if ( !$access_token = $this->access_token( get_option('updraft_googledrive_token'), get_option('updraft_googledrive_clientid'), get_option('updraft_googledrive_secret') )) {
843
- $this->log('ERROR: Have not yet obtained an access token from Google (has the user authorised?)');
844
- return new WP_Error( "no_access_token", "Have not yet obtained an access token from Google (has the user authorised?");
845
- }
846
-
847
- $this->gdocs_access_token = $access_token;
848
-
849
- foreach ($backup_array as $file) {
850
- $file_path = trailingslashit(get_option('updraft_dir')).$file;
851
- $file_name = basename($file_path);
852
- $this->log("$file_name: Attempting to upload to Google Drive");
853
- $timer_start = microtime(true);
854
- if ( $id = $this->googledrive_upload_file( $file_path, $file_name, get_option('updraft_googledrive_remotepath')) ) {
855
- $this->log('OK: Archive ' . $file_name . ' uploaded to Google Drive in ' . ( round(microtime( true ) - $timer_start,2) ) . ' seconds (id: '.$id.')' );
856
- $this->uploaded_file($file, $id);
857
  } else {
858
- $this->error("$file_name: Failed to upload to Google Drive" );
859
- $this->log("ERROR: $file_name: Failed to upload to Google Drive" );
860
  }
861
  }
862
- $this->prune_retained_backups("googledrive",$access_token,get_option('updraft_googledrive_remotepath'));
 
863
  }
864
 
865
- function ftp_backup($backup_array) {
866
- if( !class_exists('ftp_wrapper')) {
867
- require_once(dirname(__FILE__).'/includes/ftp.class.php');
868
- }
869
- //handle SSL and errors at some point TODO
870
- $ftp = new ftp_wrapper(get_option('updraft_server_address'),get_option('updraft_ftp_login'),get_option('updraft_ftp_pass'));
871
- $ftp->passive = true;
872
- $ftp->connect();
873
- //$ftp->make_dir(); we may need to recursively create dirs? TODO
874
-
875
- $ftp_remote_path = trailingslashit(get_option('updraft_ftp_remote_path'));
876
- foreach($backup_array as $file) {
877
- $fullpath = trailingslashit(get_option('updraft_dir')).$file;
878
- if ($ftp->put($fullpath,$ftp_remote_path.$file,FTP_BINARY)) {
879
- $this->log("ERROR: $file_name: Successfully uploaded via FTP");
880
- $this->uploaded_file($file);
881
- } else {
882
- $this->error("$file_name: Failed to upload to FTP" );
883
- $this->log("ERROR: $file_name: Failed to upload to FTP" );
884
- }
885
- }
886
- $this->prune_retained_backups("ftp",$ftp,$ftp_remote_path);
887
- }
888
-
889
  function delete_local($file) {
890
  if(get_option('updraft_delete_local')) {
891
  $this->log("Deleting local file: $file");
@@ -1475,101 +1216,19 @@ class UpdraftPlus {
1475
  }
1476
 
1477
  function download_backup($file) {
1478
- switch(get_option('updraft_service')) {
1479
- case 'googledrive':
1480
- $this->download_googledrive_backup($file);
1481
- break;
1482
- case 's3':
1483
- $this->download_s3_backup($file);
1484
- break;
1485
- case 'ftp':
1486
- $this->download_ftp_backup($file);
1487
- break;
1488
- default:
1489
- $this->error('Automatic backup restoration is only available via S3, FTP, and local. Email and downloaded backup restoration must be performed manually.');
1490
- }
1491
- }
1492
-
1493
- function download_googledrive_backup($file) {
1494
 
1495
- require_once(dirname(__FILE__).'/includes/class-gdocs.php');
 
1496
 
1497
- // Do we have an access token?
1498
- if ( !$access_token = $this->access_token( get_option('updraft_googledrive_token'), get_option('updraft_googledrive_clientid'), get_option('updraft_googledrive_secret') )) {
1499
- $this->error('ERROR: Have not yet obtained an access token from Google (has the user authorised?)');
1500
- return false;
1501
- }
1502
-
1503
- $this->gdocs_access_token = $access_token;
1504
-
1505
- // Make sure $this->gdocs is a UpdraftPlus_GDocs object, or give an error
1506
- if ( is_wp_error( $e = $this->need_gdocs() ) ) return false;
1507
-
1508
- $ids = get_option('updraft_file_ids', array());
1509
- if (!isset($ids[$file])) {
1510
- $this->error("Google Drive error: $file: could not download: could not find a record of the Google Drive file ID for this file");
1511
- return;
1512
  } else {
1513
- $content_link = $this->gdocs->get_content_link( $ids[$file], $file );
1514
- if (is_wp_error($content_link)) {
1515
- $this->error("Could not find $file in order to download it (id: ".$ids[$file].")");
1516
- foreach ($content_link->get_error_messages() as $msg) {
1517
- $this->error($msg);
1518
- }
1519
- return false;
1520
- }
1521
- // Actually download the thing
1522
- $download_to = trailingslashit(get_option('updraft_dir')).$file;
1523
- $this->gdocs->download_data($content_link, $download_to);
1524
-
1525
- if (filesize($download_to) >0) {
1526
- return true;
1527
- } else {
1528
- $this->error("Google Drive error: zero-size file was downloaded");
1529
- return false;
1530
- }
1531
-
1532
  }
1533
 
1534
- return;
1535
-
1536
  }
1537
-
1538
- function download_s3_backup($file) {
1539
- if(!class_exists('S3')) {
1540
- require_once(dirname(__FILE__).'/includes/S3.php');
1541
- }
1542
- $s3 = new S3(get_option('updraft_s3_login'), get_option('updraft_s3_pass'));
1543
- $bucket_name = untrailingslashit(get_option('updraft_s3_remote_path'));
1544
- $bucket_path = "";
1545
- if (preg_match("#^([^/]+)/(.*)$#",$bucket_name,$bmatches)) {
1546
- $bucket_name = $bmatches[1];
1547
- $bucket_path = $bmatches[2]."/";
1548
- }
1549
- if (@$s3->getBucketLocation($bucket_name)) {
1550
- $fullpath = trailingslashit(get_option('updraft_dir')).$file;
1551
- if (!$s3->getObject($bucket_name, $bucket_path.$file, $fullpath)) {
1552
- $this->error("S3 Error: Failed to download $fullpath. Check your permissions and credentials.");
1553
- }
1554
- } else {
1555
- $this->error("S3 Error: Failed to access bucket $bucket_name. Check your permissions and credentials.");
1556
- }
1557
- }
1558
-
1559
- function download_ftp_backup($file) {
1560
- if( !class_exists('ftp_wrapper')) require_once(dirname(__FILE__).'/includes/ftp.class.php');
1561
-
1562
- //handle SSL and errors at some point TODO
1563
- $ftp = new ftp_wrapper(get_option('updraft_server_address'),get_option('updraft_ftp_login'),get_option('updraft_ftp_pass'));
1564
- $ftp->passive = true;
1565
- $ftp->connect();
1566
- //$ftp->make_dir(); we may need to recursively create dirs? TODO
1567
 
1568
- $ftp_remote_path = trailingslashit(get_option('updraft_ftp_remote_path'));
1569
- $fullpath = trailingslashit(get_option('updraft_dir')).$file;
1570
- $ftp->get($fullpath,$ftp_remote_path.$file,FTP_BINARY);
1571
- }
1572
-
1573
  function restore_backup($timestamp) {
1574
  global $wp_filesystem;
1575
  $backup_history = get_option('updraft_backup_history');
@@ -1667,8 +1326,7 @@ class UpdraftPlus {
1667
  $credentials = request_filesystem_credentials("options-general.php?page=updraftplus&action=updraft_create_backup_dir");
1668
  WP_Filesystem($credentials);
1669
  if ( $wp_filesystem->errors->get_error_code() ) {
1670
- foreach ( $wp_filesystem->errors->get_error_messages() as $message )
1671
- show_message($message);
1672
  exit;
1673
  }
1674
 
@@ -1677,12 +1335,10 @@ class UpdraftPlus {
1677
  $updraft_dir = ($updraft_dir)?$updraft_dir:$default_backup_dir;
1678
 
1679
  //chmod the backup dir to 0777. ideally we'd rather chgrp it but i'm not sure if it's possible to detect the group apache is running under (or what if it's not apache...)
1680
- if(!$wp_filesystem->mkdir($updraft_dir, 0777)) {
1681
- return false;
1682
- }
1683
  return true;
1684
  }
1685
-
1686
 
1687
  function memory_check_current() {
1688
  # Returns in megabytes
@@ -2273,7 +1929,7 @@ echo $delete_local; ?> /> <br>Check this to delete the local backup file (only s
2273
  </tr>
2274
  <tr>
2275
  <th>Debug mode:</th>
2276
- <td><input type="checkbox" name="updraft_debug_mode" value="1" <?php echo $debug_mode; ?> /> <br>Check this for more information, if something is going wrong. Will also drop a log file in your backup directory which you can examine. You <strong>must</strong> send me this log if you are filing a bug report.</td>
2277
  </tr>
2278
  <tr>
2279
  <td>
4
  Plugin URI: http://wordpress.org/extend/plugins/updraftplus
5
  Description: Uploads, themes, plugins, and your DB can be automatically backed up to Amazon S3, Google Drive, FTP, or emailed, on separate schedules.
6
  Author: David Anderson.
7
+ Version: 1.0.18
8
  Donate link: http://david.dw-perspective.org.uk/donate
9
  License: GPLv3 or later
10
  Author URI: http://wordshell.net
18
 
19
  Encrypt filesystem, if memory allows (and have option for abort if not); split up into multiple zips when needed
20
  // Does not delete old custom directories upon a restore?
21
+ // Re-do making of zip files to allow resumption (every x files, store the state in a transient)
22
  */
23
 
24
  /* Portions copyright 2010 Paul Kehrer
44
  // 15 minutes
45
  @set_time_limit(900);
46
 
47
+ if (!isset($updraftplus)) $updraftplus = new UpdraftPlus();
48
 
49
+ if (!$updraftplus->memory_check(192)) {
50
  # TODO: Better solution is to split the backup set into manageable chunks based on this limit
51
  @ini_set('memory_limit', '192M'); //up the memory limit for large backup files
52
  }
53
 
54
+ define('UPDRAFTPLUS_DIR', dirname(__FILE__));
55
  define('UPDRAFT_DEFAULT_OTHERS_EXCLUDE','upgrade,cache,updraft,index.php');
56
 
57
  class UpdraftPlus {
58
 
59
+ var $version = '1.0.18';
60
 
61
  var $dbhandle;
62
  var $errors = array();
88
  // Handle Google OAuth 2.0 - ?page=updraftplus&action=auth
89
  // Also handle action=downloadlog
90
  function handle_url_actions() {
91
+ // First, basic security check: must be an admin page, with ability to manage options, with the right parameters
92
+ if ( is_admin() && current_user_can('manage_options') && isset( $_GET['page'] ) && $_GET['page'] == 'updraftplus' && isset($_GET['action']) ) {
93
  if ($_GET['action'] == 'auth') {
94
  if ( isset( $_GET['state'] ) ) {
95
  if ( $_GET['state'] == 'token' )
170
  return;
171
  }
172
 
173
+ // Get a Google account refresh token using the code received from gdrive_auth_request
 
 
174
  function gdrive_auth_token() {
175
  if( isset( $_GET['code'] ) ) {
176
  $post_vars = array(
201
  }
202
  }
203
 
204
+ // Revoke a Google account refresh token
 
 
205
  function gdrive_auth_revoke() {
206
  $ignore = wp_remote_get('https://accounts.google.com/o/oauth2/revoke?token='.get_option('updraft_googledrive_token'));
207
  update_option('updraft_googledrive_token','');
322
  if (is_array($to_delete)) {
323
  foreach ($to_delete as $key => $file) {
324
  if (is_file($bdir.'/'.$file)) {
325
+ $this->log("Deleting the file we created: ".$file);
326
  @unlink($bdir.'/'.$file);
327
  }
328
  }
432
 
433
  @fclose($this->logfile_handle);
434
 
435
+ // Don't delete the log file now; delete it upon rotation
436
+ //if (!get_option('updraft_debug_mode')) @unlink($this->logfile_name);
437
 
438
  }
439
 
481
 
482
  }
483
 
484
+ // Dispatch to the relevant function
485
  function cloud_backup($backup_array) {
486
+
487
+ $service = get_option('updraft_service');
488
+ $this->log("Cloud backup selection: ".$service);
489
+ @set_time_limit(900);
490
+
491
+ $method_include = UPDRAFTPLUS_DIR.'/methods/'.$service.'.php';
492
+ if (file_exists($method_include)) require_once($method_include);
493
+
494
+ if (function_exists("updraftplus_${service}_backup")) {
495
+ // New style - external, allowing more plugability
496
+ call_user_func("updraftplus_${service}_backup", $backup_array);
497
+ } else {
498
+ $this->prune_retained_backups("local");
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
499
  }
500
  }
501
 
504
  $this->log("Retain: beginning examination of existing backup sets");
505
  $updraft_retain = get_option('updraft_retain');
506
  // Number of backups to retain
507
+ $retain = (isset($updraft_retain)) ? get_option('updraft_retain') : 1;
508
  $this->log("Retain: user setting: number to retain = $retain");
509
  // Returns an array, most recent first, of backup sets
510
  $backup_history = $this->get_backup_history();
601
  unset($backup_to_examine['themes']);
602
  unset($backup_to_examine['uploads']);
603
  unset($backup_to_examine['others']);
 
604
  }
605
  }
606
  // Delete backup set completely if empty, o/w just remove DB
607
+ if (count($backup_to_examine) == 0 || (count($backup_to_examine) == 1 && isset($backup_to_examine['nonce']))) {
608
  $this->log("$backup_datestamp: this backup set is now empty; will remove from history");
609
  unset($backup_history[$backup_datestamp]);
610
+ if (isset($backup_to_examine['nonce'])) {
611
+ $fullpath = trailingslashit(get_option('updraft_dir')).'log.'.$backup_to_examine['nonce'].'.txt';
612
+ if (is_file($fullpath)) {
613
+ $this->log("$backup_datestamp: deleting log file (log.".$backup_to_examine['nonce'].".txt)");
614
+ @unlink($fullpath);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
615
  } else {
616
+ $this->log("$backup_datestamp: corresponding log file not found - must have already been deleted");
 
617
  }
618
  } else {
619
+ $this->log("$backup_datestamp: no nonce record found in the backup set, so cannot delete any remaining log file");
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
620
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
621
  } else {
622
+ $this->log("$backup_datestamp: this backup set remains non-empty; will retain in history");
623
+ $backup_history[$backup_datestamp] = $backup_to_examine;
624
  }
625
  }
626
+ $this->log("Retain: saving new backup history (sets now: ".count($backup_history).") and finishing retain operation");
627
+ update_option('updraft_backup_history',$backup_history);
628
  }
629
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
630
  function delete_local($file) {
631
  if(get_option('updraft_delete_local')) {
632
  $this->log("Deleting local file: $file");
1216
  }
1217
 
1218
  function download_backup($file) {
1219
+ $service = get_option('updraft_service');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1220
 
1221
+ $method_include = UPDRAFTPLUS_DIR.'/methods/'.$service.'.php';
1222
+ if (file_exists($method_include)) require_once($method_include);
1223
 
1224
+ if (function_exists("updraftplus_download_${service}_backup")) {
1225
+ call_user_func("updraftplus_download_${service}_backup", $file);
 
 
 
 
 
 
 
 
 
 
 
 
 
1226
  } else {
1227
+ $this->error('Automatic backup restoration is not available with the method: $service.');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1228
  }
1229
 
 
 
1230
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1231
 
 
 
 
 
 
1232
  function restore_backup($timestamp) {
1233
  global $wp_filesystem;
1234
  $backup_history = get_option('updraft_backup_history');
1326
  $credentials = request_filesystem_credentials("options-general.php?page=updraftplus&action=updraft_create_backup_dir");
1327
  WP_Filesystem($credentials);
1328
  if ( $wp_filesystem->errors->get_error_code() ) {
1329
+ foreach ( $wp_filesystem->errors->get_error_messages() as $message ) show_message($message);
 
1330
  exit;
1331
  }
1332
 
1335
  $updraft_dir = ($updraft_dir)?$updraft_dir:$default_backup_dir;
1336
 
1337
  //chmod the backup dir to 0777. ideally we'd rather chgrp it but i'm not sure if it's possible to detect the group apache is running under (or what if it's not apache...)
1338
+ if(!$wp_filesystem->mkdir($updraft_dir, 0777)) return false;
1339
+
 
1340
  return true;
1341
  }
 
1342
 
1343
  function memory_check_current() {
1344
  # Returns in megabytes
1929
  </tr>
1930
  <tr>
1931
  <th>Debug mode:</th>
1932
+ <td><input type="checkbox" name="updraft_debug_mode" value="1" <?php echo $debug_mode; ?> /> <br>Check this to receive more information on the backup process - useful if something is going wrong. You <strong>must</strong> send me this log if you are filing a bug report.</td>
1933
  </tr>
1934
  <tr>
1935
  <td>