Handle hostnames with upper-case letters
[webmin.git] / htaccess-htpasswd / save_dir.cgi
1 #!/usr/local/bin/perl
2 # save_dir.cgi
3 # Create or update a .htaccess file
4
5 require './htaccess-lib.pl';
6 &ReadParse();
7 $can_create || &error($text{'dir_ecannotcreate'});
8 @dirs = &list_directories();
9 &error_setup($text{'dir_err'});
10 &foreign_require($apachemod, "apache-lib.pl");
11
12 # Work out what .htaccess file to use
13 $in{'dir'} =~ s/\\/\//g;        # use forward slashes
14 if ($in{'new'} && $in{'dir'} !~ /^([a-z]:)?\//i && $default_dir ne "/") {
15         # Make path absolute
16         $in{'dir'} = "$default_dir/$in{'dir'}";
17         }
18 ($dir) = grep { $_->[0] eq $in{'dir'} } @dirs;
19 if ($in{'new'}) {
20         $dir && &error($text{'dir_eclash'});
21         $htaccess = "$in{'dir'}/$config{'htaccess'}";
22         }
23 else {
24         $htaccess = "$dir->[0]/$config{'htaccess'}";
25         }
26
27 # Check for button that redirects to the Apache module for editing all
28 # options in .htaccess file
29 if ($in{'apache'}) {
30         &redirect("../apache/htaccess_index.cgi?file=".
31                   &urlize($htaccess));
32         exit;
33         }
34
35 &lock_file($htaccess);
36 &lock_file($directories_file);
37
38 # Get the apache directives for the .htaccess file, if any
39 $authz = $apache::httpd_modules{'mod_auth_digest'} >= 2.2;
40 $auf = $in{'crypt'} == 3 && !$authz ? "AuthDigestFile" : "AuthUserFile";
41 $agf = $in{'crypt'} == 3 && !$authz ? "AuthDigestGroupFile" : "AuthGroupFile";
42 $conf = &foreign_call($apachemod, "get_htaccess_config", $htaccess);
43 $currfile = &foreign_call($apachemod, "find_directive",
44                           $auf, $conf, 1);
45 $currgfile = &foreign_call($apachemod, "find_directive",
46                            $agf, $conf, 1);
47 &lock_file($currfile) if ($currfile);
48
49 # Make sure it is allowed, and create new file if needed
50 &switch_user();
51 &can_access_dir($htaccess) || &error($text{'dir_ecannot'});
52 $missing = !-r $htaccess;
53 &open_tempfile(TEST, ">>$htaccess", 1) || &error(&text('dir_ehtaccess', $htaccess, $!));
54 &close_tempfile(TEST);
55 if ($missing) {
56         &set_ownership_permissions(
57                 undef, undef, oct($config{'perms'}) || 0644, $htaccess);
58         }
59
60 if ($in{'delete'} || $in{'remove'}) {
61         if ($in{'remove'}) {
62                 # Blow away .htaccess, htpasswd and htgroups
63                 &unlink_logged($htaccess);
64                 &unlink_logged($currfile) if ($currfile && !-d $currfile);
65                 &unlink_logged($currgfile) if ($currgfile && !-d $currgfile);
66                 }
67         else {
68                 # Take the authentication directives out of .htaccess
69                 &foreign_call($apachemod, "save_directive",
70                               "require", [ ], $conf, $conf);
71                 }
72         @dirs = grep { $_ ne $dir } @dirs;
73         }
74 else {
75         # Validate inputs
76         if ($in{'new'}) {
77                 $in{'dir'} =~ /^([a-z]:)?\// && -d $in{'dir'} ||
78                         &error($text{'dir_edir'});
79                 }
80
81         # Parse users file option
82         if (!$can_htpasswd) {
83                 # Users file is always automatic
84                 $file = $in{'new'} ? "$in{'dir'}/$config{'htpasswd'}"
85                                    : $dir->[1];
86                 }
87         elsif ($in{'auto'}) {
88                 # User choose for it to be automatic
89                 $file = "$in{'dir'}/$config{'htpasswd'}";
90                 }
91         else {
92                 # Entered by user
93                 $in{'file'} || &error($text{'dir_efile'});
94                 if ($in{'file'} !~ /^([a-z]:)?\//) {
95                         $file = "$in{'dir'}/$in{'file'}";
96                         }
97                 else {
98                         $file = $in{'file'};
99                         }
100                 }
101         -d $file && &error(&text('dir_efiledir', $file));
102
103         # Parse groups file option
104         if (!$can_htgroups) {
105                 # Groups file is always fixed, or none
106                 $gfile = $in{'new'} ? undef : $dir->[3];
107                 }
108         elsif ($in{'gauto'} == 2) {
109                 # No groups file
110                 $gfile = undef;
111                 }
112         elsif ($in{'gauto'} == 1) {
113                 # User choose for groups file to be automatic
114                 $gfile = "$in{'dir'}/$config{'htgroups'}";
115                 }
116         else {
117                 # Groups file was entered by user
118                 $in{'file'} || &error($text{'dir_egfile'});
119                 if ($in{'gfile'} !~ /^([a-z]:)?\//) {
120                         $gfile = "$in{'dir'}/$in{'gfile'}";
121                         }
122                 else {
123                         $gfile = $in{'gfile'};
124                         }
125                 }
126         -d $gfile && &error(&text('dir_egfiledir', $gfile));
127
128         # Parse require option
129         @require = ( $in{'require_mode'} );
130         if ($in{'require_mode'} eq "user") {
131                 @users = split(/\s+/, $in{'require_user'});
132                 @users || &error($text{'dir_erequire_user'});
133                 push(@require, @users);
134                 }
135         elsif ($in{'require_mode'} eq "group") {
136                 @groups = split(/\s+/, $in{'require_group'});
137                 @groups || &error($text{'dir_erequire_group'});
138                 push(@require, @groups);
139                 }
140
141         # Parse Webmin sync
142         $sync = join(",", grep { $in{'sync_'.$_} }
143                                ('create', 'update', 'delete'));
144         $sync ||= "-";
145
146         if ($in{'new'}) {
147                 # Either update an existing .htaccess to ensure that all
148                 # needed directives exist, or create from scratch
149
150                 # Use the existing users path if there is one, otherwise add
151                 $currfile = &foreign_call($apachemod, "find_directive",
152                                           $auf, $conf, 1);
153                 if ($currfile) {
154                         $file = $currfile;
155                         }
156                 else {
157                         &foreign_call($apachemod, "save_directive",
158                                       $auf, [ "\"$file\"" ], $conf, $conf);
159                         }
160
161                 # Use the existing groups path if there is one, otherwise add
162                 $currgfile = &foreign_call($apachemod, "find_directive",
163                                            $agf, $conf, 1);
164                 if ($currgfile) {
165                         $gfile = $currgfile;
166                         }
167                 elsif ($gfile) {
168                         &foreign_call($apachemod, "save_directive",
169                                       $agf, [ "\"$gfile\"" ], $conf,$conf);
170                         }
171
172                 # Add an auth type if needed
173                 $currtype = &foreign_call($apachemod, "find_directive",
174                                           "AuthType", $conf, 1);
175                 if (!$currtype) {
176                         &foreign_call($apachemod, "save_directive",
177                                      "AuthType",
178                                      [ $in{'crypt'} == 3 ? "Digest" : "Basic" ],
179                                      $conf, $conf);
180                         }
181
182                 # Add a realm if needed
183                 $currrealm = &foreign_call($apachemod, "find_directive",
184                                            "AuthName", $conf, 1);
185                 if (!$currrealm) {
186                         $in{'realm'} || &error($text{'dir_erealm'});
187                         &foreign_call($apachemod, "save_directive", "AuthName",
188                                       [ "\"$in{'realm'}\"" ], $conf, $conf);
189                         }
190
191                 # Add a require if needed
192                 $currrequire = &foreign_call($apachemod, "find_directive",
193                                              "require", $conf, 1);
194                 if (!$currrequire) {
195                         &foreign_call($apachemod, "save_directive",
196                                       "require", [ join(" ", @require) ],
197                                                 $conf, $conf);
198                         }
199
200                 # Add AuthDigestProvider if needed
201                 if ($authz && $in{'crypt'} == 3) {
202                         &foreign_call($apachemod, "save_directive",
203                                       "AuthDigestProvider",
204                                       [ "file" ], $conf, $conf);
205                         }
206
207                 # Add 'extra directives' if needed
208                 local $edline;
209                 foreach $edline (split(/\t+/, $config{'extra_directives'})) {
210                         local ($ed, $edval);
211                         $edline =~ m/(.*?)\s+(.*)/;
212                         ($ed, $edval) = ($1, $2);
213                         $curred = &foreign_call($apachemod, "find_directive",
214                                                          $ed, $conf);
215                         if (!$curred) {
216                                 &foreign_call($apachemod, "save_directive",
217                                           $ed, [$edval], $conf, $conf);
218                                 }
219                         }
220
221                 # Add to the known directories list
222                 $sync = "-" if (!$can_sync);
223                 $dir = [ $in{'dir'}, $file, $in{'crypt'}, $sync, $gfile ];
224                 push(@dirs, $dir);
225                 }
226         else {
227                 # Just update the users and groups file paths, realm and
228                 # require directive
229                 &foreign_call($apachemod, "save_directive",
230                               $auf, [ $file ],
231                                         $conf, $conf);
232                 &foreign_call($apachemod, "save_directive",
233                               $agf, $gfile ? [ $gfile ] : [ ],
234                                         $conf, $conf);
235                 &foreign_call($apachemod, "save_directive",
236                               "AuthName", [ "\"$in{'realm'}\"" ],
237                                         $conf, $conf);
238                 &foreign_call($apachemod, "save_directive",
239                               "require", [ join(" ", @require) ],
240                                         $conf, $conf);
241
242                 # Update the known directories list
243                 $dir->[1] = $file;
244                 $dir->[2] = $in{'crypt'};
245                 $dir->[3] = $sync if ($can_sync);
246                 $dir->[4] = $gfile;
247                 }
248
249         # Create an empty users file if needed
250         if (!-r $file) {
251                 &lock_file($file);
252                 &open_tempfile(FILE, ">$file", 1, 1) ||
253                         &error(&text('dir_ehtpasswd', $file, $!));
254                 &close_tempfile(FILE) ||
255                         &error(&text('dir_ehtpasswd', $file, $!));
256                 &unlock_file($file);
257                 &set_ownership_permissions(
258                         undef, undef, oct($config{'perms'}) || 0644, $file);
259                 }
260
261         # Create an empty groups file if needed
262         if ($gfile && !-r $gfile) {
263                 &lock_file($gfile);
264                 &open_tempfile(FILE, ">$gfile", 1, 1) ||
265                         &error(&text('dir_ehtgroup', $gfile, $!));
266                 &close_tempfile(FILE) ||
267                         &error(&text('dir_ehtgroup', $gfile, $!));
268                 &unlock_file($gfile);
269                 &set_ownership_permissions(
270                         undef, undef, oct($config{'perms'}) || 0644, $gfile);
271                 }
272         }
273
274 &flush_file_lines();
275 &switch_back();
276
277 &save_directories(\@dirs);
278 &unlock_all_files();
279 &webmin_log($in{'delete'} || $in{'remove'} ? "delete" :
280             $in{'new'} ? "create" : "modify",
281             "dir", $dir->[0]);
282 &redirect("");
283