Handle hostnames with upper-case letters
[webmin.git] / apache / create_virt.cgi
1 #!/usr/local/bin/perl
2 # create_virt.cgi
3 # Create a new virtual host.
4
5 require './apache-lib.pl';
6 &ReadParse();
7 $access{'create'} || &error($text{'cvirt_ecannot'});
8 &error_setup($text{'cvirt_err'});
9 $conf = &get_config();
10
11 # get directives from clone
12 if ($in{'clone'} ne '') {
13         $clone = $conf->[$in{'clone'}];
14         @cmems = grep { $_->{'name'} ne 'ServerName' &&
15                         $_->{'name'} ne 'Port' &&
16                         $_->{'name'} ne 'DocumentRoot' &&
17                         $_->{'name'} ne 'ServerAlias' } @{$clone->{'members'}};
18         }
19
20 # Parse and find the specified address to listen on
21 if ($in{'addr_def'} == 1) {
22         if ($httpd_modules{'core'} >= 1.2) { $addr = "_default_"; }
23         else { $addr = "*"; }
24         }
25 elsif ($in{'addr_def'} == 2) {
26         $addr = "*";
27         }
28 elsif ($in{'addr'} !~ /\S/) {
29         &error($text{'cvirt_eaddr1'});
30         }
31 else {
32         foreach $a (split(/\s+/, $in{'addr'})) {
33                 &to_ipaddress($a) || &to_ip6address($a) ||
34                         &error(&text('cvirt_eaddr2', $a));
35                 push(@addrs, &check_ip6address($a) ? "[$a]" : $a);
36                 }
37         $addr = join(" ", @addrs);
38         }
39
40 # Parse and find the specified port
41 $defport = &find_directive("Port", $conf) || 80;
42 if ($in{'port_mode'} == 0) {
43         $port = "";
44         $portnum = $defport;
45         }
46 elsif ($in{'port_mode'} == 1) {
47         $port = ":*";
48         $portnum = "*";
49         }
50 elsif ($in{'port'} !~ /^\d+$/) {
51         &error(&text('cvirt_eport', $in{'port'}));
52         }
53 else {
54         $port = ":$in{'port'}";
55         $portnum = $in{'port'};
56         }
57
58 if (!$in{'name_def'}) {
59         @names = split(/\s+/, $in{'name'});
60         @names || &error(&text('cvirt_ename', $in{'name'}));
61         }
62
63 # Check if the virtual server already exists
64 if (!$in{'name_def'}) {
65         $aclname = "$in{'name'}$port";
66         @virt = &find_directive_struct("VirtualHost", $conf);
67         foreach $v (@virt) {
68                 local ($clash) = grep { $_ eq $aclname } &virt_acl_name($v);
69                 $clash && &error($text{'cvirt_etaken'});
70                 }
71         }
72
73 # Check if the root directory is allowed
74 !$in{'root'} || &allowed_auth_file($in{'root'}) ||
75         &error(&text('cvirt_eroot3', $in{'root'}));
76
77 if ($in{'root'} && !-d $in{'root'}) {
78         # create the document root
79         mkdir($in{'root'}, 0755) ||
80                 &error(&text('cvirt_eroot2', $in{'root'}, $!));
81         $user = &find_directive("User", $conf);
82         $group = &find_directive("Group", $conf);
83         $uid = $user ? getpwnam($user) : 0;
84         $gid = $group ? getgrnam($group) : 0;
85         chown($uid, $gid, $in{'root'});
86         }
87
88 # find file to add to
89 if ($in{'fmode'} == 0) {
90         # Use the first file in config (usually httpd.conf)
91         $vconf = &get_virtual_config();
92         $f = $vconf->[0]->{'file'};
93         for($j=0; $vconf->[$j]->{'file'} eq $f; $j++) { }
94         $l = $vconf->[$j-1]->{'eline'}+1;
95         }
96 elsif ($in{'fmode'} == 1) {
97         # Use the standard file/directory for virtual hosts
98         local $vfile = &server_root($config{'virt_file'});
99         if (!-d $vfile) {
100                 # Just appending to a file
101                 $f = $vfile;
102                 }
103         elsif ($in{'name_def'}) {
104                 # No server name, so use webmin.UTIME.conf
105                 $linkfile = "webmin.".time().".conf";
106                 $f = "$vfile/$linkfile";
107                 }
108         else {
109                 # Work out a filename
110                 $tmpl = $config{'virt_name'} || '${DOM}.conf';
111                 %hash = ( 'dom' => $in{'name'},
112                           'ip' => $addr );
113                 $linkfile = &substitute_template($tmpl, \%hash);
114                 $f = "$vfile/$linkfile";
115                 }
116         }
117 else {
118         # Use a user-specified file
119         $f = $in{'file'};
120         }
121 -r $f || open(FILE, ">>$f") || &error(&text('cvirt_efile', &html_escape($f), $!));
122 close(FILE);
123
124 &lock_apache_files();
125 &lock_file($f);
126 &before_changing();
127  
128 # Check each IP address for a needed Listen and NameVirtualHost directive
129 foreach $a (@addrs) {
130         local $ip = &to_ipaddress($a);
131         if ($in{'listen'} && $ip) {
132                 # add Listen if needed
133                 local @listen = &find_directive("Listen", $conf);
134                 local $lfound;
135                 foreach $l (@listen) {
136                         if ($portnum eq "*") {
137                                 # Look for any Listen directive that would match the IP
138                                 $lfound++ if ($l eq "*" ||
139                                               $l =~ /^\d+$/ ||
140                                               ($l =~ /^(\S+):(\d+)$/ &&
141                                                &to_ipaddress("$1") eq $ip) ||
142                                               &to_ipaddress($l) eq $ip);
143                                 }
144                         else {
145                                 # Look for a Listen directive that would match
146                                 # the specified port and IP
147                                 $lfound++ if (($l eq '*' && $portnum == $defport) ||
148                                               ($l =~ /^\*:(\d+)$/ && $portnum == $1) ||
149                                               ($l =~ /^0\.0\.0\.0:(\d+)$/ && $portnum == $1) ||
150                                               ($l =~ /^\d+$/ && $portnum == $l) ||
151                                               ($l =~ /^(\S+):(\d+)$/ &&
152                                                &to_ipaddress("$1") eq $ip &&
153                                                $2 == $portnum) ||
154                                               (&to_ipaddress($l) eq $ip));
155                                 }
156                         }
157                 if (!$lfound && @listen > 0) {
158                         # Apache is listening on some IP addresses, but not the
159                         # entered one.
160                         local $lip;
161                         if ($httpd_modules{'core'} >= 2) {
162                                 $lip = $in{'port_mode'} == 2 ? "$ip:$in{'port'}"
163                                                              : "$ip:80";
164                                 }
165                         else {
166                                 $lip = $in{'port_mode'} == 2 ? "$ip:$in{'port'}" : $ip;
167                                 }
168                         &save_directive("Listen", [ @listen, $lip ], $conf, $conf);
169                         }
170                 }
171
172         # add NameVirtualHost if needed
173         if ($in{'nv'} && !$in{'addr_def'} && $ip) {
174                 local $found;
175                 local @nv = &find_directive("NameVirtualHost", $conf);
176                 foreach $nv (@nv) {
177                         $found++ if (&to_ipaddress($nv) eq $ip ||
178                                      $nv =~ /^(\S+):(\S+)/ &&
179                                       &to_ipaddress("$1") eq $ip ||
180                                      $nv eq '*' ||
181                                      $nv =~ /^\*:(\d+)$/ && $1 eq $portnum ||
182                                      $nv =~ /^0\.0\.0\.0:(\d+)$/ && $1 eq $portnum);
183                         }
184                 if (!$found) {
185                         &save_directive("NameVirtualHost", [ @nv, $ip ], $conf, $conf);
186                         }
187                 }
188         }
189
190 # Create the structure
191 if (@addrs) {
192         $ap = join(" ", map { $_.$port } @addrs);
193         }
194 else {
195         $ap = $addr.$port;
196         }
197 @mems = ( );
198 $virt = { 'name' => 'VirtualHost',
199           'value' => $ap,
200           'file' => $f,
201           'type' => 1,
202           'members' => \@mems };
203 push(@mems, { 'name' => 'DocumentRoot',
204               'value' => "\"$in{'root'}\"" }) if ($in{'root'});
205 if (@names) {
206         push(@mems, { 'name' => 'ServerName',
207                       'value' => $names[0] });
208         shift(@names);
209         foreach $sa (@names) {
210                 push(@mems, { 'name' => 'ServerAlias',
211                               'value' => $sa });
212                 }
213         }
214 push(@mems, @cmems);
215
216 if ($in{'adddir'} && $in{'root'}) {
217         # Add a <Directory> section for the root
218         push(@mems, { 'name' => 'Directory',
219                       'value' => "\"$in{'root'}\"",
220                       'type' => 1,
221                       'members' => [
222                         { 'name' => 'allow',
223                           'value' => 'from all' },
224                         { 'name' => 'Options',
225                           'value' => '+Indexes' },
226                         ] });
227         }
228
229 # Save to the file
230 &save_directive_struct(undef, $virt, $conf, $conf);
231 &flush_file_lines();
232 &unlock_file($f);
233 &unlock_apache_files();
234
235 # Create a symlink from another dir, if requested (as in Debian)
236 if ($linkfile) {
237         &create_webfile_link($f);
238         }
239
240 # Make sure it was really added
241 undef(@get_config_cache);
242 $conf = &get_config();
243 $found = 0;
244 foreach $v (&find_directive_struct("VirtualHost", $conf)) {
245         next if ($v->{'value'} ne $ap);
246         if (@names) {
247                 $nsn = &find_directive("ServerName", $v->{'members'});
248                 next if ($nsn ne $names[0]);
249                 }
250         $found = 1;
251         }
252 if (!$found) {
253         &rollback_apache_config();
254         &error(&text('cvirt_emissing', $f, "../config.cgi?$module_name"));
255         }
256
257 &after_changing();
258 &webmin_log("virt", "create", ($in{'name_def'} ? $addr : $in{'name'}).$port,
259             \%in);
260
261 # add to acl
262 if ($access{'virts'} ne '*') {
263         $access{'virts'} .= " $aclname";
264         &save_module_acl(\%access);
265         }
266 &redirect("");
267