apache2_plugin.inc.php 59.8 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);
491
		}  // end copy error docs
492

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

496
497
498
499
500
		$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);
501
		}
502

503
504
505
506
507
		$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);
508
		}
509

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

522
		if($this->action == 'insert' || $data["new"]["system_user"] != $data["old"]["system_user"]) {
523
			// Chown and chmod the directories below the document root
524
			$this->_exec('chown -R '.$username.':'.$groupname.' '.escapeshellcmd($data['new']['document_root']));
525
526
			// 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) {
527
				$this->_exec('chown '.$username.':'.$groupname.' '.escapeshellcmd($data['new']['document_root']));
528
			} else {
529
				$this->_exec('chown root:root '.escapeshellcmd($data['new']['document_root']));
530
			}
531
		}
532
533
534



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

538
539
540
			$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'));
541

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

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

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

557
558
559
				//* 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';
560
				$app->system->add_user_to_group($groupname, escapeshellcmd($web_config['user']));
561
				$app->system->server_conf['group_datei'] = $tmp_groupfile;
562
563
				unset($tmp_groupfile);
			}
564

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

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

570
571
572
573
			/*
			* 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
			*/
574

575
			//* Check if there is a jailkit user for this site
576
			$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'");
577
			if($tmp['number'] > 0) {
578
579
				$this->_exec('chmod 755 '.escapeshellcmd($data['new']['document_root']));
				$this->_exec('chown root:root '.escapeshellcmd($data['new']['document_root']));
580
581
			}
			unset($tmp);
582
583

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

586
587
588
			$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']));
589

590
591
			// 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
592
		}
593

594
595
596
		// 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');
597
598


599
		//* Write the custom php.ini file, if custom_php_ini filed is not empty
600
		$custom_php_ini_dir = $web_config['website_basedir'].'/conf/'.$data['new']['system_user'];
601
		if(!is_dir($web_config['website_basedir'].'/conf')) mkdir($web_config['website_basedir'].'/conf');
602
		if(trim($data['new']['custom_php_ini']) != '') {
603
604
605
			$has_custom_php_ini = true;
			if(!is_dir($custom_php_ini_dir)) mkdir($custom_php_ini_dir);
			$php_ini_content = '';
606
			if($data['new']['php'] == 'mod') {
607
608
				$master_php_ini_path = $web_config['php_ini_path_apache'];
			} else {
609
610
611
612
613
				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'];
				}
614
615
616
617
			}
			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";
			}
618
			$php_ini_content .= trim($data['new']['custom_php_ini']);
619
620
621
622
623
624
625
626
			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
627
		$app->load('tpl');
628

629
		$tpl = new tpl();
630
631
632
633
634
635
636
637
638
639
640
641
		$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);
642

643
		// Check if a SSL cert exists
644
645
646
647
648
649
		$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';

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

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

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

663
664
		// Rewrite rules
		$rewrite_rules = array();
665
666
		if($data['new']['redirect_type'] != '') {
			if(substr($data['new']['redirect_path'],-1) != '/') $data['new']['redirect_path'] .= '/';
tbrehm's avatar
tbrehm committed
667
			/* Disabled path extension
668
669
			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']).'/';
670
			}
tbrehm's avatar
tbrehm committed
671
			*/
672

673
674
675
			$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']);
676

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

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

745
746
747
748
		//* If we have some alias records
		if(count($server_alias) > 0) {
			$server_alias_str = '';
			$n = 0;
749

750
751
752
753
754
755
			// 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);
756

757
758
759
760
			$tpl->setVar('alias',trim($server_alias_str));
		} else {
			$tpl->setVar('alias','');
		}
761

762
763
764
765
766
767
		if(count($rewrite_rules) > 0) {
			$tpl->setVar('rewrite_enabled',1);
		} else {
			$tpl->setVar('rewrite_enabled',0);
		}
		$tpl->setLoop('redirects',$rewrite_rules);
768
769
770

		/**
		 * install fast-cgi starter script and add script aliasd config
771
772
773
774
		 * first we create the script directory if not already created, then copy over the starter script
		 * settings are copied over from the server ini config for now
		 * TODO: Create form for fastcgi configs per site.
		 */
775

776
777
		if ($data['new']['php'] == 'fast-cgi') {
			$fastcgi_config = $app->getconf->get_server_config($conf['server_id'], 'fastcgi');
778

779
780
			$fastcgi_starter_path = str_replace('[system_user]',$data['new']['system_user'],$fastcgi_config['fastcgi_starter_path']);
			$fastcgi_starter_path = str_replace('[client_id]',$client_id,$fastcgi_starter_path);
781
782

			if (!is_dir($fastcgi_starter_path)) {
783
784
				exec('mkdir -p '.escapeshellcmd($fastcgi_starter_path));
				//exec('chown '.$data['new']['system_user'].':'.$data['new']['system_group'].' '.escapeshellcmd($fastcgi_starter_path));
785
786


787
				$app->log('Creating fastcgi starter script directory: '.$fastcgi_starter_path,LOGLEVEL_DEBUG);
788
			}
789

790
			exec('chown -R '.$data['new']['system_user'].':'.$data['new']['system_group'].' '.escapeshellcmd($fastcgi_starter_path));
791

792
			$fcgi_tpl = new tpl();
793
			$fcgi_tpl->newTemplate('php-fcgi-starter.master');
794
795
796
797
			
			if($has_custom_php_ini) {
				$fcgi_tpl->setVar('php_ini_path',escapeshellcmd($custom_php_ini_dir));
			} else {
798
				$fcgi_tpl->setVar('php_ini_path',escapeshellcmd($fastcgi_config['fastcgi_phpini_path']));
799
			}
800
801
802
803
804
			$fcgi_tpl->setVar('document_root',escapeshellcmd($data['new']['document_root']));
			$fcgi_tpl->setVar('php_fcgi_children',escapeshellcmd($fastcgi_config['fastcgi_children']));
			$fcgi_tpl->setVar('php_fcgi_max_requests',escapeshellcmd($fastcgi_config['fastcgi_max_requests']));
			$fcgi_tpl->setVar('php_fcgi_bin',escapeshellcmd($fastcgi_config['fastcgi_bin']));
			$fcgi_tpl->setVar('security_level',intval($web_config['security_level']));
805

806
			$php_open_basedir = ($data['new']['php_open_basedir'] == '')?$data['new']['document_root']:$data['new']['php_open_basedir'];
807
			$fcgi_tpl->setVar('open_basedir', escapeshellcmd($php_open_basedir));
808

809
			$fcgi_starter_script = escapeshellcmd($fastcgi_starter_path.$fastcgi_config['fastcgi_starter_script']);
810
811
			file_put_contents($fcgi_starter_script,$fcgi_tpl->grab());
			unset($fcgi_tpl);
812

813
			$app->log('Creating fastcgi starter script: '.$fcgi_starter_script,LOGLEVEL_DEBUG);
814
815