Version Description
- 12/26/2012 =
- First steps towards modularising cloud upload methods
Download this release
Release Info
Developer | DavidAnderson |
Plugin | UpdraftPlus WordPress Backup Plugin |
Version | 1.0.18 |
Comparing to | |
See all releases |
Code changes from version 1.0.16 to 1.0.18
- methods/email.php +18 -0
- methods/ftp.php +44 -0
- methods/googledrive.php +190 -0
- methods/s3.php +128 -0
- readme.txt +4 -1
- 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 |
-
|
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.
|
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 |
-
$
|
47 |
|
48 |
-
if(!$
|
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.
|
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 |
-
|
|
|
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("
|
327 |
@unlink($bdir.'/'.$file);
|
328 |
}
|
329 |
}
|
@@ -433,7 +432,8 @@ class UpdraftPlus {
|
|
433 |
|
434 |
@fclose($this->logfile_handle);
|
435 |
|
436 |
-
|
|
|
437 |
|
438 |
}
|
439 |
|
@@ -481,36 +481,21 @@ class UpdraftPlus {
|
|
481 |
|
482 |
}
|
483 |
|
|
|
484 |
function cloud_backup($backup_array) {
|
485 |
-
|
486 |
-
|
487 |
-
|
488 |
-
|
489 |
-
|
490 |
-
|
491 |
-
|
492 |
-
|
493 |
-
|
494 |
-
|
495 |
-
|
496 |
-
|
497 |
-
|
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 |
-
|
627 |
-
|
628 |
-
|
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("
|
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->
|
859 |
-
$
|
860 |
}
|
861 |
}
|
862 |
-
$this->
|
|
|
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 |
-
|
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 |
-
|
|
|
1496 |
|
1497 |
-
|
1498 |
-
|
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 |
-
$
|
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 |
-
|
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
|
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>
|