|
1
|
|
|
<?php |
|
2
|
|
|
// don't call the file directly |
|
3
|
|
|
defined( 'ABSPATH' ) or die(); |
|
4
|
|
|
|
|
5
|
|
|
class VaultPress_Filesystem { |
|
6
|
|
|
|
|
7
|
|
|
var $type = null; |
|
8
|
|
|
var $dir = null; |
|
9
|
|
|
var $keys = array( 'ino', 'uid', 'gid', 'size', 'mtime', 'blksize', 'blocks' ); |
|
10
|
|
|
|
|
11
|
|
|
function __construct() { |
|
12
|
|
|
} |
|
13
|
|
|
|
|
14
|
|
|
function want( $type ) { |
|
15
|
|
|
$vp = VaultPress::init(); |
|
16
|
|
|
|
|
17
|
|
|
if ( $type == 'plugins' ) { |
|
18
|
|
|
$this->dir = realpath( $vp->resolve_content_dir() . 'plugins' ); |
|
19
|
|
|
$this->type = 'p'; |
|
20
|
|
|
return true; |
|
21
|
|
|
} |
|
22
|
|
|
if ( $type == 'themes' ) { |
|
23
|
|
|
$this->dir = realpath( $vp->resolve_content_dir() . 'themes' ); |
|
24
|
|
|
$this->type = 't'; |
|
25
|
|
|
return true; |
|
26
|
|
|
} |
|
27
|
|
|
if ( $type == 'uploads' ) { |
|
28
|
|
|
$this->dir = realpath( $vp->resolve_upload_path() ); |
|
29
|
|
|
$this->type = 'u'; |
|
30
|
|
|
return true; |
|
31
|
|
|
} |
|
32
|
|
|
if ( $type == 'content' ) { |
|
33
|
|
|
$this->dir = realpath( $vp->resolve_content_dir() ); |
|
34
|
|
|
$this->type = 'c'; |
|
35
|
|
|
return true; |
|
36
|
|
|
} |
|
37
|
|
|
if ( $type == 'root' ) { |
|
38
|
|
|
$this->dir = realpath( ABSPATH ); |
|
39
|
|
|
$this->type = 'r'; |
|
40
|
|
|
return true; |
|
41
|
|
|
} |
|
42
|
|
|
die( 'naughty naughty' ); |
|
43
|
|
|
} |
|
44
|
|
|
|
|
45
|
|
|
function fdump( $file ) { |
|
46
|
|
|
header("Content-Type: application/octet-stream;"); |
|
47
|
|
|
header("Content-Transfer-Encoding: binary"); |
|
48
|
|
|
@ob_end_clean(); |
|
|
|
|
|
|
49
|
|
|
if ( !file_exists( $file ) || !is_readable( $file ) ) { |
|
50
|
|
|
$file_name = basename( $file ); |
|
51
|
|
|
if ( 'wp-config.php' == $file_name ) { |
|
52
|
|
|
$dir = dirname( $file ); |
|
53
|
|
|
$dir = explode( DIRECTORY_SEPARATOR, $dir ); |
|
54
|
|
|
array_pop( $dir ); |
|
55
|
|
|
$dir = implode( DIRECTORY_SEPARATOR, $dir ); |
|
56
|
|
|
$file = trailingslashit( $dir ) . $file_name; |
|
57
|
|
|
if ( !file_exists( $file ) || !is_readable( $file ) ) |
|
58
|
|
|
die( "no such file" ); |
|
59
|
|
|
} else { |
|
60
|
|
|
die( "no such file" ); |
|
61
|
|
|
} |
|
62
|
|
|
} |
|
63
|
|
|
if ( !is_file( $file ) && !is_link( $file ) ) |
|
64
|
|
|
die( "can only dump files" ); |
|
65
|
|
|
$fp = @fopen( $file, 'rb' ); |
|
66
|
|
|
if ( !$fp ) |
|
67
|
|
|
die( "could not open file" ); |
|
68
|
|
|
while ( !feof( $fp ) ) |
|
69
|
|
|
echo @fread( $fp, 8192 ); |
|
70
|
|
|
@fclose( $fp ); |
|
|
|
|
|
|
71
|
|
|
die(); |
|
72
|
|
|
} |
|
73
|
|
|
|
|
74
|
|
|
function exec_checksum( $file, $method ) { |
|
75
|
|
|
if ( !function_exists( 'exec' ) ) |
|
76
|
|
|
return false; |
|
77
|
|
|
$out = array(); |
|
78
|
|
|
if ( 'md5' == $method ) |
|
79
|
|
|
$method_bin = 'md5sum'; |
|
80
|
|
|
if ( 'sha1' == $method ) |
|
81
|
|
|
$method_bin = 'sha1sum'; |
|
82
|
|
|
$checksum = ''; |
|
83
|
|
|
exec( sprintf( '%s %s', escapeshellcmd( $method_bin ), escapeshellarg( $file ) ), $out ); |
|
|
|
|
|
|
84
|
|
|
if ( !empty( $out ) ) |
|
85
|
|
|
$checksum = trim( array_shift( explode( ' ', array_pop( $out ) ) ) ); |
|
|
|
|
|
|
86
|
|
|
if ( !empty( $checksum ) ) |
|
87
|
|
|
return $checksum; |
|
88
|
|
|
return false; |
|
89
|
|
|
} |
|
90
|
|
|
|
|
91
|
|
|
function checksum_file( $file, $method ) { |
|
92
|
|
|
$use_exec = false; |
|
93
|
|
|
if ( filesize( $file ) >= 104857600 ) |
|
94
|
|
|
$use_exec = true; |
|
95
|
|
|
switch( $method ) { |
|
96
|
|
View Code Duplication |
case 'md5': |
|
97
|
|
|
if ( $use_exec ) { |
|
98
|
|
|
$checksum = $this->exec_checksum( $file, $method ); |
|
99
|
|
|
if ( !empty( $checksum ) ) |
|
100
|
|
|
return $checksum; |
|
101
|
|
|
} |
|
102
|
|
|
return md5_file( $file ); |
|
103
|
|
|
break; |
|
|
|
|
|
|
104
|
|
View Code Duplication |
case 'sha1': |
|
105
|
|
|
if ( $use_exec ) { |
|
106
|
|
|
$checksum = $this->exec_checksum( $file, $method ); |
|
107
|
|
|
if ( !empty( $checksum ) ) |
|
108
|
|
|
return $checksum; |
|
109
|
|
|
} |
|
110
|
|
|
return sha1_file( $file ); |
|
111
|
|
|
break; |
|
|
|
|
|
|
112
|
|
|
default: |
|
113
|
|
|
return false; |
|
114
|
|
|
break; |
|
|
|
|
|
|
115
|
|
|
} |
|
116
|
|
|
} |
|
117
|
|
|
|
|
118
|
|
|
function stat( $file, $md5=true, $sha1=true ) { |
|
119
|
|
|
if ( ! file_exists( $file ) ) |
|
120
|
|
|
return false; |
|
121
|
|
|
|
|
122
|
|
|
$rval = array(); |
|
123
|
|
|
foreach ( stat( $file ) as $i => $v ) { |
|
124
|
|
|
if ( is_numeric( $i ) ) |
|
125
|
|
|
continue; |
|
126
|
|
|
$rval[$i] = $v; |
|
127
|
|
|
} |
|
128
|
|
|
$rval['type'] = filetype( $file ); |
|
129
|
|
|
if ( $rval['type'] == 'file' ) { |
|
130
|
|
|
if ( $md5 ) |
|
131
|
|
|
$rval['md5'] = $this->checksum_file( $file, 'md5' ); |
|
132
|
|
|
if ( $sha1 ) |
|
133
|
|
|
$rval['sha1'] = $this->checksum_file( $file, 'sha1' ); |
|
134
|
|
|
} |
|
135
|
|
|
$dir = $this->dir; |
|
136
|
|
|
if ( 0 !== strpos( $file, $dir ) && 'wp-config.php' == basename( $file ) ) { |
|
137
|
|
|
$dir = explode( DIRECTORY_SEPARATOR, $dir ); |
|
138
|
|
|
array_pop( $dir ); |
|
139
|
|
|
$dir = implode( DIRECTORY_SEPARATOR, $dir ); |
|
140
|
|
|
} |
|
141
|
|
|
$rval['full_path'] = realpath( $file ); |
|
142
|
|
|
|
|
143
|
|
|
// Avoid rebuilding path tidy-up regex when fetching multiple entries |
|
144
|
|
|
static $last_dir = null; |
|
145
|
|
|
static $dir_regex = null; |
|
146
|
|
|
if ( $last_dir !== $dir ) { |
|
147
|
|
|
$dir_regex = '#' . preg_quote( $dir ) . '#'; |
|
148
|
|
|
$last_dir = $dir; |
|
149
|
|
|
} |
|
150
|
|
|
|
|
151
|
|
|
$rval['path'] = preg_replace( $dir_regex, '', $file, 1 ); |
|
152
|
|
|
return $rval; |
|
153
|
|
|
} |
|
154
|
|
|
|
|
155
|
|
|
function ls( $what, $md5=false, $sha1=false, $limit=null, $offset=null, $full_list=false ) { |
|
156
|
|
|
clearstatcache(); |
|
157
|
|
|
$path = realpath($this->dir . $what); |
|
158
|
|
|
$dir = $this->dir; |
|
159
|
|
View Code Duplication |
if ( !$path && '/wp-config.php' == $what ) { |
|
160
|
|
|
$dir = explode( DIRECTORY_SEPARATOR, $dir ); |
|
161
|
|
|
array_pop( $dir ); |
|
162
|
|
|
$dir = implode( DIRECTORY_SEPARATOR, $dir ); |
|
163
|
|
|
$path = realpath( $dir . $what ); |
|
164
|
|
|
} |
|
165
|
|
|
if ( is_file($path) ) |
|
166
|
|
|
return $this->stat( $path, $md5, $sha1 ); |
|
167
|
|
|
if ( is_dir($path) ) { |
|
168
|
|
|
$entries = array(); |
|
169
|
|
|
$current = 0; |
|
170
|
|
|
$offset = (int)$offset; |
|
171
|
|
|
$orig_limit = (int)$limit; |
|
172
|
|
|
$limit = $offset + (int)$limit; |
|
173
|
|
|
foreach ( (array)$this->scan_dir( $path ) as $i ) { |
|
174
|
|
|
if ( !$full_list && !$this->should_backup_file( $i ) ) |
|
175
|
|
|
continue; |
|
176
|
|
|
$current++; |
|
177
|
|
|
if ( $offset >= $current ) |
|
178
|
|
|
continue; |
|
179
|
|
|
if ( $limit && $limit < $current ) |
|
180
|
|
|
break; |
|
181
|
|
|
|
|
182
|
|
|
// don't sha1 files over 100MB if we are batching due to memory consumption |
|
183
|
|
|
if ( $sha1 && $orig_limit > 1 && is_file( $i ) && (int)@filesize( $i ) > 104857600 ) |
|
184
|
|
|
$sha1 = false; |
|
185
|
|
|
|
|
186
|
|
|
$entries[] = $this->stat( $i, $md5, $sha1 ); |
|
187
|
|
|
} |
|
188
|
|
|
return $entries; |
|
189
|
|
|
} |
|
190
|
|
|
} |
|
191
|
|
|
|
|
192
|
|
|
function should_backup_file( $filepath ) { |
|
193
|
|
|
$vp = VaultPress::init(); |
|
194
|
|
|
if ( is_dir( $filepath ) ) |
|
195
|
|
|
$filepath = trailingslashit( $filepath ); |
|
196
|
|
|
$regex_patterns = $vp->get_should_ignore_files(); |
|
197
|
|
|
foreach ( $regex_patterns as $pattern ) { |
|
198
|
|
|
$matches = array(); |
|
199
|
|
|
if ( preg_match( $pattern, $filepath, $matches ) ) { |
|
200
|
|
|
return false; |
|
201
|
|
|
} |
|
202
|
|
|
} |
|
203
|
|
|
return true; |
|
204
|
|
|
} |
|
205
|
|
|
|
|
206
|
|
|
function validate( $file ) { |
|
207
|
|
|
$rpath = realpath( $this->dir.$file ); |
|
208
|
|
|
$dir = $this->dir; |
|
209
|
|
View Code Duplication |
if ( !$rpath && '/wp-config.php' == $file ) { |
|
210
|
|
|
$dir = explode( DIRECTORY_SEPARATOR, $dir ); |
|
211
|
|
|
array_pop( $dir ); |
|
212
|
|
|
$dir = implode( DIRECTORY_SEPARATOR, $dir ); |
|
213
|
|
|
$rpath = realpath( $dir . $file ); |
|
214
|
|
|
} |
|
215
|
|
|
if ( !$rpath ) |
|
216
|
|
|
die( serialize( array( 'type' => 'null', 'path' => $file ) ) ); |
|
217
|
|
|
if ( is_dir( $rpath ) ) |
|
218
|
|
|
$rpath = "$rpath/"; |
|
219
|
|
|
if ( strpos( $rpath, $dir ) !== 0 ) |
|
220
|
|
|
return false; |
|
221
|
|
|
return true; |
|
222
|
|
|
} |
|
223
|
|
|
|
|
224
|
|
|
function dir_examine( $subdir='', $recursive=true, $origin=false ) { |
|
225
|
|
|
$res = array(); |
|
226
|
|
|
if ( !$subdir ) |
|
227
|
|
|
$subdir='/'; |
|
228
|
|
|
$dir = $this->dir . $subdir; |
|
229
|
|
|
if ( $origin === false ) |
|
230
|
|
|
$origin = $this->dir . $subdir; |
|
231
|
|
|
if ( is_file($dir) ) { |
|
232
|
|
|
if ( $origin == $dir ) |
|
233
|
|
|
$name = str_replace( $this->dir, '/', $subdir ); |
|
234
|
|
|
else |
|
235
|
|
|
$name = str_replace( $origin, '/', $dir ); |
|
236
|
|
|
$res[$name] = $this->stat( $dir.$entry ); |
|
|
|
|
|
|
237
|
|
|
return $res; |
|
238
|
|
|
} |
|
239
|
|
|
$d = dir( $dir ); |
|
240
|
|
|
if ( !$d ) |
|
241
|
|
|
return $res; |
|
242
|
|
|
while ( false !== ( $entry = $d->read() ) ) { |
|
243
|
|
|
$rpath = realpath( $dir.$entry ); |
|
244
|
|
|
$bname = basename( $rpath ); |
|
|
|
|
|
|
245
|
|
|
if ( is_link( $dir.$entry ) ) |
|
246
|
|
|
continue; |
|
247
|
|
|
if ( $entry == '.' || $entry == '..' || $entry == '...' ) |
|
248
|
|
|
continue; |
|
249
|
|
|
if ( !$this->validate( $subdir.$entry ) ) |
|
|
|
|
|
|
250
|
|
|
continue; |
|
251
|
|
|
$name = str_replace( $origin, '/', $dir.$entry ); |
|
252
|
|
|
$res[$name] = $this->stat( $dir.$entry ); |
|
253
|
|
|
if ( $recursive && is_dir( $this->dir.$subdir.'/'.$entry ) ) { |
|
254
|
|
|
$res = array_merge( $res, $this->dir_examine( $subdir.$entry.'/', $recursive, $origin ) ); |
|
|
|
|
|
|
255
|
|
|
} |
|
256
|
|
|
} |
|
257
|
|
|
return $res; |
|
258
|
|
|
} |
|
259
|
|
|
|
|
260
|
|
|
function dir_checksum( $base, &$list, $recursive=true ) { |
|
261
|
|
|
if ( $list == null ) |
|
262
|
|
|
$list = array(); |
|
263
|
|
|
|
|
264
|
|
|
if ( 0 !== strpos( $base, $this->dir ) ) |
|
265
|
|
|
$base = $this->dir . rtrim( $base, '/' ); |
|
266
|
|
|
|
|
267
|
|
|
$shortbase = substr( $base, strlen( $this->dir ) ); |
|
268
|
|
|
if ( !$shortbase ) |
|
269
|
|
|
$shortbase = '/'; |
|
270
|
|
|
$stat = stat( $base ); |
|
|
|
|
|
|
271
|
|
|
$directories = array(); |
|
272
|
|
|
$files = (array)$this->scan_dir( $base ); |
|
273
|
|
|
array_push( $files, $base ); |
|
274
|
|
|
foreach ( $files as $file ) { |
|
275
|
|
|
if ( $file !== $base && @is_dir( $file ) ) { |
|
276
|
|
|
$directories[] = $file; |
|
277
|
|
|
continue; |
|
278
|
|
|
} |
|
279
|
|
|
$stat = @stat( $file ); |
|
280
|
|
|
if ( !$stat ) |
|
281
|
|
|
continue; |
|
282
|
|
|
$shortstat = array(); |
|
283
|
|
|
foreach( $this->keys as $key ) { |
|
284
|
|
|
if ( isset( $stat[$key] ) ) |
|
285
|
|
|
$shortstat[$key] = $stat[$key]; |
|
286
|
|
|
} |
|
287
|
|
|
$list[$shortbase][basename( $file )] = $shortstat; |
|
288
|
|
|
} |
|
289
|
|
|
$list[$shortbase] = md5( serialize( $list[$shortbase] ) ); |
|
290
|
|
|
if ( !$recursive ) |
|
291
|
|
|
return $list; |
|
292
|
|
|
foreach ( $directories as $dir ) { |
|
293
|
|
|
$this->dir_checksum( $dir, $list, $recursive ); |
|
294
|
|
|
} |
|
295
|
|
|
return $list; |
|
296
|
|
|
} |
|
297
|
|
|
|
|
298
|
|
|
function scan_dir( $path ) { |
|
299
|
|
|
$files = array(); |
|
300
|
|
|
|
|
301
|
|
|
if ( false === is_readable( $path ) ) { |
|
302
|
|
|
return array(); |
|
303
|
|
|
} |
|
304
|
|
|
|
|
305
|
|
|
$dh = opendir( $path ); |
|
306
|
|
|
|
|
307
|
|
|
if ( false === $dh ) { |
|
308
|
|
|
return array(); |
|
309
|
|
|
} |
|
310
|
|
|
|
|
311
|
|
|
while ( false !== ( $file = readdir( $dh ) ) ) { |
|
312
|
|
|
if ( $file == '.' || $file == '..' ) continue; |
|
313
|
|
|
$files[] = "$path/$file"; |
|
314
|
|
|
} |
|
315
|
|
|
|
|
316
|
|
|
closedir( $dh ); |
|
317
|
|
|
sort( $files ); |
|
318
|
|
|
return $files; |
|
319
|
|
|
} |
|
320
|
|
|
} |
|
321
|
|
|
|
If you suppress an error, we recommend checking for the error condition explicitly: