From f17718f87dd8604e0b6ead593861bc9643ce2f05 Mon Sep 17 00:00:00 2001 From: Florian Schaal Date: Sat, 4 Jan 2014 19:27:07 +0100 Subject: [PATCH] add mail-backup --- .../classes/plugin_backuplist_mail.inc.php | 129 +++++++++++++ interface/web/mail/form/mail_user.tform.php | 34 ++++ .../web/mail/lib/lang/en_mail_backup_list.lng | 12 ++ interface/web/mail/lib/lang/en_mail_user.lng | 6 + interface/web/mail/mail_user_edit.php | 9 + .../web/mail/templates/mail_user_backup.htm | 39 ++++ .../mail/templates/mail_user_backup_list.htm | 47 +++++ .../classes/cron.d/500-backup_mail.inc.php | 175 ++++++++++++++++++ .../plugins-available/backup_plugin.inc.php | 46 ++++- 9 files changed, 495 insertions(+), 2 deletions(-) create mode 100644 interface/lib/classes/plugin_backuplist_mail.inc.php create mode 100644 interface/web/mail/lib/lang/en_mail_backup_list.lng create mode 100644 interface/web/mail/templates/mail_user_backup.htm create mode 100644 interface/web/mail/templates/mail_user_backup_list.htm create mode 100644 server/lib/classes/cron.d/500-backup_mail.inc.php diff --git a/interface/lib/classes/plugin_backuplist_mail.inc.php b/interface/lib/classes/plugin_backuplist_mail.inc.php new file mode 100644 index 0000000000..5bef570d8d --- /dev/null +++ b/interface/lib/classes/plugin_backuplist_mail.inc.php @@ -0,0 +1,129 @@ +newTemplate('templates/mail_user_backup_list.htm'); + + //* Loading language file + $lng_file = "lib/lang/".$_SESSION["s"]["language"]."_mail_backup_list.lng"; + include($lng_file); + $listTpl->setVar($wb); + + $message = ''; + $error = ''; + + if(isset($_GET['backup_action'])) { + $backup_id = $app->functions->intval($_GET['backup_id']); +/* + if($_GET['backup_action'] == 'download' && $backup_id > 0) { + $sql = "SELECT count(action_id) as number FROM sys_remoteaction WHERE action_state = 'pending' AND action_type = 'backup_download' AND action_param = '$backup_id'"; + $tmp = $app->db->queryOneRecord($sql); + if($tmp['number'] == 0) { + $message .= $wb['download_info_txt']; + $sql = "INSERT INTO sys_remoteaction (server_id, tstamp, action_type, action_param, action_state, response) " . + "VALUES (". + (int)$this->form->dataRecord['server_id'] . ", " . + time() . ", " . + "'backup_download', " . + "'".$backup_id."', " . + "'pending', " . + "''" . + ")"; + $app->db->query($sql); + } else { + $error .= $wb['download_pending_txt']; + } + } +*/ + if($_GET['backup_action'] == 'restore' && $backup_id > 0) { + $sql = "SELECT count(action_id) as number FROM sys_remoteaction WHERE action_state = 'pending' AND action_type = 'backup_restore' AND action_param = '$backup_id'"; + $tmp = $app->db->queryOneRecord($sql); + if($tmp['number'] == 0) { + $message .= $wb['restore_info_txt']; + $sql = "INSERT INTO sys_remoteaction (server_id, tstamp, action_type, action_param, action_state, response) " . + "VALUES (". + (int)$this->form->dataRecord['server_id'] . ", " . + time() . ", " . + "'backup_restore', " . + "'".$backup_id."', " . + "'pending', " . + "''" . + ")"; + $app->db->query($sql); + } else { + $error .= $wb['restore_pending_txt']; + } + } + } + + //* Get the data + $sql = "SELECT * FROM mail_backup WHERE mailuser_id = ".$this->form->id." ORDER BY tstamp DESC"; + $records = $app->db->queryAllRecords($sql); + $bgcolor = "#FFFFFF"; + if(is_array($records)) { + foreach($records as $rec) { + // Change of color + $bgcolor = ($bgcolor == "#FFFFFF")?"#EEEEEE":"#FFFFFF"; + $rec["bgcolor"] = $bgcolor; + $rec['date'] = date($app->lng('conf_format_datetime'),$rec['tstamp']); + $rec['backup_type'] = $wb[('backup_type_'.$rec['backup_type'])]; + $records_new[] = $rec; + } + } + + $listTpl->setLoop('records',@$records_new); + + $listTpl->setVar('parent_id',$this->form->id); + $listTpl->setVar('msg',$message); + $listTpl->setVar('error',$error); + + // Setting Returnto information in the session + $list_name = 'backup_list'; + $_SESSION["s"]["list"][$list_name]["parent_id"] = $this->form->id; + $_SESSION["s"]["list"][$list_name]["parent_name"] = $app->tform->formDef["name"]; + $_SESSION["s"]["list"][$list_name]["parent_tab"] = $_SESSION["s"]["form"]["tab"]; + $_SESSION["s"]["list"][$list_name]["parent_script"] = $app->tform->formDef["action"]; + $_SESSION["s"]["form"]["return_to"] = $list_name; + return $listTpl->grab(); + } // end function +} // end class + +?> diff --git a/interface/web/mail/form/mail_user.tform.php b/interface/web/mail/form/mail_user.tform.php index 21fc51461e..03f458a64e 100644 --- a/interface/web/mail/form/mail_user.tform.php +++ b/interface/web/mail/form/mail_user.tform.php @@ -346,5 +346,39 @@ if ($_SESSION["s"]["user"]["typ"] == 'admin' && $global_config['mail']['mailbox_ ); } +//* Backup +$form["tabs"]['backup'] = array ( + 'title' => "Backup", + 'width' => 100, + 'template' => "templates/mail_user_backup.htm", + 'readonly' => false, + 'fields' => array ( + ################################## + # Begin Datatable fields + ################################## + 'backup_interval' => array ( + 'datatype' => 'VARCHAR', + 'formtype' => 'SELECT', + 'default' => '', + 'value' => array('none' => 'no_backup_txt', 'daily' => 'daily_backup_txt', 'weekly' => 'weekly_backup_txt', 'monthly' => 'monthly_backup_txt') + ), + 'backup_copies' => array ( + 'datatype' => 'INTEGER', + 'formtype' => 'SELECT', + 'default' => '', + 'value' => array('1' => '1', '2' => '2', '3' => '3', '4' => '4', '5' => '5', '6' => '6', '7' => '7', '8' => '8', '9' => '9', '10' => '10') + ), + ################################## + # ENDE Datatable fields + ################################## + ), + 'plugins' => array ( + 'backup_records' => array ( + 'class' => 'plugin_backuplist_mail', + 'options' => array( + ) + ) + ) +); ?> diff --git a/interface/web/mail/lib/lang/en_mail_backup_list.lng b/interface/web/mail/lib/lang/en_mail_backup_list.lng new file mode 100644 index 0000000000..320ff5023c --- /dev/null +++ b/interface/web/mail/lib/lang/en_mail_backup_list.lng @@ -0,0 +1,12 @@ + diff --git a/interface/web/mail/lib/lang/en_mail_user.lng b/interface/web/mail/lib/lang/en_mail_user.lng index f180deea83..c275c1a329 100644 --- a/interface/web/mail/lib/lang/en_mail_user.lng +++ b/interface/web/mail/lib/lang/en_mail_user.lng @@ -47,4 +47,10 @@ $wb['generate_password_txt'] = 'Generate Password'; $wb['repeat_password_txt'] = 'Repeat Password'; $wb['password_mismatch_txt'] = 'The passwords do not match.'; $wb['password_match_txt'] = 'The passwords do match.'; +$wb["backup_interval_txt"] = 'Backup interval'; +$wb["backup_copies_txt"] = 'Number of backup copies'; +$wb['no_backup_txt'] = 'No backup'; +$wb['daily_backup_txt'] = 'Daily'; +$wb['weekly_backup_txt'] = 'Weekly'; +$wb['monthly_backup_txt'] = 'Monthly'; ?> diff --git a/interface/web/mail/mail_user_edit.php b/interface/web/mail/mail_user_edit.php index f71aa7de58..61f27cb260 100644 --- a/interface/web/mail/mail_user_edit.php +++ b/interface/web/mail/mail_user_edit.php @@ -319,6 +319,15 @@ class page_action extends tform_actions { } // end if email addess changed + //* Change backup options when user mail backup options have been changed + if(isset($this->dataRecord['backup_interval']) && ($this->dataRecord['backup_interval'] != $this->oldDataRecord['backup_interval'] || $this->dataRecord['backup_copies'] != $this->oldDataRecord['backup_copies'])) { + $backup_interval = $this->dataRecord['backup_interval']; + $backup_copies = $this->dataRecord['backup_copies']; + $app->db->datalogUpdate('mail_user', "backup_interval = '$backup_interval', backup_copies = '$backup_copies'", 'mailuser_id', $rec['mailuser_id']); + unset($backup_copies); + unset($backup_interval); + } // end if backup options changed + } } diff --git a/interface/web/mail/templates/mail_user_backup.htm b/interface/web/mail/templates/mail_user_backup.htm new file mode 100644 index 0000000000..e3aa096bcc --- /dev/null +++ b/interface/web/mail/templates/mail_user_backup.htm @@ -0,0 +1,39 @@ +

+

+ + +
+

{tmpl_var name='configuration_error_txt'}

+
+
{tmpl_var name='config_error_tstamp'} : 
{tmpl_var name='config_error_msg'}
+
+
+
+ +
+ +
+
Backup +
+ + +
+
+ + +
+
+ {tmpl_var name='backup_records'} + + +
+ + +
+
+ +
diff --git a/interface/web/mail/templates/mail_user_backup_list.htm b/interface/web/mail/templates/mail_user_backup_list.htm new file mode 100644 index 0000000000..7fc61fd098 --- /dev/null +++ b/interface/web/mail/templates/mail_user_backup_list.htm @@ -0,0 +1,47 @@ + +

+
+ +

ERROR

+
+

+ +
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
{tmpl_var name='search_limit'}
{tmpl_var name="date"}{tmpl_var name="filename"}{tmpl_var name="filesize"} +
+ + +
+
{tmpl_var name='globalsearch_noresults_text_txt'}
+
+
+ +
diff --git a/server/lib/classes/cron.d/500-backup_mail.inc.php b/server/lib/classes/cron.d/500-backup_mail.inc.php new file mode 100644 index 0000000000..b5a61f271a --- /dev/null +++ b/server/lib/classes/cron.d/500-backup_mail.inc.php @@ -0,0 +1,175 @@ +getconf->get_server_config($conf['server_id'], 'server'); + $backup_dir = $server_config['backup_dir']; + $backup_mode = $server_config['backup_mode']; + if($backup_mode == '') $backup_mode = 'userzip'; + $backup_dir_permissions =0750; + + if($backup_dir != '') { +/* + //* mount backup directory, if necessary + $run_backups = true; + $server_config['backup_dir_mount_cmd'] = trim($server_config['backup_dir_mount_cmd']); + if($server_config['backup_dir_is_mount'] == 'y' && $server_config['backup_dir_mount_cmd'] != ''){ + if(!$app->system->is_mounted($backup_dir)){ + exec(escapeshellcmd($server_config['backup_dir_mount_cmd'])); + sleep(1); + if(!$app->system->is_mounted($backup_dir)) $run_backups = false; + } + } +*/ + + $mail_config = $app->getconf->get_server_config($conf['server_id'], 'mail'); + + if(!is_dir($backup_dir)) { + mkdir(escapeshellcmd($backup_dir), $backup_dir_permissions, true); + } else { + chmod(escapeshellcmd($backup_dir), $backup_dir_permissions); + } + + $sql = "SELECT * FROM mail_user WHERE server_id = '".$conf['server_id']."' AND maildir <> ''"; + $records = $app->db->queryAllRecords($sql); + if(is_array($records)) { + foreach($records as $rec) { + //* Do the mailbox backup + if($rec['backup_interval'] == 'daily' or ($rec['backup_interval'] == 'weekly' && date('w') == 0) or ($rec['backup_interval'] == 'monthly' && date('d') == '01')) { + $sql="SELECT * FROM mail_domain WHERE domain = '".$app->db->quote(explode("@",$rec['email'])[1])."'"; + $domain_rec=$app->db->queryOneRecord($sql); + $mail_backup_dir = $backup_dir.'/mail'.$domain_rec['domain_id']; + + if(!is_dir($mail_backup_dir)) mkdir($mail_backup_dir, 0750); + chmod($mail_backup_dir, $backup_dir_permissions); + + $domain_dir=explode('/',$rec['maildir']); + $_temp=array_pop($domain_dir);unset($_temp); + $domain_dir=implode('/',$domain_dir); + $source_dir=array_pop(explode('/',$rec['maildir'])); + + $mail_backup_file = 'mail'.$rec['mailuser_id'].'_'.date('Y-m-d_H-i'); + + if($backup_mode == 'userzip') { + $mail_backup_file.='.zip'; + exec('cd '.$rec['homedir'].' && zip -b /tmp -r '.$mail_backup_dir.'/'.$mail_backup_file.' '.$source_dir.' > /dev/nul'); + //exec('cd '.$rec['homedir'].' && zip -b /tmp -r '.$mail_backup_dir.'/'.$mail_backup_file.' '.$source_dir.' > /dev/nul'); + } else { + /* Create a tar.gz backup */ + $mail_backup_file.='.tar.gz'; + exec(escapeshellcmd('tar pczf '.$mail_backup_dir.'/'.$mail_backup_file.' --directory '.$domain_dir.' '.$source_dir), $tmp_output, $retval); + } + if($retval == 0){ + chown($mail_backup_dir.'/'.$mail_backup_file, 'root'); + chgrp($mail_backup_dir.'/'.$mail_backup_file, 'root'); + chmod($mail_backup_dir.'/'.$mail_backup_file, 0640); + /* Insert mail backup record in database */ + $sql = "INSERT INTO mail_backup (server_id,parent_domain_id,mailuser_id,backup_mode,tstamp,filename,filesize) VALUES (".$conf['server_id'].",".$domain_rec['domain_id'].",".$rec['mailuser_id'].",'".$backup_mode."',".time().",'".$app->db->quote($mail_backup_file)."','".$app->functions->formatBytes(filesize($mail_backup_dir.'/'.$mail_backup_file))."')"; + $app->db->query($sql); + if($app->db->dbHost != $app->dbmaster->dbHost) $app->dbmaster->query($sql); + } else { + /* Backup failed - remove archive */ + if(is_file($mail_backup_dir.'/'.$mail_backup_file)) unlink($mail_backup_dir.'/'.$mail_backup_file); + $app->log($mail_backup_file.' NOK:'.$tmp_output, LOGLEVEL_DEBUG); + } + /* Remove old backups */ + $backup_copies = intval($rec['backup_copies']); + $dir_handle = dir($mail_backup_dir); + $files = array(); + while (false !== ($entry = $dir_handle->read())) { + if($entry != '.' && $entry != '..' && substr($entry,0,4+strlen($rec['mailuser_id'])) == 'mail'.$rec['mailuser_id'] && is_file($mail_backup_dir.'/'.$entry)) { + $files[] = $entry; + } + } + $dir_handle->close(); + rsort($files); + for ($n = $backup_copies; $n <= 10; $n++) { + if(isset($files[$n]) && is_file($mail_backup_dir.'/'.$files[$n])) { + unlink($mail_backup_dir.'/'.$files[$n]); + $sql = "DELETE FROM mail_backup WHERE server_id = ".$conf['server_id']." AND parent_domain_id = ".$domain_rec['domain_id']." AND filename = '".$app->db->quote($files[$n])."'"; + $app->db->query($sql); + if($app->db->dbHost != $app->dbmaster->dbHost) $app->dbmaster->query($sql); + } + } + unset($files); + unset($dir_handle); + } + /* Remove inactive backups */ + if($rec['backup_interval'] == 'none') { + /* remove backups from db */ + $sql = "DELETE FROM mail_backup WHERE server_id = ".$conf['server_id']." AND parent_domain_id = ".$domain_rec['domain_id']." AND mailuser_id = ".$rec['mailuser_id']; + $app->db->query($sql); + if($app->db->dbHost != $app->dbmaster->dbHost) $app->dbmaster->query($sql); + /* remove archives */ + $mail_backup_dir = $backup_dir.'/mail'.$rec['sys_userid']; + $mail_backup_file = 'mail'.$rec['mailuser_id'].'_*'; + if(is_dir($mail_backup_dir)) { + foreach (glob($mail_backup_dir.'/'.$mail_backup_file) as $filename) { + unlink($filename); + } + } + } + } + } + } + + parent::onRunJob(); + } + + /* this function is optional if it contains no custom code */ + public function onAfterRun() { + global $app; + + parent::onAfterRun(); + } + +} + +?> diff --git a/server/plugins-available/backup_plugin.inc.php b/server/plugins-available/backup_plugin.inc.php index 6c6ef14520..d2590b48f0 100644 --- a/server/plugins-available/backup_plugin.inc.php +++ b/server/plugins-available/backup_plugin.inc.php @@ -62,6 +62,7 @@ class backup_plugin { $backup_id = intval($data); $backup = $app->dbmaster->queryOneRecord("SELECT * FROM web_backup WHERE backup_id = $backup_id"); + $mail_backup = $app->dbmaster->queryOneRecord("SELECT * FROM mail_backup WHERE backup_id = $backup_id"); if(is_array($backup)) { @@ -149,9 +150,49 @@ class backup_plugin { } } } + //* Restore a mail backup - florian@schaal-24.de + } elseif (is_array($mail_backup) && $action_name == 'backup_restore') { + $app->uses('ini_parser,file,getconf'); + $server_config = $app->getconf->get_server_config($conf['server_id'], 'server'); + $mail_config = $app->getconf->get_server_config($conf['server_id'], 'mail'); + + $domain_rec = $app->db->queryOneRecord("SELECT * FROM mail_domain WHERE domain_id = ".intval($mail_backup['parent_domain_id'])); + + $backup_dir = $server_config['backup_dir'].'/mail'.$domain_rec['domain_id']; + $mail_backup_file = $backup_dir.'/'.$mail_backup['filename']; + + $sql = "SELECT * FROM mail_user WHERE server_id = '".$conf['server_id']."' AND mailuser_id = ".intval($mail_backup['mailuser_id']); + $record = $app->db->queryOneRecord($sql); + + //* strip mailbox from maildir + $domain_dir=explode('/',$record['maildir']); + $_temp=array_pop($domain_dir);unset($_temp); + $domain_dir=implode('/',$domain_dir); + if(file_exists($mail_backup_file) && $record['homedir'] != '' && $record['homedir'] != '/' && !stristr($mail_backup_file,'..') && !stristr($mail_backup_file,'etc') && $mail_config['homedir_path'] == $record['homedir'] && is_dir($domain_dir)) { + if($mail_backup['backup_mode'] == 'userzip') { + $command = 'sudo -u '.$mail_config['mailuser_name'].' unzip -qq -o '.escapeshellarg($mail_backup_file).' -d '.escapeshellarg($domain_dir).' 2> /dev/null'; + exec($command,$tmp_output, $retval); + if($retval == 0){ + $app->log('Restored Mail backup '.$mail_backup_file,LOGLEVEL_DEBUG); + } else { + $app->log('Unable to restore Mail backup '.$mail_backup_file.' '.$tmp_output,LOGLEVEL_ERROR); + } + } + if($mail_backup['backup_mode'] == 'rootgz') { + $command='tar xfz '.escapeshellarg($mail_backup_file).' --directory '.escapeshellarg($domain_dir); + exec($command,$tmp_output, $retval); + if($retval == 0){ + $app->log('Restored Mail backup '.$mail_backup_file,LOGLEVEL_DEBUG); + } else { + $app->log('Unable to restore Mail backup '.$mail_backup_file.' '.$tmp_output,LOGLEVEL_ERROR); + } + } + } else { + $app->log('Unable to restore Mail backup '.$mail_backup_file.' due to misconfiguration',LOGLEVEL_ERROR); + } } else { - $app->log('No backup with ID '.$backup_id.' found.', LOGLEVEL_DEBUG); + $app->log('No backup with ID '.$backup_id.' found.',LOGLEVEL_DEBUG); } return 'ok'; @@ -159,4 +200,5 @@ class backup_plugin { } // end class -?> +?> + -- GitLab