| 1 |
<?php
|
| 2 |
// $Id:
|
| 3 |
|
| 4 |
function fbu_perm() {
|
| 5 |
return array ('administer file backup');
|
| 6 |
}
|
| 7 |
|
| 8 |
function fbu_help($section = '') {
|
| 9 |
switch ($section) {
|
| 10 |
case 'admin/modules/fbu':
|
| 11 |
case 'admin/modules#description':
|
| 12 |
$output = t('Receive filesystem backups by mail.');
|
| 13 |
break;
|
| 14 |
case 'admin/help#fbu':
|
| 15 |
$output .= t("
|
| 16 |
<p>The fbu module allows site administrators to receive backups of their server's file system by email.</p>");
|
| 17 |
break;
|
| 18 |
}
|
| 19 |
return $output;
|
| 20 |
}
|
| 21 |
|
| 22 |
function fbu_settings() {
|
| 23 |
if($_POST['op'] == t('Run backup now')) {
|
| 24 |
fbu_make_backup(TRUE);
|
| 25 |
}
|
| 26 |
|
| 27 |
$tasks = unserialize(variable_get('fbu_stored_tasks', serialize(array())));
|
| 28 |
$tasks_count = count($tasks);
|
| 29 |
drupal_set_message(t('There are %count tasks in the backup queue', array('%count' => $tasks_count)));
|
| 30 |
|
| 31 |
$disabled = false;
|
| 32 |
if(!@include_once('File/Archive.php') ) {
|
| 33 |
drupal_set_message(t('This module depends on the following %pear packages: <ol><li>%file</li><li>%mime</li></ol>You must install these before using this module.', array('%pear' => '<a href="http://pear.php.net/">PEAR</a>', '%file' => '<a href="http://pear.php.net/package/File_Archive">File_Archive</a>', '%mime' => '<a href="http://pear.php.net/package/MIME_Type">MIME_Type</a>')), 'error');
|
| 34 |
$disabled = true;
|
| 35 |
}
|
| 36 |
|
| 37 |
// Backups
|
| 38 |
$group = form_textfield(t('Email address'), 'fbu_email', variable_get('fbu_email', variable_get('site_mail', ini_get('sendmail_from'))), 45, 255, t('Email address to which backups will be sent.'));
|
| 39 |
$period = drupal_map_assoc(array(0, 600, 1800, 3600, 10800, 21600, 32400, 43200, 86400, 172800, 259200, 604800, 1209600, 2419200, 4838400, 9676800), 'format_interval');
|
| 40 |
$preiod[0] = t('disabled');
|
| 41 |
$group .= form_select(t('Automatically backup files every'), 'fbu_files_backup_interval', variable_get('fbu_files_backup_interval', 0), $period, t('Select how often you wish to have your files automatically backed up. Busy sites should choose more frequent backups. Requires crontab.'));
|
| 42 |
$backup_path = variable_get('fbu_auto_backup_path', FILE_DIRECTORY_TEMP);
|
| 43 |
if (variable_get('fbu_files_backup_interval', 0)) {
|
| 44 |
if (!file_check_directory($backup_path)) {
|
| 45 |
form_set_error('fbu_auto_backup_path', t('The automatic backup path does not exist, or is not writeable. Automatic backups will not begin until you fix this error.'));
|
| 46 |
}
|
| 47 |
else if ($test = tempnam($backup_path, 'fbu.')) {
|
| 48 |
file_delete($test);
|
| 49 |
}
|
| 50 |
else {
|
| 51 |
form_set_error('fbu_auto_backup_path', t('The automatic backup path exists, but is not writeable. Automatic backups will not begin until you fix this error.'));
|
| 52 |
}
|
| 53 |
}
|
| 54 |
else {
|
| 55 |
$attributes = array('disabled'=>'disabled');
|
| 56 |
}
|
| 57 |
$group .= form_textfield(t('Automatic backup path'), 'fbu_auto_backup_path', $backup_path, 30, 255, t('If automatic backups are enabled, you must specify a directory where you would like to store the backup files. The path must be absolute and for security reasons should not be accesible to the web.'), $attributes);
|
| 58 |
$group .= form_select(t('Compress and archive backups with'), 'fbu_archive_type', variable_get('fbu_archive_type', 'zip'), drupal_map_assoc(array('zip', 'tar', 'gz', 'bz2', 'tgz', 'tbz')), t('Which compression type do you prefer? See %url for the pros and cons of the different types.', array('%url' => '<a href="http://www.sitepoint.com/blogs/2005/08/06/file_archive/">here</a>')));
|
| 59 |
|
| 60 |
if (!count($radios)) {
|
| 61 |
$attributes['disabled'] = 'disabled';
|
| 62 |
}
|
| 63 |
else {
|
| 64 |
$group .= form_radios(t('Compress automatic backups'), 'fbu_auto_backup_compress', variable_get('fbu_auto_backup_compress', 0), $radios);
|
| 65 |
}
|
| 66 |
|
| 67 |
$size_options['0.5'] = t('%kb KB', array('%kb' => '512'));
|
| 68 |
$size_options[1] = t('%mb MB', array('%mb' => '1'));
|
| 69 |
$size_options[3] = t('%mb MB', array('%mb' => '3'));
|
| 70 |
$size_options[5] = t('%mb MB', array('%mb' => '5'));
|
| 71 |
$size_options[10] = t('%mb MB', array('%mb' => '10'));
|
| 72 |
$size_options[15] = t('%mb MB', array('%mb' => '15'));
|
| 73 |
$size_options[20] = t('%mb MB', array('%mb' => '20'));
|
| 74 |
$group .= form_select(t('Max email attachment size'), 'fbu_max_email', variable_get('fbu_max_email', 5), $size_options, t('Size is measured before compression, so emails will typically be somewhat smaller. Please note that the attachment can never be smaller than the largest attached file. If you have 10MB files on your server, the fbu module will attempt to send them as attachments.'));
|
| 75 |
|
| 76 |
$group .= form_radios(t('Complete backup or only the new files'), 'fbu_complete_only_new', variable_get('fbu_complete_only_new', 1), array(t('complete'), t('only new')), t('<em>Complete</em> backups will make copy of every file in your document root directory. <em>Only new</em> backups will only make a backup of those files that are new or modified since the last backup. It is worth noting that the very first time you make a backup using this module, the only <em>new mode</em> will function like complete.'));
|
| 77 |
|
| 78 |
|
| 79 |
$output = form_group(t('Filesystem backup'), $group);
|
| 80 |
$disabled = $disabled ? array('disabled' => 'disabled') : null;
|
| 81 |
$output .= form_submit(t('Run backup now'), 'op', $disabled);
|
| 82 |
return $output;
|
| 83 |
}
|
| 84 |
|
| 85 |
function fbu_cron() {
|
| 86 |
if ($interval = variable_get('fbu_files_backup_interval', 0)) {
|
| 87 |
// see if it's time for another auto-backup
|
| 88 |
if ((time() - $interval) >= variable_get('fbu_files_backup_interval', 0)) {
|
| 89 |
fbu_make_backup();
|
| 90 |
fbu_process_mail_queue();
|
| 91 |
}
|
| 92 |
}
|
| 93 |
}
|
| 94 |
|
| 95 |
function fbu_make_backup($manual = FALSE) {
|
| 96 |
global $fbu_time;
|
| 97 |
|
| 98 |
$fbu_time = time();
|
| 99 |
|
| 100 |
$tmpdir = variable_get('fbu_auto_backup_path', FILE_DIRECTORY_TEMP);
|
| 101 |
$scandir = $_SERVER['DOCUMENT_ROOT'];
|
| 102 |
|
| 103 |
// make sure we have permission to save our temp file
|
| 104 |
$archives = array();
|
| 105 |
if (file_check_directory($tmpdir)) {
|
| 106 |
$time = time();
|
| 107 |
|
| 108 |
$tasks = unserialize(variable_get('fbu_stored_tasks', serialize(array())));
|
| 109 |
|
| 110 |
// if there are no stored tasks, start over and read the whole directory.
|
| 111 |
if (!count($tasks)) {
|
| 112 |
// update the fbu_last_backup variable. This is the only place this occurs.
|
| 113 |
variable_set('fbu_last_backup', $time);
|
| 114 |
|
| 115 |
// Get all the files
|
| 116 |
$files = file_scan_directory($scandir, '$', array('.', '..', 'CVS'), 'fbu_get_filemtime');
|
| 117 |
|
| 118 |
while (count($files)) {
|
| 119 |
$task = array();
|
| 120 |
for ($i=0; $i<50; ++$i) {
|
| 121 |
$file = array_pop($files);
|
| 122 |
$task[$file->filename] = $file;
|
| 123 |
}
|
| 124 |
$tasks[] = $task;
|
| 125 |
}
|
| 126 |
}
|
| 127 |
|
| 128 |
$files = array_pop($tasks);
|
| 129 |
|
| 130 |
variable_set('fbu_stored_tasks', serialize($tasks));
|
| 131 |
|
| 132 |
// Filter the arrays by modified date
|
| 133 |
$files = array_filter($files, 'fbu_filter_by_modified');
|
| 134 |
|
| 135 |
// Sort the files by modified date
|
| 136 |
usort($files, "fbu_sort_by_modified");
|
| 137 |
|
| 138 |
$sets = fbu_get_included_files($files, $scandir);
|
| 139 |
|
| 140 |
$failure = false;
|
| 141 |
$mail_queue = array();
|
| 142 |
foreach ($sets as $set) {
|
| 143 |
$mail_queue[] = fbu_archive($scandir, $tmpdir, $set);
|
| 144 |
}
|
| 145 |
|
| 146 |
fbu_process_mail_queue($mail_queue, $manual, $tmpdir);
|
| 147 |
}
|
| 148 |
}
|
| 149 |
|
| 150 |
|
| 151 |
function fbu_get_included_files($files, $dir) {
|
| 152 |
$sets = array();
|
| 153 |
|
| 154 |
$maxsize = (int)(1048576 * (double)(variable_get('fbu_max_email', 5)));
|
| 155 |
$size = 0;
|
| 156 |
$count = 0;
|
| 157 |
$lastmodified = 0;
|
| 158 |
foreach ($files as $file) {
|
| 159 |
$size += filesize($file->filename);
|
| 160 |
if ($maxsize < $size) {
|
| 161 |
$size = 0;
|
| 162 |
$count++;
|
| 163 |
}
|
| 164 |
$sets[$count][] = str_replace($dir .'/', '', $file->filename);
|
| 165 |
}
|
| 166 |
|
| 167 |
foreach ($sets as $key => $set) {
|
| 168 |
$sets[$key] = '('. implode(')|(', $set). ')';
|
| 169 |
}
|
| 170 |
|
| 171 |
return $sets;
|
| 172 |
}
|
| 173 |
|
| 174 |
function fbu_archive($scandir, $tmpdir, $ereg) {
|
| 175 |
global $fbu_time;
|
| 176 |
|
| 177 |
// every backup made has a unique number
|
| 178 |
$count = db_next_id('fbu');
|
| 179 |
$path = variable_get('fbu_auto_backup_path', FILE_DIRECTORY_TEMP);
|
| 180 |
$filename = $count. '_'. format_date($fbu_time, 'custom', 'Ym-dHi_'). variable_get('site_name', 'drupal'). '.'. variable_get('fbu_archive_type', 'zip');
|
| 181 |
|
| 182 |
// PEAR Magic
|
| 183 |
require_once "File/Archive.php";
|
| 184 |
File_Archive::setOption("zipCompressionLevel", 9);
|
| 185 |
|
| 186 |
//Ask File_Archive to use the cache object we just created
|
| 187 |
File_Archive::setOption("cache", $cache);
|
| 188 |
|
| 189 |
// This writes the archive files. It filters based on the list of files in $ereg.
|
| 190 |
File_Archive::extract(
|
| 191 |
File_Archive::filter(
|
| 192 |
File_Archive::predEreg($ereg),
|
| 193 |
File_Archive::read($scandir)
|
| 194 |
),
|
| 195 |
File_Archive::toArchive($tmpdir.'/'.$filename,
|
| 196 |
File_Archive::toFiles()
|
| 197 |
)
|
| 198 |
);
|
| 199 |
watchdog('fbu', "archive function returning $filename");
|
| 200 |
return $filename;
|
| 201 |
}
|
| 202 |
|
| 203 |
function fbu_process_mail_queue($mail_queue, $manual, $tmpdir) {
|
| 204 |
$failure = false;
|
| 205 |
foreach ($mail_queue as $filename) {
|
| 206 |
// The mail text
|
| 207 |
$text = "-- Drupal fbu.module files backup\n";
|
| 208 |
$text .= "--\n";
|
| 209 |
$text .= "-- Files:\n";
|
| 210 |
$text .= '---- '. $filename. "\n";
|
| 211 |
$text .= "-- Date: ". format_date(time(), 'large') ."\n\n";
|
| 212 |
|
| 213 |
// prepare the attachments for the mail
|
| 214 |
$attachment = new stdClass();
|
| 215 |
$attachment->path = $tmpdir ."/$filename";
|
| 216 |
$attachment->filename = $filename;
|
| 217 |
|
| 218 |
// mail it!
|
| 219 |
$sent = fbu_send_mail(array($attachment), $text, $manual);
|
| 220 |
if (!$sent) {
|
| 221 |
$failure = true;
|
| 222 |
}
|
| 223 |
|
| 224 |
// remove it.
|
| 225 |
unlink($tmpdir.'/'.$filename);
|
| 226 |
}
|
| 227 |
}
|
| 228 |
|
| 229 |
|
| 230 |
function fbu_send_mail($attachments, $text, $manual) {
|
| 231 |
// Send mail
|
| 232 |
$mail = new fbu_mail();
|
| 233 |
$mail->from = variable_get('fbu_email', variable_get('site_mail', ini_get('sendmail_from')));
|
| 234 |
$mail->headers = 'Errors-To: [EMAIL='. $mail->from. ']'. $mail->from. '[/EMAIL]';
|
| 235 |
$mail->to = $mail->from;
|
| 236 |
$mail->subject = t('Filesystem backup from %site', array('%site' => variable_get('site_name', 'drupal')));
|
| 237 |
$mail->body = $text;
|
| 238 |
$attached = array();
|
| 239 |
foreach ($attachments as $attachment) {
|
| 240 |
$file = fread(fopen($attachment->path, "r"), filesize($attachment->path));
|
| 241 |
$mail->add_attachment("$file", $attachment->filename, "Content-Transfer-Encoding: base64 /9j/4AAQSkZJRgABAgEASABIAAD/7QT+UGhvdG9zaG");
|
| 242 |
$attached[] = $attachment->filename;
|
| 243 |
}
|
| 244 |
$attached = implode(', ', $attached);
|
| 245 |
$message = '';
|
| 246 |
|
| 247 |
// send the mail, with attachments
|
| 248 |
$sent = $mail->send();
|
| 249 |
|
| 250 |
if ($sent) {
|
| 251 |
$message = t('File backup files sent to %mail: %files', array('%files' => $attached, '%mail' => $mail->to));
|
| 252 |
watchdog('fbu', $message);
|
| 253 |
}
|
| 254 |
else {
|
| 255 |
$message = t('File backup files sending <strong>FAILED</strong> to %mail: %files', array('%files' => $attached, '%mail' => $mail->to));
|
| 256 |
watchdog('fbu', $message, WATCHDOG_ERROR);
|
| 257 |
}
|
| 258 |
return $sent;
|
| 259 |
}
|
| 260 |
|
| 261 |
function fbu_sort_by_modified($a, $b) {
|
| 262 |
$a = fbu_get_filemtime($a->filename);
|
| 263 |
$b = fbu_get_filemtime($b->filename);
|
| 264 |
|
| 265 |
if ($a == $b) {
|
| 266 |
return 0;
|
| 267 |
}
|
| 268 |
return ($a < $b) ? -1 : 1;
|
| 269 |
}
|
| 270 |
|
| 271 |
function fbu_filter_by_modified($file) {
|
| 272 |
$time = variable_get('fbu_complete_only_new', 1) ? variable_get('fbu_last_backup', 0) : 0;
|
| 273 |
return(fbu_get_filemtime($file->filename) > $time);
|
| 274 |
}
|
| 275 |
|
| 276 |
function fbu_get_filemtime($filename) {
|
| 277 |
static $modtimes;
|
| 278 |
if (is_null($modtimes)) {
|
| 279 |
$modtimes = array();
|
| 280 |
}
|
| 281 |
|
| 282 |
if (is_null($modtimes[$filename])) {
|
| 283 |
$modtimes[$filename] = filemtime($filename);
|
| 284 |
}
|
| 285 |
|
| 286 |
return $modtimes[$filename];
|
| 287 |
}
|
| 288 |
|
| 289 |
|
| 290 |
/* Define a mail class to send mail with attachment. This is only here until we get a real mail backend.
|
| 291 |
* see http://www.php.net/function.mail
|
| 292 |
*/
|
| 293 |
class fbu_mail {
|
| 294 |
var $parts;
|
| 295 |
var $to;
|
| 296 |
var $from;
|
| 297 |
var $headers;
|
| 298 |
var $subject;
|
| 299 |
var $body;
|
| 300 |
|
| 301 |
function mime_mail() {
|
| 302 |
$this->parts = array();
|
| 303 |
$this->to = "";
|
| 304 |
$this->from = "";
|
| 305 |
$this->subject = "";
|
| 306 |
$this->body = "";
|
| 307 |
$this->headers = "";
|
| 308 |
}
|
| 309 |
|
| 310 |
function add_attachment($message, $name = "", $ctype = "application/octet-stream") {
|
| 311 |
$this->parts [] = array (
|
| 312 |
"ctype" => $ctype,
|
| 313 |
"message" => $message,
|
| 314 |
"encode" => $encode,
|
| 315 |
"name" => $name
|
| 316 |
);
|
| 317 |
}
|
| 318 |
|
| 319 |
|
| 320 |
function build_message($part) {
|
| 321 |
$message = $part["message"];
|
| 322 |
$message = chunk_split(base64_encode($message));
|
| 323 |
$encoding = "base64";
|
| 324 |
return "Content-Type: ".$part["ctype"].($part["name"]? "; name = \"".$part["name"]."\"" : "")."\nContent-Transfer-Encoding: $encoding\n\n$message\n";
|
| 325 |
}
|
| 326 |
|
| 327 |
function build_multipart() {
|
| 328 |
$boundary = "b".md5(uniqid(time()));
|
| 329 |
$multipart = "Content-Type: multipart/mixed; boundary = $boundary\n\nThis is a MIME encoded message.\n\n--$boundary";
|
| 330 |
for($i = sizeof($this->parts)-1; $i>=0; $i--) $multipart .= "\n".$this->build_message($this->parts[$i]). "--$boundary";
|
| 331 |
return $multipart.= "--\n";
|
| 332 |
}
|
| 333 |
|
| 334 |
|
| 335 |
function send() {
|
| 336 |
$mime = "";
|
| 337 |
if (!empty($this->from)) $mime .= "From: ".$this->from. "\n";
|
| 338 |
if (!empty($this->headers)) $mime .= $this->headers. "\n";
|
| 339 |
if (!empty($this->body)) $this->add_attachment($this->body, "", "text/plain");
|
| 340 |
$mime .= "MIME-Version: 1.0\n".$this->build_multipart();
|
| 341 |
return mail($this->to, $this->subject, "", $mime);
|
| 342 |
}
|
| 343 |
}
|
| 344 |
|
| 345 |
?>
|