Fix double-escaping of virtual host name
[webmin.git] / apache / index.cgi
1 #!/usr/local/bin/perl
2 # index.cgi
3 # Display a list of all virtual servers, and links for various types
4 # of global configuration
5
6 require './apache-lib.pl';
7 &ReadParse();
8
9 # check for the executable
10 if (!($httpd = &find_httpd())) {
11         &ui_print_header(undef, $text{'index_title'}, "", undef, 1, 1);
12         print &text('index_eserver', "<tt>$config{'httpd_path'}</tt>",
13                     "$gconfig{'webprefix'}/config.cgi?$module_name"),"<p>\n";
14         &foreign_require("software", "software-lib.pl");
15         $lnk = &software::missing_install_link("apache", $text{'index_apache'},
16                         "../$module_name/", $text{'index_title'});
17         print $lnk,"<p>\n" if ($lnk);
18         &ui_print_footer("/", $text{'index'});
19         exit;
20         }
21
22 # check for the base directory
23 if (!(-d $config{'httpd_dir'})) {
24         &ui_print_header(undef, $text{'index_title'}, "", undef, 1, 1);
25         print &text('index_eroot', "<tt>$config{'httpd_dir'}</tt>",
26                     "$gconfig{'webprefix'}/config.cgi?$module_name"),"<p>\n";
27         &ui_print_footer("/", $text{'index'});
28         exit;
29         }
30
31 # check if this is a new apache install, by looking for -dist files
32 $conf = "$config{'httpd_dir'}/etc";
33 if (!-d $conf) { $conf = "$config{'httpd_dir'}/conf"; }
34 if (!$config{'httpd_conf'} && !(-e "$conf/httpd.conf") &&
35     (-e "$conf/httpd.conf-dist") &&
36     (-d $conf)) {
37         # copy all the .dist files and fix up @@ServerRoot@@ references
38         # Only needed for apache versions < 1.3, which don't do this as part
39         # of the 'make install'
40         $sroot = $config{'httpd_dir'};
41         opendir(CONF, $conf);
42         foreach $f (readdir(CONF)) {
43                 if ($f =~ /^(.*)-dist$/) {
44                         open(DIST, "$conf/$f");
45                         @dist = <DIST>;
46                         close(DIST);
47                         &open_tempfile(REAL, ">$conf/$1");
48                         foreach (@dist) {
49                                 s/\/usr\/local\/etc\/httpd/$sroot/g;
50                                 s/\@\@ServerRoot\@\@/$sroot/g;
51                                 &print_tempfile(REAL, $_);
52                                 }
53                         &close_tempfile(REAL);
54                         }
55                 }
56         close(CONF);
57         }
58
59 # check for an -example file
60 if ($config{'httpd_conf'} && !-e $config{'httpd_conf'} &&
61     -e $config{'httpd_conf'}."-example") {
62         system("cp ".quotemeta($config{'httpd_conf'}."-example")." ".
63                      quotemeta($config{'httpd_conf'}));
64         }
65
66 # check for the httpd.conf file
67 ($htconf, $htconfchecked) = &find_httpd_conf();
68 if (!$htconf) {
69         # still doesn't exist!
70         &ui_print_header(undef, $text{'index_title'}, "", undef, 1, 1);
71         print "<p>\n";
72         print &text('index_econf', "<tt>$htconfchecked</tt>",
73                     "$gconfig{'webprefix'}/config.cgi?$module_name"),"<p>\n";
74         &ui_print_footer("/", $text{'index'});
75         exit;
76         }
77
78 # check for multiple port directives
79 $conf = &get_config();
80 @prt = &find_directive("Port", $conf);
81 if (@prt > 1 && $httpd_modules{'core'} < 1.3) {
82         &ui_print_header(undef, $text{'index_title'}, "", undef, 1, 1);
83         print "<p>\n";
84         print &text('index_eports', 'Port'),"<p>\n";
85         &ui_print_footer("/", $text{'index'});
86         exit;
87         }
88
89 # add default server
90 $defport = &find_directive("Port", $conf, 1);
91 if (&can_edit_virt()) {
92         local $sn = &find_directive("ServerName", $conf, 1);
93         push(@vidx, undef);
94         push(@vname, $text{'index_defserv'});
95         push(@vlink, "virt_index.cgi");
96         push(@vdesc, $text{'index_defdesc1'});
97         push(@vaddr, $text{'index_any'});
98         push(@vport, $text{'index_any'});
99         push(@vserv, $sn);
100         push(@vroot, &def(&find_directive("DocumentRoot", $conf, 1),
101                           $text{'index_auto'}));
102         push(@vproxy, undef);
103         $sn ||= &get_system_hostname();
104         push(@vurl, $defport ? "http://$sn:$defport/" : "http://$sn/");
105         $showing_default++;
106         }
107
108 # add other servers
109 @virt = &find_directive_struct("VirtualHost", $conf);
110 if ($httpd_modules{'core'} >= 1.3) {
111         # build list of name-based virtual host IP addresses
112         @nv = &find_directive("NameVirtualHost", $conf);
113         foreach $nv (@nv) {
114                 if ($nv =~ /:(\d+)$/) {
115                         push(@nvports, $1);
116                         }
117                 $nv =~ s/:\d+$//;
118                 $nv{$nv =~ /^\*/ ? "*" : &to_ipaddress($nv)}++;
119                 }
120         @nvports = &unique(@nvports);
121         }
122 elsif ($httpd_modules{'core'} >= 1.2) {
123         # only one name-based virtual host IP address - the default address
124         $ba = &find_directive("ServerName", $conf);
125         $nv{&to_ipaddress($ba ? $ba : &get_system_hostname())}++;
126         }
127 @virt = grep { &can_edit_virt($_) } @virt;
128 if ($config{'show_order'} == 1) {
129         # sort by server name
130         @virt = sort { &server_name_sort($a) cmp &server_name_sort($b) } @virt;
131         }
132 elsif ($config{'show_order'} == 2) {
133         # sort by IP address
134         @virt = sort { &server_ip_sort($a) cmp &server_ip_sort($b) } @virt;
135         }
136 foreach $v (@virt) {
137         $vm = $v->{'members'};
138         if ($v->{'words'}->[0] =~ /^\[(\S+)\]:(\d+)$/) {
139                 # IPv6 address and port
140                 $addr = $1;
141                 $port = $2;
142                 }
143         elsif ($v->{'words'}->[0] =~ /^(\S+):(\d+)$/) {
144                 # IPv4 address and port
145                 $addr = $1;
146                 $port = $2;
147                 }
148         else {
149                 # Address, perhaps v6, with default port
150                 $addr = $v->{'words'}->[0];
151                 $addr = $1 if ($addr =~ /^\[(\S+)\]$/);
152                 if ($httpd_modules{'core'} < 2.0) {
153                         $port = &def(&find_directive("Port", $conf), 80);
154                         }
155                 else {
156                         $port = "*";
157                         }
158                 }
159         $idx = &indexof($v, @$conf);
160         push(@vidx, $idx);
161         push(@vname, $text{'index_virt'});
162         push(@vlink, "virt_index.cgi?virt=$idx");
163         $sname = &find_directive("ServerName", $vm);
164         local $daddr = $addr eq "_default_" ||
165                        ($addr eq "*" && $httpd_modules{'core'} < 1.2);
166         if ($nv{"*"}) {
167                 if ($daddr) {
168                         push(@vdesc, &text('index_vnamed', "<tt>$sname</tt>"));
169                         }
170                 else {
171                         push(@vdesc, &text('index_vname', "<tt>$sname</tt>",
172                                            "<tt>$addr</tt>"));
173                         }
174                 }
175         elsif (!$daddr && $nv{&to_ipaddress($addr)}) {
176                 push(@vdesc, &text('index_vname', "<tt>$sname</tt>",
177                                    "<tt>$addr</tt>"));
178                 }
179         elsif (!$daddr && $addr eq "*") {
180                 push(@vdesc, &text('index_vnamed', "<tt>$sname</tt>"));
181                 }
182         elsif ($daddr && $port eq "*") {
183                 push(@vdesc, $text{'index_vdef'});
184                 $vdesc[0] = $text{'index_defdesc2'};
185                 }
186         elsif ($daddr) {
187                 push(@vdesc, &text('index_vport', $port));
188                 }
189         elsif ($port eq "*") {
190                 push(@vdesc, &text('index_vaddr', "<tt>$addr</tt>"));
191                 }
192         else {
193                 push(@vdesc, &text('index_vaddrport', "<tt>$addr</tt>", $port));
194                 }
195         push(@vaddr, $addr eq "_default_" || $addr eq "*" ? $text{'index_any'}
196                                                           : $addr);
197         push(@vport, $port eq "*" ? $text{'index_any'} : $port);
198         local $sn = &find_vdirective("ServerName", $vm, $conf, 1);
199         push(@vserv, $sn);
200         $pp = &find_directive_struct("ProxyPass", $vm);
201         if ($pp->{'words'}->[0] eq "/") {
202                 push(@vproxy, $pp->{'words'}->[1]);
203                 push(@vroot, undef);
204                 }
205         else {
206                 push(@vproxy, undef);
207                 push(@vroot, &def(&find_vdirective("DocumentRoot",$vm,$conf,1),
208                                   $text{'index_default'}));
209                 }
210         $cname{$v} = sprintf "%s:%s (%s)",
211                         $vserv[$#vserv] || $text{'index_auto'},
212                         $vport[$#vport],
213                         $vproxy[$#vproxy] || $vroot[$#vroot];
214         local $sp = $port eq "*" ? $defport : $port;
215         local $prot = "http";
216         if (&find_vdirective("SSLEngine", $vm, $conf, 1) eq "on") {
217                 $prot = "https";
218                 }
219         push(@vurl, $sp ? "$prot://$sn:$sp/" : "$prot://$sn/");
220         }
221
222 if (@vlink == 1 && !$access{'global'} && $access{'virts'} ne "*" &&
223     !$access{'create'} && $access{'noconfig'}) {
224         # Can only manage one vhost, so go direct to it
225         &redirect($vlink[0]);
226         exit;
227         }
228
229 # Page header
230 $ver = $httpd_modules{'core'};
231 $ver =~ s/^(\d+)\.(\d)(\d+)$/$1.$2.$3/;
232 &ui_print_header(undef, $text{'index_title'}, "", undef, 1, 1, undef,
233         &restart_button()."<br>".
234         &help_search_link("apache", "man", "doc", "google"), undef, undef,
235         &text('index_version', $ver));
236
237 # Show tabs
238 @tabs = map { [ $_, $text{'index_tab'.$_}, "index.cgi?mode=$_" ] }
239             ( $access{'global'} ? ( "global" ) : ( ),
240               "list",
241               $access{'create'} ? ( "create" ) : ( ) );
242 print &ui_tabs_start(\@tabs, "mode", $in{'mode'} || "list", 1);
243
244 # global config options
245 if ($access{'global'}) {
246         print &ui_tabs_start_tab("mode", "global");
247         #print $text{'index_descglobal'},"<p>\n";
248         if ($access{'global'} == 1) {
249                 $ht_icon = { "icon" => "images/dir.gif",
250                              "name" => $text{'htaccess_title'},
251                              "link" => "htaccess.cgi" };
252                 if (&can_configure_apache_modules()) {
253                         $rc_icon = { "icon" => "images/mods.gif",
254                                      "name" => $text{'mods_title'},
255                                      "link" => "edit_mods.cgi" };
256                         }
257                 elsif (!$config{'auto_mods'}) {
258                         $rc_icon = { "icon" => "images/recon.gif",
259                                      "name" => $text{'reconfig_title'},
260                                      "link" =>
261                                 "reconfig_form.cgi?size=$httpd_size&vol=1" };
262                         }
263                 $df_icon = { "icon" => "images/defines.gif",
264                              "name" => $text{'defines_title'},
265                              "link" => "edit_defines.cgi" };
266                 $ed_icon = { "icon" => "images/edit.gif",
267                              "name" => $text{'manual_configs'},
268                              "link" => "allmanual_form.cgi" };
269                 $ds_icon = { "icon" => "images/virt.gif",
270                              "name" => $text{'index_defserv'},
271                              "link" => "virt_index.cgi" };
272                 &config_icons("global", "edit_global.cgi?",
273                               $ht_icon, $rc_icon, $df_icon,
274                               $access{'types'} eq '*' &&
275                               $access{'virts'} eq '*' ? ( $ed_icon ) : ( ),
276                               $showing_default &&
277                               @vname > $config{'max_servers'} &&
278                               $config{'max_servers'} ? ( $ds_icon ) : ( ) );
279                 }
280         else {
281                 &icons_table([ "htaccess.cgi" ],
282                              [ $text{'htaccess_title'} ],
283                              [ "images/dir.gif" ]);
284                 }
285         print &ui_tabs_end_tab();
286         }
287
288 # work out select links
289 print &ui_tabs_start_tab("mode", "list");
290 #print $text{'index_desclist'},"<p>\n";
291 $showdel = $access{'vaddr'} && ($vidx[0] || $vidx[1]);
292 @links = ( );
293 if ($showdel) {
294         push(@links, &select_all_link("d"),
295                      &select_invert_link("d"));
296         }
297
298 # display servers
299 $formno = 0;
300 if ($config{'max_servers'} && @vname > $config{'max_servers'}) {
301         # as search form for people with lots and lots of servers
302         print "<b>$text{'index_toomany'}</b><p>\n";
303         print "<form action=search_virt.cgi>\n";
304         print "<b>$text{'index_find'}</b> <select name=field>\n";
305         print "<option value=name checked> $text{'index_name'}\n";
306         print "<option value=port> $text{'index_port'}\n";
307         print "<option value=addr> $text{'index_addr'}\n";
308         print "<option value=root> $text{'index_root'}\n";
309         print "</select> <select name=match>\n";
310         print "<option value=0 checked>$text{'index_equals'}\n";
311         print "<option value=1>$text{'index_matches'}\n";
312         print "<option value=2>$text{'index_nequals'}\n";
313         print "<option value=3>$text{'index_nmatches'}\n";
314         print "</select> <input name=what size=15>&nbsp;&nbsp;\n";
315         print "<input type=submit value=\"$text{'find'}\"></form><p>\n";
316         $formno++;
317         }
318 elsif ($config{'show_list'} && scalar(@vname)) {
319         # as list for people with lots of servers
320         if ($showdel) {
321                 print &ui_form_start("delete_vservs.cgi", "post");
322                 }
323         print &ui_links_row(\@links);
324         print &ui_columns_start([
325                 $showdel ? ( "" ) : ( ),
326                 $text{'index_type'},
327                 $text{'index_addr'},
328                 $text{'index_port'},
329                 $text{'index_name'},
330                 $text{'index_root'},
331                 $text{'index_url'} ], 100);
332         for($i=0; $i<@vname; $i++) {
333                 local @cols;
334                 push(@cols, "<a href=\"$vlink[$i]\">$vname[$i]</a>");
335                 push(@cols, &html_escape($vaddr[$i]));
336                 push(@cols, &html_escape($vport[$i]));
337                 push(@cols, $vserv[$i] || $text{'index_auto'});
338                 push(@cols, &html_escape($vproxy[$i]) ||
339                             &html_escape($vroot[$i]));
340                 push(@cols, "<a href=$vurl[$i]>$text{'index_view'}</a>");
341                 if ($showdel && $vidx[$i]) {
342                         print &ui_checked_columns_row(\@cols, undef,
343                                                       "d", $vidx[$i]);
344                         }
345                 elsif ($showdel) {
346                         print &ui_columns_row([ "", @cols ]);
347                         }
348                 else {
349                         print &ui_columns_row(\@cols);
350                         }
351                 }
352         print &ui_columns_end();
353         print &ui_links_row(\@links);
354         if ($showdel) {
355                 print &ui_form_end([ [ "delete", $text{'index_delete'} ] ]);
356                 }
357         }
358 else {
359         # as icons for niceness
360         if ($showdel) {
361                 print &ui_form_start("delete_vservs.cgi", "post");
362                 }
363         print &ui_links_row(\@links);
364         print "<table width=100% cellpadding=5>\n";
365         for($i=0; $i<@vname; $i++) {
366                 print "<tr> <td valign=top align=center nowrap>";
367                 &generate_icon("images/virt.gif", $vname[$i], $vlink[$i],
368                                undef, undef, undef,
369                                $vidx[$i] && $access{'vaddr'} ?
370                                         &ui_checkbox("d", $vidx[$i]) : "");
371                 print "</td> <td valign=top>\n";
372                 print "$vdesc[$i]<br>\n";
373                 print "<table width=100%><tr>\n";
374                 print "<td width=30%><b>$text{'index_addr'}</b> ",
375                       &html_escape($vaddr[$i]),"<br>\n";
376                 print "<b>$text{'index_port'}</b> ",
377                       &html_escape($vport[$i]),"</td>\n";
378                 print "<td width=70%><b>$text{'index_name'}</b> ",
379                       (&html_escape($vserv[$i]) || $text{'index_auto'}),
380                       "<br>\n";
381                 if ($vproxy[$i]) {
382                         print "<b>$text{'index_proxy'}</b> ",
383                               &html_escape($vproxy[$i]),"</td> </tr>\n";
384                         }
385                 else {
386                         print "<b>$text{'index_root'}</b> ",
387                               &html_escape($vroot[$i]),"</td> </tr>\n";
388                         }
389                 print "</table></td> </tr>\n";
390                 }
391         print "</table>\n";
392         print &ui_links_row(\@links);
393         if ($showdel) {
394                 print &ui_form_end([ [ "delete", $text{'index_delete'} ] ]);
395                 }
396         }
397 print &ui_tabs_end_tab();
398
399 if ($access{'create'}) {
400         print &ui_tabs_start_tab("mode", "create");
401         #print $text{'index_desccreate'},"<p>\n";
402         print &ui_form_start("create_virt.cgi");
403         print &ui_table_start($text{'index_create'}, undef, 2);
404
405         # IP address
406         print &ui_table_row($text{'index_newaddr'},
407                 &ui_radio("addr_def", 2,
408                   [ [ 1, "$text{'index_any1'}<br>" ],
409                     [ 2, "$text{'index_any2'}<br>" ],
410                     [ 0, $text{'index_any0'}." ".
411                          &ui_textbox("addr", undef, 40) ] ])."<br>\n".
412                 &ui_checkbox("nv", 1, $text{'index_nv'}, 1)."<br>".
413                 &ui_checkbox("listen", 1, $text{'index_listen'}, 1));
414
415         # Work out sensible default port
416
417         # Port
418         print &ui_table_row($text{'index_port'},
419                 &choice_input(@nvports ? 2 : 0,
420                               "port_mode", "0",
421                               "$text{'index_default'},0",
422                                 "$text{'index_any'},1", ",2").
423                 &ui_textbox("port", @nvports ? $nvports[0] : undef, 5));
424
425         # Document root
426         print &ui_table_row($text{'index_root'},
427                 &ui_textbox("root", undef, 40)." ".
428                 &file_chooser_button("root", 1, $formno)."<br>".
429                 &ui_checkbox("adddir", 1, $text{'index_adddir'}, 1));
430
431         # Server name
432         print &ui_table_row($text{'index_name'},
433                 &opt_input("", "name", $text{'index_auto'}, 30));
434
435         # Add to file
436         print &ui_table_row($text{'index_file'},
437                 &ui_radio("fmode", $config{'virt_file'} ? 1 : 0,
438                   [ [ 0, &text('index_fmode0', "<tt>httpd.conf</tt>")."<br>" ],
439                     $config{'virt_file'} ? (
440                       [ 1, &text(-d $config{'virt_file'} ? 'index_fmode1d'
441                                                       : 'index_fmode1',
442                               "<tt>$config{'virt_file'}</tt>")."<br>" ] ) : ( ),
443                     [ 2, $text{'index_fmode2'}." ".
444                          &ui_textbox("file", undef, 40)." ".
445                          &file_chooser_button("file", 0, $formno) ] ]));
446
447         # Server to clone
448         print &ui_table_row($text{'index_clone'},
449                 &ui_select("clone", undef,
450                   [ [ undef, $text{'index_noclone'} ],
451                     map { [ &indexof($_, @$conf), $cname{$_} ] } @virt ]));
452
453         print &ui_table_end();
454         print &ui_form_end([ [ undef, $text{'index_crnow'} ] ]);
455         print &ui_tabs_end_tab();
456         }
457
458 print &ui_tabs_end(1);
459
460 &ui_print_footer("/", $text{'index'});
461
462 sub server_name_sort
463 {
464 return &def(&find_vdirective("ServerName", $_[0]->{'members'}, $conf),
465             $_[0]->{'value'});
466 }
467
468 sub server_ip_sort
469 {
470 local $addr = $_[0]->{'words'}->[0] =~ /^(\S+):(\S+)/ ? $1 :
471                 $_[0]->{'words'}->[0];
472 return $addr eq '_default_' || $addr eq '*' ? undef :
473        &check_ipaddress($addr) ? $addr :
474        $addr =~ /^\[(\S+)\]$/ && &check_ip6address($1) ? $1 :
475                                  &to_ipaddress($addr);
476 }
477