apache2_plugin.inc.php 60 KB
Newer Older
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
<?php

/*
Copyright (c) 2007 - 2009, Till Brehm, projektfarm Gmbh
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.
*/

class apache2_plugin {
32

33 34
	var $plugin_name = 'apache2_plugin';
	var $class_name = 'apache2_plugin';
35

36 37
	// private variables
	var $action = '';
38

39 40 41 42
	//* This function is called during ispconfig installation to determine
	//  if a symlink shall be created for this plugin.
	function onInstall() {
		global $conf;
43

44 45 46 47 48
		if($conf['services']['web'] == true) {
			return true;
		} else {
			return false;
		}
49

50
	}
51 52


53 54 55
	/*
	 	This function is called when the plugin is loaded
	*/
56

57 58
	function onLoad() {
		global $app;
59

60 61 62 63 64 65
		/*
		Register for the events
		*/
		$app->plugins->registerEvent('web_domain_insert',$this->plugin_name,'ssl');
		$app->plugins->registerEvent('web_domain_update',$this->plugin_name,'ssl');
		$app->plugins->registerEvent('web_domain_delete',$this->plugin_name,'ssl');
66

67 68 69
		$app->plugins->registerEvent('web_domain_insert',$this->plugin_name,'insert');
		$app->plugins->registerEvent('web_domain_update',$this->plugin_name,'update');
		$app->plugins->registerEvent('web_domain_delete',$this->plugin_name,'delete');
70

71 72 73
		$app->plugins->registerEvent('server_ip_insert',$this->plugin_name,'server_ip');
		$app->plugins->registerEvent('server_ip_update',$this->plugin_name,'server_ip');
		$app->plugins->registerEvent('server_ip_delete',$this->plugin_name,'server_ip');
74 75 76 77

		$app->plugins->registerEvent('webdav_user_insert',$this->plugin_name,'webdav');
		$app->plugins->registerEvent('webdav_user_update',$this->plugin_name,'webdav');
		$app->plugins->registerEvent('webdav_user_delete',$this->plugin_name,'webdav');
78 79
		
		$app->plugins->registerEvent('client_delete',$this->plugin_name,'client_delete');
80
	}
81

82 83 84
	// Handle the creation of SSL certificates
	function ssl($event_name,$data) {
		global $app, $conf;
85 86 87 88 89 90

		// load the server configuration options
		$app->uses('getconf');
		$web_config = $app->getconf->get_server_config($conf['server_id'], 'web');
		if ($web_config['CA_path']!='' && !file_exists($web_config['CA_path'].'/openssl.cnf'))
			$app->log("CA path error, file does not exist:".$web_config['CA_path'].'/openssl.conf',LOGLEVEL_ERROR);	
91 92 93
		
		//* Only vhosts can have a ssl cert
		if($data["new"]["type"] != "vhost") return;
94

95 96 97 98 99 100 101
		if(!is_dir($data['new']['document_root'].'/ssl')) exec('mkdir -p '.$data['new']['document_root'].'/ssl');
		$ssl_dir = $data['new']['document_root'].'/ssl';
		$domain = $data['new']['ssl_domain'];
		$key_file = $ssl_dir.'/'.$domain.'.key.org';
		$key_file2 = $ssl_dir.'/'.$domain.'.key';
		$csr_file = $ssl_dir.'/'.$domain.'.csr';
		$crt_file = $ssl_dir.'/'.$domain.'.crt';
102

103
		//* Create a SSL Certificate
104 105
		if($data['new']['ssl_action'] == 'create') {
			$rand_file = $ssl_dir.'/random_file';
106 107 108 109 110 111 112 113 114 115 116
			$rand_data = md5(uniqid(microtime(),1));
			for($i=0; $i<1000; $i++) {
				$rand_data .= md5(uniqid(microtime(),1));
				$rand_data .= md5(uniqid(microtime(),1));
				$rand_data .= md5(uniqid(microtime(),1));
				$rand_data .= md5(uniqid(microtime(),1));
			}
			file_put_contents($rand_file, $rand_data);

			$ssl_password = substr(md5(uniqid(microtime(),1)), 0, 15);

117 118 119
			$ssl_cnf = "        RANDFILE               = $rand_file

        [ req ]
120
        default_bits           = 2048
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
        default_keyfile        = keyfile.pem
        distinguished_name     = req_distinguished_name
        attributes             = req_attributes
        prompt                 = no
        output_password        = $ssl_password

        [ req_distinguished_name ]
        C                      = ".$data['new']['ssl_country']."
        ST                     = ".$data['new']['ssl_state']."
        L                      = ".$data['new']['ssl_locality']."
        O                      = ".$data['new']['ssl_organisation']."
        OU                     = ".$data['new']['ssl_organisation_unit']."
        CN                     = $domain
        emailAddress           = webmaster@".$data['new']['domain']."

        [ req_attributes ]
        challengePassword              = A challenge password";
138

139
			$ssl_cnf_file = $ssl_dir.'/openssl.conf';
140
			file_put_contents($ssl_cnf_file,$ssl_cnf);
141

142 143 144 145 146 147 148 149
			$rand_file = escapeshellcmd($rand_file);
			$key_file = escapeshellcmd($key_file);
			$key_file2 = escapeshellcmd($key_file2);
			$ssl_days = 3650;
			$csr_file = escapeshellcmd($csr_file);
			$config_file = escapeshellcmd($ssl_cnf_file);
			$crt_file = escapeshellcmd($crt_file);

150
			if(is_file($ssl_cnf_file)) {
151
				
152 153
				exec("openssl genrsa -des3 -rand $rand_file -passout pass:$ssl_password -out $key_file 2048");
				exec("openssl req -new -passin pass:$ssl_password -passout pass:$ssl_password -key $key_file -out $csr_file -days $ssl_days -config $config_file");
154 155 156
				exec("openssl rsa -passin pass:$ssl_password -in $key_file -out $key_file2");

				if(file_exists($web_config['CA_path'].'/openssl.cnf'))
157
				{
158
					exec("openssl ca -batch -out $crt_file -config ".$web_config['CA_path']."/openssl.cnf -passin pass:".$web_config['CA_pass']." -in $csr_file");
159
					$app->log("Creating CA-signed SSL Cert for: $domain",LOGLEVEL_DEBUG);
160 161 162
					if (filesize($crt_file)==0 || !file_exists($crt_file)) $app->log("CA-Certificate signing failed.  openssl ca -out $crt_file -config ".$web_config['CA_path']."/openssl.cnf -passin pass:".$web_config['CA_pass']." -in $csr_file",LOGLEVEL_ERROR);
				};
				if (filesize($crt_file)==0 || !file_exists($crt_file)){
163 164 165
					exec("openssl req -x509 -passin pass:$ssl_password -passout pass:$ssl_password -key $key_file -in $csr_file -out $crt_file -days $ssl_days -config $config_file ");
					$app->log("Creating self-signed SSL Cert for: $domain",LOGLEVEL_DEBUG);
				};
166
			
167
			}
168

169
			exec('chmod 400 '.$key_file2);
170 171 172 173
			@unlink($config_file);
			@unlink($rand_file);
			$ssl_request = $app->db->quote(file_get_contents($csr_file));
			$ssl_cert = $app->db->quote(file_get_contents($crt_file));
174
			/* Update the DB of the (local) Server */
175 176
			$app->db->query("UPDATE web_domain SET ssl_request = '$ssl_request', ssl_cert = '$ssl_cert' WHERE domain = '".$data['new']['domain']."'");
			$app->db->query("UPDATE web_domain SET ssl_action = '' WHERE domain = '".$data['new']['domain']."'");
177
			/* Update also the master-DB of the Server-Farm */
178 179
			$app->dbmaster->query("UPDATE web_domain SET ssl_request = '$ssl_request', ssl_cert = '$ssl_cert' WHERE domain = '".$data['new']['domain']."'");
			$app->dbmaster->query("UPDATE web_domain SET ssl_action = '' WHERE domain = '".$data['new']['domain']."'");
180
		}
181

182
		//* Save a SSL certificate to disk
183 184 185 186 187 188 189 190 191
		if($data["new"]["ssl_action"] == 'save') {
			$ssl_dir = $data["new"]["document_root"]."/ssl";
			$domain = $data["new"]["ssl_domain"];
			$csr_file = $ssl_dir.'/'.$domain.".csr";
			$crt_file = $ssl_dir.'/'.$domain.".crt";
			$bundle_file = $ssl_dir.'/'.$domain.".bundle";
			if(trim($data["new"]["ssl_request"]) != '') file_put_contents($csr_file,$data["new"]["ssl_request"]);
			if(trim($data["new"]["ssl_cert"]) != '') file_put_contents($crt_file,$data["new"]["ssl_cert"]);
			if(trim($data["new"]["ssl_bundle"]) != '') file_put_contents($bundle_file,$data["new"]["ssl_bundle"]);
192
			/* Update the DB of the (local) Server */
193
			$app->db->query("UPDATE web_domain SET ssl_action = '' WHERE domain = '".$data['new']['domain']."'");
194
			/* Update also the master-DB of the Server-Farm */
195 196
			$app->dbmaster->query("UPDATE web_domain SET ssl_action = '' WHERE domain = '".$data['new']['domain']."'");
			$app->log('Saving SSL Cert for: '.$domain,LOGLEVEL_DEBUG);
197
		}
198

199
		//* Delete a SSL certificate
200 201 202 203 204 205
		if($data['new']['ssl_action'] == 'del') {
			$ssl_dir = $data['new']['document_root'].'/ssl';
			$domain = $data['new']['ssl_domain'];
			$csr_file = $ssl_dir.'/'.$domain.'.csr';
			$crt_file = $ssl_dir.'/'.$domain.'.crt';
			$bundle_file = $ssl_dir.'/'.$domain.'.bundle';
206
			if(file_exists($web_config['CA_path'].'/openssl.cnf'))
207
				{
208
					exec("openssl ca -batch -config ".$web_config['CA_path']."/openssl.cnf -passin pass:".$web_config['CA_pass']." -revoke $crt_file");
209 210
					$app->log("Revoking CA-signed SSL Cert for: $domain",LOGLEVEL_DEBUG);
				};
211 212 213
			unlink($csr_file);
			unlink($crt_file);
			unlink($bundle_file);
214
			/* Update the DB of the (local) Server */
215 216
			$app->db->query("UPDATE web_domain SET ssl_request = '', ssl_cert = '' WHERE domain = '".$data['new']['domain']."'");
			$app->db->query("UPDATE web_domain SET ssl_action = '' WHERE domain = '".$data['new']['domain']."'");
217
			/* Update also the master-DB of the Server-Farm */
218 219 220
			$app->dbmaster->query("UPDATE web_domain SET ssl_request = '', ssl_cert = '' WHERE domain = '".$data['new']['domain']."'");
			$app->dbmaster->query("UPDATE web_domain SET ssl_action = '' WHERE domain = '".$data['new']['domain']."'");
			$app->log('Deleting SSL Cert for: '.$domain,LOGLEVEL_DEBUG);
221
		}
222 223


224
	}
225 226


227 228
	function insert($event_name,$data) {
		global $app, $conf;
229

230 231 232
		$this->action = 'insert';
		// just run the update function
		$this->update($event_name,$data);
233 234


235
	}
236 237


238 239
	function update($event_name,$data) {
		global $app, $conf;
240

241
		if($this->action != 'insert') $this->action = 'update';
242

243
		if($data['new']['type'] != 'vhost' && $data['new']['parent_domain_id'] > 0) {
244

245 246
			$old_parent_domain_id = intval($data['old']['parent_domain_id']);
			$new_parent_domain_id = intval($data['new']['parent_domain_id']);
247

248
			// If the parent_domain_id has been changed, we will have to update the old site as well.
249 250 251 252
			if($this->action == 'update' && $data['new']['parent_domain_id'] != $data['old']['parent_domain_id']) {
				$tmp = $app->db->queryOneRecord('SELECT * FROM web_domain WHERE domain_id = '.$old_parent_domain_id." AND active = 'y'");
				$data['new'] = $tmp;
				$data['old'] = $tmp;
253 254 255
				$this->action = 'update';
				$this->update($event_name,$data);
			}
256

257
			// This is not a vhost, so we need to update the parent record instead.
258 259 260
			$tmp = $app->db->queryOneRecord('SELECT * FROM web_domain WHERE domain_id = '.$new_parent_domain_id." AND active = 'y'");
			$data['new'] = $tmp;
			$data['old'] = $tmp;
261 262
			$this->action = 'update';
		}
263

264
		// load the server configuration options
265 266
		$app->uses('getconf');
		$web_config = $app->getconf->get_server_config($conf['server_id'], 'web');
267

268
		//* Check if this is a chrooted setup
269
		if($web_config['website_basedir'] != '' && @is_file($web_config['website_basedir'].'/etc/passwd')) {
270
			$apache_chrooted = true;
271
			$app->log('Info: Apache is chrooted.',LOGLEVEL_DEBUG);
272 273 274
		} else {
			$apache_chrooted = false;
		}
275

276 277
		if($data['new']['document_root'] == '') {
			$app->log('document_root not set',LOGLEVEL_WARN);
278 279
			return 0;
		}
280 281
		if($data['new']['system_user'] == 'root' or $data['new']['system_group'] == 'root') {
			$app->log('Websites cannot be owned by the root user or group.',LOGLEVEL_WARN);
282 283
			return 0;
		}
284

285
		//* If the client of the site has been changed, we have a change of the document root
286
		if($this->action == 'update' && $data['new']['document_root'] != $data['old']['document_root']) {
287

288
			//* Get the old client ID
289 290
			$old_client = $app->dbmaster->queryOneRecord('SELECT client_id FROM sys_group WHERE sys_group.groupid = '.intval($data['old']['sys_groupid']));
			$old_client_id = intval($old_client['client_id']);
291
			unset($old_client);
292

293
			//* Remove the old symlinks
294
			$tmp_symlinks_array = explode(':',$web_config['website_symlinks']);
295 296
			if(is_array($tmp_symlinks_array)) {
				foreach($tmp_symlinks_array as $tmp_symlink) {
297 298
					$tmp_symlink = str_replace('[client_id]',$old_client_id,$tmp_symlink);
					$tmp_symlink = str_replace('[website_domain]',$data['old']['domain'],$tmp_symlink);
299 300 301
					// Remove trailing slash
					if(substr($tmp_symlink, -1, 1) == '/') $tmp_symlink = substr($tmp_symlink, 0, -1);
					// create the symlinks, if not exist
302
					if(is_link($tmp_symlink)) {
303 304
						exec('rm -f '.escapeshellcmd($tmp_symlink));
						$app->log('Removed symlink: rm -f '.$tmp_symlink,LOGLEVEL_DEBUG);
305 306 307
					}
				}
			}
308

309
			//* Move the site data
310
			$tmp_docroot = explode('/',$data['new']['document_root']);
311 312
			unset($tmp_docroot[count($tmp_docroot)-1]);
			$new_dir = implode('/',$tmp_docroot);
313

314
			$tmp_docroot = explode('/',$data['old']['document_root']);
315 316
			unset($tmp_docroot[count($tmp_docroot)-1]);
			$old_dir = implode('/',$tmp_docroot);
317

318
			exec('rm -rf '.$data['new']['document_root']);
319
			if(!is_dir($new_dir)) exec('mkdir -p '.$new_dir);
320 321
			exec('mv '.$data['old']['document_root'].' '.$new_dir);
			$app->log('Moving site to new document root: mv '.$data['old']['document_root'].' '.$new_dir,LOGLEVEL_DEBUG);
322

323 324 325
			// Handle the change in php_open_basedir
			$data['new']['php_open_basedir'] = str_replace($data['old']['document_root'],$data['new']['document_root'],$data['old']['php_open_basedir']);

326
			//* Change the owner of the website files to the new website owner
327
			exec('chown --recursive --from='.escapeshellcmd($data['old']['system_user']).':'.escapeshellcmd($data['old']['system_group']).' '.escapeshellcmd($data['new']['system_user']).':'.escapeshellcmd($data['new']['system_group']).' '.$new_dir);
328

329 330
			//* Change the home directory and group of the website user
			$command = 'usermod';
331
			$command .= ' --home '.escapeshellcmd($data['new']['document_root']);
332
			$command .= ' --gid '.escapeshellcmd($data['new']['system_group']);
333
			$command .= ' '.escapeshellcmd($data['new']['system_user']);
334
			exec($command);
335

336
			if($apache_chrooted) $this->_exec('chroot '.escapeshellcmd($web_config['website_basedir']).' '.$command);
337 338


339
		}
340

341
		//print_r($data);
342

343 344 345 346 347 348 349
		// Check if the directories are there and create them if necessary.
		if(!is_dir($data['new']['document_root'].'/web')) exec('mkdir -p '.$data['new']['document_root'].'/web');
		if(!is_dir($data['new']['document_root'].'/web/error') and $data['new']['errordocs']) exec('mkdir -p '.$data['new']['document_root'].'/web/error');
		//if(!is_dir($data['new']['document_root'].'/log')) exec('mkdir -p '.$data['new']['document_root'].'/log');
		if(!is_dir($data['new']['document_root'].'/ssl')) exec('mkdir -p '.$data['new']['document_root'].'/ssl');
		if(!is_dir($data['new']['document_root'].'/cgi-bin')) exec('mkdir -p '.$data['new']['document_root'].'/cgi-bin');
		if(!is_dir($data['new']['document_root'].'/tmp')) exec('mkdir -p '.$data['new']['document_root'].'/tmp');
350

351
		// Remove the symlink for the site, if site is renamed
352 353 354
		if($this->action == 'update' && $data['old']['domain'] != '' && $data['new']['domain'] != $data['old']['domain']) {
			if(is_dir('/var/log/ispconfig/httpd/'.$data['old']['domain'])) exec('rm -rf /var/log/ispconfig/httpd/'.$data['old']['domain']);
			if(is_link($data['old']['document_root'].'/log')) unlink($data['old']['document_root'].'/log');
355
		}
356

357
		// Create the symlink for the logfiles
358 359 360 361
		if(!is_dir('/var/log/ispconfig/httpd/'.$data['new']['domain'])) exec('mkdir -p /var/log/ispconfig/httpd/'.$data['new']['domain']);
		if(!is_link($data['new']['document_root'].'/log')) {
			exec('ln -s /var/log/ispconfig/httpd/'.$data['new']['domain'].' '.$data['new']['document_root'].'/log');
			$app->log('Creating symlink: ln -s /var/log/ispconfig/httpd/'.$data['new']['domain'].' '.$data['new']['document_root'].'/log',LOGLEVEL_DEBUG);
362 363 364
		}
		/*
		// Create the symlink for the logfiles
365 366 367 368
		// This does not work as vlogger cannot log trough symlinks.
		if($this->action == 'update' && $data['old']['domain'] != '' && $data['new']['domain'] != $data['old']['domain']) {
			if(is_dir($data['old']['document_root'].'/log')) exec('rm -rf '.$data['old']['document_root'].'/log');
			if(is_link('/var/log/ispconfig/httpd/'.$data['old']['domain'])) unlink('/var/log/ispconfig/httpd/'.$data['old']['domain']);
369 370 371
		}
		
		// Create the symlink for the logfiles
372 373 374 375
		if(!is_dir($data['new']['document_root'].'/log')) exec('mkdir -p '.$data['new']['document_root'].'/log');
		if(!is_link('/var/log/ispconfig/httpd/'.$data['new']['domain'])) {
			exec('ln -s '.$data['new']['document_root'].'/log /var/log/ispconfig/httpd/'.$data['new']['domain']);
			$app->log('Creating symlink: ln -s '.$data['new']['document_root'].'/log /var/log/ispconfig/httpd/'.$data['new']['domain'],LOGLEVEL_DEBUG);
376 377
		}
		*/
378

379
		// Get the client ID
380 381
		$client = $app->dbmaster->queryOneRecord('SELECT client_id FROM sys_group WHERE sys_group.groupid = '.intval($data['new']['sys_groupid']));
		$client_id = intval($client['client_id']);
382
		unset($client);
383

384
		// Remove old symlinks, if site is renamed
385 386
		if($this->action == 'update' && $data['old']['domain'] != '' && $data['new']['domain'] != $data['old']['domain']) {
			$tmp_symlinks_array = explode(':',$web_config['website_symlinks']);
387 388
			if(is_array($tmp_symlinks_array)) {
				foreach($tmp_symlinks_array as $tmp_symlink) {
389 390
					$tmp_symlink = str_replace('[client_id]',$client_id,$tmp_symlink);
					$tmp_symlink = str_replace('[website_domain]',$data['old']['domain'],$tmp_symlink);
391 392 393 394
					// Remove trailing slash
					if(substr($tmp_symlink, -1, 1) == '/') $tmp_symlink = substr($tmp_symlink, 0, -1);
					// remove the symlinks, if not exist
					if(is_link($tmp_symlink)) {
395 396
						exec('rm -f '.escapeshellcmd($tmp_symlink));
						$app->log('Removed symlink: rm -f '.$tmp_symlink,LOGLEVEL_DEBUG);
397 398 399 400
					}
				}
			}
		}
401

402
		// Create the symlinks for the sites
403
		$tmp_symlinks_array = explode(':',$web_config['website_symlinks']);
404 405
		if(is_array($tmp_symlinks_array)) {
			foreach($tmp_symlinks_array as $tmp_symlink) {
406 407
				$tmp_symlink = str_replace('[client_id]',$client_id,$tmp_symlink);
				$tmp_symlink = str_replace('[website_domain]',$data['new']['domain'],$tmp_symlink);
408 409 410
				// Remove trailing slash
				if(substr($tmp_symlink, -1, 1) == '/') $tmp_symlink = substr($tmp_symlink, 0, -1);
				//* Remove symlink if target folder has been changed.
411
				if($data['old']['document_root'] != '' && $data['old']['document_root'] != $data['new']['document_root'] && is_link($tmp_symlink)) {
412 413 414 415
					unlink($tmp_symlink);
				}
				// create the symlinks, if not exist
				if(!is_link($tmp_symlink)) {
416 417
					exec('ln -s '.escapeshellcmd($data['new']['document_root']).'/ '.escapeshellcmd($tmp_symlink));
					$app->log('Creating symlink: ln -s '.$data['new']['document_root'].'/ '.$tmp_symlink,LOGLEVEL_DEBUG);
418 419 420
				}
			}
		}
421 422


latham's avatar
latham committed
423 424 425 426 427 428 429 430

        // Install the Standard or Custom Error, Index and other related files
        // /usr/local/ispconfig/server/conf is for the standard files
        // /usr/local/ispconfig/server/conf-custom is for the custom files
        // setting a local var here
           
        // normally $conf['templates'] = "/usr/local/ispconfig/server/conf";

431
		if($this->action == 'insert' && $data['new']['type'] == 'vhost') {
432
			// Copy the error pages
433 434
			if($data['new']['errordocs']) {
				$error_page_path = escapeshellcmd($data['new']['document_root']).'/web/error/';
latham's avatar
latham committed
435 436
				if (file_exists($conf['templates'] . '-custom/error/'.substr(escapeshellcmd($conf['language']),0,2))) {
					exec('cp ' . $conf['templates'] . '-custom/error/'.substr(escapeshellcmd($conf['language']),0,2).'/* '.$error_page_path);
437 438
				}
				else {
latham's avatar
latham committed
439 440
					if (file_exists($conf['templates'] . '-custom/error/400.html')) {
						exec('cp '. $conf['templates'] .'-custom/error/*.html '.$error_page_path);
441 442
					}
					else {
latham's avatar
latham committed
443
						exec('cp ' . $conf['templates'] . '/error/'.substr(escapeshellcmd($conf['language']),0,2).'/* '.$error_page_path);
444 445
					}
				}
446
				exec('chmod -R a+r '.$error_page_path);
447 448
			}

latham's avatar
latham committed
449 450 451 452 453 454 455 456 457 458 459 460 461
			if (file_exists($conf['templates'] . '-custom/index/standard_index.html_'.substr(escapeshellcmd($conf['language']),0,2))) {
				exec('cp ' . $conf['templates'] . '-custom/index/standard_index.html_'.substr(escapeshellcmd($conf['language']),0,2).' '.escapeshellcmd($data['new']['document_root']).'/web/index.html');
            
			if(is_file($conf['templates'] . '-custom/index/favicon.ico')) {
                exec('cp ' . $conf['templates'] . '-custom/index/favicon.ico '.escapeshellcmd($data['new']['document_root']).'/web/');
            }
			if(is_file($conf['templates'] . '-custom/index/robots.txt')) {
                exec('cp ' . $conf['templates'] . '-custom/index/robots.txt '.escapeshellcmd($data['new']['document_root']).'/web/');
                }
                if(is_file($conf['templates'] . '-custom/index/.htaccess')) {
                    exec('cp ' . $conf['templates'] . '-custom/index/.htaccess '.escapeshellcmd($data['new']['document_root']).'/web/');
                }
            }
462
			else {
latham's avatar
latham committed
463 464
				if (file_exists($conf['templates'] . '-custom/index/standard_index.html')) {
					exec('cp ' . $conf['templates'] . '-custom/index/standard_index.html '.escapeshellcmd($data['new']['document_root']).'/web/index.html');
465 466
				}
				else {
latham's avatar
latham committed
467 468 469 470
					exec('cp ' . $conf['templates'] . '/index/standard_index.html_'.substr(escapeshellcmd($conf['language']),0,2).' '.escapeshellcmd($data['new']['document_root']).'/web/index.html');
					if(is_file($conf['templates'] . '/index/favicon.ico')) exec('cp ' . $conf['templates'] . '/index/favicon.ico '.escapeshellcmd($data['new']['document_root']).'/web/');
					if(is_file($conf['templates'] . '/index/robots.txt')) exec('cp ' . $conf['templates'] . '/index/robots.txt '.escapeshellcmd($data['new']['document_root']).'/web/');
					if(is_file($conf['templates'] . '/index/.htaccess')) exec('cp ' . $conf['templates'] . '/index/.htaccess '.escapeshellcmd($data['new']['document_root']).'/web/');
471 472
				}
			}
473
			exec('chmod -R a+r '.escapeshellcmd($data['new']['document_root']).'/web/');
474 475

			//** Copy the error documents on update when the error document checkbox has been activated and was deactivated before
476
		} elseif ($this->action == 'update' && $data['new']['type'] == 'vhost' && $data['old']['errordocs'] == 0 && $data['new']['errordocs'] == 1) {
477

478
			$error_page_path = escapeshellcmd($data['new']['document_root']).'/web/error/';
latham's avatar
latham committed
479 480
			if (file_exists($conf['templates'] . '-custom/error/'.substr(escapeshellcmd($conf['language']),0,2))) {
				exec('cp ' . $conf['templates'] . '-custom/error/'.substr(escapeshellcmd($conf['language']),0,2).'/* '.$error_page_path);
481 482
			}
			else {
latham's avatar
latham committed
483 484
				if (file_exists($conf['templates'] . '-custom/error/400.html')) {
					exec('cp ' . $conf['templates'] . '-custom/error/*.html '.$error_page_path);
485 486
				}
				else {
latham's avatar
latham committed
487
					exec('cp ' . $conf['templates'] . '/error/'.substr(escapeshellcmd($conf['language']),0,2).'/* '.$error_page_path);
488 489
				}
			}
490
			exec('chmod -R a+r '.$error_page_path);
latham's avatar
latham committed
491
			exec('chown -R '.$data['new']['system_user'].':'.$data['new']['system_group'].' '.$error_page_path);
492
		}  // end copy error docs
493

494
		// Create group and user, if not exist
495
		$app->uses('system');
496

497 498 499 500 501
		$groupname = escapeshellcmd($data['new']['system_group']);
		if($data['new']['system_group'] != '' && !$app->system->is_group($data['new']['system_group'])) {
			exec('groupadd '.$groupname);
			if($apache_chrooted) $this->_exec('chroot '.escapeshellcmd($web_config['website_basedir']).' groupadd '.$groupname);
			$app->log('Adding the group: '.$groupname,LOGLEVEL_DEBUG);
502
		}
503

504 505 506 507 508
		$username = escapeshellcmd($data['new']['system_user']);
		if($data['new']['system_user'] != '' && !$app->system->is_user($data['new']['system_user'])) {
			exec('useradd -d '.escapeshellcmd($data['new']['document_root'])." -g $groupname -G sshusers $username -s /bin/false");
			if($apache_chrooted) $this->_exec('chroot '.escapeshellcmd($web_config['website_basedir']).' useradd -d '.escapeshellcmd($data['new']['document_root'])." -g $groupname -G sshusers $username -s /bin/false");
			$app->log('Adding the user: '.$username,LOGLEVEL_DEBUG);
509
		}
510

511 512
		// Set the quota for the user
		if($username != '' && $app->system->is_user($username)) {
513 514
			if($data['new']['hd_quota'] > 0) {
				$blocks_soft = $data['new']['hd_quota'] * 1024;
515 516 517 518
				$blocks_hard = $blocks_soft + 1024;
			} else {
				$blocks_soft = $blocks_hard = 0;
			}
519
			exec("setquota -u $username $blocks_soft $blocks_hard 0 0 -a &> /dev/null");
520
			exec('setquota -T -u '.$username.' 604800 604800 -a &> /dev/null');
521
		}
522

523
		if($this->action == 'insert' || $data["new"]["system_user"] != $data["old"]["system_user"]) {
524
			// Chown and chmod the directories below the document root
525
			$this->_exec('chown -R '.$username.':'.$groupname.' '.escapeshellcmd($data['new']['document_root']));
526 527
			// The document root itself has to be owned by root in normal level and by the web owner in security level 20
			if($web_config['security_level'] == 20) {
528
				$this->_exec('chown '.$username.':'.$groupname.' '.escapeshellcmd($data['new']['document_root']));
529
			} else {
530
				$this->_exec('chown root:root '.escapeshellcmd($data['new']['document_root']));
531
			}
532
		}
533 534 535



536
		//* If the security level is set to high
tbrehm's avatar
tbrehm committed
537
		if($web_config['security_level'] == 20) {
538

539 540 541
			$this->_exec('chmod 751 '.escapeshellcmd($data['new']['document_root']));
			$this->_exec('chmod 751 '.escapeshellcmd($data['new']['document_root']).'/*');
			$this->_exec('chmod 710 '.escapeshellcmd($data['new']['document_root'].'/web'));
542

543 544
			// make tmp directory writable for Apache and the website users
			$this->_exec('chmod 777 '.escapeshellcmd($data['new']['document_root'].'/tmp'));
545 546 547
			
			// Set Log symlink to 755 to make the logs accessible by the FTP user
			$this->_exec("chmod 755 ".escapeshellcmd($data["new"]["document_root"])."/log");
548

549 550
			$command = 'usermod';
			$command .= ' --groups sshusers';
551
			$command .= ' '.escapeshellcmd($data['new']['system_user']);
552
			$this->_exec($command);
553

554
			//* if we have a chrooted Apache environment
555
			if($apache_chrooted) {
556
				$this->_exec('chroot '.escapeshellcmd($web_config['website_basedir']).' '.$command);
557

558 559 560
				//* add the apache user to the client group in the chroot environment
				$tmp_groupfile = $app->system->server_conf['group_datei'];
				$app->system->server_conf['group_datei'] = $web_config['website_basedir'].'/etc/group';
561
				$app->system->add_user_to_group($groupname, escapeshellcmd($web_config['user']));
562
				$app->system->server_conf['group_datei'] = $tmp_groupfile;
563 564
				unset($tmp_groupfile);
			}
565

566
			//* add the Apache user to the client group
tbrehm's avatar
tbrehm committed
567
			$app->system->add_user_to_group($groupname, escapeshellcmd($web_config['user']));
568

569
			$this->_exec('chown '.$username.':'.$groupname.' '.escapeshellcmd($data['new']['document_root']));
570

571 572 573 574
			/*
			* Workaround for jailkit: If jailkit is enabled for the site, the 
			* website root has to be owned by the root user and we have to chmod it to 755 then
			*/
575

576
			//* Check if there is a jailkit user for this site
577
			$tmp = $app->db->queryOneRecord('SELECT count(shell_user_id) as number FROM shell_user WHERE parent_domain_id = '.$data['new']['domain_id']." AND chroot = 'jailkit'");
578
			if($tmp['number'] > 0) {
579 580
				$this->_exec('chmod 755 '.escapeshellcmd($data['new']['document_root']));
				$this->_exec('chown root:root '.escapeshellcmd($data['new']['document_root']));
581 582
			}
			unset($tmp);
583 584

			// If the security Level is set to medium
tbrehm's avatar
tbrehm committed
585
		} else {
586

587 588 589
			$this->_exec('chmod 755 '.escapeshellcmd($data['new']['document_root']));
			$this->_exec('chmod 755 '.escapeshellcmd($data['new']['document_root'].'/*'));
			$this->_exec('chown root:root '.escapeshellcmd($data['new']['document_root']));
590

591 592
			// make temp directory writable for Apache and the website users
			$this->_exec('chmod 777 '.escapeshellcmd($data['new']['document_root'].'/tmp'));
tbrehm's avatar
tbrehm committed
593
		}
594

595 596 597
		// Change the ownership of the error log to the owner of the website
		if(!@is_file($data['new']['document_root'].'/log/error.log')) exec('touch '.escapeshellcmd($data['new']['document_root']).'/log/error.log');
		$this->_exec('chown '.$username.':'.$groupname.' '.escapeshellcmd($data['new']['document_root']).'/log/error.log');
598 599


600
		//* Write the custom php.ini file, if custom_php_ini filed is not empty
601
		$custom_php_ini_dir = $web_config['website_basedir'].'/conf/'.$data['new']['system_user'];
602
		if(!is_dir($web_config['website_basedir'].'/conf')) mkdir($web_config['website_basedir'].'/conf');
603
		if(trim($data['new']['custom_php_ini']) != '') {
604 605 606
			$has_custom_php_ini = true;
			if(!is_dir($custom_php_ini_dir)) mkdir($custom_php_ini_dir);
			$php_ini_content = '';
607
			if($data['new']['php'] == 'mod') {
608 609
				$master_php_ini_path = $web_config['php_ini_path_apache'];
			} else {
610 611 612 613 614
				if($data["new"]['php'] == 'fast-cgi' && file_exists($fastcgi_config["fastcgi_phpini_path"])) {
					$master_php_ini_path = $fastcgi_config["fastcgi_phpini_path"];
				} else {
					$master_php_ini_path = $web_config['php_ini_path_cgi'];
				}
615 616 617 618
			}
			if($master_php_ini_path != '' && substr($master_php_ini_path,-7) == 'php.ini' && is_file($master_php_ini_path)) {
				$php_ini_content .= file_get_contents($master_php_ini_path)."\n";
			}
619
			$php_ini_content .= trim($data['new']['custom_php_ini']);
620 621 622 623 624 625 626 627
			file_put_contents($custom_php_ini_dir.'/php.ini',$php_ini_content);
		} else {
			$has_custom_php_ini = false;
			if(is_file($custom_php_ini_dir.'/php.ini')) unlink($custom_php_ini_dir.'/php.ini');
		}


		//* Create the vhost config file
628
		$app->load('tpl');
629

630
		$tpl = new tpl();
631 632 633 634 635 636 637 638 639 640 641 642
		$tpl->newTemplate('vhost.conf.master');

		$vhost_data = $data['new'];
		$vhost_data['web_document_root'] = $data['new']['document_root'].'/web';
		$vhost_data['web_document_root_www'] = $web_config['website_basedir'].'/'.$data['new']['domain'].'/web';
		$vhost_data['web_basedir'] = $web_config['website_basedir'];
		$vhost_data['security_level'] = $web_config['security_level'];
		$vhost_data['allow_override'] = ($data['new']['allow_override'] == '')?'All':$data['new']['allow_override'];
		$vhost_data['php_open_basedir'] = ($data['new']['php_open_basedir'] == '')?$data['new']['document_root']:$data['new']['php_open_basedir'];
		$vhost_data['ssl_domain'] = $data['new']['ssl_domain'];
		$vhost_data['has_custom_php_ini'] = $has_custom_php_ini;
		$vhost_data['custom_php_ini_dir'] = escapeshellcmd($custom_php_ini_dir);
643

644
		// Check if a SSL cert exists
645 646 647 648 649 650
		$ssl_dir = $data['new']['document_root'].'/ssl';
		$domain = $data['new']['ssl_domain'];
		$key_file = $ssl_dir.'/'.$domain.'.key';
		$crt_file = $ssl_dir.'/'.$domain.'.crt';
		$bundle_file = $ssl_dir.'/'.$domain.'.bundle';

651
		if($domain!='' && $data['new']['ssl'] == 'y' && @is_file($crt_file) && @is_file($key_file) && (@filesize($crt_file)>0)  && (@filesize($key_file)>0)) {
652 653
			$vhost_data['ssl_enabled'] = 1;
			$app->log('Enable SSL for: '.$domain,LOGLEVEL_DEBUG);
654
		} else {
655
			$vhost_data['ssl_enabled'] = 0;
656
			$app->log('SSL Disabled. '.$domain,LOGLEVEL_DEBUG);
657
		}
658

659
		if(@is_file($bundle_file)) $vhost_data['has_bundle_cert'] = 1;
660

661
		//$vhost_data['document_root'] = $data['new']['document_root'].'/web';
662
		$tpl->setVar($vhost_data);
663

664 665
		// Rewrite rules
		$rewrite_rules = array();
666 667
		if($data['new']['redirect_type'] != '') {
			if(substr($data['new']['redirect_path'],-1) != '/') $data['new']['redirect_path'] .= '/';
tbrehm's avatar
tbrehm committed
668
			/* Disabled path extension
669 670
			if($data['new']['redirect_type'] == 'no' && substr($data['new']['redirect_path'],0,4) != 'http') {
				$data['new']['redirect_path'] = $data['new']['document_root'].'/web'.realpath($data['new']['redirect_path']).'/';
671
			}
tbrehm's avatar
tbrehm committed
672
			*/
673

674 675 676
			$rewrite_rules[] = array(	'rewrite_domain' 	=> $data['new']['domain'],
					'rewrite_type' 		=> ($data['new']['redirect_type'] == 'no')?'':'['.$data['new']['redirect_type'].']',
					'rewrite_target' 	=> $data['new']['redirect_path']);
677

678
			switch($data['new']['subdomain']) {
679
				case 'www':
680 681 682
					$rewrite_rules[] = array(	'rewrite_domain' 	=> 'www.'.$data['new']['domain'],
							'rewrite_type' 		=> ($data['new']['redirect_type'] == 'no')?'':'['.$data['new']['redirect_type'].']',
							'rewrite_target' 	=> $data['new']['redirect_path']);
683 684 685
					break;
				case '*':
				// TODO
686 687 688
				//$rewrite_rules[] = array(	'rewrite_domain' 	=> '*'.$alias['domain'],
				//							'rewrite_type' 		=> $alias['redirect_type'],
				//							'rewrite_target' 	=> $alias['redirect_path']);
689 690
					break;
			}
691
		}
692

693
		// get alias domains (co-domains and subdomains)
694
		$aliases = $app->db->queryAllRecords('SELECT * FROM web_domain WHERE parent_domain_id = '.$data['new']['domain_id']." AND active = 'y'");
695
		$server_alias = array();
696
		switch($data['new']['subdomain']) {
697
			case 'www':
698
				$server_alias[] .= 'www.'.$data['new']['domain'].' ';
699 700
				break;
			case '*':
701
				$server_alias[] .= '*.'.$data['new']['domain'].' ';
702 703
				break;
		}
704 705
		if(is_array($aliases)) {
			foreach($aliases as $alias) {
706
				switch($alias['subdomain']) {
707
					case 'www':
708
						$server_alias[] .= 'www.'.$alias['domain'].' '.$alias['domain'].' ';
709 710
						break;
					case '*':
711
						$server_alias[] .= '*.'.$alias['domain'].' '.$alias['domain'].' ';
712 713
						break;
					default:
714
						$server_alias[] .= $alias['domain'].' ';
715 716
						break;
				}
717
				$app->log('Add server alias: '.$alias['domain'],LOGLEVEL_DEBUG);
718
				// Rewriting
719 720
				if($alias['redirect_type'] != '') {
					if(substr($data['new']['redirect_path'],-1) != '/') $data['new']['redirect_path'] .= '/';
tbrehm's avatar
tbrehm committed
721
					/* Disabled the path extension
722 723
					if($data['new']['redirect_type'] == 'no' && substr($data['new']['redirect_path'],0,4) != 'http') {
						$data['new']['redirect_path'] = $data['new']['document_root'].'/web'.realpath($data['new']['redirect_path']).'/';
724
					}
tbrehm's avatar
tbrehm committed
725
					*/
726 727 728 729
					$rewrite_rules[] = array(	'rewrite_domain' 	=> $alias['domain'],
							'rewrite_type' 		=> ($alias['redirect_type'] == 'no')?'':'['.$alias['redirect_type'].']',
							'rewrite_target' 	=> $alias['redirect_path']);
					switch($alias['subdomain']) {
730
						case 'www':
731 732 733
							$rewrite_rules[] = array(	'rewrite_domain' 	=> 'www.'.$alias['domain'],
									'rewrite_type' 		=> ($alias['redirect_type'] ==