apache2_plugin.inc.php 72.9 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
		
		$app->plugins->registerEvent('web_folder_user_insert',$this->plugin_name,'web_folder_user');
		$app->plugins->registerEvent('web_folder_user_update',$this->plugin_name,'web_folder_user');
		$app->plugins->registerEvent('web_folder_user_delete',$this->plugin_name,'web_folder_user');
		
tbrehm's avatar
tbrehm committed
85
		$app->plugins->registerEvent('web_folder_update',$this->plugin_name,'web_folder_update');
86
87
		$app->plugins->registerEvent('web_folder_delete',$this->plugin_name,'web_folder_delete');
		
88
	}
89

90
91
92
	// Handle the creation of SSL certificates
	function ssl($event_name,$data) {
		global $app, $conf;
93
94
95
96
97
98

		// 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);	
99
100
101
		
		//* Only vhosts can have a ssl cert
		if($data["new"]["type"] != "vhost") return;
102

103
104
105
106
107
108
109
		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';
110

111
		//* Create a SSL Certificate
112
113
		if($data['new']['ssl_action'] == 'create') {
			$rand_file = $ssl_dir.'/random_file';
114
115
116
117
118
119
120
121
122
123
124
			$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);

125
126
127
			$ssl_cnf = "        RANDFILE               = $rand_file

        [ req ]
128
        default_bits           = 2048
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
        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";
146

147
			$ssl_cnf_file = $ssl_dir.'/openssl.conf';
148
			file_put_contents($ssl_cnf_file,$ssl_cnf);
149

150
151
152
153
154
155
156
157
			$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);

158
			if(is_file($ssl_cnf_file)) {
159
				
160
161
				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");
162
163
164
				exec("openssl rsa -passin pass:$ssl_password -in $key_file -out $key_file2");

				if(file_exists($web_config['CA_path'].'/openssl.cnf'))
165
				{
166
					exec("openssl ca -batch -out $crt_file -config ".$web_config['CA_path']."/openssl.cnf -passin pass:".$web_config['CA_pass']." -in $csr_file");
167
					$app->log("Creating CA-signed SSL Cert for: $domain",LOGLEVEL_DEBUG);
168
169
170
					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)){
171
172
173
					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);
				};
174
			
175
			}
176

177
			exec('chmod 400 '.$key_file2);
178
179
180
181
			@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));
182
			/* Update the DB of the (local) Server */
183
184
			$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']."'");
185
			/* Update also the master-DB of the Server-Farm */
186
187
			$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']."'");
188
		}
189

190
		//* Save a SSL certificate to disk
191
192
193
194
195
196
197
198
199
		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"]);
200
			/* Update the DB of the (local) Server */
201
			$app->db->query("UPDATE web_domain SET ssl_action = '' WHERE domain = '".$data['new']['domain']."'");
202
			/* Update also the master-DB of the Server-Farm */
203
204
			$app->dbmaster->query("UPDATE web_domain SET ssl_action = '' WHERE domain = '".$data['new']['domain']."'");
			$app->log('Saving SSL Cert for: '.$domain,LOGLEVEL_DEBUG);
205
		}
206

207
		//* Delete a SSL certificate
208
209
210
211
212
213
		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';
214
			if(file_exists($web_config['CA_path'].'/openssl.cnf'))
215
				{
216
					exec("openssl ca -batch -config ".$web_config['CA_path']."/openssl.cnf -passin pass:".$web_config['CA_pass']." -revoke $crt_file");
217
218
					$app->log("Revoking CA-signed SSL Cert for: $domain",LOGLEVEL_DEBUG);
				};
219
220
221
			unlink($csr_file);
			unlink($crt_file);
			unlink($bundle_file);
222
			/* Update the DB of the (local) Server */
223
224
			$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']."'");
225
			/* Update also the master-DB of the Server-Farm */
226
227
228
			$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);
229
		}
230
231


232
	}
233
234


235
236
	function insert($event_name,$data) {
		global $app, $conf;
237

238
239
240
		$this->action = 'insert';
		// just run the update function
		$this->update($event_name,$data);
241
242


243
	}
244
245


246
247
	function update($event_name,$data) {
		global $app, $conf;
248

249
		if($this->action != 'insert') $this->action = 'update';
250

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

253
254
			$old_parent_domain_id = intval($data['old']['parent_domain_id']);
			$new_parent_domain_id = intval($data['new']['parent_domain_id']);
255

256
			// If the parent_domain_id has been changed, we will have to update the old site as well.
257
258
259
260
			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;
261
262
263
				$this->action = 'update';
				$this->update($event_name,$data);
			}
264

265
			// This is not a vhost, so we need to update the parent record instead.
266
267
268
			$tmp = $app->db->queryOneRecord('SELECT * FROM web_domain WHERE domain_id = '.$new_parent_domain_id." AND active = 'y'");
			$data['new'] = $tmp;
			$data['old'] = $tmp;
269
270
			$this->action = 'update';
		}
271

272
		// load the server configuration options
273
274
		$app->uses('getconf');
		$web_config = $app->getconf->get_server_config($conf['server_id'], 'web');
275

276
		//* Check if this is a chrooted setup
277
		if($web_config['website_basedir'] != '' && @is_file($web_config['website_basedir'].'/etc/passwd')) {
278
			$apache_chrooted = true;
279
			$app->log('Info: Apache is chrooted.',LOGLEVEL_DEBUG);
280
281
282
		} else {
			$apache_chrooted = false;
		}
283

284
285
		if($data['new']['document_root'] == '') {
			$app->log('document_root not set',LOGLEVEL_WARN);
286
287
			return 0;
		}
288
289
		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);
290
291
			return 0;
		}
292

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

296
			//* Get the old client ID
297
298
			$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']);
299
			unset($old_client);
300

301
			//* Remove the old symlinks
302
			$tmp_symlinks_array = explode(':',$web_config['website_symlinks']);
303
304
			if(is_array($tmp_symlinks_array)) {
				foreach($tmp_symlinks_array as $tmp_symlink) {
305
306
					$tmp_symlink = str_replace('[client_id]',$old_client_id,$tmp_symlink);
					$tmp_symlink = str_replace('[website_domain]',$data['old']['domain'],$tmp_symlink);
307
308
309
					// Remove trailing slash
					if(substr($tmp_symlink, -1, 1) == '/') $tmp_symlink = substr($tmp_symlink, 0, -1);
					// create the symlinks, if not exist
310
					if(is_link($tmp_symlink)) {
311
312
						exec('rm -f '.escapeshellcmd($tmp_symlink));
						$app->log('Removed symlink: rm -f '.$tmp_symlink,LOGLEVEL_DEBUG);
313
314
315
					}
				}
			}
316

317
			//* Move the site data
318
			$tmp_docroot = explode('/',$data['new']['document_root']);
319
320
			unset($tmp_docroot[count($tmp_docroot)-1]);
			$new_dir = implode('/',$tmp_docroot);
321

322
			$tmp_docroot = explode('/',$data['old']['document_root']);
323
324
			unset($tmp_docroot[count($tmp_docroot)-1]);
			$old_dir = implode('/',$tmp_docroot);
325

326
			exec('rm -rf '.$data['new']['document_root']);
327
			if(!is_dir($new_dir)) exec('mkdir -p '.$new_dir);
328
329
			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);
330

331
332
333
			// 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']);

334
			//* Change the owner of the website files to the new website owner
335
			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);
336

337
338
			//* Change the home directory and group of the website user
			$command = 'usermod';
339
			$command .= ' --home '.escapeshellcmd($data['new']['document_root']);
340
			$command .= ' --gid '.escapeshellcmd($data['new']['system_group']);
341
			$command .= ' '.escapeshellcmd($data['new']['system_user']);
342
			exec($command);
343

344
			if($apache_chrooted) $this->_exec('chroot '.escapeshellcmd($web_config['website_basedir']).' '.$command);
345
346


347
		}
348

349
		//print_r($data);
350

351
352
353
354
355
356
357
		// 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');
358

359
		// Remove the symlink for the site, if site is renamed
360
361
362
		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');
363
		}
364

365
		// Create the symlink for the logfiles
366
367
		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')) {
368
369
370
371
372
373
374
//			exec("ln -s /var/log/ispconfig/httpd/".$data["new"]["domain"]." ".$data["new"]["document_root"]."/log");
			if ($web_config["website_symlinks_rel"] == 'y') {
				$this->create_relative_link("/var/log/ispconfig/httpd/".$data["new"]["domain"], $data["new"]["document_root"]."/log");
			} else {
				exec("ln -s /var/log/ispconfig/httpd/".$data["new"]["domain"]." ".$data["new"]["document_root"]."/log");
			}

375
			$app->log('Creating symlink: ln -s /var/log/ispconfig/httpd/'.$data['new']['domain'].' '.$data['new']['document_root'].'/log',LOGLEVEL_DEBUG);
376
377
378
		}
		/*
		// Create the symlink for the logfiles
379
380
381
382
		// 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']);
383
384
385
		}
		
		// Create the symlink for the logfiles
386
387
388
389
		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);
390
391
		}
		*/
392

393
		// Get the client ID
394
395
		$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']);
396
		unset($client);
397

398
		// Remove old symlinks, if site is renamed
399
400
		if($this->action == 'update' && $data['old']['domain'] != '' && $data['new']['domain'] != $data['old']['domain']) {
			$tmp_symlinks_array = explode(':',$web_config['website_symlinks']);
401
402
			if(is_array($tmp_symlinks_array)) {
				foreach($tmp_symlinks_array as $tmp_symlink) {
403
404
					$tmp_symlink = str_replace('[client_id]',$client_id,$tmp_symlink);
					$tmp_symlink = str_replace('[website_domain]',$data['old']['domain'],$tmp_symlink);
405
406
407
408
					// 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)) {
409
410
						exec('rm -f '.escapeshellcmd($tmp_symlink));
						$app->log('Removed symlink: rm -f '.$tmp_symlink,LOGLEVEL_DEBUG);
411
412
413
414
					}
				}
			}
		}
415

416
		// Create the symlinks for the sites
417
		$tmp_symlinks_array = explode(':',$web_config['website_symlinks']);
418
419
		if(is_array($tmp_symlinks_array)) {
			foreach($tmp_symlinks_array as $tmp_symlink) {
420
421
				$tmp_symlink = str_replace('[client_id]',$client_id,$tmp_symlink);
				$tmp_symlink = str_replace('[website_domain]',$data['new']['domain'],$tmp_symlink);
422
423
424
				// Remove trailing slash
				if(substr($tmp_symlink, -1, 1) == '/') $tmp_symlink = substr($tmp_symlink, 0, -1);
				//* Remove symlink if target folder has been changed.
425
				if($data['old']['document_root'] != '' && $data['old']['document_root'] != $data['new']['document_root'] && is_link($tmp_symlink)) {
426
427
428
429
					unlink($tmp_symlink);
				}
				// create the symlinks, if not exist
				if(!is_link($tmp_symlink)) {
430
431
432
433
434
435
436
//					exec("ln -s ".escapeshellcmd($data["new"]["document_root"])."/ ".escapeshellcmd($tmp_symlink));
					if ($web_config["website_symlinks_rel"] == 'y') {
						$this->create_relative_link(escapeshellcmd($data["new"]["document_root"]), escapeshellcmd($tmp_symlink));
					} else {
						exec("ln -s ".escapeshellcmd($data["new"]["document_root"])."/ ".escapeshellcmd($tmp_symlink));
					}

437
					$app->log('Creating symlink: ln -s '.$data['new']['document_root'].'/ '.$tmp_symlink,LOGLEVEL_DEBUG);
438
439
440
				}
			}
		}
441
442


latham's avatar
latham committed
443
444
445
446
447
448
449
450

        // 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";

451
		if($this->action == 'insert' && $data['new']['type'] == 'vhost') {
452
			// Copy the error pages
453
454
			if($data['new']['errordocs']) {
				$error_page_path = escapeshellcmd($data['new']['document_root']).'/web/error/';
455
456
				if (file_exists($conf['rootpath'] . '/conf-custom/error/'.substr(escapeshellcmd($conf['language']),0,2))) {
					exec('cp ' . $conf['rootpath'] . '/conf-custom/error/'.substr(escapeshellcmd($conf['language']),0,2).'/* '.$error_page_path);
457
458
				}
				else {
459
460
					if (file_exists($conf['rootpath'] . '/conf-custom/error/400.html')) {
						exec('cp '. $conf['rootpath'] . '/conf-custom/error/*.html '.$error_page_path);
461
462
					}
					else {
463
						exec('cp ' . $conf['rootpath'] . '/conf/error/'.substr(escapeshellcmd($conf['language']),0,2).'/* '.$error_page_path);
464
465
					}
				}
466
				exec('chmod -R a+r '.$error_page_path);
467
468
			}

469
470
			if (file_exists($conf['rootpath'] . '/conf-custom/index/standard_index.html_'.substr(escapeshellcmd($conf['language']),0,2))) {
				exec('cp ' . $conf['rootpath'] . '/conf-custom/index/standard_index.html_'.substr(escapeshellcmd($conf['language']),0,2).' '.escapeshellcmd($data['new']['document_root']).'/web/index.html');
latham's avatar
latham committed
471
            
472
473
			if(is_file($conf['rootpath'] . '/conf-custom/index/favicon.ico')) {
                exec('cp ' . $conf['rootpath'] . '/conf-custom/index/favicon.ico '.escapeshellcmd($data['new']['document_root']).'/web/');
latham's avatar
latham committed
474
            }
475
476
			if(is_file($conf['rootpath'] . '/conf-custom/index/robots.txt')) {
                exec('cp ' . $conf['rootpath'] . '/conf-custom/index/robots.txt '.escapeshellcmd($data['new']['document_root']).'/web/');
latham's avatar
latham committed
477
                }
478
479
                if(is_file($conf['rootpath'] . '/conf-custom/index/.htaccess')) {
                    exec('cp ' . $conf['rootpath'] . '/conf-custom/index/.htaccess '.escapeshellcmd($data['new']['document_root']).'/web/');
latham's avatar
latham committed
480
481
                }
            }
482
			else {
483
484
				if (file_exists($conf['rootpath'] . '/conf-custom/index/standard_index.html')) {
					exec('cp ' . $conf['rootpath'] . '/conf-custom/index/standard_index.html '.escapeshellcmd($data['new']['document_root']).'/web/index.html');
485
486
				}
				else {
487
488
489
490
					exec('cp ' . $conf['rootpath'] . '/conf/index/standard_index.html_'.substr(escapeshellcmd($conf['language']),0,2).' '.escapeshellcmd($data['new']['document_root']).'/web/index.html');
					if(is_file($conf['rootpath'] . '/conf/index/favicon.ico')) exec('cp ' . $conf['rootpath'] . '/conf/index/favicon.ico '.escapeshellcmd($data['new']['document_root']).'/web/');
					if(is_file($conf['rootpath'] . '/conf/index/robots.txt')) exec('cp ' . $conf['rootpath'] . '/conf/index/robots.txt '.escapeshellcmd($data['new']['document_root']).'/web/');
					if(is_file($conf['rootpath'] . '/conf/index/.htaccess')) exec('cp ' . $conf['rootpath'] . '/conf/index/.htaccess '.escapeshellcmd($data['new']['document_root']).'/web/');
491
492
				}
			}
493
			exec('chmod -R a+r '.escapeshellcmd($data['new']['document_root']).'/web/');
494
495

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

498
			$error_page_path = escapeshellcmd($data['new']['document_root']).'/web/error/';
499
500
			if (file_exists($conf['rootpath'] . '/conf-custom/error/'.substr(escapeshellcmd($conf['language']),0,2))) {
				exec('cp ' . $conf['rootpath'] . '/conf-custom/error/'.substr(escapeshellcmd($conf['language']),0,2).'/* '.$error_page_path);
501
502
			}
			else {
503
504
				if (file_exists($conf['rootpath'] . '/conf-custom/error/400.html')) {
					exec('cp ' . $conf['rootpath'] . '/conf-custom/error/*.html '.$error_page_path);
505
506
				}
				else {
507
					exec('cp ' . $conf['rootpath'] . '/conf/error/'.substr(escapeshellcmd($conf['language']),0,2).'/* '.$error_page_path);
508
509
				}
			}
510
			exec('chmod -R a+r '.$error_page_path);
latham's avatar
latham committed
511
			exec('chown -R '.$data['new']['system_user'].':'.$data['new']['system_group'].' '.$error_page_path);
512
		}  // end copy error docs
513

514
		// Create group and user, if not exist
515
		$app->uses('system');
516

517
518
519
520
521
		$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);
522
		}
523

524
525
526
527
528
		$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);
529
		}
530

531
532
		// Set the quota for the user
		if($username != '' && $app->system->is_user($username)) {
533
534
			if($data['new']['hd_quota'] > 0) {
				$blocks_soft = $data['new']['hd_quota'] * 1024;
535
536
537
538
				$blocks_hard = $blocks_soft + 1024;
			} else {
				$blocks_soft = $blocks_hard = 0;
			}
539
			exec("setquota -u $username $blocks_soft $blocks_hard 0 0 -a &> /dev/null");
540
			exec('setquota -T -u '.$username.' 604800 604800 -a &> /dev/null');
541
		}
542

543
		if($this->action == 'insert' || $data["new"]["system_user"] != $data["old"]["system_user"]) {
544
			// Chown and chmod the directories below the document root
545
			$this->_exec('chown -R '.$username.':'.$groupname.' '.escapeshellcmd($data['new']['document_root']));
546
547
			// 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) {
548
				$this->_exec('chown '.$username.':'.$groupname.' '.escapeshellcmd($data['new']['document_root']));
549
			} else {
550
				$this->_exec('chown root:root '.escapeshellcmd($data['new']['document_root']));
551
			}
552
		}
553
554
555



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

559
560
561
			$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'));
562

563
564
			// make tmp directory writable for Apache and the website users
			$this->_exec('chmod 777 '.escapeshellcmd($data['new']['document_root'].'/tmp'));
565
566
567
			
			// Set Log symlink to 755 to make the logs accessible by the FTP user
			$this->_exec("chmod 755 ".escapeshellcmd($data["new"]["document_root"])."/log");
568

569
570
			$command = 'usermod';
			$command .= ' --groups sshusers';
571
			$command .= ' '.escapeshellcmd($data['new']['system_user']);
572
			$this->_exec($command);
573

574
			//* if we have a chrooted Apache environment
575
			if($apache_chrooted) {
576
				$this->_exec('chroot '.escapeshellcmd($web_config['website_basedir']).' '.$command);
577

578
579
580
				//* 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';
581
				$app->system->add_user_to_group($groupname, escapeshellcmd($web_config['user']));
582
				$app->system->server_conf['group_datei'] = $tmp_groupfile;
583
584
				unset($tmp_groupfile);
			}
585

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

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

591
592
593
594
			/*
			* 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
			*/
595

596
			//* Check if there is a jailkit user for this site
597
			$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'");
598
			if($tmp['number'] > 0) {
599
600
				$this->_exec('chmod 755 '.escapeshellcmd($data['new']['document_root']));
				$this->_exec('chown root:root '.escapeshellcmd($data['new']['document_root']));
601
602
			}
			unset($tmp);
603
604

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

607
608
609
			$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']));
610

611
612
			// 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
613
		}
614

615
616
617
		// 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');
618
619


620
		//* Write the custom php.ini file, if custom_php_ini filed is not empty
621
		$custom_php_ini_dir = $web_config['website_basedir'].'/conf/'.$data['new']['system_user'];
622
		if(!is_dir($web_config['website_basedir'].'/conf')) mkdir($web_config['website_basedir'].'/conf');
623
		if(trim($data['new']['custom_php_ini']) != '') {
624
625
626
			$has_custom_php_ini = true;
			if(!is_dir($custom_php_ini_dir)) mkdir($custom_php_ini_dir);
			$php_ini_content = '';
627
			if($data['new']['php'] == 'mod') {
628
629
				$master_php_ini_path = $web_config['php_ini_path_apache'];
			} else {
630
631
632
633
634
				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'];
				}
635
636
637
638
			}
			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";
			}
639
			$php_ini_content .= trim($data['new']['custom_php_ini']);
640
641
642
643
644
645
646
647
			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
648
		$app->load('tpl');
649

650
		$tpl = new tpl();
651
652
653
654
655
656
657
658
659
660
661
662
		$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);
663

664
		// Check if a SSL cert exists
665
666
667
668
669
670
		$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';

671
		if($domain!='' && $data['new']['ssl'] == 'y' && @is_file($crt_file) && @is_file($key_file) && (@filesize($crt_file)>0)  && (@filesize($key_file)>0)) {
672
673
			$vhost_data['ssl_enabled'] = 1;
			$app->log('Enable SSL for: '.$domain,LOGLEVEL_DEBUG);
674
		} else {
675
			$vhost_data['ssl_enabled'] = 0;
676
			$app->log('SSL Disabled. '.$domain,LOGLEVEL_DEBUG);
677
		}
678

679
		if(@is_file($bundle_file)) $vhost_data['has_bundle_cert'] = 1;
680

681
		//$vhost_data['document_root'] = $data['new']['document_root'].'/web';
Falko Timme's avatar
Falko Timme committed
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
		
		// Set SEO Redirect
		if($data['new']['seo_redirect'] != '' && ($data['new']['subdomain'] == 'www' || $data['new']['subdomain'] == '*')){
			$vhost_data['seo_redirect_enabled'] = 1;
			if($data['new']['seo_redirect'] == 'non_www_to_www'){
				$vhost_data['seo_redirect_origin_domain'] = $data['new']['domain'];
				$vhost_data['seo_redirect_target_domain'] = 'www.'.$data['new']['domain'];
			}
			if($data['new']['seo_redirect'] == 'www_to_non_www'){
				$vhost_data['seo_redirect_origin_domain'] = 'www.'.$data['new']['domain'];
				$vhost_data['seo_redirect_target_domain'] = $data['new']['domain'];
			}
		} else {
			$vhost_data['seo_redirect_enabled'] = 0;
		}
		
698
		$tpl->setVar($vhost_data);
699

700
701
		// Rewrite rules
		$rewrite_rules = array();
702
703
		if($data['new']['redirect_type'] != '') {
			if(substr($data['new']['redirect_path'],-1) != '/') $data['new']['redirect_path'] .= '/';
704
705
706
707
708
709
710
			if(substr($data['new']['redirect_path'],0,8) == '[scheme]'){
				$rewrite_target = 'http'.substr($data['new']['redirect_path'],8);
				$rewrite_target_ssl = 'https'.substr($data['new']['redirect_path'],8);
			} else {
				$rewrite_target = $data['new']['redirect_path'];
				$rewrite_target_ssl = $data['new']['redirect_path'];
			}
tbrehm's avatar
tbrehm committed
711
			/* Disabled path extension
712
713
			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']).'/';
714
			}
tbrehm's avatar
tbrehm committed
715
			*/
716

717
			switch($data['new']['subdomain']) {
718
				case 'www':
Falko Timme's avatar
Falko Timme committed
719
720
721
722
723
					$rewrite_rules[] = array(	'rewrite_domain' 	=> '^'.$data['new']['domain'],
						'rewrite_type' 		=> ($data['new']['redirect_type'] == 'no')?'':'['.$data['new']['redirect_type'].']',
						'rewrite_target' 	=> $rewrite_target,
						'rewrite_target_ssl' => $rewrite_target_ssl);
					$rewrite_rules[] = array(	'rewrite_domain' 	=> '^www.'.$data['new']['domain'],
724
							'rewrite_type' 		=> ($data['new']['redirect_type'] == 'no')?'':'['.$data['new']['redirect_type'].']',
Falko Timme's avatar
Falko Timme committed
725
726
							'rewrite_target' 	=> $rewrite_target,
							'rewrite_target_ssl' => $rewrite_target_ssl);
727
728
					break;
				case '*':
Falko Timme's avatar
Falko Timme committed
729
730
731
732
					$rewrite_rules[] = array(	'rewrite_domain' 	=> $data['new']['domain'],
						'rewrite_type' 		=> ($data['new']['redirect_type'] == 'no')?'':'['.$data['new']['redirect_type'].']',
						'rewrite_target' 	=> $rewrite_target,
						'rewrite_target_ssl' => $rewrite_target_ssl);
733
					break;
Falko Timme's avatar
Falko Timme committed
734
735
736
737
738
				default:
					$rewrite_rules[] = array(	'rewrite_domain' 	=> '^'.$data['new']['domain'],
						'rewrite_type' 		=> ($data['new']['redirect_type'] == 'no')?'':'['.$data['new']['redirect_type'].']',
						'rewrite_target' 	=> $rewrite_target,
						'rewrite_target_ssl' => $rewrite_target_ssl);
739
			}
740
		}
741

742
		// get alias domains (co-domains and subdomains)
743
		$aliases = $app->db->queryAllRecords('SELECT * FROM web_domain WHERE parent_domain_id = '.$data['new']['domain_id']." AND active = 'y'");
744
		$server_alias = array();
745
		switch($data['new']['subdomain']) {
746
			case 'www':
747
				$server_alias[] .= 'www.'.$data['new']['domain'].' ';
748
749
				break;
			case '*':
750
				$server_alias[] .= '*.'.$data['new']['domain'].' ';
751
752
				break;
		}
753
754
		if(is_array($aliases)) {
			foreach($aliases as $alias) {
755
				switch($alias['subdomain']) {
756
					case 'www':
757
						$server_alias[] .= 'www.'.$alias['domain'].' '.$alias['domain'].' ';
758
759
						break;
					case '*':
760
						$server_alias[] .= '*.'.$alias['domain'].' '.$alias['domain'].' ';
761
762
						break;
					default:
763
						$server_alias[] .= $alias['domain'].' ';
764
765
						break;
				}
766
				$app->log('Add server alias: '.$alias['domain'],LOGLEVEL_DEBUG);
767
				// Rewriting
768
				if($alias['redirect_type'] != '') {
Falko Timme's avatar
Falko Timme committed
769
770
771
772
773
774
775
776
					if(substr($alias['redirect_path'],-1) != '/') $alias['redirect_path'] .= '/';
					if(substr($alias['redirect_path'],0,8) == '[scheme]'){
						$rewrite_target = 'http'.substr($alias['redirect_path'],8);
						$rewrite_target_ssl = 'https'.substr($alias['redirect_path'],8);
					} else {
						$rewrite_target = $alias['redirect_path'];
						$rewrite_target_ssl = $alias['redirect_path'];
					}
tbrehm's avatar
tbrehm committed
777
					/* Disabled the path extension
778
779
					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']).'/';
780
					}
tbrehm's avatar
tbrehm committed
781
					*/
Falko Timme's avatar
Falko Timme committed
782
					
783
					switch($alias['subdomain']) {
784
						case 'www':
Falko Timme's avatar
Falko Timme committed
785
786
787
788
789
							$rewrite_rules[] = array(	'rewrite_domain' 	=> '^'.$alias['domain'],
								'rewrite_type' 		=> ($alias['redirect_type'] == 'no')?'':'['.$alias['redirect_type'].']',
								'rewrite_target' 	=> $rewrite_target,
								'rewrite_target_ssl' => $rewrite_target_ssl);
							$rewrite_rules[] = array(	'rewrite_domain' 	=> '^www.'.$alias['domain'],
790
									'rewrite_type' 		=> ($alias['redirect_type'] == 'no')?'':'['.$alias['redirect_type'].']',
Falko Timme's avatar
Falko Timme committed
791
792
									'rewrite_target' 	=> $rewrite_target,
									'rewrite_target_ssl' => $rewrite_target_ssl);
793
794
							break;
						case '*':
Falko Timme's avatar
Falko Timme committed
795
796
797
798
							$rewrite_rules[] = array(	'rewrite_domain' 	=> $alias['domain'],
								'rewrite_type' 		=> ($alias['redirect_type'] == 'no')?'':'['.$alias['redirect_type'].']',
								'rewrite_target' 	=> $rewrite_target,
								'rewrite_target_ssl' => $rewrite_target_ssl);
799
							break;
Falko Timme's avatar
Falko Timme committed
800
801
802
803
804
						default:
							$rewrite_rules[] = array(	'rewrite_domain' 	=> '^'.$alias['domain'],
								'rewrite_type' 		=> ($alias['redirect_type'] == 'no')?'':'['.$alias['redirect_type'].']',
								'rewrite_target' 	=> $rewrite_target,
								'rewrite_target_ssl' => $rewrite_target_ssl);
805
					}
806
807
808
				}
			}
		}
809

810
811
812
813
		//* If we have some alias records
		if(count($server_alias) > 0) {
			$server_alias_str = '';
			$n = 0;
814

815
816
817
818
819
820
			// begin a new ServerAlias line after 30 alias domains
			foreach($server_alias as $tmp_alias) {
				if($n % 30 == 0) $server_alias_str .= "\n    ServerAlias ";
				$server_alias_str .= $tmp_alias;
			}
			unset($tmp_alias);
821

822
823
824
825
			$tpl->setVar('alias',trim($server_alias_str));
		} else {
			$tpl->setVar('alias','');
		}
826

827
828
829