Prevent XSS
[webmin.git] / chooser.cgi
1 #!/usr/local/bin/perl
2 # chooser.cgi
3 # Outputs HTML for a frame-based file chooser 
4
5 @icon_map = (   "c", "text.gif",
6                 "txt", "text.gif",
7                 "pl", "text.gif",
8                 "cgi", "text.gif",
9                 "html", "text.gif",
10                 "htm", "text.gif",
11                 "gif", "image.gif",
12                 "jpg", "image.gif",
13                 "tar", "binary.gif"
14                 );
15
16 $trust_unknown_referers = 1;
17 require (-r './web-lib.pl' ? './web-lib.pl' : '../web-lib.pl');
18 require (-r './ui-lib.pl' ? './ui-lib.pl' : '../ui-lib.pl');
19 &init_config();
20 %access = &get_module_acl();
21
22 # Work out root directory
23 if (!$access{'root'}) {
24         local @uinfo = getpwnam($remote_user);
25         $rootdir = $uinfo[7] ? $uinfo[7] : "/";
26         }
27 else {
28         $rootdir = $access{'root'};
29         }
30
31 # Switch to correct Unix user
32 if (&supports_users()) {
33         if (&get_product_name() eq 'usermin') {
34                 # Always run as Usermin login
35                 &switch_to_remote_user();
36                 }
37         else {
38                 # ACL determines
39                 $fileunix = $access{'fileunix'} || $remote_user;
40                 @uinfo = getpwnam($fileunix);
41                 if (@uinfo) {
42                         ($(, $)) = ( $uinfo[3],
43                                      "$uinfo[3] ".join(" ", $uinfo[3],
44                                                    &other_groups($uinfo[0])) );
45                         ($>, $<) = ( $uinfo[2], $uinfo[2] );
46                         }
47                 }
48         }
49
50 &ReadParse(undef, undef, 1);
51 if ($gconfig{'os_type'} eq 'windows') {
52         # On Windows, chroot should be empty if not use, and default path
53         # should be c:/
54         if ($in{'chroot'} eq "/") {
55                 $in{'chroot'} = "";
56                 }
57         if ($rootdir eq "/") {
58                 $rootdir = "c:";
59                 }
60         }
61 if ($in{'add'}) {
62         # Only use last filename by default
63         $in{'file'} =~ s/\s+$//;
64         if ($in{'file'} =~ /\n(.*)$/) {
65                 $in{'file'} = $1;
66                 }
67         }
68 if ($in{'file'} =~ /^(([a-z]:)?.*\/)([^\/]*)$/i && $in{'file'} !~ /\.\./) {
69         # File entered is valid
70         $dir = $1;
71         $file = $3;
72         }
73 else {
74         # Fall back to default
75         $dir = $rootdir;
76         $dir .= '/' if ($dir !~ /\/$/);
77         $file = "";
78         }
79 $add = int($in{'add'});
80
81 if (!(-d $in{'chroot'}.$dir)) {
82         # Entered directory does not exist
83         $dir = $rootdir.'/';
84         $file = "";
85         }
86 if (!&allowed_dir($dir)) {
87         # Directory is outside allowed root
88         $dir = $rootdir.'/';
89         $file = "";
90         }
91
92 # Work out the top allowed dir
93 $topdir = $rootdir eq "/" || $rootdir eq "c:" ? $rootdir :
94           $access{'otherdirs'} ? "/" : $rootdir;
95 $uchroot = &urlize($in{'chroot'});
96 $utype = &urlize($in{'type'});
97 $ufile = &urlize($in{'file'});
98
99 if ($in{'frame'} == 0) {
100         # base frame
101         &PrintHeader();
102         if ($in{'type'} == 0) {
103                 print "<title>$text{'chooser_title1'}</title>\n";
104                 }
105         elsif ($in{'type'} == 1) {
106                 print "<title>$text{'chooser_title2'}</title>\n";
107                 }
108         print "<frameset rows='*,50'>\n";
109         print "<frame marginwidth=5 marginheight=5 name=topframe ",
110              "src=\"chooser.cgi?frame=1&file=".$ufile.
111              "&chroot=".$uchroot."&type=".$utype."&add=$add\">\n";
112         print "<frame marginwidth=0 marginheight=0 name=bottomframe ",
113               "src=\"chooser.cgi?frame=2&file=".$ufile.
114               "&chroot=".$uchroot."&type=".$utype."&add=$add\" scrolling=no>\n";
115         print "</frameset>\n";
116         }
117 elsif ($in{'frame'} == 1) {
118         # List of files in this directory
119         &popup_header();
120         print <<EOF;
121 <script>
122 function fileclick(f, d)
123 {
124 curr = top.frames[1].document.forms[0].elements[1].value;
125 if (curr == f) {
126         // Double-click! Enter directory or select file
127         if (d) {
128                 // Enter this directory
129                 location = "chooser.cgi?frame=1&add=$add&chroot=$uchroot&type=$utype&file="+f+"/";
130                 }
131         else {
132                 // Select this file and close the window
133                 if ($add == 0) {
134                         top.opener.ifield.value = f;
135                         }
136                 else {
137                         if (top.opener.ifield.value != "") {
138                                 top.opener.ifield.value += "\\n";
139                                 }
140                         top.opener.ifield.value += f;
141                         }
142                 top.close();
143                 }
144         }
145 else {
146         top.frames[1].document.forms[0].elements[1].value = f;
147         }
148 }
149
150 function parentdir(p)
151 {
152 top.frames[1].document.forms[0].elements[1].value = p;
153 location = "chooser.cgi?frame=1&chroot=$uchroot&type=$utype&file="+p;
154 }
155 </script>
156 EOF
157
158         print "<b>",&text('chooser_dir', &html_escape($dir)),"</b>\n";
159         opendir(DIR, $in{'chroot'}.$dir) ||
160                 &popup_error(&text('chooser_eopen', "$!"));
161         print &ui_columns_start(undef, 100);
162         foreach $f (sort { $a cmp $b } readdir(DIR)) {
163                 $path = "$in{'chroot'}$dir$f";
164                 if ($f eq ".") { next; }
165                 if ($f eq ".." && ($dir eq "/" || $dir eq $topdir.'/')) { next; }
166                 if ($f =~ /^\./ && $f ne ".." && $access{'nodot'}) { next; }
167                 if (!(-d $path) && $in{'type'} == 1) { next; }
168
169                 @st = stat($path);
170                 $isdir = 0; undef($icon);
171                 if (-d $path) { $icon = "dir.gif"; $isdir = 1; }
172                 elsif ($path =~ /\.([^\.\/]+)$/) { $icon = $icon_map{$1}; }
173                 if (!$icon) { $icon = "unknown.gif"; }
174
175                 if ($f eq "..") {
176                         $dir =~ /^(.*\/)[^\/]+\/$/;
177                         $link = "<a href=\"\" onClick='parentdir(\"".&html_escape(quotemeta($1))."\"); return false'>";
178                         }
179                 else {
180                         $link = "<a href=\"\" onClick='fileclick(\"".&html_escape(quotemeta("$dir$f"))."\", $isdir); return false'>";
181                         }
182                 local @cols;
183                 push(@cols, "$link<img border=0 src=$gconfig{'webprefix'}/images/$icon></a>");
184                 push(@cols, "$link".&html_escape($f)."</a>");
185                 push(@cols, &nice_size($st[7]));
186                 @tm = localtime($st[9]);
187                 push(@cols, sprintf "<tt>%.2d/%s/%.4d</tt>",
188                         $tm[3], $text{'smonth_'.($tm[4]+1)}, $tm[5]+1900);
189                 push(@cols, sprintf "<tt>%.2d:%.2d</tt>", $tm[2], $tm[1]);
190                 print &ui_columns_row(\@cols);
191                 }
192         closedir(DIR);
193         print &ui_columns_end();
194         &popup_footer();
195         }
196 elsif ($in{'frame'} == 2) {
197         # Current file and OK/cancel buttons
198         &popup_header();
199         print <<EOF;
200 <script>
201 function filechosen()
202 {
203 if ($add == 0) {
204         top.opener.ifield.value = document.forms[0].path.value;
205         }
206 else {
207         if (top.opener.ifield.value != "") {
208                 top.opener.ifield.value += "\\n";
209                 }
210         top.opener.ifield.value += document.forms[0].path.value;
211         }
212 top.close();
213 }
214 </script>
215 EOF
216         print &ui_form_start(undef, undef, undef,
217                 "onSubmit='filechosen(); return false'");
218         print &ui_table_start(undef, "width=100%", 2);
219         print &ui_table_row(undef,
220                 &ui_submit($text{'chooser_ok'})." ".
221                 &ui_textbox("path", $dir.$file, 45, 0, undef,
222                             "style='width:90%'"), 2);
223         print &ui_table_end();
224         print &ui_form_end();
225         &popup_footer();
226         }
227
228 # allowed_dir(dir)
229 # Returns 1 if some directory should be listable
230 sub allowed_dir
231 {
232 local ($dir) = @_;
233 return 1 if ($rootdir eq "" || $rootdir eq "/" || $rootdir eq "c:");
234 foreach my $allowed ($rootdir, split(/\t+/, $access{'otherdirs'})) {
235         return 1 if (&is_under_directory($allowed, $dir));
236         }
237 return 0;
238 }