Handle hostnames with upper-case letters
[webmin.git] / servers / link.cgi
1 #!/usr/local/bin/perl
2 # link.cgi
3 # Forward the URL from path_info on to another webmin server
4
5 if ($ENV{'PATH_INFO'} =~ /^\/(\d+)\/([a-zA-Z0-9\-\/]+)\.(jar|class|gif|png)$/) {
6         # Allow fetches of Java classes and images without a referer header,
7         # as Java sometimes doesn't provide these
8         $trust_unknown_referers = 1;
9         }
10 use strict;
11 use warnings;
12 require './servers-lib.pl';
13 our (%text, %gconfig, %access, $module_name, %tconfig);
14 $ENV{'PATH_INFO'} =~ /^\/(\d+)(.*)$/ ||
15         &error("Bad PATH_INFO : $ENV{'PATH_INFO'}");
16 my $id = $1;
17 my $path = $2 ? &urlize("$2") : '/';
18 $path =~ s/^%2F/\//;
19 if ($ENV{'QUERY_STRING'}) {
20         $path .= '?'.$ENV{'QUERY_STRING'};
21         }
22 elsif (@ARGV) {
23         $path .= '?'.join('+', @ARGV);
24         }
25 my $s = &get_server($id);
26 &can_use_server($s) || &error($text{'link_ecannot'});
27 $access{'links'} || &error($text{'link_ecannot'});
28 my $url = "$gconfig{'webprefix'}/$module_name/link.cgi/$s->{'id'}";
29 $| = 1;
30 my $meth = $ENV{'REQUEST_METHOD'};
31 my %miniserv;
32 &get_miniserv_config(\%miniserv);
33
34 my ($user, $pass);
35 if ($s->{'autouser'}) {
36         # Login is variable .. check if we have it yet
37         if ($ENV{'HTTP_COOKIE'} =~ /$id=(\S+)/) {
38                 # Yes - set the login and password to use
39                 ($user, $pass) = split(/:/, &decode_base64("$1"));
40                 }
41         else {
42                 # No - need to display a login form
43                 &ui_print_header(undef, $text{'login_title'}, "");
44
45                 print &text('login_desc', "<tt>$s->{'host'}</tt>"),"<p>\n";
46
47                 print &ui_form_start(
48                         "$gconfig{'webprefix'}/$module_name/login.cgi", "post");
49                 print &ui_hidden("id", $id);
50
51                 print &ui_table_start($text{'login_header'}, undef, 2);
52                 print &ui_table_row($text{'login_user'},
53                         &ui_textbox("user", undef, 20));
54                 print &ui_table_row($text{'login_pass'},
55                         &ui_password("pass", undef, 20));
56                 print &ui_table_end();
57
58                 print &ui_form_end([ [ undef, $text{'login_login'} ] ]);
59
60                 &ui_print_footer("", $text{'index_return'});
61                 exit;
62                 }
63         }
64 elsif ($s->{'sameuser'}) {
65         # Login comes from this server
66         $user = $main::remote_user;
67         defined($main::remote_pass) || &error($text{'login_esame'});
68         $pass = $main::remote_pass;
69         }
70 else {
71         # Login is fixed
72         $user = $s->{'user'};
73         $pass = $s->{'pass'};
74         }
75
76 # Connect to the server
77 my $con = &make_http_connection($s->{'ip'} || $s->{'host'}, $s->{'port'},
78                                 $s->{'ssl'}, $meth, $path);
79 &error($con) if (!ref($con));
80
81 # Send request headers
82 &write_http_connection($con, "Host: $s->{'host'}\r\n");
83 &write_http_connection($con, "User-agent: Webmin\r\n");
84 my $auth = &encode_base64("$user:$pass");
85 $auth =~ s/\n//g;
86 &write_http_connection($con, "Authorization: basic $auth\r\n");
87 my ($http_host, $http_port);
88 if ($ENV{'HTTP_HOST'} =~ /^(\S+):(\d+)$/) {
89         # Browser supplies port
90         $http_host = $1;
91         $http_port = $2;
92         }
93 elsif ($ENV{'HTTP_HOST'}) {
94         # Browser only supplies host
95         $http_host = $ENV{'HTTP_HOST'};
96         $http_port = $ENV{'SERVER_PORT'} || $miniserv{'port'} || 80;
97         }
98 else {
99         # Web server supplies host and port
100         $http_host = $ENV{'SERVER_NAME'};
101         $http_port = $ENV{'SERVER_PORT'};
102         }
103 &write_http_connection($con, sprintf(
104                         "Webmin-servers: %s://%s:%d/%s\n",
105                         $ENV{'HTTPS'} eq "ON" ? "https" : "http",
106                         $http_host, $http_port,
107                         $tconfig{'inframe'} ? "" : "$module_name/"));
108 my $cl = $ENV{'CONTENT_LENGTH'};
109 &write_http_connection($con, "Content-length: $cl\r\n") if ($cl);
110 &write_http_connection($con, "Content-type: $ENV{'CONTENT_TYPE'}\r\n")
111         if ($ENV{'CONTENT_TYPE'});
112 &write_http_connection($con, "\r\n");
113 my $post;
114 if ($cl) {
115         &read_fully(\*STDIN, \$post, $cl);
116         &write_http_connection($con, $post);
117         }
118
119 # read back the headers
120 my $dummy = &read_http_connection($con);
121 my (%header, $headers);
122 while(1) {
123         my $headline;
124         ($headline = &read_http_connection($con)) =~ s/\r|\n//g;
125         last if (!$headline);
126         $headline =~ /^(\S+):\s+(.*)$/ || &error("Bad header");
127         $header{lc($1)} = $2;
128         $headers .= $headline."\n";
129         }
130
131 my $defport = $s->{'ssl'} ? 443 : 80;
132 if ($header{'location'} &&
133     ($header{'location'} =~ /^(http|https):\/\/$s->{'host'}:$s->{'port'}(.*)$/||
134      $header{'location'} =~ /^(http|https):\/\/$s->{'host'}(.*)/ &&
135      $s->{'port'} == $defport)) {
136         # fix a redirect
137         &redirect("$url$2");
138         exit;
139         }
140 elsif ($header{'www-authenticate'}) {
141         # Invalid login
142         if ($s->{'autouser'}) {
143                 print "Set-Cookie: $id=; path=/\n";
144                 &error(&text('link_eautologin', $s->{'host'},
145                      "$gconfig{'webprefix'}/$module_name/link.cgi/$id/"));
146                 }
147         else {
148                 &error(&text('link_elogin', $s->{'host'}, $user));
149                 }
150         }
151 else {
152         # just output the headers
153         print $headers,"\n";
154         }
155
156 # read back the rest of the page
157 if ($header{'content-type'} =~ /text\/html/ && !$header{'x-no-links'}) {
158         # Fix up HTML
159         while($_ = &read_http_connection($con)) {
160                 s/src='(\/[^']*)'/src='$url$1'/gi;
161                 s/src="(\/[^"]*)"/src="$url$1"/gi;
162                 s/src=(\/[^ "'>]*)/src=$url$1/gi;
163                 s/href='(\/[^']*)'/href='$url$1'/gi;
164                 s/href="(\/[^"]*)"/href="$url$1"/gi;
165                 s/href=(\/[^ >"']*)/href=$url$1/gi;
166                 s/action='(\/[^']*)'/action='$url$1'/gi;
167                 s/action="(\/[^"]*)"/action="$url$1"/gi;
168                 s/action=(\/[^ "'>]*)/action=$url$1/gi;
169                 s/\.location\s*=\s*'(\/[^']*)'/.location='$url$1'/gi;
170                 s/\.location\s*=\s*"(\/[^']*)"/.location="$url$1"/gi;
171                 s/window.open\("(\/[^"]*)"/window.open\("$url$1"/gi;
172                 s/name=return\s+value="(\/[^"]*)"/name=return value="$url$1"/gi;
173                 s/param\s+name=config\s+value='(\/[^']*)'/param name=config value='$url$1'/gi;
174                 s/param\s+name=config\s+value="(\/[^']*)"/param name=config value="$url$1"/gi;
175                 s/param\s+name=config\s+value=(\/[^']*)/param name=config value=$url$1/gi;
176                 print;
177                 }
178         }
179 elsif ($header{'content-type'} =~ /text\/css/ && !$header{'x-no-links'}) {
180         # Fix up CSS
181         while($_ = &read_http_connection($con)) {
182                 s/url\("(\/[^"]*)"\)/url\("$url$1"\)/gi;
183                 print;
184                 }
185         }
186 else {
187         # Just pass through
188         while(my $buf = &read_http_connection($con, 1024)) {
189                 print $buf;
190                 }
191         }
192 &close_http_connection($con);
193