500-backup_mail.inc.php 10.8 KB
Newer Older
Florian Schaal's avatar
Florian Schaal committed
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
<?php
/*
Copyright (c) 2013, Florian Schaal, info@schaal-24.de
All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright notice,
      this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright notice,
      this list of conditions and the following disclaimer in the documentation
      and/or other materials provided with the distribution.
    * Neither the name of ISPConfig nor the names of its contributors
      may be used to endorse or promote products derived from this software without
      specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

30
class cronjob_backup_mail extends cronjob {
Florian Schaal's avatar
Florian Schaal committed
31 32 33

	// job schedule
	protected $_schedule = '0 0 * * *';
34
	private $tmp_backup_dir = '';
Florian Schaal's avatar
Florian Schaal committed
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53

	/* this function is optional if it contains no custom code */
	public function onPrepare() {
		global $app;

		parent::onPrepare();
	}

	/* this function is optional if it contains no custom code */
	public function onBeforeRun() {
		global $app;

		return parent::onBeforeRun();
	}

	public function onRunJob() {
		global $app, $conf;

		$server_config = $app->getconf->get_server_config($conf['server_id'], 'server');
54
		$mail_config = $app->getconf->get_server_config($conf['server_id'], 'mail');
55 56
		$global_config = $app->getconf->get_global_config('sites');
		
57
		$backup_dir = trim($server_config['backup_dir']);
58 59
		$backup_dir_permissions =0750;

Florian Schaal's avatar
Florian Schaal committed
60 61 62 63 64
		$backup_mode = $server_config['backup_mode'];
		if($backup_mode == '') $backup_mode = 'userzip';

		if($backup_dir != '') {
			$run_backups = true;
65 66
			//* mount backup directory, if necessary
			if( $server_config['backup_dir_is_mount'] == 'y' && !$app->system->mount_backup_dir($backup_dir) ) $run_backups = false;
Florian Schaal's avatar
Florian Schaal committed
67

68
			$records = $app->db->queryAllRecords("SELECT * FROM mail_user WHERE server_id = ? AND maildir != ''", intval($conf['server_id']));
69
			if(is_array($records) && $run_backups) {
70 71 72 73 74 75
				if(!is_dir($backup_dir)) {
					mkdir(escapeshellcmd($backup_dir), $backup_dir_permissions, true);
				} else {
					chmod(escapeshellcmd($backup_dir), $backup_dir_permissions);
				}

Florian Schaal's avatar
Florian Schaal committed
76 77
				foreach($records as $rec) {
					//* Do the mailbox backup
78 79 80 81 82 83
					$email = $rec['email'];
					$temp = explode("@",$email);
					$domain = $temp[1];
					unset($temp);
					$domain_rec=$app->db->queryOneRecord("SELECT * FROM mail_domain WHERE domain = ?", $domain);

Florian Schaal's avatar
Florian Schaal committed
84
					if($rec['backup_interval'] == 'daily' or ($rec['backup_interval'] == 'weekly' && date('w') == 0) or ($rec['backup_interval'] == 'monthly' && date('d') == '01')) {
Dominik's avatar
Dominik committed
85 86 87 88 89 90
						
						$backupusername = 'root';
						$backupgroup = 'root';
						if ($global_config['backups_include_into_web_quota'] == 'y') {
							// this only works, if mail and webdomains are on the same server
							// find webdomain fitting to maildomain
91 92
							$sql = "SELECT * FROM web_domain WHERE domain = ?";
							$webdomain = $app->db->queryOneRecord($sql, $domain_rec['domain']);
Dominik's avatar
Dominik committed
93 94 95
							// if this is not also the website, find website now
							if ($webdomain && ($webdomain['parent_domain_id'] != 0)) {
								do {
96 97
									$sql = "SELECT * FROM web_domain WHERE domain_id = ?";
									$webdomain = $app->db->queryOneRecord($sql, $webdomain['parent_domain_id']);
Dominik's avatar
Dominik committed
98 99 100 101 102 103 104 105
								} while ($webdomain && ($webdomain['parent_domain_id'] != 0));
							}
							// if webdomain is found, change username/group now
							if ($webdomain) {
								$backupusername = $webdomain['system_user'];
								$backupgroup = $webdomain['system_group'];
							}
						}						
Florian Schaal's avatar
Florian Schaal committed
106

107
						$mail_backup_dir = $backup_dir.'/mail'.$domain_rec['domain_id'];
Florian Schaal's avatar
Florian Schaal committed
108 109
						if(!is_dir($mail_backup_dir)) mkdir($mail_backup_dir, 0750);
						chmod($mail_backup_dir, $backup_dir_permissions);
Dominik's avatar
Dominik committed
110 111
						chown($mail_backup_dir, $backupusername);
						chgrp($mail_backup_dir, $backupgroup);
Florian Schaal's avatar
Florian Schaal committed
112

113 114
						$mail_backup_file = 'mail'.$rec['mailuser_id'].'_'.date('Y-m-d_H-i');

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
						// in case of mdbox -> create backup with doveadm before zipping
						if ($rec['maildir_format'] == 'mdbox') {
							if (empty($this->tmp_backup_dir)) $this->tmp_backup_dir = $rec['maildir'];
							// Create temporary backup-mailbox
							exec("su -c 'dsync backup -u \"".$rec["email"]."\" mdbox:".$this->tmp_backup_dir."/backup'", $tmp_output, $retval);
		
							if($backup_mode == 'userzip') {
								$mail_backup_file.='.zip';
								exec('cd '.$this->tmp_backup_dir.' && zip '.$mail_backup_dir.'/'.$mail_backup_file.' -b /tmp -r backup > /dev/null && rm -rf backup', $tmp_output, $retval);
							}
							else {
								$mail_backup_file.='.tar.gz';
								exec(escapeshellcmd('tar pczf '.$mail_backup_dir.'/'.$mail_backup_file.' --directory '.$this->tmp_backup_dir.' backup && rm -rf '.$this->tmp_backup_dir.'/backup'), $tmp_output, $retval);
							}
							
							if ($retval != 0) {
								// Cleanup
								if (file_exists($this->tmp_backup_dir.'/backup')) exec('rm -rf '.$this->tmp_backup_dir.'/backup');
							}
						}
						else {
							$domain_dir=explode('/',$rec['maildir']);
							$_temp=array_pop($domain_dir);unset($_temp);
							$domain_dir=implode('/',$domain_dir);
							
							$parts=explode('/',$rec['maildir']);
							$source_dir=array_pop($parts);
							unset($parts);
							
							//* create archives
							if($backup_mode == 'userzip') {
								$mail_backup_file.='.zip';
								exec('cd '.$domain_dir.' && zip '.$mail_backup_dir.'/'.$mail_backup_file.' -b /tmp -r '.$source_dir.' > /dev/null', $tmp_output, $retval);
							} 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);
							}
Florian Schaal's avatar
Florian Schaal committed
153
						}
154
						
Florian Schaal's avatar
Florian Schaal committed
155
						if($retval == 0){
156 157
							chown($mail_backup_dir.'/'.$mail_backup_file, $backupusername);
							chgrp($mail_backup_dir.'/'.$mail_backup_file, $backupgroup);
Florian Schaal's avatar
Florian Schaal committed
158 159
							chmod($mail_backup_dir.'/'.$mail_backup_file, 0640);
							/* Insert mail backup record in database */
160
							$filesize = filesize($mail_backup_dir.'/'.$mail_backup_file);
161
							$sql = "INSERT INTO mail_backup (server_id, parent_domain_id, mailuser_id, backup_mode, tstamp, filename, filesize) VALUES (?, ?, ?, ?, ?, ?, ?)";
162 163 164
							$app->db->query($sql, $conf['server_id'], $domain_rec['domain_id'], $rec['mailuser_id'], $backup_mode, time(), $mail_backup_file, $filesize);	
							if($app->db->dbHost != $app->dbmaster->dbHost) $app->dbmaster->query($sql, $conf['server_id'], $domain_rec['domain_id'], $rec['mailuser_id'], $backup_mode, time(), $mail_backup_file, $filesize);
							unset($filesize);
Florian Schaal's avatar
Florian Schaal committed
165 166 167
						} else {
							/* Backup failed - remove archive */
							if(is_file($mail_backup_dir.'/'.$mail_backup_file)) unlink($mail_backup_dir.'/'.$mail_backup_file);
168 169 170 171
							// And remove backup-mdbox
							if ($rec['maildir_format'] == 'mdbox') {
								if(file_exists($rec['maildir'].'/backup'))  exec("su -c 'rm -rf ".$rec['maildir']."/backup'");
							}
172
							$app->log($mail_backup_file.' NOK:'.implode('',$tmp_output), LOGLEVEL_DEBUG);
Florian Schaal's avatar
Florian Schaal committed
173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
						}
						/* 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]);
188 189 190
								$sql = "DELETE FROM mail_backup WHERE server_id = ? AND parent_domain_id = ? AND filename = ?";
								$app->db->query($sql, $conf['server_id'], $domain_rec['domain_id'], $files[$n]);
								if($app->db->dbHost != $app->dbmaster->dbHost) $app->dbmaster->query($sql, $conf['server_id'], $domain_rec['domain_id'], $files[$n]);
Florian Schaal's avatar
Florian Schaal committed
191 192 193 194 195 196
							}
						}
						unset($files);
						unset($dir_handle);
					}
					/* Remove inactive backups */
197 198
					if($rec['backup_interval'] == 'none' || $rec['backup_interval'] == '') {

Florian Schaal's avatar
Florian Schaal committed
199
						/* remove archives */
200
						$mail_backup_dir = realpath($backup_dir.'/mail'.$domain_rec['domain_id']);
Florian Schaal's avatar
typo  
Florian Schaal committed
201
						$mail_backup_file = 'mail'.$rec['mailuser_id'].'_';
Florian Schaal's avatar
Florian Schaal committed
202
						if(is_dir($mail_backup_dir)) {
203 204 205
							$dir_handle = opendir($mail_backup_dir.'/');
							while ($file = readdir($dir_handle)) {
								if(!is_dir($file)) {
206 207 208
									if(substr($file,0,strlen($mail_backup_file)) == $mail_backup_file) {
										unlink ($mail_backup_dir.'/'.$file);
									}
209
								}
Florian Schaal's avatar
Florian Schaal committed
210
							}
211 212 213
							if(count(glob($mail_backup_dir."/*", GLOB_NOSORT)) === 0) {
								rmdir($mail_backup_dir);
							}
Florian Schaal's avatar
Florian Schaal committed
214
						}
215 216 217 218 219
						/* remove backups from db */
						$sql = "DELETE FROM mail_backup WHERE server_id = ? AND parent_domain_id = ? AND mailuser_id = ?";
						$app->db->query($sql, $conf['server_id'], $domain_rec['domain_id'], $rec['mailuser_id']);
						if($app->db->dbHost != $app->dbmaster->dbHost) $app->dbmaster->query($sql, $conf['server_id'], $domain_rec['domain_id'], $rec['mailuser_id']);

Florian Schaal's avatar
Florian Schaal committed
220 221
					}
				}
Dominik's avatar
Dominik committed
222 223 224 225 226

				// remove non-existing backups from database
				$backups = $app->db->queryAllRecords("SELECT * FROM mail_backup WHERE server_id = ?", $conf['server_id']);
				if(is_array($backups) && !empty($backups)){
					foreach($backups as $backup){
227 228
						$mail_backup_dir = $backup_dir.'/mail'.$backup['parent_domain_id'];
						if(!is_file($mail_backup_dir.'/'.$backup['filename'])){
Dominik's avatar
Dominik committed
229 230 231 232 233 234
							$sql = "DELETE FROM mail_backup WHERE server_id = ? AND parent_domain_id = ? AND filename = ?";
							$app->db->query($sql, $conf['server_id'], $backup['parent_domain_id'], $backup['filename']);
							if($app->db->dbHost != $app->dbmaster->dbHost) $app->dbmaster->query($sql);
						}
					}
				}
Florian Schaal's avatar
Florian Schaal committed
235
				if( $server_config['backup_dir_is_mount'] == 'y' ) $app->system->umount_backup_dir($backup_dir);
236 237
				//* end run_backups
			}
Florian Schaal's avatar
Florian Schaal committed
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252
		}

		parent::onRunJob();
	}

	/* this function is optional if it contains no custom code */
	public function onAfterRun() {
		global $app;

		parent::onAfterRun();
	}

}

?>