· 5 years ago · Feb 08, 2021, 09:14 PM
1# Virtualmin API plugins for Nginx
2
3use strict;
4use warnings;
5use Time::Local;
6require 'virtualmin-nginx-lib.pl';
7our (%text, %config, $module_name, %access);
8
9# feature_name()
10# Returns a short name for this feature
11sub feature_name
12{
13return $text{'feat_name'};
14}
15
16# feature_losing(&domain)
17# Returns a description of what will be deleted when this feature is removed
18sub feature_losing
19{
20return $text{'feat_losing'};
21}
22
23# feature_disname(&domain)
24# Returns a description of what will be turned off when this feature is disabled
25sub feature_disname
26{
27return $text{'feat_disname'};
28}
29
30# feature_label(in-edit-form)
31# Returns the name of this feature, as displayed on the domain creation and
32# editing form
33sub feature_label
34{
35my ($edit) = @_;
36return $edit ? $text{'feat_label2'} : $text{'feat_label'};
37}
38
39sub feature_hlink
40{
41return "label";
42}
43
44# feature_check()
45# Checks if Nginx is actually installed, returns an error if not
46sub feature_check
47{
48if (!-r $config{'nginx_config'}) {
49 return &text('feat_econfig', "<tt>$config{'nginx_config'}</tt>");
50 }
51elsif (!&has_command($config{'nginx_cmd'})) {
52 return &text('feat_ecmd', "<tt>$config{'nginx_cmd'}</tt>");
53 }
54else {
55 return undef;
56 }
57}
58
59# feature_depends(&domain)
60# Nginx needs a Unix login for the domain
61sub feature_depends
62{
63my ($d) = @_;
64return $text{'feat_edepunix'} if (!$d->{'unix'} && !$d->{'parent'});
65return $text{'feat_edepdir'} if (!$d->{'dir'} && !$d->{'alias'});
66return $text{'feat_eapache'} if ($d->{'web'});
67return undef;
68}
69
70# feature_clash(&domain, [field])
71# Returns undef if there is no clash for this domain for this feature, or
72# an error message if so
73sub feature_clash
74{
75my ($d, $field) = @_;
76if (!$field || $field eq 'dom') {
77 my $s = &find_domain_server($d);
78 return $text{'feat_clash'} if ($s);
79 }
80return undef;
81}
82
83# feature_suitable([&parentdom], [&aliasdom], [&subdom])
84# Returns 1 if some feature can be used with the specified alias and
85# parent domains
86sub feature_suitable
87{
88my ($parentdom, $aliasdom, $subdom) = @_;
89return $subdom ? 0 : 1;
90}
91
92# feature_import(domain-name, user-name, db-name)
93# Returns 1 if this feature is already enabled for some domain being imported,
94# or 0 if not
95sub feature_import
96{
97my ($dname, $user, $db) = @_;
98return &find_domain_server({ 'dom' => $dname }) ? 1 : 0;
99}
100
101# feature_setup(&domain)
102# Called when this feature is added, with the domain object as a parameter
103sub feature_setup
104{
105my ($d) = @_;
106
107if (!$d->{'alias'}) {
108 &lock_all_config_files();
109 my $conf = &get_config();
110 my $http = &find("http", $conf);
111
112 # Pick ports
113 my $tmpl = &virtual_server::get_template($d->{'template'});
114 $d->{'web_port'} ||= $tmpl->{'web_port'} || 80;
115
116 if ($d->{'virt6'}) {
117 # Disable IPv6 default listen in default server
118 foreach my $dserver (&find("server", $http)) {
119 foreach my $l (&find("listen", $dserver)) {
120 if ($l->{'words'}->[0] eq
121 "[::]:".$d->{'web_port'}) {
122 my $name = &find_value("server_name",
123 $dserver);
124 &$virtual_server::first_print(
125 &text('feat_setupdefault', $name));
126 &save_directive($dserver, [ $l ], [ ]);
127 &$virtual_server::second_print(
128 $virtual_server::text{'setup_done'});
129 last;
130 }
131 }
132 }
133 }
134
135 # Bump up server_names_hash if too low
136 my $snh = &find_value("server_names_hash_bucket_size", $http);
137 $snh ||= int((split(/\//, &get_default("server_names_hash_bucket_size")))[0]);
138 if ($snh <= 32) {
139 &save_directive($http, "server_names_hash_bucket_size",
140 [ 128 ]);
141 }
142
143 # Create a whole new server
144 &$virtual_server::first_print($text{'feat_setup'});
145
146 # Create the server object
147 my $server = { 'name' => 'server',
148 'type' => 1,
149 'words' => [ ],
150 'members' => [ ] };
151 $server->{'file'} = &get_add_to_file($d->{'dom'});
152
153 # Add domain name field
154 push(@{$server->{'members'}},
155 { 'name' => 'server_name',
156 'words' => [ &domain_server_names($d) ] });
157
158 # Add listen on the correct IP and port
159 my $portstr = $d->{'web_port'} == 80 ? '' : ':'.$d->{'web_port'};
160 push(@{$server->{'members'}},
161 { 'name' => 'listen',
162 'words' => [ $d->{'ip'}.$portstr ] });
163 if ($d->{'ip6'}) {
164 push(@{$server->{'members'}},
165 { 'name' => 'listen',
166 'words' => [ '['.$d->{'ip6'}.']'.$portstr,
167 $d->{'virt6'} ? ( 'default' ) : ( ) ] });
168 }
169
170 # Set the root correctly
171 push(@{$server->{'members'}},
172 { 'name' => 'root',
173 'words' => [ &virtual_server::public_html_dir($d) ] });
174
175 # Allow sensible index files
176 push(@{$server->{'members'}},
177 { 'name' => 'index',
178 'words' => [ 'index.html', 'index.htm', 'index.php' ] });
179
180 # Branode - WP Restrictions
181 push(@{$server->{'members'}},
182 { 'name' => 'include',
183 'words' => [ 'global/restrictions.conf' ] });
184
185 # Branode - Lets Encrypt
186 push(@{$server->{'members'}},
187 { 'name' => 'include',
188 'words' => [ 'global/letsencrypt.conf' ] });
189
190 # Add a location for the root
191 #push(@{$server->{'members'}},
192 # { 'name' => 'location',
193 # 'words' => [ '/' ],
194 # 'type' => 1,
195 # 'members' => [
196 # { 'name' => 'root',
197 # 'words' => [ &virtual_server::public_html_dir($d) ] },
198 # ],
199 # });
200
201 # Add log files
202 my $alog = &virtual_server::get_apache_template_log($d, 0);
203 push(@{$server->{'members'}},
204 { 'name' => 'access_log',
205 'words' => [ $alog ] });
206 my $elog = &virtual_server::get_apache_template_log($d, 1);
207 push(@{$server->{'members'}},
208 { 'name' => 'error_log',
209 'words' => [ $elog ] });
210
211 # Add custom directives
212 my $extra_dirs = $tmpl->{$module_name};
213 $extra_dirs ||= $config{'extra_dirs'};
214 $extra_dirs = "" if (!$extra_dirs || $extra_dirs eq "none");
215 if ($extra_dirs) {
216 $extra_dirs = &virtual_server::substitute_domain_template(
217 $extra_dirs, $d);
218 my $temp = &transname();
219 my $fh = "EXTRA";
220 &open_tempfile($fh, ">$temp", 0, 1);
221 &print_tempfile($fh,
222 join("\n", split(/\t+/, $extra_dirs))."\n");
223 &close_tempfile($fh);
224 my $econf = &read_config_file($temp, 1);
225 &recursive_clear_lines(@$econf);
226 push(@{$server->{'members'}}, @$econf);
227 &unlink_file($temp);
228 }
229
230 &save_directive($http, [ ], [ $server ]);
231 &flush_config_file_lines();
232 &unlock_all_config_files();
233 &create_server_link($server);
234 &virtual_server::setup_apache_logs($d, $alog, $elog);
235 &virtual_server::link_apache_logs($d, $alog, $elog);
236 &virtual_server::register_post_action(\&print_apply_nginx);
237 $d->{'proxy_pass_mode'} ||= 0;
238 $d->{'proxy_pass'} ||= "";
239 if ($d->{'proxy_pass_mode'}) {
240 &setup_nginx_proxy_pass($d);
241 }
242 &$virtual_server::second_print($virtual_server::text{'setup_done'});
243
244 # Set up fcgid or FPM server
245 my $mode = $tmpl->{'web_php_suexec'} == 3 ? "fpm" : "fcgid";
246 &$virtual_server::first_print($text{'feat_php'.$mode});
247
248 # Create initial config block for running PHP scripts. The port gets
249 # filled in later by save_domain_php_mode
250 &lock_all_config_files();
251 my @params = &list_fastcgi_params($server);
252 push(@params, map { $_->{'words'} }
253 &find("fastcgi_param", $server));
254 &save_directive($server, "fastcgi_param",
255 [ map { { 'words' => $_ } } @params ]);
256
257 #Branode - Prevent DoS attack limiting access on specific files
258 my $branode_wp_rate_limit = { 'name' => 'location',
259 'words' => [ '~', '(wp-login|xmlrpc)\.php' ],
260 'type' => 1,
261 'members' => [
262 { 'name' => 'limit_req',
263 'words' => [ 'zone=wplimit', 'burst=1', 'nodelay' ],
264 },
265 { 'name' => 'limit_req_status',
266 'words' => [ '444' ],
267 },
268 { 'name' => 'fastcgi_pass',
269 'words' => [ 'localhost:8000' ],
270 },
271 ],
272 };
273 &save_directive($server, [ ], [ $branode_wp_rate_limit ]);
274
275 # Add location
276 my $ploc = { 'name' => 'location',
277 'words' => [ '~', '\.php(/|$)' ],
278 'type' => 1,
279 'members' => [
280 { 'name' => 'try_files',
281 'words' => [ '$uri', '$fastcgi_script_name', '=404' ],
282 },
283 ],
284 };
285 &save_directive($server, [ ], [ $ploc ]);
286
287 #Branode - WordPress default
288 my $branodewordpress = { 'name' => 'location',
289 'words' => [ '/' ],
290 'type' => 1,
291 'members' => [
292 { 'name' => 'try_files',
293 'words' => [ '"/wp-content/cache/cache-enabler/${http_host}${request_uri}https-index.html"', '$uri', '$uri/', '/index.php?$args$query_string' ],
294 },
295 ],
296 };
297 &save_directive($server, [ ], [ $branodewordpress ]);
298
299 #Branode - Leverage browser caching
300 my $branodebrowsercache = { 'name' => 'location',
301 'words' => [ '~*', '\.(jpg|jpeg|png|gif|webp|svg|ico|css|js|tgz|tar|gz|rar|bz2|doc|ppt|pdf|eot|otf|woff|woff2|ttf|ogg|webm|mp4)$' ],
302 'type' => 1,
303 'members' => [
304 { 'name' => 'expires',
305 'words' => [ '6M' ],
306 },
307 ],
308 };
309 &save_directive($server, [ ], [ $branodebrowsercache ]);
310
311 # Add extra directive
312 &save_directive($server, "fastcgi_split_path_info",
313 [ { 'name' => 'fastcgi_split_path_info',
314 'words' => [ &split_quoted_string('^(.+\.php)(/.+)$') ]
315 } ]);
316
317 # Branode - DHparam
318 &save_directive($server, "ssl_dhparam",
319 [ { 'name' => 'ssl_dhparam',
320 'words' => [ 'global/dhparam.pem' ]
321 } ]);
322
323 &flush_config_file_lines();
324 &unlock_all_config_files();
325
326 # Setup the selected PHP mode
327 &virtual_server::save_domain_php_mode($d, $mode);
328 &$virtual_server::second_print(
329 $virtual_server::text{'setup_done'});
330
331 # Add the user nginx runs as to the domain's group
332 my $web_user = &get_nginx_user();
333 if ($web_user && $web_user ne 'none') {
334 &virtual_server::add_user_to_domain_group(
335 $d, $web_user, 'setup_webuser');
336 }
337
338 # Create empty log files and make them writable by Nginx and
339 # the domain owner
340 foreach my $l ($alog, $elog) {
341 my $fh = "LOG";
342 &open_tempfile($fh, ">>$l", 0, 1);
343 &close_tempfile($fh);
344 &set_nginx_log_permissions($d, $l);
345 }
346
347 return 1;
348 }
349else {
350 # Add to existing one as an alias
351 &$virtual_server::first_print($text{'feat_setupalias'});
352 &lock_all_config_files();
353 my $target = &virtual_server::get_domain($d->{'alias'});
354 my $server = &find_domain_server($target);
355 if (!$server) {
356 &unlock_all_config_files();
357 &$virtual_server::second_print(
358 &text('feat_efind', $target->{'dom'}));
359 return 0;
360 }
361
362 my $obj = &find("server_name", $server);
363 foreach my $n (&domain_server_names($d)) {
364 if (&indexoflc($n, @{$obj->{'words'}}) < 0) {
365 push(@{$obj->{'words'}}, $n);
366 }
367 }
368 &save_directive($server, "server_name", [ $obj ]);
369
370 $d->{'web_port'} = 80;
371 &flush_config_file_lines();
372 &unlock_all_config_files();
373 &virtual_server::register_post_action(\&print_apply_nginx);
374
375 &$virtual_server::second_print($virtual_server::text{'setup_done'});
376 return 1;
377 }
378}
379
380# feature_modify(&domain, &old-domain)
381# Change the Nginx domain name or home directory
382sub feature_modify
383{
384my ($d, $oldd) = @_;
385
386# Special case - converting an alias domain into a non-alias. Just delete and
387# re-create
388if ($oldd->{'alias'} && !$d->{'alias'}) {
389 &feature_delete($oldd);
390 &feature_setup($d);
391 return 1;
392 }
393
394if (!$d->{'alias'}) {
395 # Changing a real virtual host
396 &lock_all_config_files();
397 my $changed = 0;
398 my $old_alog = &get_nginx_log($d, 0);
399 my $old_elog = &get_nginx_log($d, 1);
400
401 # Update domain name in server_name
402 if ($d->{'dom'} ne $oldd->{'dom'}) {
403 &$virtual_server::first_print($text{'feat_modifydom'});
404 my $server = &find_domain_server($oldd);
405 if (!$server) {
406 &$virtual_server::second_print(
407 &text('feat_efind', $oldd->{'dom'}));
408 return 0;
409 }
410 &recursive_change_directives($server, $oldd->{'dom'},
411 $d->{'dom'}, 0, 0, 1);
412 &$virtual_server::second_print(
413 $virtual_server::text{'setup_done'});
414 $changed++;
415 }
416
417 # Update home directory in all directives
418 if ($d->{'home'} ne $oldd->{'home'}) {
419 &$virtual_server::first_print($text{'feat_modifyhome'});
420 my $server = &find_domain_server($d);
421 if (!$server) {
422 &$virtual_server::second_print(
423 &text('feat_efind', $d->{'dom'}));
424 return 0;
425 }
426 &recursive_change_directives(
427 $server, $oldd->{'home'}, $d->{'home'}, 0, 0, 0);
428 &recursive_change_directives(
429 $server, $oldd->{'home'}.'/', $d->{'home'}.'/', 0, 1,0);
430 &$virtual_server::second_print(
431 $virtual_server::text{'setup_done'});
432 $changed++;
433 }
434
435 # Update IPv4 address
436 if ($d->{'ip'} ne $oldd->{'ip'}) {
437 &$virtual_server::first_print($text{'feat_modifyip'});
438 my $server = &find_domain_server($d);
439 if (!$server) {
440 &$virtual_server::second_print(
441 &text('feat_efind', $d->{'dom'}));
442 return 0;
443 }
444 my @listen = &find("listen", $server);
445 foreach my $l (@listen) {
446 if ($l->{'words'}->[0] eq $oldd->{'ip'}) {
447 $l->{'words'}->[0] = $d->{'ip'};
448 }
449 elsif ($l->{'words'}->[0] =~ /^(\S+):(\d+)$/ &&
450 $1 eq $oldd->{'ip'}) {
451 $l->{'words'}->[0] = $d->{'ip'}.":".$2;
452 }
453 }
454 &save_directive($server, "listen", \@listen);
455
456 # Remove IP in server_names
457 my $obj = &find("server_name", $server);
458 my $idx = &indexof($oldd->{'ip'}, @{$obj->{'words'}});
459 if ($idx >= 0) {
460 splice(@{$obj->{'words'}}, $idx, 0);
461 &save_directive($server, "server_name", [ $obj ]);
462 }
463
464 &$virtual_server::second_print(
465 $virtual_server::text{'setup_done'});
466 $changed++;
467 }
468
469 # Update IPv6 address (or add or remove)
470 if (($d->{'ip6'} || "") ne ($oldd->{'ip6'} || "") ||
471 ($d->{'virt6'} || 0) ne ($oldd->{'virt6'} || 0)) {
472 &$virtual_server::first_print($text{'feat_modifyip6'});
473 my $server = &find_domain_server($d);
474 if (!$server) {
475 &$virtual_server::second_print(
476 &text('feat_efind', $d->{'dom'}));
477 return 0;
478 }
479 my @listen = &find("listen", $server);
480 my @newlisten;
481 my $ob = $oldd->{'ip6'} ? "[".$oldd->{'ip6'}."]" : "";
482 my $nb = $d->{'ip6'} ? "[".$d->{'ip6'}."]" : "";
483 foreach my $l (@listen) {
484 my @w = @{$l->{'words'}};
485 if ($ob && $w[0] eq $ob) {
486 # Found old address with no port - replace
487 # or remove
488 if ($nb) {
489 $w[0] = $nb;
490 push(@newlisten, { 'words' => \@w });
491 }
492 }
493 elsif ($ob && $w[0] =~ /^\Q$ob\E:(\d+)$/) {
494 # Found old address with a port - replace with
495 # same port or remove
496 if ($nb) {
497 $w[0] = $nb.":".$1;
498 push(@newlisten, { 'words' => \@w });
499 }
500 }
501 else {
502 # Found un-related address, save it
503 push(@newlisten, { 'words' => \@w });
504 }
505 }
506 if ($d->{'ip6'} && !$oldd->{'ip6'}) {
507 push(@newlisten, { 'words' => [ $nb ] });
508 }
509 &save_directive($server, "listen", \@newlisten);
510 &$virtual_server::second_print(
511 $virtual_server::text{'setup_done'});
512 $changed++;
513 }
514
515 # Update port, if changed
516 if ($d->{'web_port'} != $oldd->{'web_port'}) {
517 &$virtual_server::first_print($text{'feat_modifyport'});
518 my $server = &find_domain_server($d);
519 if (!$server) {
520 &$virtual_server::second_print(
521 &text('feat_efind', $d->{'dom'}));
522 return 0;
523 }
524 my @listen = &find("listen", $server);
525 my @newlisten;
526 foreach my $l (@listen) {
527 my @w = @{$l->{'words'}};
528 my $p = $w[0] =~ /:(\d+)$/ ? $1 : 80;
529 if ($p == $oldd->{'web_port'}) {
530 $w[0] =~ s/:\d+$//;
531 $w[0] .= ":".$d->{'web_port'}
532 if ($d->{'web_port'} != 80);
533 }
534 push(@newlisten, { 'words' => \@w });
535 }
536 &save_directive($server, "listen", \@newlisten);
537 &$virtual_server::second_print(
538 $virtual_server::text{'setup_done'});
539 $changed++;
540 }
541
542 # Update proxy settings if needed
543 if ($d->{'proxy_pass_mode'} ne $oldd->{'proxy_pass_mode'} ||
544 $d->{'proxy_pass'} ne $oldd->{'proxy_pass'}) {
545 &$virtual_server::first_print($text{'feat_modifyproxy'});
546 &remove_nginx_proxy_pass($oldd);
547 &setup_nginx_proxy_pass($d);
548 &$virtual_server::second_print(
549 $virtual_server::text{'setup_done'});
550 }
551
552 # Rename log files if needed
553 my $new_alog = &virtual_server::get_apache_template_log($d, 0);
554 my $new_elog = &virtual_server::get_apache_template_log($d, 1);
555 if (defined($old_alog) && defined($old_elog) && $old_alog ne $new_alog) {
556 &$virtual_server::first_print($text{'feat_modifylog'});
557 my $server = &find_domain_server($d);
558 if (!$server) {
559 &$virtual_server::second_print(
560 &text('feat_efind', $oldd->{'dom'}));
561 return 0;
562 }
563 &feature_change_web_access_log($d, $new_alog);
564 &rename_logged($old_alog, $new_alog);
565 if ($old_elog ne $new_elog) {
566 &feature_change_web_error_log($d, $new_elog);
567 &rename_logged($old_elog, $new_elog);
568 }
569 &virtual_server::link_apache_logs($d, $new_alog, $new_elog);
570 &$virtual_server::second_print(
571 $virtual_server::text{'setup_done'});
572 }
573
574 # Flush files and restart
575 &flush_config_file_lines();
576 &unlock_all_config_files();
577 if ($changed) {
578 &virtual_server::register_post_action(\&print_apply_nginx);
579 }
580
581 # Rename config file name, if changed
582 if ($d->{'dom'} ne $oldd->{'dom'}) {
583 my $newfile = &get_add_to_file($d->{'dom'});
584 my $server = &find_domain_server($d);
585 if ($server->{'file'} ne $newfile &&
586 $server->{'file'} =~ /\Q$oldd->{'dom'}\E/) {
587 &$virtual_server::first_print($text{'feat_modifyfile'});
588 &delete_server_link($server);
589 &rename_logged($server->{'file'}, $newfile);
590 $server->{'file'} = $newfile;
591 &create_server_link($server);
592 &flush_config_cache();
593 &$virtual_server::second_print(
594 $virtual_server::text{'setup_done'});
595 }
596 }
597
598 # Update fcgid user, by tearing down and re-running. Killing needs to
599 # be done in the new home, as it may have been moved already
600 if ($d->{'user'} ne $oldd->{'user'} ||
601 $d->{'home'} ne $oldd->{'home'}) {
602 &$virtual_server::first_print($text{'feat_modifyphp'});
603 my $oldd_copy = { %$oldd };
604 my $mode = &feature_get_web_php_mode($d);
605 if ($mode eq "fcgid") {
606 $oldd_copy->{'home'} = $d->{'home'};
607 &delete_php_fcgi_server($oldd_copy);
608 &delete_php_fcgi_server($oldd);
609 &setup_php_fcgi_server($d);
610 }
611 elsif ($mode eq "fpm") {
612 &virtual_server::delete_php_fpm_pool($oldd);
613 &virtual_server::create_php_fpm_pool($d);
614 }
615 &$virtual_server::second_print(
616 $virtual_server::text{'setup_done'});
617 }
618
619 # Update owner of log files
620 if ($d->{'user'} ne $oldd->{'user'}) {
621 my $alog = &get_nginx_log($d, 0);
622 my $elog = &get_nginx_log($d, 1);
623 foreach my $l ($alog, $elog) {
624 &set_nginx_log_permissions($d, $l);
625 }
626 }
627
628 # Add Nginx user to the group for the new domain
629 if ($d->{'user'} ne $oldd->{'user'}) {
630 my $web_user = &get_nginx_user();
631 if ($web_user && $web_user ne 'none') {
632 &virtual_server::add_user_to_domain_group(
633 $d, $web_user, 'setup_webuser');
634 }
635 }
636
637 if ($d->{'home'} ne $oldd->{'home'}) {
638 # Update session dir and upload path in php.ini files
639 my @fixes = (
640 [ "session.save_path", $oldd->{'home'}, $d->{'home'}, 1 ],
641 [ "upload_tmp_dir", $oldd->{'home'}, $d->{'home'}, 1 ],
642 );
643 &virtual_server::fix_php_ini_files($d, \@fixes);
644 }
645 }
646else {
647 # Changing inside an alias
648 &lock_all_config_files();
649 my $changed = 0;
650
651 # Change domain name in alias target
652 if ($d->{'dom'} ne $oldd->{'dom'}) {
653 &$virtual_server::first_print($text{'feat_modifyalias'});
654 my $target = &virtual_server::get_domain($d->{'alias'});
655 my $server = &find_domain_server($target);
656 if (!$server) {
657 &unlock_all_config_files();
658 &$virtual_server::second_print(
659 &text('feat_efind', $target->{'dom'}));
660 return 0;
661 }
662 my $obj = &find("server_name", $server);
663 foreach my $n (&domain_server_names($oldd)) {
664 @{$obj->{'words'}} = grep { $_ ne $n }
665 @{$obj->{'words'}};
666 }
667 foreach my $n (&domain_server_names($d)) {
668 push(@{$obj->{'words'}}, $n);
669 }
670 my $oldstar = &indexof("*.".$oldd->{'dom'}, @{$obj->{'words'}});
671 if ($oldstar >= 0) {
672 $obj->{'words'}->[$oldstar] = "*.".$d->{'dom'};
673 }
674 &save_directive($server, "server_name", [ $obj ]);
675 $changed++;
676 &$virtual_server::second_print(
677 $virtual_server::text{'setup_done'});
678 }
679
680 # Flush files and restart
681 &flush_config_file_lines();
682 &unlock_all_config_files();
683 if ($changed) {
684 &virtual_server::register_post_action(\&print_apply_nginx);
685 }
686
687 }
688}
689
690# feature_delete(&domain)
691# Remove the Nginx virtual host for a domain
692sub feature_delete
693{
694my ($d) = @_;
695
696if (!$d->{'alias'}) {
697 # Remove the whole server
698 &$virtual_server::first_print($text{'feat_delete'});
699 &lock_all_config_files();
700 my $mode = &feature_get_web_php_mode($d);
701 my $conf = &get_config();
702 my $http = &find("http", $conf);
703 my $server = &find_domain_server($d);
704 if (!$server) {
705 &unlock_all_config_files();
706 &$virtual_server::second_print(
707 &text('feat_efind', $d->{'dom'}));
708 return 0;
709 }
710 my $alog = &get_nginx_log($d, 0);
711 my $elog = &get_nginx_log($d, 1);
712 &save_directive($http, [ $server ], [ ]);
713 &flush_config_file_lines();
714 &unlock_all_config_files();
715 &delete_server_link($server);
716 &delete_server_file_if_empty($server);
717 if ($mode eq "fcgid") {
718 &delete_php_fcgi_server($d);
719 }
720 elsif ($mode eq "fpm") {
721 &virtual_server::delete_php_fpm_pool($d);
722 }
723 &virtual_server::register_post_action(\&print_apply_nginx);
724 &$virtual_server::second_print($virtual_server::text{'setup_done'});
725
726 # Remove log files too, if outside home
727 if ($alog && !&is_under_directory($d->{'home'}, $alog)) {
728 &$virtual_server::first_print($text{'feat_deletelogs'});
729 &unlink_file($alog);
730 if ($elog && !&is_under_directory($d->{'home'}, $elog)) {
731 &unlink_file($elog);
732 }
733 &$virtual_server::second_print(
734 $virtual_server::text{'setup_done'});
735 }
736
737 return 1;
738 }
739else {
740 # Delete from alias
741 &$virtual_server::first_print($text{'feat_deletealias'});
742 &lock_all_config_files();
743 my $target = &virtual_server::get_domain($d->{'alias'});
744 my $server = &find_domain_server($target);
745 if (!$server) {
746 &unlock_all_config_files();
747 &$virtual_server::second_print(
748 &text('feat_efind', $target->{'dom'}));
749 return 0;
750 }
751
752 my $obj = &find("server_name", $server);
753 foreach my $n (&domain_server_names($d), "*.".$d->{'dom'}) {
754 @{$obj->{'words'}} = grep { $_ ne $n } @{$obj->{'words'}};
755 }
756 &save_directive($server, "server_name", [ $obj ]);
757
758 &flush_config_file_lines();
759 &unlock_all_config_files();
760 &virtual_server::register_post_action(\&print_apply_nginx);
761
762 &$virtual_server::second_print($virtual_server::text{'setup_done'});
763 return 1;
764 }
765}
766
767# feature_disable(&domain)
768# Disable the website by adding a redirect from /
769sub feature_disable
770{
771my ($d) = @_;
772if ($d->{'alias'}) {
773 # Disabling is the same as deletion for an alias
774 my $target = &virtual_server::get_domain($d->{'alias'});
775 if ($target->{'disabled'}) {
776 return 1;
777 }
778 $d->{'disable_alias_nginx_delete'} = 1;
779 return &feature_delete($d);
780 }
781else {
782 &$virtual_server::first_print($text{'feat_disable'});
783 &lock_all_config_files();
784 my $server = &find_domain_server($d);
785 if (!$server) {
786 &unlock_all_config_files();
787 &$virtual_server::second_print(
788 &text('feat_efind', $d->{'dom'}));
789 return 0;
790 }
791 my $tmpl = &virtual_server::get_template($d->{'template'});
792 my @locs = &find("location", $server);
793 my ($clash) = grep { $_->{'words'}->[0] eq '~' &&
794 $_->{'words'}->[1] eq '/.*' } @locs;
795
796 if ($tmpl->{'disabled_url'} eq 'none') {
797 # Disable is done via local HTML
798 my $dis = &virtual_server::disabled_website_html($d);
799 my $msg = $tmpl->{'disabled_web'} eq 'none' ?
800 "<h1>Website Disabled</h1>\n" :
801 join("\n", split(/\t/, $tmpl->{'disabled_web'}));
802 $msg = &virtual_server::substitute_domain_template($msg, $d);
803 my $fh = "DISABLED";
804 &open_lock_tempfile($fh, ">$dis");
805 &print_tempfile($fh, $msg);
806 &close_tempfile($fh);
807 no warnings "once";
808 &set_ownership_permissions(
809 undef, undef, 0644, $virtual_server::disabled_website);
810 use warnings "once";
811
812 # Add location to force use of it
813 if (!$clash) {
814 $dis =~ /^(.*)(\/[^\/]+)$/;
815 my ($disdir, $disfile) = ($1, $2);
816 my $loc =
817 { 'name' => 'location',
818 'words' => [ '~', '/.*' ],
819 'type' => 1,
820 'members' => [
821 { 'name' => 'root',
822 'words' => [ $disdir ] },
823 { 'name' => 'rewrite',
824 'words' => [ '^/.*', $disfile, 'break' ] },
825 ],
826 };
827 &save_directive($server, [ ], [ $loc ], $locs[0]);
828 }
829 }
830 else {
831 # Disable is done via redirect
832 my $url = &virtual_server::substitute_domain_template(
833 $tmpl->{'disabled_url'}, $d);
834 if (!$clash) {
835 my $loc =
836 { 'name' => 'location',
837 'words' => [ '~', '/.*' ],
838 'type' => 1,
839 'members' => [
840 { 'name' => 'rewrite',
841 'words' => [ '^/.*', $url, 'break' ] },
842 ],
843 };
844 &save_directive($server, [ ], [ $loc ], $locs[0]);
845 }
846 }
847
848 &flush_config_file_lines();
849 &unlock_all_config_files();
850 &virtual_server::register_post_action(\&print_apply_nginx);
851
852 &$virtual_server::second_print($virtual_server::text{'setup_done'});
853 }
854}
855
856# feature_enable(&domain)
857# Undo the effects of feature_disable
858sub feature_enable
859{
860my ($d) = @_;
861if ($d->{'alias'}) {
862 # Enabling alias is the same as re-setting it up
863 if ($d->{'disable_alias_nginx_delete'}) {
864 delete($d->{'disable_alias_nginx_delete'});
865 return &feature_setup($d);
866 }
867 return 1;
868 }
869else {
870 &$virtual_server::first_print($text{'feat_enable'});
871 &lock_all_config_files();
872 my $server = &find_domain_server($d);
873 if (!$server) {
874 &unlock_all_config_files();
875 &$virtual_server::second_print(
876 &text('feat_efind', $d->{'dom'}));
877 return 0;
878 }
879
880 my @locs = &find("location", $server);
881 my ($loc) = grep { $_->{'words'}->[0] eq '~' &&
882 $_->{'words'}->[1] eq '/.*' } @locs;
883 if ($loc) {
884 my $rewrite = &find_value("rewrite", $loc);
885 if ($rewrite eq '^/.*') {
886 &save_directive($server, [ $loc ], [ ]);
887 }
888 }
889
890 &flush_config_file_lines();
891 &unlock_all_config_files();
892 &virtual_server::register_post_action(\&print_apply_nginx);
893
894 &$virtual_server::second_print($virtual_server::text{'setup_done'});
895 }
896}
897
898# feature_validate(&domain)
899# Checks if this feature is properly setup for the virtual server, and returns
900# an error message if any problem is found
901sub feature_validate
902{
903my ($d) = @_;
904
905# Does server exist?
906my $server = &find_domain_server($d);
907return &text('feat_evalidate',
908 "<tt>".&virtual_server::show_domain_name($d)."</tt>") if (!$server);
909
910# Check root directory
911if (!$d->{'alias'}) {
912 my $rootdir = &find_value("root", $server);
913 my $phd = &virtual_server::public_html_dir($d);
914 return &text('feat_evalidateroot',
915 "<tt>".&html_escape($rootdir)."</tt>",
916 "<tt>".&html_escape($phd)."</tt>") if ($rootdir ne $phd);
917 }
918
919# Is alias target what we expect?
920if ($d->{'alias'}) {
921 my $target = &virtual_server::get_domain($d->{'alias'});
922 my $targetserver = &find_domain_server($target);
923 return &text('feat_evalidatetarget',
924 "<tt>".&virtual_server::show_domain_name($target)."</tt>")
925 if (!$targetserver);
926 return &text('feat_evalidatediff',
927 "<tt>".&virtual_server::show_domain_name($target)."</tt>")
928 if ($targetserver ne $server);
929 }
930
931# Check for IPs and port
932if (!$d->{'alias'}) {
933 my @listen = &find_value("listen", $server);
934 my $found = 0;
935 foreach my $l (@listen) {
936 $found++ if ($l eq $d->{'ip'} &&
937 $d->{'web_port'} == 80 ||
938 $l =~ /^\Q$d->{'ip'}\E:(\d+)$/ &&
939 $d->{'web_port'} == $1);
940 }
941 $found || return &text('feat_evalidateip',
942 $d->{'ip'}, $d->{'web_port'});
943 if ($d->{'virt6'}) {
944 my $found6 = 0;
945 foreach my $l (@listen) {
946 $found6++ if ($l eq "[".$d->{'ip6'}."]" &&
947 $d->{'web_port'} == 80 ||
948 $l =~ /^\[\Q$d->{'ip6'}\E\]:(\d+)$/ &&
949 $d->{'web_port'} == $1);
950 }
951 $found6 || return &text('feat_evalidateip6',
952 $d->{'ip6'}, $d->{'web_port'});
953 }
954 }
955
956return undef;
957}
958
959# feature_webmin(&main-domain, &all-domains)
960# Returns a list of webmin module names and ACL hash references to be set for
961# the Webmin user when this feature is enabled
962# (optional)
963sub feature_webmin
964{
965my ($d, $alld) = @_;
966my @doms = map { $_->{'dom'} } grep { $_->{$module_name} } @$alld;
967if (@doms) {
968 # Grant access to Nginx module
969 my @rv;
970 push(@rv, [ $module_name,
971 { 'vhosts' => join(' ', @doms),
972 'root' => $d->{'home'},
973 'global' => 0,
974 'logs' => 0,
975 'user' => $d->{'user'},
976 'edit' => 0,
977 'stop' => 0,
978 } ] );
979
980 # Grant access to system logs
981 my @extras;
982 foreach my $sd (@doms) {
983 push(@extras, &get_nginx_log($d, 0));
984 push(@extras, &get_nginx_log($d, 1));
985 }
986 push(@rv, [ "syslog",
987 { 'extras' => join("\t", @extras),
988 'any' => 0,
989 'noconfig' => 1,
990 'noedit' => 1,
991 'syslog' => 0,
992 'others' => 0 } ]);
993
994 # Grant access to phpini module
995 my @pconfs;
996 foreach my $sd (grep { $_->{$module_name} } @$alld) {
997 my $mode = &feature_get_web_php_mode($sd);
998 if ($mode ne "fpm") {
999 # Allow access to .ini files
1000 foreach my $ini (&virtual_server::list_domain_php_inis($sd)) {
1001 my @st = stat($ini->[1]);
1002 if (@st && $st[4] == $sd->{'uid'}) {
1003 push(@pconfs, $ini->[1]."=".
1004 &text('webmin_phpini', $sd->{'dom'}));
1005 }
1006 }
1007 }
1008 elsif ($mode eq "fpm") {
1009 # Allow access to FPM configs for PHP overrides
1010 my $conf = &virtual_server::get_php_fpm_config();
1011 if ($conf) {
1012 my $file = $conf->{'dir'}."/".
1013 $sd->{'id'}.".conf";
1014 push(@pconfs, $file."=".
1015 &text('webmin_phpini', $sd->{'dom'}));
1016 }
1017 }
1018 }
1019 if (@pconfs) {
1020 push(@rv, [ "phpini",
1021 { 'php_inis' => join("\t", @pconfs),
1022 'noconfig' => 1,
1023 'global' => 0,
1024 'anyfile' => 0,
1025 'user' => $d->{'user'},
1026 'manual' => 1 } ]);
1027 }
1028
1029 return @rv;
1030 }
1031else {
1032 return ( );
1033 }
1034}
1035
1036# feature_modules()
1037# Returns a list of the modules that domain owners with this feature may be
1038# granted access to. Used in server templates.
1039sub feature_modules
1040{
1041return ( [ $module_name, $text{'feat_module'} ] );
1042}
1043
1044# feature_links(&domain)
1045# Returns an array of link objects for webmin modules for this feature
1046sub feature_links
1047{
1048my ($d) = @_;
1049my $server = &find_domain_server($d);
1050return ( ) if (!$server);
1051
1052# Link to edit Nginx config for domain
1053my @rv = ( { 'mod' => $module_name,
1054 'desc' => $text{'feat_edit'},
1055 'page' => 'edit_server.cgi?id='.&server_id($server),
1056 'cat' => 'services' } );
1057
1058# Links to logs
1059foreach my $log ([ 0, $text{'links_anlog'} ],
1060 [ 1, $text{'links_enlog'} ]) {
1061 my $lf = &get_nginx_log($d, $log->[0]);
1062 if ($lf) {
1063 my $param = &virtual_server::master_admin() ? "file" : "extra";
1064 push(@rv, { 'mod' => 'syslog',
1065 'desc' => $log->[1],
1066 'page' => "save_log.cgi?view=1&".
1067 "$param=".&urlize($lf),
1068 'cat' => 'logs',
1069 });
1070 }
1071 }
1072
1073# Links to edit PHP configs
1074my $mode = &feature_get_web_php_mode($d);
1075if ($mode eq "fcgid") {
1076 # Link to edit per-version php.ini files
1077 foreach my $ini (&virtual_server::find_domain_php_ini_files($d)) {
1078 push(@rv, { 'mod' => 'phpini',
1079 'desc' => $ini->[0] ?
1080 &text('links_phpini2', $ini->[0]) :
1081 &text('links_phpini'),
1082 'page' => 'list_ini.cgi?file='.
1083 &urlize($ini->[1]),
1084 'cat' => 'services',
1085 });
1086 }
1087 }
1088elsif ($mode eq "fpm") {
1089 # Link to edit FPM configs with PHP settings
1090 my $conf = &virtual_server::get_php_fpm_config();
1091 if ($conf) {
1092 my $file = $conf->{'dir'}."/".$d->{'id'}.".conf";
1093 push(@rv, { 'mod' => 'phpini',
1094 'desc' => &text('links_phpini'),
1095 'page' => 'list_ini.cgi?file='.&urlize($file),
1096 'cat' => 'services',
1097 });
1098 }
1099 }
1100
1101return @rv;
1102}
1103
1104# print_apply_nginx()
1105# Restart Nginx, and print a message
1106sub print_apply_nginx
1107{
1108&$virtual_server::first_print($text{'feat_apply'});
1109if (&is_nginx_running()) {
1110 my $test = &test_config();
1111 if ($test && $test =~ /Cannot\s+assign/i) {
1112 # Maybe new address has just come up .. wait 5 secs and re-try
1113 sleep(5);
1114 $test = &test_config();
1115 }
1116 if ($test) {
1117 &$virtual_server::second_print(
1118 &text('feat_econfig2', "<tt>".&html_escape($test)."</tt>"));
1119 }
1120 else {
1121 my $err = apply_nginx();
1122 if ($err) {
1123 &$virtual_server::second_print(
1124 &text('feat_eapply',
1125 "<tt>".&html_escape($test)."</tt>"));
1126 }
1127 else {
1128 &$virtual_server::second_print(
1129 $virtual_server::text{'setup_done'});
1130 }
1131 }
1132 }
1133else {
1134 &$virtual_server::second_print($text{'feat_notrunning'});
1135 }
1136}
1137
1138# feature_provides_web()
1139sub feature_provides_web
1140{
1141return 1; # Nginx is a webserver
1142}
1143
1144sub feature_web_supports_suexec
1145{
1146return -1; # PHP is always run as domain owner
1147}
1148
1149sub feature_web_supports_cgi
1150{
1151return 0; # No CGI support
1152}
1153
1154sub feature_web_supported_php_modes
1155{
1156my @rv = ('fcgid');
1157if (&virtual_server::get_php_fpm_config()) {
1158 push(@rv, 'fpm');
1159 }
1160return @rv;
1161}
1162
1163# feature_get_web_php_mode(&domain)
1164sub feature_get_web_php_mode
1165{
1166my ($d) = @_;
1167my $server = &find_domain_server($d);
1168$server || return undef;
1169my @locs = &find("location", $server);
1170my ($loc) = grep { $_->{'words'}->[0] eq '~' &&
1171 ($_->{'words'}->[1] eq '\.php$' ||
1172 $_->{'words'}->[1] eq '\.php(/|$)') } @locs;
1173my $fpmsock = &virtual_server::get_php_fpm_socket_file($d, 1);
1174my $fpmport = $d->{'php_fpm_port'};
1175if ($loc) {
1176 my ($pass) = &find("fastcgi_pass", $loc);
1177 if ($pass && $pass->{'words'}->[0] =~ /^(localhost|unix):(.*)$/) {
1178 if ($1 eq "unix" && $2 eq $fpmsock) {
1179 return 'fpm';
1180 }
1181 elsif ($1 eq "localhost" && $fpmport && $2 eq $fpmport) {
1182 return 'fpm';
1183 }
1184 else {
1185 return 'fcgid';
1186 }
1187 }
1188 }
1189return undef;
1190}
1191
1192# feature_save_web_php_mode(&domain, mode)
1193sub feature_save_web_php_mode
1194{
1195my ($d, $mode) = @_;
1196my $tmpl = &virtual_server::get_template($d->{'template'});
1197my $server = &find_domain_server($d);
1198my $oldmode = &feature_get_web_php_mode($d) || "";
1199if ($oldmode eq "fpm" && $mode ne "fpm") {
1200 # Shut down FPM pool
1201 &virtual_server::delete_php_fpm_pool($d);
1202 }
1203elsif ($oldmode eq "fcgid" && $mode ne "fcgid") {
1204 # Shut down FCGI server
1205 &delete_php_fcgi_server($d);
1206 delete($d->{'nginx_php_port'});
1207 }
1208
1209my $port;
1210if ($mode eq "fcgid" && $oldmode ne "fcgid") {
1211 # Setup FCGI server on a new port
1212 my $ok;
1213 $d->{'nginx_php_version'} ||= $tmpl->{'web_phpver'};
1214 $d->{'nginx_php_children'} ||= $config{'child_procs'} ||
1215 $tmpl->{'web_phpchildren'} || 1;
1216 ($ok, $port) = &setup_php_fcgi_server($d);
1217 $ok || return $port;
1218 $d->{'nginx_php_port'} = $port;
1219 }
1220elsif ($mode eq "fpm" && $oldmode ne "fpm") {
1221 # Setup FPM pool
1222 if (!$d->{'php_fpm_version'}) {
1223 # Work out the default FPM version from the template
1224 my @avail = &virtual_server::list_available_php_versions(
1225 $d, "fpm");
1226 @avail || &error("No FPM versions found!");
1227 my $fpm;
1228 if ($tmpl->{'web_phpver'}) {
1229 ($fpm) = grep { $_->[0] eq $tmpl->{'web_phpver'} }
1230 @avail;
1231 }
1232 $fpm ||= $avail[0];
1233 $d->{'php_fpm_version'} = $fpm->[0];
1234 }
1235 &virtual_server::create_php_fpm_pool($d);
1236 $port = $d->{'php_fpm_port'} ||
1237 &virtual_server::get_php_fpm_socket_file($d);
1238 }
1239
1240# Update the port in the config, if changed
1241if ($port) {
1242 my @locs = &find("location", $server);
1243 my ($loc) = grep { $_->{'words'}->[0] eq '~' &&
1244 ($_->{'words'}->[1] eq '\.php$' ||
1245 $_->{'words'}->[1] eq '\.php(/|$)') } @locs;
1246 if ($loc) {
1247 &lock_file($loc->{'file'});
1248 &save_directive($loc, "fastcgi_pass",
1249 $port =~ /^\d+$/ ? [ "localhost:".$port ]
1250 : [ "unix:".$port ]);
1251 &flush_file_lines($loc->{'file'});
1252 &unlock_file($loc->{'file'});
1253 &virtual_server::register_post_action(\&print_apply_nginx);
1254 }
1255 }
1256return undef;
1257}
1258
1259# feature_list_web_php_directories(&domain)
1260# Only one version is supported in Nginx
1261sub feature_list_web_php_directories
1262{
1263my ($d) = @_;
1264my $mode = &feature_get_web_php_mode($d);
1265my @avail = &virtual_server::list_available_php_versions($d, $mode);
1266if ($mode eq 'fcgid') {
1267 # Map from the PHP FPM binary to the version number
1268 my ($defver) = &get_domain_php_version($d);
1269 my $phpcmd = &find_php_fcgi_server($d);
1270 if ($phpcmd) {
1271 foreach my $vers (@avail) {
1272 if ($vers->[1] && $vers->[1] eq $phpcmd) {
1273 $defver = $vers->[0];
1274 }
1275 }
1276 }
1277 return ( { 'dir' => &virtual_server::public_html_dir($d),
1278 'mode' => 'fcgid',
1279 'version' => $defver } );
1280 }
1281elsif ($mode eq 'fpm') {
1282 # Find the FPM version installed that matches the version in use
1283 my $ver = $d->{'php_fpm_version'} || $avail[0]->[0];
1284 my ($a) = grep { $_->[0] eq $ver } @avail;
1285 if (!$a) {
1286 # Selected version doesn't exist .. assume first one
1287 $a = $avail[0];
1288 }
1289 if (@avail) {
1290 return ( { 'dir' => &virtual_server::public_html_dir($d),
1291 'version' => $a->[0],
1292 'mode' => $mode } );
1293 }
1294 else {
1295 return ( );
1296 }
1297 }
1298return ( ); # Should never happen
1299}
1300
1301# feature_save_web_php_directory(&domain, dir, version)
1302# Change the PHP version for the whole site
1303sub feature_save_web_php_directory
1304{
1305my ($d, $dir, $ver) = @_;
1306$dir eq &virtual_server::public_html_dir($d) ||
1307 &error($text{'feat_ephpdir'});
1308my $mode = &feature_get_web_php_mode($d);
1309my @avail = &virtual_server::list_available_php_versions($d, $mode);
1310if ($mode eq "fpm") {
1311 # If the FPM version changed, just reset up
1312 if (!$d->{'php_fpm_version'}) {
1313 # Assume currently on first version
1314 $d->{'php_fpm_version'} = $avail[0]->[0];
1315 }
1316 if ($ver ne $d->{'php_fpm_version'}) {
1317 &virtual_server::delete_php_fpm_pool($d);
1318 $d->{'php_fpm_version'} = $ver;
1319 &virtual_server::save_domain($d);
1320 &virtual_server::create_php_fpm_pool($d);
1321 }
1322 }
1323else {
1324 # Assume this is FCGId mode
1325
1326 # Get the current version
1327 my $phpcmd = &find_php_fcgi_server($d);
1328 my $defver;
1329 if ($phpcmd) {
1330 foreach my $vers (@avail) {
1331 if ($vers->[1] && $vers->[1] eq $phpcmd) {
1332 $defver = $vers->[0];
1333 }
1334 }
1335 }
1336
1337 # Change if needed
1338 if ($defver ne $ver || !$d->{'nginx_php_version'}) {
1339 $d->{'nginx_php_version'} = $ver;
1340 &virtual_server::save_domain($d);
1341 &delete_php_fcgi_server($d);
1342 &setup_php_fcgi_server($d);
1343 }
1344 }
1345
1346return undef;
1347}
1348
1349# feature_delete_web_php_directory(&domain, dir)
1350# Cannot delete the PHP version for a directory ever, so this does nothing
1351sub feature_delete_web_php_directory
1352{
1353my ($d, $dir) = @_;
1354}
1355
1356# feature_get_fcgid_max_execution_time(&domain)
1357# Returns the timeout set by fastcgi_read_timeout
1358sub feature_get_fcgid_max_execution_time
1359{
1360my ($d) = @_;
1361my $server = &find_domain_server($d);
1362if ($server) {
1363 my $ver = &get_nginx_version();
1364 $ver =~ s/^(\d+\.\d+)(.*)/$1/;
1365 if ($ver >= 1.6) {
1366 # New format directive
1367 my ($t) = grep { $_->{'words'}->[0] eq "read_timeout" }
1368 &find("fastcgi_param", $server);
1369 my $v = $t ? $t->{'words'}->[1] : undef;
1370 return !$v ? undef : $v == 9999 ? undef : $v;
1371 }
1372 else {
1373 # Old format directive
1374 my $t = &find_value("fastcgi_read_timeout", $server);
1375 return $t == 9999 ? undef : $t if ($t);
1376 }
1377 return &get_default("fastcgi_read_timeout");
1378 }
1379}
1380
1381# feature_set_fcgid_max_execution_time(&domain, timeout)
1382# Sets the fcgi timeout with fastcgi_read_timeout
1383sub feature_set_fcgid_max_execution_time
1384{
1385my ($d, $max) = @_;
1386&lock_all_config_files();
1387my $server = &find_domain_server($d);
1388if ($server) {
1389 my $ver = &get_nginx_version();
1390 $ver =~ s/^(\d+\.\d+)(.*)/$1/;
1391 if ($ver >= 1.6) {
1392 # New format directive
1393 my @p = &find("fastcgi_param", $server);
1394 @p = grep { $_->{'words'}->[0] ne 'read_timeout' } @p;
1395 push(@p, { 'name' => 'fastcgi_param',
1396 'words' => [ "read_timeout", ($max || 9999) ] });
1397 &save_directive($server, "fastcgi_param", \@p);
1398 }
1399 else {
1400 # Old format directive
1401 &save_directive($server, "fastcgi_read_timeout",
1402 [ $max || 9999 ]);
1403 }
1404 }
1405&flush_config_file_lines();
1406&unlock_all_config_files();
1407&virtual_server::register_post_action(\&print_apply_nginx);
1408}
1409
1410# feature_restart_web_php(&domain)
1411# Restart the fcgi server for this domain, if one is running
1412sub feature_restart_web_php
1413{
1414my ($d) = @_;
1415if ($d->{'nginx_php_port'}) {
1416 &foreign_require("init");
1417 my $name = &init_script_name($d);
1418 &init::restart_action($name);
1419 }
1420}
1421
1422# feature_restart_web()
1423# Applies the webserver configuration
1424sub feature_restart_web
1425{
1426&print_apply_nginx();
1427}
1428
1429# feature_restart_web_command()
1430# Returns the Nginx log rotation command
1431sub feature_restart_web_command
1432{
1433return $config{'rotate_cmd'} || $config{'apply_cmd'};
1434}
1435
1436# feature_get_web_php_children(&domain)
1437# Defaults to 1, but can be changed by environment variable
1438sub feature_get_web_php_children
1439{
1440my ($d) = @_;
1441my $mode = &feature_get_web_php_mode($d);
1442if ($mode eq "fcgid") {
1443 # Stored in the domain's config
1444 return $d->{'nginx_php_children'} || 1;
1445 }
1446elsif ($mode eq "fpm") {
1447 # Read from FPM config file
1448 my $conf = &virtual_server::get_php_fpm_config();
1449 return -1 if (!$conf);
1450 my $file = $conf->{'dir'}."/".$d->{'id'}.".conf";
1451 my $lref = &read_file_lines($file, 1);
1452 my $childs = 0;
1453 foreach my $l (@$lref) {
1454 if ($l =~ /pm.max_children\s*=\s*(\d+)/) {
1455 $childs = $1;
1456 }
1457 }
1458 &unflush_file_lines($file);
1459 return $childs == 9999 ? 0 : $childs;
1460 }
1461else {
1462 return undef;
1463 }
1464}
1465
1466# feature_save_web_php_children(&domain, children)
1467# Update the PHP init script and running process with the new child count
1468sub feature_save_web_php_children
1469{
1470my ($d, $children) = @_;
1471$d->{'nginx_php_children'} ||= 1;
1472if ($children != $d->{'nginx_php_children'}) {
1473 $d->{'nginx_php_children'} = $children;
1474 my $mode = &feature_get_web_php_mode($d);
1475 if ($mode eq "fcgid") {
1476 # Set in the fcgid init script / command line
1477 &delete_php_fcgi_server($d);
1478 &setup_php_fcgi_server($d);
1479 }
1480 elsif ($mode eq "fpm") {
1481 # Set in the FPM config
1482 my $conf = &virtual_server::get_php_fpm_config();
1483 return 0 if (!$conf);
1484 my $file = $conf->{'dir'}."/".$d->{'id'}.".conf";
1485 return 0 if (!-r $file);
1486 &lock_file($file);
1487 my $lref = &read_file_lines($file);
1488 $children = 9999 if ($children == 0); # Unlimited
1489 foreach my $l (@$lref) {
1490 if ($l =~ /pm.max_children\s*=\s*(\d+)/) {
1491 $l = "pm.max_children = $children";
1492 }
1493 }
1494 &flush_file_lines($file);
1495 &unlock_file($file);
1496 &virtual_server::register_post_action(
1497 \&virtual_server::restart_php_fpm_server);
1498 }
1499 &virtual_server::save_domain($d);
1500 }
1501return undef;
1502}
1503
1504# feature_startstop()
1505# Returns info for restarting Nginx
1506sub feature_startstop
1507{
1508my $pid = &is_nginx_running();
1509my @links = ( { 'link' => '/'.$module_name.'/',
1510 'desc' => $text{'feat_manage'},
1511 'manage' => 1 } );
1512if ($pid) {
1513 return ( { 'status' => 1,
1514 'name' => $text{'feat_sname'},
1515 'desc' => $text{'feat_sstop'},
1516 'restartdesc' => $text{'feat_srestart'},
1517 'longdesc' => $text{'feat_sstopdesc'},
1518 'links' => \@links } );
1519 }
1520else {
1521 return ( { 'status' => 0,
1522 'name' => $text{'feat_sname'},
1523 'desc' => $text{'feat_sstart'},
1524 'longdesc' => $text{'feat_sstartdesc'},
1525 'links' => \@links } );
1526 }
1527}
1528
1529# feature_stop_service()
1530# Stop the Nginx webserver, from the System Information page
1531sub feature_stop_service
1532{
1533return &stop_nginx();
1534}
1535
1536# feature_start_service()
1537# Start the Nginx webserver, from the System Information page
1538sub feature_start_service
1539{
1540return &start_nginx();
1541}
1542
1543# feature_bandwidth(&domain, start, &bw-hash)
1544# Searches through log files for records after some date, and updates the
1545# day counters in the given hash
1546sub feature_bandwidth
1547{
1548my ($d, $start, $bwinfo) = @_;
1549my @logs = ( &get_nginx_log($d, 0) );
1550return if ($d->{'alias'} || $d->{'subdom'}); # never accounted separately
1551my $max_ltime = $start;
1552foreach my $l (&unique(@logs)) {
1553 foreach my $f (&virtual_server::all_log_files($l, $max_ltime)) {
1554 local $_;
1555 my $LOG;
1556 if ($f =~ /\.gz$/i) {
1557 open($LOG, "<", "gunzip -c ".quotemeta($f)." |");
1558 }
1559 elsif ($f =~ /\.Z$/i) {
1560 open($LOG, "<", "uncompress -c ".quotemeta($f)." |");
1561 }
1562 else {
1563 open($LOG, "<", $f);
1564 }
1565 while(<$LOG>) {
1566 if (/^(\S+)\s+(\S+)\s+(\S+)\s+\[(\d+)\/(\S+)\/(\d+):(\d+):(\d+):(\d+)\s+(\S+)\]\s+"([^"]*)"\s+(\S+)\s+(\S+)/) {
1567 # Valid-looking log line .. work out the time
1568 no warnings "once";
1569 my $ltime = timelocal($9, $8, $7, $4, $virtual_server::apache_mmap{lc($5)}, $6-1900);
1570 use warnings "once";
1571 if ($ltime > $start) {
1572 my $day = int($ltime / (24*60*60));
1573 $bwinfo->{"web_".$day} += $13;
1574 }
1575 $max_ltime = $ltime if ($ltime > $max_ltime);
1576 }
1577 }
1578 close($LOG);
1579 }
1580 }
1581return $max_ltime;
1582}
1583
1584# feature_get_web_domain_star(&domain)
1585# Checks if all sub-domains are matched for this domain
1586sub feature_get_web_domain_star
1587{
1588my ($d) = @_;
1589my $server = &find_domain_server($d);
1590return undef if (!$server);
1591my $obj = &find("server_name", $server);
1592foreach my $w (@{$obj->{'words'}}) {
1593 if ($w eq "*.".$d->{'dom'}) {
1594 return 1;
1595 }
1596 }
1597return 0;
1598}
1599
1600# feature_save_web_domain_star(&domain, star)
1601# Add *.domain to server_name if missing
1602sub feature_save_web_domain_star
1603{
1604my ($d, $star) = @_;
1605&lock_all_config_files();
1606my $server = &find_domain_server($d);
1607return undef if (!$server);
1608my $obj = &find("server_name", $server);
1609my $idx = &indexof("*.".$d->{'dom'}, @{$obj->{'words'}});
1610if ($star && $idx < 0) {
1611 # Need to add
1612 push(@{$obj->{'words'}}, "*.".$d->{'dom'});
1613 &save_directive($server, "server_name", [ $obj ]);
1614 }
1615elsif (!$star && $idx >= 0) {
1616 # Need to remove
1617 splice(@{$obj->{'words'}}, $idx, 1);
1618 &save_directive($server, "server_name", [ $obj ]);
1619 }
1620&flush_config_file_lines();
1621&unlock_all_config_files();
1622&virtual_server::register_post_action(\&print_apply_nginx);
1623}
1624
1625# feature_get_web_log(&domain, errorlog)
1626# Returns the path to the access or error log
1627sub feature_get_web_log
1628{
1629my ($d, $errorlog) = @_;
1630return &get_nginx_log($d, $errorlog);
1631}
1632
1633sub feature_supports_web_redirects
1634{
1635return 1; # Always supported
1636}
1637
1638# feature_list_web_redirects(&domain)
1639# Finds redirects from rewrite directives in the Nginx config
1640sub feature_list_web_redirects
1641{
1642my ($d) = @_;
1643my $server = &find_domain_server($d);
1644return () if (!$server);
1645my @rv;
1646my $phd = &virtual_server::public_html_dir($d);
1647my @rewrites = &find("rewrite", $server);
1648foreach my $i (&find("if", $server)) {
1649 my @w = @{$i->{'words'}};
1650 if ($i->{'type'} &&
1651 @{$i->{'members'}} == 1 &&
1652 $w[0] eq "\$scheme" && $w[1] eq "=" &&
1653 ($w[2] eq "http" || $w[2] eq "https")) {
1654 # May contain relevant rewrites
1655 my ($r) = &find("rewrite", $i);
1656 $r->{'_scheme'} = $w[2];
1657 $r->{'_if'} = $i;
1658 push(@rewrites, $r);
1659 }
1660 }
1661foreach my $r (@rewrites) {
1662 my $redirect;
1663 if ($r->{'words'}->[0] =~ /^\^\\Q(\/.*)\\E(\(\.\*\))?/ &&
1664 $r->{'words'}->[2] eq 'break') {
1665 # Regular redirect
1666 $redirect = { 'path' => $1,
1667 'dest' => $r->{'words'}->[1],
1668 'object' => $r,
1669 };
1670 if ($2) {
1671 if ($redirect->{'dest'} =~ s/\$1$//) {
1672 $redirect->{'regexp'} = 0;
1673 }
1674 else {
1675 $redirect->{'regexp'} = 1;
1676 }
1677 }
1678 }
1679 elsif ($r->{'words'}->[0] eq '^/(?!.well-known)(.*)' &&
1680 $r->{'words'}->[2] eq 'break') {
1681 # Special case for / which excludes .well-known
1682 $redirect = { 'path' => '^/(?!.well-known)',
1683 'dest' => $r->{'words'}->[1],
1684 'object' => $r,
1685 'regexp' => 1,
1686 };
1687 }
1688 if ($redirect) {
1689 if ($r->{'words'}->[1] =~ /^(http|https):/) {
1690 $redirect->{'alias'} = 0;
1691 }
1692 else {
1693 $redirect->{'dest'} = $phd.$redirect->{'dest'};
1694 $redirect->{'alias'} = 1;
1695 }
1696 if ($r->{'_scheme'}) {
1697 $redirect->{$r->{'_scheme'}} = 1;
1698 $redirect->{'ifobject'} = $r->{'_if'};
1699 }
1700 else {
1701 $redirect->{'http'} = $redirect->{'https'} = 1;
1702 }
1703 $redirect->{'id'} = ($redirect->{'alias'} ? 'alias_' : 'redirect_').$redirect->{'path'};
1704 push(@rv, $redirect);
1705 }
1706 }
1707return @rv;
1708}
1709
1710# feature_create_web_redirect(&domain, &redirect)
1711# Add a redirect using a rewrite directive
1712sub feature_create_web_redirect
1713{
1714my ($d, $redirect) = @_;
1715my $server = &find_domain_server($d);
1716return &text('redirect_efind', $d->{'dom'}) if (!$server);
1717my $phd = &virtual_server::public_html_dir($d);
1718my $dest = $redirect->{'dest'};
1719if ($dest !~ /^(http|https):/) {
1720 $dest =~ s/^\Q$phd\E// || return &text('redirect_ephd', $phd);
1721 }
1722my $re = $redirect->{'path'};
1723if ($re ne '^/(?!.well-known)') {
1724 $re = '^\\Q'.$re.'\\E';
1725 }
1726my $r = { 'name' => 'rewrite',
1727 'words' => [ $re, $dest, 'break' ],
1728 };
1729if ($redirect->{'regexp'}) {
1730 # All sub-directories go to same dest path
1731 $r->{'words'}->[0] .= "(.*)";
1732 }
1733else {
1734 # Redirect sub-directory to same sub-dir on dest
1735 $r->{'words'}->[0] .= "(.*)";
1736 $r->{'words'}->[1] .= "\$1";
1737 }
1738&lock_all_config_files();
1739if ($redirect->{'http'} && $redirect->{'https'}) {
1740 # Can just go at top level
1741 &save_directive($server, [ ], [ $r ]);
1742 }
1743else {
1744 # Put under an 'if' statement
1745 my $s = $redirect->{'http'} ? 'http' : 'https';
1746 my $i = { 'name' => 'if',
1747 'type' => 1,
1748 'members' => [ $r ],
1749 'words' => [ '$scheme', '=', $s ] };
1750 &save_directive($server, [ ], [ $i ]);
1751 }
1752&flush_config_file_lines();
1753&unlock_all_config_files();
1754&virtual_server::register_post_action(\&print_apply_nginx);
1755return undef;
1756}
1757
1758# feature_delete_web_redirect(&domain, &redirect)
1759# Remove a redirect using a rewrite directive
1760sub feature_delete_web_redirect
1761{
1762my ($d, $redirect) = @_;
1763my $server = &find_domain_server($d);
1764return &text('redirect_efind', $d->{'dom'}) if (!$server);
1765return $text{'redirect_eobj'} if (!$redirect->{'object'});
1766&lock_all_config_files();
1767if ($redirect->{'ifobject'}) {
1768 &save_directive($server, [ $redirect->{'ifobject'} ], [ ]);
1769 }
1770else {
1771 &save_directive($server, [ $redirect->{'object'} ], [ ]);
1772 }
1773&flush_config_file_lines();
1774&unlock_all_config_files();
1775&virtual_server::register_post_action(\&print_apply_nginx);
1776return undef;
1777}
1778
1779sub feature_supports_web_balancers
1780{
1781return 2; # Supports multiple backends
1782}
1783
1784# feature_list_web_balancers(&domain)
1785# Finds location blocks that just have a proxy_pass in them
1786sub feature_list_web_balancers
1787{
1788my ($d) = @_;
1789my $server = &find_domain_server($d);
1790return &text('redirect_efind', $d->{'dom'}) if (!$server);
1791my @rv;
1792my @locations = &find("location", $server);
1793my $conf = &get_config();
1794my $http = &find("http", $conf);
1795my %upstreams = map { $_->{'words'}->[0], $_ } &find("upstream", $http);
1796foreach my $l (@locations) {
1797 next if (@{$l->{'words'}} > 1);
1798 my $pp = &find_value("proxy_pass", $l);
1799 next if (!$pp && @{$l->{'members'}});
1800 my $b = { 'path' => $l->{'words'}->[0],
1801 'location' => $l };
1802 if (!$pp) {
1803 # No URL, so proxying disabled
1804 $b->{'none'} = 1;
1805 }
1806 elsif ($pp =~ /^http:\/\/([^\/]+)$/ && $upstreams{$1}) {
1807 # Mapped to an upstream block, with multiple URLs
1808 $b->{'balancer'} = $1;
1809 my $u = $upstreams{$1};
1810 $b->{'urls'} = [ map { &upstream_to_url($_) }
1811 &find_value("server", $u) ];
1812 $b->{'upstream'} = $u;
1813 }
1814 else {
1815 # Just one URL
1816 $b->{'urls'} = [ $pp ];
1817 }
1818 push(@rv, $b);
1819 }
1820return @rv;
1821}
1822
1823# feature_create_web_balancer(&domain, &balancer)
1824# Create a location block for proxying to some URLs
1825sub feature_create_web_balancer
1826{
1827my ($d, $balancer) = @_;
1828my $server = &find_domain_server($d);
1829return &text('redirect_efind', $d->{'dom'}) if (!$server);
1830my ($clash) = grep { $_->{'words'}->[0] eq $balancer->{'path'} }
1831 &find("location", $server);
1832$clash && return &text('redirect_eclash', $balancer->{'path'});
1833&lock_all_config_files();
1834my @urls = $balancer->{'none'} ? ( ) : @{$balancer->{'urls'}};
1835my $err = &validate_balancer_urls(@urls);
1836return $err if ($err);
1837my $url;
1838if (@urls > 1) {
1839 $balancer->{'balancer'} ||= 'virtualmin_'.time().'_'.$$;
1840 $url = 'http://'.$balancer->{'balancer'};
1841 my $conf = &get_config();
1842 my $http = &find("http", $conf);
1843 my ($clash) = grep { $_->{'words'}->[0] eq $balancer->{'balancer'} }
1844 &find("upstream", $http);
1845 $clash && return &text('redirect_eupstream', $balancer->{'balancer'});
1846 my $u = { 'name' => 'upstream',
1847 'words' => [ $balancer->{'balancer'} ],
1848 'type' => 1,
1849 'members' => [
1850 map { { 'name' => 'server',
1851 'words' => [ &url_to_upstream($_) ] } } @urls,
1852 ]
1853 };
1854 $balancer->{'upstream'} = $u;
1855 &save_directive($http, [ ], [ $u ]);
1856 }
1857elsif (!$balancer->{'none'}) {
1858 $url = $urls[0];
1859 }
1860my $l = { 'name' => 'location',
1861 'words' => [ $balancer->{'path'} ],
1862 'type' => 1,
1863 'members' => [ ],
1864 };
1865if ($url) {
1866 # Add rewrites to make URL sent to the proxy not include the original
1867 # path, like Apache does. Also fix up redirects
1868 my $p = $balancer->{'path'};
1869 if ($p ne '/') {
1870 $p =~ s/\/$//;
1871 push(@{$l->{'members'}},
1872 { 'name' => 'rewrite',
1873 'words' => [ '^'.$p.'$', $p.'/', 'redirect' ],
1874 },
1875 { 'name' => 'rewrite',
1876 'words' => [ '^'.$p.'(/.*)', '$1', 'break' ],
1877 },
1878 { 'name' => 'proxy_redirect',
1879 'words' => [ $url, $p ],
1880 },
1881 );
1882 }
1883 push(@{$l->{'members'}},
1884 { 'name' => 'proxy_pass',
1885 'words' => [ $url ],
1886 },
1887 );
1888 }
1889$balancer->{'location'} = $l;
1890my $before = &find_before_location($server, $balancer->{'path'});
1891&save_directive($server, [ ], [ $l ], $before);
1892&flush_config_file_lines();
1893&unlock_all_config_files();
1894&virtual_server::register_post_action(\&print_apply_nginx);
1895return undef;
1896}
1897
1898# feature_delete_web_balancer(&domain, &balancer)
1899# Deletes the location block for a proxy, and the balancer if created by
1900# Virtualmin
1901sub feature_delete_web_balancer
1902{
1903my ($d, $balancer) = @_;
1904my $server = &find_domain_server($d);
1905return &text('redirect_efind', $d->{'dom'}) if (!$server);
1906return $text{'redirect_eobj2'} if (!$balancer->{'location'});
1907&lock_all_config_files();
1908my $pp = &find_value("proxy_pass", $balancer->{'location'});
1909if ($balancer->{'upstream'}) {
1910 # Has associated upstream block .. check for other users
1911 my $conf = &get_config();
1912 my $http = &find("http", $conf);
1913 my @pps = &find_recursive("proxy_pass", $http);
1914 my @users = grep { $_->{'words'}->[0] =~
1915 /^http:\/\/\Q$balancer->{'balancer'}\E/ } @pps;
1916 if (@users <= 1) {
1917 &save_directive($http, [ $balancer->{'upstream'} ], [ ]);
1918 }
1919 }
1920&save_directive($server, [ $balancer->{'location'} ], [ ]);
1921&flush_config_file_lines();
1922&unlock_all_config_files();
1923&virtual_server::register_post_action(\&print_apply_nginx);
1924return undef;
1925}
1926
1927# feature_modify_web_balancer(&domain, &balancer, &old-balancer)
1928# Change the path or URLs of a proxy
1929sub feature_modify_web_balancer
1930{
1931my ($d, $balancer, $oldbalancer) = @_;
1932my $server = &find_domain_server($d);
1933return &text('redirect_efind', $d->{'dom'}) if (!$server);
1934return $text{'redirect_eobj2'} if (!$oldbalancer->{'location'});
1935&lock_all_config_files();
1936my $l = $oldbalancer->{'location'};
1937if ($balancer->{'path'} ne $oldbalancer->{'path'}) {
1938 $l->{'words'}->[0] = $balancer->{'path'};
1939 &save_directive($server, [ $l ], [ $l ]);
1940 }
1941my $u = $oldbalancer->{'upstream'};
1942my @urls = $balancer->{'none'} ? ( ) : @{$balancer->{'urls'}};
1943my $err = &validate_balancer_urls(@urls);
1944return $err if ($err);
1945my $url;
1946if ($u) {
1947 # Change URLs in upstream block
1948 &save_directive($u, "server", [ map { &url_to_upstream($_) } @urls ]);
1949 $url = "http://".$oldbalancer->{'balancer'};
1950 }
1951elsif (@urls > 1) {
1952 # Need to add an upstream block
1953 &error("Converting a proxy to a balancer can never happen!");
1954 }
1955else {
1956 # Just change one URL
1957 &save_directive($l, "proxy_pass", \@urls);
1958 $url = @urls ? $urls[0] : undef;
1959 }
1960if (@urls && $balancer->{'path'} ne '/') {
1961 # Add rewrites for the path
1962 my $p = $balancer->{'path'};
1963 $p =~ s/\/$//;
1964 &save_directive($l, 'rewrite',
1965 { 'name' => 'rewrite',
1966 'words' => [ '^'.$p.'$', $p.'/', 'redirect' ],
1967 },
1968 { 'name' => 'rewrite',
1969 'words' => [ '^'.$p.'(/.*)', '$1', 'break' ],
1970 },
1971 { 'name' => 'proxy_redirect',
1972 'words' => [ $url, $p ],
1973 },
1974 );
1975 }
1976esle {
1977 &save_directive($l, 'rewrite', [ ]);
1978 }
1979&flush_config_file_lines();
1980&unlock_all_config_files();
1981&virtual_server::register_post_action(\&print_apply_nginx);
1982return undef;
1983}
1984
1985sub feature_supports_webmail_redirect
1986{
1987return 1; # Can be setup using Nginx rewrites
1988}
1989
1990# feature_add_web_webmail_redirect(&domain, &tmpl)
1991# Add server names for webmail and admin, and rewrite rules to redirect to
1992# Webmin and Usermin
1993sub feature_add_web_webmail_redirect
1994{
1995my ($d, $tmpl) = @_;
1996my $server = &find_domain_server($d);
1997return &text('redirect_efind', $d->{'dom'}) if (!$server);
1998&lock_all_config_files();
1999foreach my $r ('webmail', 'admin') {
2000 next if (!$tmpl->{'web_'.$r});
2001
2002 # Work out the URL to redirect to
2003 my $url = $tmpl->{'web_'.$r.'dom'};
2004 if ($url) {
2005 # Sub in any template
2006 $url = &virtual_server::substitute_domain_template($url, $d);
2007 }
2008 else {
2009 # Work out URL
2010 my ($port, $proto);
2011 if ($r eq 'webmail') {
2012 # From Usermin
2013 if (&foreign_installed("usermin")) {
2014 &foreign_require("usermin", "usermin-lib.pl");
2015 my %miniserv;
2016 &usermin::get_usermin_miniserv_config(
2017 \%miniserv);
2018 $proto = $miniserv{'ssl'} ? 'https' : 'http';
2019 $port = $miniserv{'port'};
2020 }
2021 # Fall back to standard defaults
2022 $proto ||= "http";
2023 $port ||= 20000;
2024 }
2025 else {
2026 # From Webmin
2027 ($port, $proto) = &virtual_server::get_miniserv_port_proto();
2028 }
2029 $url = "$proto://$d->{'dom'}:$port/";
2030 }
2031
2032 # Update server_name
2033 my $obj = &find("server_name", $server);
2034 my $rhost = $r.".".$d->{'dom'};
2035 if (&indexof($rhost, @{$obj->{'words'}}) < 0) {
2036 push(@{$obj->{'words'}}, $rhost);
2037 &save_directive($server, "server_name", [ $obj ]);
2038 }
2039
2040 # Add rewrite directive, inside if block
2041 &save_directive($server, [ ], [
2042 { 'name' => 'if',
2043 'type' => 2,
2044 'words' => [ '$host', '=', $rhost ],
2045 'members' => [
2046 { 'name' => 'rewrite',
2047 'words' => [ '^/(.*)$', $url.'$1', 'redirect' ],
2048 },
2049 ]
2050 },
2051 ]);
2052 }
2053&flush_config_file_lines();
2054&unlock_all_config_files();
2055&virtual_server::register_post_action(\&print_apply_nginx);
2056return undef;
2057}
2058
2059# feature_remove_web_webmail_redirect(&domain)
2060# Delete the additional server names and rewrite rules
2061sub feature_remove_web_webmail_redirect
2062{
2063my ($d) = @_;
2064my $server = &find_domain_server($d);
2065return &text('redirect_efind', $d->{'dom'}) if (!$server);
2066&lock_all_config_files();
2067foreach my $r ('webmail', 'admin') {
2068 # Update server_name
2069 my $obj = &find("server_name", $server);
2070 my $rhost = $r.".".$d->{'dom'};
2071 my $idx = &indexof($rhost, @{$obj->{'words'}});
2072 if ($idx >= 0) {
2073 splice(@{$obj->{'words'}}, $idx, 1);
2074 &save_directive($server, "server_name", [ $obj ]);
2075 }
2076
2077 # Remove if block for the rewrite
2078 my @ifs = &find("if", $server);
2079 foreach my $i (@ifs) {
2080 if ($i->{'words'}->[0] eq '$host' &&
2081 $i->{'words'}->[1] eq '=' &&
2082 $i->{'words'}->[2] eq $rhost) {
2083 &save_directive($server, [ $i ], [ ]);
2084 }
2085 }
2086 }
2087&flush_config_file_lines();
2088&unlock_all_config_files();
2089&virtual_server::register_post_action(\&print_apply_nginx);
2090return undef;
2091}
2092
2093# feature_get_web_webmail_redirect(&domain)
2094# Check if the webmail and admin server_names are in place
2095sub feature_get_web_webmail_redirect
2096{
2097my ($d) = @_;
2098my $server = &find_domain_server($d);
2099return 0 if (!$server);
2100my $obj = &find("server_name", $server);
2101my @rv;
2102foreach my $r ("webmail", "admin") {
2103 my $rhost = $r.".".$d->{'dom'};
2104 push(@rv, $rhost) if (&indexof($rhost, @{$obj->{'words'}}) >= 0);
2105 }
2106return @rv;
2107}
2108
2109sub feature_supports_web_default
2110{
2111return 1; # Websites can be made the default
2112}
2113
2114# feature_set_web_default(&domain)
2115# Make this domain's site the default by adding it's IP to server_name
2116sub feature_set_web_default
2117{
2118my ($d) = @_;
2119my $server = &find_domain_server($d);
2120return &text('redirect_efind', $d->{'dom'}) if (!$server);
2121&lock_all_config_files();
2122
2123# Add IP to server_name for this server
2124my $obj = &find("server_name", $server);
2125my $idx = &indexof($d->{'ip'}, @{$obj->{'words'}});
2126if ($idx < 0) {
2127 push(@{$obj->{'words'}}, $d->{'ip'});
2128 &save_directive($server, "server_name", [ $obj ]);
2129 }
2130
2131# Remove IP from server_name for other servers
2132my $conf = &get_config();
2133my $http = &find("http", $conf);
2134foreach my $os (&find("server", $http)) {
2135 next if ($os eq $server);
2136 my $obj = &find("server_name", $os);
2137 my $idx = &indexof($d->{'ip'}, @{$obj->{'words'}});
2138 if ($idx >= 0) {
2139 splice(@{$obj->{'words'}}, $idx, 1);
2140 &save_directive($os, "server_name", [ $obj ]);
2141 }
2142 }
2143
2144&flush_config_file_lines();
2145&unlock_all_config_files();
2146&virtual_server::register_post_action(\&print_apply_nginx);
2147return undef;
2148}
2149
2150# feature_is_web_default(&domain)
2151# Returns 1 if the server's IP is in server_names
2152sub feature_is_web_default
2153{
2154my ($d) = @_;
2155my $server = &find_domain_server($d);
2156return 0 if (!$server);
2157my $obj = &find("server_name", $server);
2158return &indexof($d->{'ip'}, @{$obj->{'words'}}) >= 0 ? 1 : 0;
2159}
2160
2161# feature_save_web_passphrase(&domain)
2162# Not possible with Nginx
2163sub feature_save_web_passphrase
2164{
2165my ($d) = @_;
2166if ($d->{'ssl_pass'}) {
2167 &error($text{'feat_epassphrase'});
2168 }
2169}
2170
2171# feature_get_web_ssl_file(&domain, mode)
2172# Return the SSL cert or key file in the Nginx config
2173sub feature_get_web_ssl_file
2174{
2175my ($d, $mode) = @_;
2176my $server = &find_domain_server($d);
2177return undef if (!$server);
2178if ($mode eq 'cert') {
2179 return &find_value($server, "ssl_certificate");
2180 }
2181elsif ($mode eq 'key') {
2182 return &find_value($server, "ssl_certificate_key");
2183 }
2184elsif ($mode eq 'ca') {
2185 # Always appeneded to the cert file
2186 return $d->{'ssl_chain'};
2187 }
2188return undef;
2189}
2190
2191# feature_save_web_ssl_file(&domain, mode, file)
2192# Set the SSL cert or key file in the Nginx config
2193sub feature_save_web_ssl_file
2194{
2195my ($d, $mode, $file) = @_;
2196&lock_all_config_files();
2197my $server = &find_domain_server($d);
2198return &text('feat_efind', $d->{'dom'}) if (!$server);
2199if ($mode eq 'cert') {
2200 &save_directive($server, "ssl_certificate",
2201 $file ? [ $file ] : [ ]);
2202 }
2203elsif ($mode eq 'key') {
2204 &save_directive($server, "ssl_certificate_key",
2205 $file ? [ $file ] : [ ]);
2206 }
2207elsif ($mode eq 'ca') {
2208 # Dovecot needs cert and CA in the same file!
2209 if ($d->{'ssl_combined'} && -r $d->{'ssl_combined'}) {
2210 # Combined file already exists
2211 if ($file) {
2212 # Use the combined file
2213 &save_directive($server, "ssl_certificate", [ $d->{'ssl_combined'} ]);
2214 }
2215 else {
2216 # Revert to just the cert file
2217 &save_directive($server, "ssl_certificate", [ $d->{'ssl_cert'} ]);
2218 }
2219 }
2220 else {
2221 # Append to cert file as well
2222 my $certfile = &find_value("ssl_certificate", $server);
2223 $certfile || return $text{'feat_echainfile'};
2224 my @certs = &split_ssl_certs(&read_file_contents($certfile));
2225 my $fh = "CERT";
2226 if ($file) {
2227 # Append chained cert to main cert
2228 my $chain = &read_file_contents($file);
2229 &open_tempfile($fh, ">$certfile");
2230 &print_tempfile($fh, join("", $certs[0], $chain));
2231 &close_tempfile($fh);
2232 }
2233 else {
2234 # Use only main cert
2235 &open_tempfile($fh, ">$certfile");
2236 &print_tempfile($fh, $certs[0]);
2237 &close_tempfile($fh);
2238 }
2239 }
2240 }
2241&flush_config_file_lines();
2242&unlock_all_config_files();
2243&virtual_server::register_post_action(\&print_apply_nginx);
2244return undef;
2245}
2246
2247# feature_backup(&domain, file, &opts, homeformat?, incremental?, as-owner,
2248# &all-opts)
2249# Backup this domain's Nginx directives to a file
2250sub feature_backup
2251{
2252my ($d, $file, $opts, $homefmt, $increment, $asd, $allopts) = @_;
2253return 1 if ($d->{'alias'});
2254
2255# Write config directives from the server block to a file
2256&$virtual_server::first_print($text{'feat_backup'});
2257&lock_all_config_files();
2258my $server = &find_domain_server($d);
2259if (!$server) {
2260 &unlock_all_config_files();
2261 &$virtual_server::second_print(
2262 &text('feat_efind', $d->{'dom'}));
2263 return 0;
2264 }
2265my $lref = &read_file_lines($server->{'file'}, 1);
2266my $fh = "BACKUP";
2267&virtual_server::open_tempfile_as_domain_user($d, $fh, ">$file");
2268my %adoms = map { $_->{'dom'}, 1 }
2269 &virtual_server::get_domain_by("alias", $d->{'id'});
2270foreach my $l (@$lref[($server->{'line'}+1) .. ($server->{'eline'}-1)]) {
2271 $l = &fix_server_name_line($l, \%adoms);
2272 &print_tempfile($fh, $l."\n") if ($l);
2273 }
2274&virtual_server::close_tempfile_as_domain_user($d, $fh);
2275if ($server->{'file'} eq &get_add_to_file($d->{'dom'}) &&
2276 $config{'add_to'} && -d $config{'add_to'}) {
2277 # Domain has it's own file, so save it completely for use
2278 # when restoring
2279 &virtual_server::copy_write_as_domain_user(
2280 $d, $server->{'file'}, $file."_complete");
2281 my $clref = &virtual_server::read_file_lines_as_domain_user(
2282 $d, $file."_complete");
2283 foreach my $l (@$clref) {
2284 $l = &fix_server_name_line($l, \%adoms);
2285 }
2286 &virtual_server::flush_file_lines_as_domain_user($d, $file."_complete");
2287 }
2288&unlock_all_config_files();
2289&$virtual_server::second_print($virtual_server::text{'setup_done'});
2290
2291# Save log files, if outside home
2292my $alog = &get_nginx_log($d, 0);
2293if ($alog && !&is_under_directory($d->{'home'}, $alog) &&
2294 !$allopts->{'dir'}->{'dirnologs'}) {
2295 &$virtual_server::first_print($text{'feat_backuplog'});
2296 &virtual_server::copy_write_as_domain_user($d, $alog, $file."_alog");
2297 my $elog = &get_nginx_log($d, 1);
2298 if ($elog && !&is_under_directory($d->{'home'}, $elog)) {
2299 &virtual_server::copy_write_as_domain_user(
2300 $d, $elog, $file."_elog");
2301 }
2302 &$virtual_server::second_print($virtual_server::text{'setup_done'});
2303 }
2304
2305return 1;
2306}
2307
2308# feature_restore(&domain, file, &opts, &all-opts, home-format, &old-domain)
2309# Re-created this domain's Nginx directives from a file
2310sub feature_restore
2311{
2312my ($d, $file, undef, undef, undef, $oldd) = @_;
2313return 1 if ($d->{'alias'});
2314
2315# Replace lines in the server block with those from the backup file
2316&$virtual_server::first_print($text{'feat_restore'});
2317&lock_all_config_files();
2318my $server = &find_domain_server($d);
2319if (!$server) {
2320 &unlock_all_config_files();
2321 &$virtual_server::second_print(
2322 &text('feat_efind', $d->{'dom'}));
2323 return 0;
2324 }
2325my $alog = &get_nginx_log($d, 0);
2326my $elog = &get_nginx_log($d, 1);
2327if ($server->{'file'} eq &get_add_to_file($d->{'dom'}) &&
2328 $config{'add_to'} && -d $config{'add_to'} &&
2329 -s $file."_complete") {
2330 # Domain is in its own file, and backup includes the whole file .. so
2331 # just copy it into place
2332 ©_source_dest($file."_complete", $server->{'file'});
2333 }
2334else {
2335 # Just replace server block for this domain
2336 my $lref = &read_file_lines($server->{'file'});
2337 my $srclref = &read_file_lines($file, 1);
2338 splice(@$lref, $server->{'line'}+1,
2339 $server->{'eline'}-$server->{'line'}-1,
2340 @$srclref);
2341 &flush_file_lines($server->{'file'});
2342 }
2343&flush_config_cache();
2344$server = &find_domain_server($d);
2345if (!$server) {
2346 &$virtual_server::second_print(
2347 &text('feat_erestorefind', $d->{'dom'}));
2348 return 0;
2349 }
2350
2351# Put back old log file paths
2352&save_directive($server, "access_log", [ $alog ]) if ($alog);
2353&save_directive($server, "error_log", [ $elog ]) if ($elog);
2354
2355# Remove IP from server_name if changed
2356if ($oldd && $oldd->{'ip'} ne $d->{'ip'}) {
2357 my $obj = &find("server_name", $server);
2358 my $idx = &indexof($oldd->{'ip'}, @{$obj->{'words'}});
2359 if ($idx >= 0) {
2360 splice(@{$obj->{'words'}}, $idx, 1);
2361 &save_directive($server, "server_name", [ $obj ]);
2362 }
2363 }
2364
2365# Fix up home directory if changed
2366if ($oldd && $d->{'home'} ne $oldd->{'home'}) {
2367 &recursive_change_directives(
2368 $server, $oldd->{'home'}, $d->{'home'}, 0, 1);
2369 }
2370
2371# Put back old port for PHP server
2372if ($oldd && $oldd->{'nginx_php_port'} ne $d->{'nginx_php_port'}) {
2373 my ($l) = grep { ($_->{'words'}->[1] eq '\.php$' ||
2374 $_->{'words'}->[1] eq '\.php(/|$)') }
2375 &find("location", $server);
2376 if ($l) {
2377 &save_directive($l, "fastcgi_pass",
2378 $oldd->{'nginx_php_port'} =~ /^\d+$/ ?
2379 [ "localhost:".$oldd->{'nginx_php_port'} ] :
2380 [ "unix:".$oldd->{'nginx_php_port'} ]);
2381 $d->{'nginx_php_port'} = $oldd->{'nginx_php_port'};
2382 }
2383 }
2384
2385&flush_config_file_lines();
2386&unlock_all_config_files();
2387&virtual_server::register_post_action(\&print_apply_nginx);
2388&$virtual_server::second_print($virtual_server::text{'setup_done'});
2389
2390# Correct system-specific entries in PHP config files
2391if ($oldd) {
2392 my $sock = &virtual_server::get_php_mysql_socket($d);
2393 my @fixes = (
2394 [ "session.save_path", $oldd->{'home'}, $d->{'home'}, 1 ],
2395 [ "upload_tmp_dir", $oldd->{'home'}, $d->{'home'}, 1 ],
2396 );
2397 if ($sock ne 'none') {
2398 push(@fixes, [ "mysql.default_socket", undef, $sock ]);
2399 }
2400 &virtual_server::fix_php_ini_files($d, \@fixes);
2401 }
2402
2403# Fix broken PHP extension_dir directives
2404&virtual_server::fix_php_extension_dir($d);
2405
2406# Re-check HTML dirs
2407&virtual_server::find_html_cgi_dirs($d);
2408
2409# Restart PHP server, in case php.ini got changed by the restore
2410&feature_restart_web_php($d);
2411
2412# Restore log files
2413if (-r $file."_alog") {
2414 &$virtual_server::first_print($text{'feat_restorelog'});
2415 ©_source_dest($file."_alog", $alog);
2416 &set_nginx_log_permissions($d, $alog);
2417 if (-r $file."_elog") {
2418 ©_source_dest($file."_elog", $elog);
2419 &set_nginx_log_permissions($d, $elog);
2420 }
2421 &$virtual_server::second_print($virtual_server::text{'setup_done'});
2422 }
2423
2424return 1;
2425}
2426
2427# feature_clone(&domain, &old-domain)
2428# Create a new Nginx virtualhost that copies from this one one
2429sub feature_clone
2430{
2431my ($d, $oldd) = @_;
2432&$virtual_server::first_print($text{'feat_clone'});
2433if ($d->{'alias'}) {
2434 # Nothing needs to be done, as the re-create as part of the cloning
2435 # will already have done everything
2436 &$virtual_server::second_print($text{'feat_clonealias'});
2437 return 1;
2438 }
2439&lock_all_config_files();
2440my $server = &find_domain_server($d);
2441if (!$server) {
2442 &unlock_all_config_files();
2443 &$virtual_server::second_print(&text('feat_efind', $d->{'dom'}));
2444 return 0;
2445 }
2446my $oldserver = &find_domain_server($d);
2447if (!$oldserver) {
2448 &unlock_all_config_files();
2449 &$virtual_server::second_print(&text('feat_efind', $oldd->{'dom'}));
2450 return 0;
2451 }
2452
2453# Preserve some settings from the clone target
2454my $alog = &get_nginx_log($d, 0);
2455my $elog = &get_nginx_log($d, 1);
2456my $obj = &find("server_name", $server);
2457
2458# Copy across all directives to the new server block, fixing the server_name
2459# so that it can be found
2460my $oldlref = &read_file_lines($oldserver->{'file'}, 1);
2461my $lref = &read_file_lines($server->{'file'});
2462my @lines = @$oldlref[$oldserver->{'line'}+1 .. $oldserver->{'eline'}-1];
2463foreach my $l (@lines) {
2464 if ($l =~ /^(\s*server_name\s+)/) {
2465 $l = $1.&join_words(@{$obj->{'words'}}).';';
2466 }
2467 }
2468splice(@$lref, $server->{'line'}+1, $server->{'eline'}-$server->{'line'}-1,
2469 @lines);
2470&flush_file_lines($server->{'file'});
2471&flush_config_cache();
2472
2473# Re-get the new server block
2474$server = &find_domain_server($d);
2475if (!$server) {
2476 &unlock_all_config_files();
2477 &$virtual_server::second_print(&text('feat_eclonefind', $d->{'dom'}));
2478 return 0;
2479 }
2480
2481# Put back old log file paths
2482&save_directive($server, "access_log", [ $alog ]) if ($alog);
2483&save_directive($server, "error_log", [ $elog ]) if ($elog);
2484
2485# Fix home dir, which is incorrect in copied directives
2486&recursive_change_directives(
2487 $server, $oldd->{'home'}, $d->{'home'}, 0, 0, 0);
2488&recursive_change_directives(
2489 $server, $oldd->{'home'}.'/', $d->{'home'}.'/', 0, 1, 0);
2490
2491# Fix domain name, which is incorrect in copied directives
2492&recursive_change_directives($server, $oldd->{'dom'},
2493 $d->{'dom'}, 0, 0, 1);
2494
2495# Fix PHP server port, which is incorrect in copied directives
2496my ($l) = grep { ($_->{'words'}->[1] eq '\.php$' ||
2497 $_->{'words'}->[1] eq '\.php(/|$)') }
2498 &find("location", $server);
2499if ($l) {
2500 &save_directive($l, "fastcgi_pass",
2501 $d->{'nginx_php_port'} =~ /^\d+$/ ?
2502 [ "localhost:".$d->{'nginx_php_port'} ] :
2503 [ "unix:".$d->{'nginx_php_port'} ]);
2504 }
2505
2506&flush_config_file_lines();
2507&unlock_all_config_files();
2508&virtual_server::register_post_action(\&print_apply_nginx);
2509
2510&$virtual_server::second_print($virtual_server::text{'setup_done'});
2511return 1;
2512}
2513
2514# feature_set_web_public_html_dir(&domain, subdir)
2515# Change the root path in the domain's server object
2516sub feature_set_web_public_html_dir
2517{
2518my ($d, $subdir) = @_;
2519my $server = &find_domain_server($d);
2520$server || return &text('redirect_efind', $d->{'dom'});
2521&lock_all_config_files();
2522&save_directive($server, "root", [ $d->{'home'}."/".$subdir ]);
2523&flush_config_file_lines();
2524&unlock_all_config_files();
2525&virtual_server::register_post_action(\&print_apply_nginx);
2526return undef;
2527}
2528
2529# feature_find_web_html_cgi_dirs(&domain)
2530# Use the root path in the domain's server to set public_html_dir and
2531# public_html_path
2532sub feature_find_web_html_cgi_dirs
2533{
2534my ($d) = @_;
2535my $server = &find_domain_server($d);
2536return undef if (!$server);
2537$d->{'public_html_path'} = &find_value("root", $server);
2538if ($d->{'public_html_path'} =~ /^\Q$d->{'home'}\E\/(.*)$/) {
2539 $d->{'public_html_dir'} = $1;
2540 }
2541elsif ($d->{'public_html_path'} eq $d->{'home'}) {
2542 # Same as home directory!
2543 $d->{'public_html_dir'} = ".";
2544 }
2545else {
2546 delete($d->{'public_html_dir'});
2547 }
2548}
2549
2550# feature_change_web_access_log(&domain, logfile)
2551# Update the access log location
2552sub feature_change_web_access_log
2553{
2554my ($d, $logfile) = @_;
2555return &change_nginx_log_file($d, $logfile, "access_log");
2556}
2557
2558# feature_change_web_error_log(&domain, logfile)
2559# Update the error log location
2560sub feature_change_web_error_log
2561{
2562my ($d, $logfile) = @_;
2563return &change_nginx_log_file($d, $logfile, "error_log");
2564}
2565
2566# feature_supports_sni([&domain])
2567# Returns 1 if Nginx supports SNI
2568sub feature_supports_sni
2569{
2570my $out = &backquote_command("$config{'nginx_cmd'} -V 2>&1 </dev/null");
2571return $out =~ /TLS\s+SNI\s+support\s+enabled/i ? 1 : 0;
2572}
2573
2574# template_input(&template)
2575# Returns HTML for editing per-template options for this plugin
2576sub template_input
2577{
2578my ($tmpl) = @_;
2579my $dirs = $tmpl->{$module_name};
2580return &ui_table_row($text{'tmpl_directives'},
2581 &ui_radio($module_name."_mode",
2582 $dirs eq "" ? 0 : $dirs eq "none" ? 1 : 2,
2583 [ [ 0, $text{'tmpl_default'} ],
2584 [ 1, $text{'tmpl_none'} ],
2585 [ 2, $text{'tmpl_below'} ] ])."<br>\n".
2586 &ui_textarea($module_name."_dirs",
2587 $dirs eq "none" ? "" : join("\n", split(/\t/, $dirs)),
2588 10, 80));
2589}
2590
2591# template_parse(&template, &in)
2592# Updates the given template object by parsing the inputs generated by
2593# template_input. All template fields must start with the module name.
2594sub template_parse
2595{
2596my ($tmpl, $in) = @_;
2597my $mode = $in->{$module_name."_mode"};
2598if ($mode == 0) {
2599 $tmpl->{$module_name} = "";
2600 }
2601elsif ($mode == 1) {
2602 $tmpl->{$module_name} = "none";
2603 }
2604else {
2605 $tmpl->{$module_name} = join("\t", split(/\r?\n/, $in->{$module_name."_dirs"}));
2606 }
2607}
2608
2609# change_nginx_log_file(&domain, file, name)
2610# Changes the log file for an access or error log
2611sub change_nginx_log_file
2612{
2613my ($d, $logfile, $name) = @_;
2614
2615# Update Nginx config
2616my $server = &find_domain_server($d);
2617$server || return &text('redirect_efind', $d->{'dom'});
2618&lock_all_config_files();
2619my $obj = &find($name, $server);
2620my @w = $obj ? @{$obj->{'words'}} : ( );
2621my $old_logfile = shift(@w);
2622&save_directive($server, $name,
2623 [ { 'name' => $name,
2624 'words' => [ $logfile, @w ] } ]);
2625&flush_config_file_lines();
2626&unlock_all_config_files();
2627&virtual_server::register_post_action(\&print_apply_nginx);
2628
2629# Actually move the file
2630if ($old_logfile && (!&same_file($logfile, $old_logfile) || -l $logfile)) {
2631 if (-e $logfile) {
2632 &unlink_file($logfile);
2633 }
2634 if (-r $old_logfile) {
2635 &rename_logged($old_logfile, $logfile);
2636 }
2637 }
2638
2639# Fix logrotate config
2640if ($d->{'logrotate'}) {
2641 my $lconf = &virtual_server::get_logrotate_section($old_logfile);
2642 if ($lconf) {
2643 my $parent = &logrotate::get_config_parent();
2644 foreach my $n (@{$lconf->{'name'}}) {
2645 if ($n eq $old_logfile) {
2646 $n = $logfile;
2647 }
2648 }
2649 &logrotate::save_directive($parent, $lconf, $lconf);
2650 &flush_file_lines($lconf->{'file'});
2651 }
2652 }
2653
2654return undef;
2655}
2656
2657# set_nginx_log_permissions(&domain, file)
2658# Sets the correct user and group perms on a log file
2659sub set_nginx_log_permissions
2660{
2661my ($d, $log) = @_;
2662my $web_user = &get_nginx_user();
2663my @uinfo = getpwnam($web_user);
2664my $web_group = getgrgid($uinfo[3]) || $uinfo[3];
2665&set_ownership_permissions($d->{'uid'}, $web_group, 0660, $log);
2666}
2667
2668# domain_server_names(&domain)
2669# Returns the list of server_name words for a domain
2670sub domain_server_names
2671{
2672my ($d) = @_;
2673return ( $d->{'dom'}, "www.".$d->{'dom'} );
2674}
2675
2676# get_nginx_log(&domain, [errorlog])
2677# Returns the location of a log file for a domain's virtual host, or undef.
2678sub get_nginx_log
2679{
2680my ($d, $want_error) = @_;
2681my $s = &find_domain_server($d);
2682if ($s) {
2683 return &find_value($want_error ? "error_log" : "access_log", $s);
2684 }
2685return undef;
2686}
2687
2688# get_nginx_user()
2689# Returns the use nginx runs as
2690sub get_nginx_user
2691{
2692my $conf = &get_config();
2693my $user = &find_value("user", $conf);
2694$user ||= &get_default("user");
2695return $user;
2696}
2697
2698# setup_nginx_proxy_pass(&domain)
2699# Add proxying or frame forward directives for a domain, if enabled
2700sub setup_nginx_proxy_pass
2701{
2702my ($d) = @_;
2703if (!$d->{'proxy_pass_mode'}) {
2704 return undef;
2705 }
2706elsif ($d->{'proxy_pass_mode'} == 1) {
2707 # Add proxy
2708 return &feature_create_web_balancer($d,
2709 { 'path' => '/', 'urls' => [ $d->{'proxy_pass'} ] });
2710 }
2711elsif ($d->{'proxy_pass_mode'} == 2) {
2712 # Add frame forward
2713 my $server = &find_domain_server($d);
2714 $server || return &text('redirect_efind', $d->{'dom'});
2715 &lock_all_config_files();
2716 &virtual_server::create_framefwd_file($d);
2717 my $ff = &virtual_server::framefwd_file($d);
2718 my $phd = &virtual_server::public_html_dir($d);
2719 $ff =~ s/^\Q$phd\E//;
2720 &save_directive($server, [ ],
2721 [ { 'name' => 'rewrite',
2722 'words' => [ '^/.*$', $ff, 'break' ] } ]);
2723 &flush_config_file_lines();
2724 &unlock_all_config_files();
2725 &virtual_server::register_post_action(\&print_apply_nginx);
2726 }
2727else {
2728 return "Unknown proxy mode $d->{'proxy_pass_mode'}";
2729 }
2730}
2731
2732# remove_nginx_proxy_pass(&domain)
2733# Remove enabled proxying or frame forward directives for a domain
2734sub remove_nginx_proxy_pass
2735{
2736my ($d) = @_;
2737if (!$d->{'proxy_pass_mode'}) {
2738 return undef;
2739 }
2740elsif ($d->{'proxy_pass_mode'} == 1) {
2741 # Remove proxy for /
2742 my @bals = &feature_list_web_balancers($d);
2743 my ($balancer) = grep { $_->{'path'} eq '/' } @bals;
2744 return &feature_delete_web_balancer($d, $balancer) if ($balancer);
2745 return undef;
2746 }
2747elsif ($d->{'proxy_pass_mode'} == 2) {
2748 # Remove frame forward
2749 my $server = &find_domain_server($d);
2750 $server || return &text('redirect_efind', $d->{'dom'});
2751 &lock_all_config_files();
2752 my $ff = &virtual_server::framefwd_file($d);
2753 my $phd = &virtual_server::public_html_dir($d);
2754 $ff =~ s/^\Q$phd\E//;
2755 my ($rewrite) = grep { $_->{'words'}->[0] eq '^/.*$' &&
2756 $_->{'words'}->[1] eq $ff }
2757 &find("rewrite", $server);
2758 if ($rewrite) {
2759 &save_directive($server, [ $rewrite ], [ ]);
2760 }
2761 &flush_config_file_lines();
2762 &unlock_all_config_files();
2763 &virtual_server::register_post_action(\&print_apply_nginx);
2764 return undef;
2765 }
2766else {
2767 return "Unknown proxy mode $d->{'proxy_pass_mode'}";
2768 }
2769}
2770
2771sub fix_server_name_line
2772{
2773my ($l, $adoms) = @_;
2774if ($l =~ /^(\s*)server_name(\s+.*);/) {
2775 # Exclude server_name entries for alias domains
2776 my $indent = $1;
2777 my @sa = &split_words($2);
2778 @sa = grep { !($adoms->{$_} ||
2779 /^([^\.]+)\.(\S+)/ && $adoms->{$2}) } @sa;
2780 return undef if (!@sa);
2781 $l = $indent."server_name ".&join_words(@sa).";";
2782 }
2783return $l;
2784}
2785
2786# feature_get_domain_web_config(domain-name, port)
2787sub feature_get_domain_web_config
2788{
2789my ($dname, $port) = @_;
2790my $conf = &get_config();
2791my $http = &find("http", $conf);
2792return undef if (!$http);
2793my @servers = &find("server", $http);
2794foreach my $s (@servers) {
2795 my $obj = &find("server_name", $s);
2796 foreach my $name (@{$obj->{'words'}}) {
2797 if (lc($name) eq lc($dname)) {
2798 return $s;
2799 }
2800 }
2801 }
2802return undef;
2803}
2804
28051;
2806