files.inc
11.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
<?php
/**
* @file
* General file handling code for Backup and Migrate.
*/
define('BACKUP_MIGRATE_FILENAME_MAXLENGTH', 255);
/**
* Add a file to the temporary files list for deletion when we're done.
*/
function backup_migrate_temp_files_add($filepath = NULL) {
static $files = array();
if (!$filepath) {
return $files;
}
else {
$files[] = $filepath;
}
}
/**
* Delete all temporary files.
*/
function _backup_migrate_temp_files_delete() {
// Delete the temp files created during this run.
foreach (backup_migrate_temp_files_add() as $file) {
$file = drupal_realpath($file);
if (file_exists($file) && is_writable($file)) {
unlink($file);
}
}
// Delete temp files abandoned for 6 or more hours.
$dir = file_stream_wrapper_get_instance_by_scheme('temporary')->getDirectoryPath();
$expire = time() - variable_get('backup_migrate_cleanup_time', 21600);
if (file_exists($dir) && is_dir($dir) && is_readable($dir) && $handle = opendir($dir)) {
while (FALSE !== ($file = @readdir($handle))) {
// Delete 'backup_migrate_' files in the temp directory that are older than the expire time.
// We should only attempt to delete writable files to prevent errors in shared environments.
// This could still cause issues in shared environments with poorly configured file permissions.
if (strpos($file, 'backup_migrate_') === 0 && is_writable("$dir/$file") && @filectime("$dir/$file") < $expire) {
unlink("$dir/$file");
}
}
closedir($handle);
}
}
/**
* Return a list of backup filetypes.
*/
function _backup_migrate_filetypes() {
backup_migrate_include('filters');
$out = backup_migrate_filters_file_types();
foreach ($out as $key => $info) {
$out[$key]['id'] = empty($info['id']) ? $key : $info['id'];
}
return $out;
}
/**
* Adjust the length of a filename to allow for a string to be appended,
* staying within the maximum filename limit.
*/
function _backup_migrate_filename_append_prepare($filename, $append_str) {
$max_name_len = BACKUP_MIGRATE_FILENAME_MAXLENGTH - drupal_strlen($append_str);
if (drupal_strlen($filename) > $max_name_len) {
$filename = drupal_substr($filename, 0, $max_name_len);
}
return $filename;
}
/**
* Construct a filename using token and some cleaning.
*/
function _backup_migrate_construct_filename($filename, $timestamp='') {
if (module_exists('token')) {
$filename = token_replace($filename);
}
$filename = preg_replace("/[^a-zA-Z0-9\.\-_]/", "", $filename);
$filename = _backup_migrate_filename_append_prepare($filename, $timestamp);
$filename .= '-' . $timestamp;
$filename = trim($filename, '-');
if (drupal_strlen($filename) == 0) {
$filename = 'untitled';
}
return $filename;
}
/**
* Construct a default filename using the site's name.
*/
function _backup_migrate_default_filename() {
if (module_exists('token')) {
return '[site:name]';
}
else {
// Cleaning the string isn't strictly necessary but it looks better in the settings field.
return variable_get('site_name', 'backup_migrate');
}
}
/**
* An output buffer callback which simply throws away the buffer instead of sending it to the browser.
*/
function _backup_migrate_file_dispose_buffer($buffer) {
return "";
}
/**
* A backup file which allows for saving to and reading from the server.
*/
class backup_file {
var $file_info = array();
var $type = array();
var $ext = array();
var $path = "";
var $name = "";
var $handle = NULL;
/**
* Construct a file object given a file path, or create a temp file for writing.
*/
function backup_file($params = array()) {
if (isset($params['filepath']) && file_exists($params['filepath'])) {
$this->set_filepath($params['filepath']);
}
else {
$this->set_file_info($params);
$this->temporary_file();
}
}
/**
* Get the file_id if the file has been saved to a destination.
*/
function file_id() {
// The default file_id is the filename. Destinations can override the file_id if needed.
return isset($this->file_info['file_id']) ? $this->file_info['file_id'] : $this->filename();
}
/**
* Get the current filepath.
*/
function filepath() {
return drupal_realpath($this->path);
}
/**
* Get the final filename.
*/
function filename($name = NULL) {
if ($name) {
$this->name = $name;
}
return $this->name .'.'. $this->extension();
}
/**
* Set the current filepath.
*/
function set_filepath($path) {
$this->path = $path;
$params = array(
'filename' => basename($path),
);
if (file_exists($path)) {
$params['filesize'] = filesize($path);
$params['filetime'] = filemtime($path);
}
$this->set_file_info($params);
}
/**
* Get one or all pieces of info for the file.
*/
function info($key = NULL) {
if ($key) {
return @$this->file_info[$key];
}
return $this->file_info;
}
/**
* Get the file extension.
*/
function extension() {
return implode(".", $this->ext);
}
/**
* Get the file type.
*/
function type() {
return $this->type;
}
/**
* Get the file mimetype.
*/
function mimetype() {
return @$this->type['filemime'] ? $this->type['filemime'] : 'application/octet-stream';
}
/**
* Get the file mimetype.
*/
function type_id() {
return @$this->type['id'];
}
/**
* Can this file be used to backup to.
*/
function can_backup() {
return @$this->type['backup'];
}
/**
* Can this file be used to restore to.
*/
function can_restore() {
return @$this->type['restore'];
}
/**
* Can this file be used to restore to.
*/
function is_recognized_type() {
return @$this->type['restore'] || @$this->type['backup'];
}
/**
* Open a file for reading or writing.
*/
function open($write = FALSE, $binary = FALSE) {
if (!$this->handle) {
$path = $this->filepath();
// Check if the file can be read/written.
if ($write && ((file_exists($path) && !is_writable($path)) || !is_writable(dirname($path)))) {
_backup_migrate_message('The file %path cannot be written to.', array('%path' => $path), 'error');
return FALSE;
}
if (!$write && !is_readable($path)) {
_backup_migrate_message('The file %path cannot be read.', array('%path' => $path), 'error');
return FALSE;
}
// Open the file.
$mode = ($write ? "w" : "r") . ($binary ? "b" : "");
$this->handle = fopen($path, $mode);
return $this->handle;
}
return NULL;
}
/**
* Close a file when we're done reading/writing.
*/
function close() {
fclose($this->handle);
$this->handle = NULL;
}
/**
* Write a line to the file.
*/
function write($data) {
if (!$this->handle) {
$this->handle = $this->open(TRUE);
}
if ($this->handle) {
fwrite($this->handle, $data);
}
}
/**
* Read a line from the file.
*/
function read($size = NULL) {
if (!$this->handle) {
$this->handle = $this->open();
}
if ($this->handle && !feof($this->handle)) {
return $size ? fread($this->handle, $size) : fgets($this->handle);
}
return NULL;
}
/**
* Write data to the file.
*/
function put_contents($data) {
file_put_contents($this->filepath(), $data);
}
/**
* Read data from the file.
*/
function get_contents() {
return file_get_contents($this->filepath());
}
/**
* Transfer file using http to client. Similar to the built in file_transfer,
* but it calls module_invoke_all('exit') so that temp files can be deleted.
*/
function transfer() {
$headers = array(
array('key' => 'Content-Type', 'value' => $this->mimetype()),
array('key' => 'Content-Disposition', 'value' => 'attachment; filename="'. $this->filename() .'"'),
);
// In some circumstances, web-servers will double compress gzipped files.
// This may help aleviate that issue by disabling mod-deflate.
if ($this->mimetype() == 'application/x-gzip') {
$headers[] = 'Content-Encoding: gzip';
}
if ($size = $this->info('filesize')) {
$headers[] = array('key' => 'Content-Length', 'value' => $size);
}
// Suppress the warning you get when the buffer is empty.
@ob_end_clean();
if ($this->open(FALSE, TRUE)) {
foreach ($headers as $header) {
// To prevent HTTP header injection, we delete new lines that are
// not followed by a space or a tab.
// See http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
$header['value'] = preg_replace('/\r?\n(?!\t| )/', '', $header['value']);
drupal_add_http_header($header['key'], $header['value']);
}
// Transfer file in 1024 byte chunks to save memory usage.
while ($data = $this->read(1024)) {
print $data;
}
$this->close();
// Ask devel.module not to print it's footer.
$GLOBALS['devel_shutdown'] = FALSE;
}
else {
drupal_not_found();
}
// Start buffering and throw away the results so that errors don't get appended to the file.
ob_start('_backup_migrate_file_dispose_buffer');
backup_migrate_cleanup();
module_invoke_all('exit');
exit();
}
/**
* Push a file extension onto the file and return the previous file path.
*/
function push_type($extension) {
$types = _backup_migrate_filetypes();
if ($type = @$types[$extension]) {
$this->push_filetype($type);
}
$out = $this->filepath();
$this->temporary_file();
return $out;
}
/**
* Push a file extension onto the file and return the previous file path.
*/
function pop_type() {
$out = new backup_file(array('filepath' => $this->filepath()));
$this->pop_filetype();
$this->temporary_file();
return $out;
}
/**
* Set the current file type.
*/
function set_filetype($type) {
$this->type = $type;
$this->ext = array($type['extension']);
}
/**
* Set the current file type.
*/
function push_filetype($type) {
$this->ext[] = $type['extension'];
$this->type = $type;
}
/**
* Pop the current file type.
*/
function pop_filetype() {
array_pop($this->ext);
$this->detect_filetype_from_extension();
}
/**
* Set the file info.
*/
function set_file_info($file_info) {
$this->file_info = $file_info;
$this->ext = explode('.', @$this->file_info['filename']);
// Remove the underscores added to file extensions by Drupal's upload security.
foreach ($this->ext as $key => $val) {
$this->ext[$key] = trim($val, '_');
}
$this->filename(array_shift($this->ext));
$this->detect_filetype_from_extension();
}
/**
* Get the filetype info of the given file, or false if the file is not a valid type.
*/
function detect_filetype_from_extension() {
$ext = end($this->ext);
$this->type = array();
$types = _backup_migrate_filetypes();
foreach ($types as $key => $type) {
if (trim($ext, "_0123456789") === $type['extension']) {
$this->type = $type;
$this->type['id'] = $key;
}
}
}
/**
* Get a temporary file name with path.
*/
function temporary_file() {
$file = drupal_tempnam('temporary://', 'backup_migrate_');
// Add the version without the extension. The tempnam function creates this for us.
backup_migrate_temp_files_add($file);
if ($this->extension()) {
$file .= '.'. $this->extension();
// Add the version with the extension. This is the one we will actually use.
backup_migrate_temp_files_add($file);
}
$this->path = $file;
}
}