Alternc  latest
Alternc logiel libre pour l'hébergement
m_hta.php
Go to the documentation of this file.
1 <?php
2 
3 /*
4  ----------------------------------------------------------------------
5  LICENSE
6 
7  This program is free software; you can redistribute it and/or
8  modify it under the terms of the GNU General Public License (GPL)
9  as published by the Free Software Foundation; either version 2
10  of the License, or (at your option) any later version.
11 
12  This program is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  GNU General Public License for more details.
16 
17  To read the license please visit http://www.gnu.org/copyleft/gpl.html
18  ----------------------------------------------------------------------
19 */
20 
21 /**
22  * This class handle folder web restricted access through .htaccess/.htpassword
23  * files.
24  *
25  * @copyright AlternC-Team 2000-2017 https://alternc.com/
26  */
27 class m_hta {
28 
29 
30  /**
31  * Password kind used in this class (hook for admin class)
32  * @return array
33  */
35  return array("hta" => "Protected folders passwords");
36  }
37 
38 
39  /**
40  * hook called by menu class to add a menu
41  * to the left panel
42  * @return array
43  */
44  function hook_menu() {
45  $obj = array(
46  'title' => _("Protected folders"),
47  'link' => 'hta_list.php',
48  'pos' => 50,
49  );
50 
51  return $obj;
52  }
53 
54 
55  /**
56  * Create a protected folder (.htaccess et .htpasswd)
57  * @param string $dir Folder to protect (relative to user root)
58  * @return boolean TRUE if the folder has been protected, or FALSE if an error occurred
59  *
60  * @global m_mem $mem
61  * @global m_bro $bro
62  * @global m_messages $msg
63  * @param string $dir
64  * @return boolean
65  */
66  function CreateDir($dir) {
67  global $bro, $msg;
68  $msg->log("hta", "createdir", $dir);
69  $absolute = $bro->convertabsolute($dir, 0);
70  if (!is_dir($absolute)) {
71  $msg->raise("ERROR", "hta", _("The folder '%s' does not exist"), $dir);
72  return false;
73  }
74  if (!file_exists("$absolute/.htaccess")) {
75  $file = @fopen("$absolute/.htaccess", "w+");
76  if (!$file) {
77  $msg->raise("ERROR", "hta", _("Error creating .htaccess file: ") . error_get_last()['message']);
78  return false;
79  }
80  fseek($file, 0);
81  $param = "AuthUserFile \"$absolute/.htpasswd\"\nAuthName \"" . _("Restricted area") . "\"\nAuthType Basic\nrequire valid-user\n";
82  fwrite($file, $param);
83  fclose($file);
84  }
85  if (!file_exists("$absolute/.htpasswd")) {
86  if (!@touch("$absolute/.htpasswd")) {
87  $msg->raise("ERROR", "hta", _("Error creating .htpasswd file: ") . error_get_last()['message']);
88  return false;
89  }
90  return true;
91  }
92  return true;
93  }
94 
95 
96  /**
97  * Returns the list of all user folder currently protected by a .htpasswd file
98  *
99  * @global m_messages $msg
100  * @global m_mem $mem
101  * @return array Array containing user folder list
102  */
103  function ListDir() {
104  global$msg, $mem;
105  $msg->debug("hta", "listdir");
106  $sortie = array();
107  $absolute = ALTERNC_HTML . "/" . substr($mem->user["login"], 0, 1) . "/" . $mem->user["login"];
108  exec("find " . escapeshellarg($absolute) . " -name .htpasswd|sort", $sortie);
109  if (!count($sortie)) {
110  $msg->raise("INFO", "hta", _("No protected folder"));
111  return false;
112  }
113  $pattern = "/^" . preg_quote(ALTERNC_HTML, "/") . "\/.\/[^\/]*\/(.*)\/\.htpasswd/";
114 
115  $r = array();
116  for ($i = 0; $i < count($sortie); $i++) {
117  $matches = array();
118  preg_match($pattern, $sortie[$i], $matches);
119  $tmpm = isset($matches[1]) ? '/' . $matches[1] : '';
120  $r[$i] = $tmpm . "/";
121  }
122  return $r;
123  }
124 
125 
126  /**
127  * Tells if a folder is protected.
128  *
129  * @global m_mem $mem
130  * @global m_messages $msg
131  * @param string $dir Folder to check
132  * @return boolean If the folder is protected, or FALSE if it is not
133  */
134  function is_protected($dir) {
135  global $mem, $msg;
136  $msg->debug("hta", "is_protected", $dir);
137  $absolute = ALTERNC_HTML . "/" . substr($mem->user["login"], 0, 1) . "/" . $mem->user["login"] . "/$dir";
138  if (file_exists("$absolute/.htpasswd")) {
139  return true;
140  } else {
141  return false;
142  }
143  }
144 
145 
146  /**
147  * Returns the list of login for a protected folder.
148  *
149  * @global m_mem $mem
150  * @global m_messages $msg
151  * @param string $dir The folder to lookup (relative to user root)
152  * @return array An array containing the list of logins from the .htpasswd file, or FALSE
153  */
154  function get_hta_detail($dir) {
155  global $mem, $msg;
156  $msg->debug("hta", "get_hta_detail");
157  $absolute = ALTERNC_HTML . "/" . substr($mem->user["login"], 0, 1) . "/" . $mem->user["login"] . "/$dir";
158  if (file_exists("$absolute/.htaccess")) {
159  /* if (!_reading_htaccess($absolute)) {
160  return false;
161  }
162  */
163  }
164  $file = @fopen("$absolute/.htpasswd", "r");
165  $i = 0;
166  $res = array();
167  if (!$file) {
168  return false;
169  }
170  // TODO: Test the validity of a .htpasswd
171  while (!feof($file)) {
172  $s = fgets($file, 1024);
173  $t = explode(":", $s);
174  if ($t[0] != $s) {
175  $res[$i] = $t[0];
176  $i = $i + 1;
177  }
178  }
179  fclose($file);
180  return $res;
181  }
182 
183 
184  /**
185  * Unprotect a folder
186  *
187  * @global m_mem $mem
188  * @global m_bro $bro
189  * @global m_messages $msg
190  * @param string $dir Folder to unprotect, relative to user root
191  * @param boolean $skip For testing purpose mainly, skips the full user path search
192  * @return boolean TRUE if the folder has been unprotected, or FALSE if an error occurred
193  */
194  function DelDir($dir, $skip = false) {
195  global $bro, $msg;
196  $msg->log("hta", "deldir", $dir);
197  $dir = $bro->convertabsolute($dir, $skip);
198  if (!$dir) {
199  $msg->raise("ERROR", "hta", ("The folder '%s' does not exist"), $dir);
200  return false;
201  }
202  $htaccess_file = "$dir/.htaccess";
203  if (!is_readable($htaccess_file)) {
204  $msg->raise("ERROR", "hta", _("I cannot read the file '%s'"), $htaccess_file);
205  }
206  $fileLines = file($htaccess_file);
207  $patternList = array(
208  "AuthUserFile.*$",
209  "AuthName.*$",
210  "AuthType Basic.*$",
211  "require valid-user.*$"
212  );
213  $count_lines = 0;
214  foreach ($fileLines as $key => $line) {
215  foreach ($patternList as $pattern) {
216  if (preg_match("/" . $pattern . "/", $line)) {
217  $count_lines++;
218  unset($fileLines[$key]);
219  }
220  }
221  }
222  // If no changes
223  if (!$count_lines) {
224  $msg->raise("ALERT", "hta", _("Unexpected: No changes made to '%s'"), $htaccess_file);
225  }
226  // If file is empty, remove it
227  if (!count($fileLines)) {
228  if (!unlink($htaccess_file)) {
229  $msg->raise("ERROR", "hta", _("I could not delete the file '%s'"), $htaccess_file);
230  }
231  } else {
232  file_put_contents($htaccess_file, implode("\n", $fileLines));
233  }
234  $htpasswd_file = "$dir/.htpasswd";
235  if (!is_writable($htpasswd_file)) {
236  $msg->raise("ERROR", "hta", _("I cannot read the file '%s'"), $htpasswd_file);
237  } else if (!unlink($htpasswd_file)) {
238  $msg->raise("ERROR", "hta", _("I cannot delete the file '%s/.htpasswd'"), $dir);
239  return false;
240  }
241 
242  return true;
243  }
244 
245 
246  /**
247  * Add a user to a protected folder
248  *
249  * @global m_messages $msg
250  * @global m_bro $bro
251  * @global m_admin $admin
252  * @param string $user
253  * @param string $password
254  * @param string $dir
255  * @param string $password The password to add (cleartext)
256  * @param string $dir The folder we add it to (relative to user root).
257  * @return boolean TRUE if the user has been added, or FALSE if an error occurred
258  */
259  function add_user($user, $password, $dir) {
260  global $msg, $bro, $admin;
261  $msg->log("hta", "add_user", $user . "/" . $dir);
262  if (empty($user)) {
263  $msg->raise("ERROR", 'hta', _("Please enter a user"));
264  return false;
265  }
266  if (empty($password)) {
267  $msg->raise("ERROR", 'hta', _("Please enter a password"));
268  return false;
269  }
270  $absolute = $bro->convertabsolute($dir, 0);
271  if (!file_exists($absolute)) {
272  $msg->raise("ERROR", "hta", _("The folder '%s' does not exist"), $dir);
273  return false;
274  }
275  // @todo delete cf!. functions.php checkloginemail definition
276  if (checkloginmail($user)) {
277  // Check this password against the password policy using common API :
278  if (is_callable(array($admin, "checkPolicy"))) {
279  if (!$admin->checkPolicy("hta", $user, $password)) {
280  return false; // The error has been raised by checkPolicy()
281  }
282  }
283 
284  $file = @fopen("$absolute/.htpasswd", "a+");
285  if (!$file) {
286  $msg->raise("ERROR", "hta", _("File already exist"));
287  return false;
288  }
289  fseek($file, 0);
290  while (!feof($file)) {
291  $s = fgets($file, 1024);
292  $t = explode(":", $s);
293  if ($t[0] == $user) {
294  $msg->raise("ERROR", "hta", _("The user '%s' already exist for this folder"), $user);
295  return false;
296  }
297  }
298  fseek($file, SEEK_END);
299  if (empty($t[1]) || substr($t[1], -1) != "\n") {
300  fwrite($file, "\n");
301  }
302  fwrite($file, "$user:" . _md5cr($password) . "\n");
303  fclose($file);
304  return true;
305  } else {
306  $msg->raise("ERROR", "hta", _("Please enter a valid username"));
307  return false;
308  }
309  }
310 
311 
312  /**
313  * Delete a user from a protected folder.
314  *
315  * @global m_bro $bro
316  * @global m_messages $msg
317  * @param array $lst An array with login to delete.
318  * @param string $dir The folder, relative to user root, where we want to delete users.
319  * @return boolean TRUE if users has been deleted, or FALSE if an error occurred.
320  */
321  function del_user($lst, $dir) {
322  global $bro, $msg;
323  $msg->log("hta", "del_user", $lst . "/" . $dir);
324  $absolute = $bro->convertabsolute($dir, 0);
325  if (!file_exists($absolute)) {
326  $msg->raise("ERROR", "hta", _("The folder '%s' does not exist"), $dir);
327  return false;
328  }
329  touch("$absolute/.htpasswd.new");
330  $file = fopen("$absolute/.htpasswd", "r");
331  $newf = fopen("$absolute/.htpasswd.new", "a");
332  if (!$file || !$newf) {
333  $msg->raise("ERROR", "hta", _("File already exist"));
334  return false;
335  }
336  reset($lst);
337  fseek($file, 0);
338  while (!feof($file)) {
339  $s = fgets($file, 1024);
340  $t = explode(":", $s);
341  if (!in_array($t[0], $lst) && ($t[0] != "\n")) {
342  fseek($newf, 0);
343  fwrite($newf, "$s");
344  }
345  }
346  fclose($file);
347  fclose($newf);
348  unlink("$absolute/.htpasswd");
349  rename("$absolute/.htpasswd.new", "$absolute/.htpasswd");
350  return true;
351  }
352 
353 
354  /**
355  * Change the password of a user in a protected folder
356  * @param string $user The users whose password should be changed
357  * @param string $newpass The new password of this user
358  * @param string $dir The folder, relative to user root, in which we will change a password
359  * @return boolean TRUE if the password has been changed, or FALSE if an error occurred
360  */
361  function change_pass($user, $newpass, $dir) {
362  global $bro, $msg, $admin;
363  $msg->log("hta", "change_pass", $user . "/" . $dir);
364  $absolute = $bro->convertabsolute($dir, 0);
365  if (!file_exists($absolute)) {
366  $msg->raise("ERROR", "hta", _("The folder '%s' does not exist"), $dir);
367  return false;
368  }
369 
370  // Check this password against the password policy using common API :
371  if (is_callable(array($admin, "checkPolicy"))) {
372  if (!$admin->checkPolicy("hta", $user, $newpass)) {
373  return false; // The error has been raised by checkPolicy()
374  }
375  }
376 
377  touch("$absolute/.htpasswd.new");
378  $file = fopen("$absolute/.htpasswd", "r");
379  $newf = fopen("$absolute/.htpasswd.new", "a");
380  if (!$file || !$newf) {
381  $msg->raise("ERROR", "hta", _("File already exist"));
382  return false;
383  }
384  while (!feof($file)) {
385  $s = fgets($file, 1024);
386  $t = explode(":", $s);
387  if ($t[0] != $user) {
388  fwrite($newf, "$s");
389  }
390  }
391  fwrite($newf, "$user:" . _md5cr($newpass) . "\n");
392  fclose($file);
393  fclose($newf);
394  unlink("$absolute/.htpasswd");
395  rename("$absolute/.htpasswd.new", "$absolute/.htpasswd");
396  return true;
397  }
398 
399 
400  /**
401  * Check that a .htaccess file is valid (for authentication)
402  *
403  * @global m_messages $msg
404  * @param type $absolute
405  * @param string $absolute Folder we want to check (relative to user root)
406  * @return boolean TRUE is the .htaccess is protecting this folder, or FALSE else
407  */
408  private function _reading_htaccess($absolute) {
409  global $msg;
410  $msg->debug("hta", "_reading_htaccess", $absolute);
411  $file = fopen("$absolute/.htaccess", "r+");
412  $lignes = array(1, 1, 1);
413  $errr = 0;
414  if (!$file) {
415  return false;
416  }
417  while (!feof($file) && !$errr) {
418  $s = fgets($file, 1024);
419  if (substr($s, 0, 12) != "RewriteCond " && substr($s, 0, 14) != "ErrorDocument " && substr($s, 0, 12) != "RewriteRule " && substr($s, 0, 14) != "RewriteEngine " && trim($s) != "") {
420  $errr = 1;
421  }
422  if (strtolower(trim($s)) == strtolower("authuserfile $absolute/.htpasswd")) {
423  $lignes[0] = 0;
424  $errr = 0;
425  } // authuserfile
426  if (strtolower(trim($s)) == "require valid-user") {
427  $lignes[1] = 0;
428  $errr = 0;
429  } //require
430  if (strtolower(trim($s)) == "authtype basic") {
431  $lignes[2] = 0;
432  $errr = 0;
433  } //authtype
434  } // Reading config file
435  fclose($file);
436  if ($errr || in_array(0, $lignes)) {
437  $msg->raise("ERROR", "hta", _("An incompatible .htaccess file exists in this folder"));
438  return false;
439  }
440  return true;
441  }
442 
443 } /* class m_hta */
444 
$mem
Definition: bootstrap.php:71
$msg
Definition: bootstrap.php:75
const ALTERNC_HTML
Definition: bootstrap.php:10
$r
Definition: aws_add.php:75
$res
Definition: index.php:111
This class handle folder web restricted access through .htaccess/.htpassword files.
Definition: m_hta.php:27
alternc_password_policy()
Password kind used in this class (hook for admin class)
Definition: m_hta.php:34
is_protected($dir)
Tells if a folder is protected.
Definition: m_hta.php:134
hook_menu()
hook called by menu class to add a menu to the left panel
Definition: m_hta.php:44
add_user($user, $password, $dir)
Add a user to a protected folder.
Definition: m_hta.php:259
del_user($lst, $dir)
Delete a user from a protected folder.
Definition: m_hta.php:321
change_pass($user, $newpass, $dir)
Change the password of a user in a protected folder.
Definition: m_hta.php:361
DelDir($dir, $skip=false)
Unprotect a folder.
Definition: m_hta.php:194
get_hta_detail($dir)
Returns the list of login for a protected folder.
Definition: m_hta.php:154
CreateDir($dir)
Create a protected folder (.htaccess et .htpasswd)
Definition: m_hta.php:66
_reading_htaccess($absolute)
Check that a .htaccess file is valid (for authentication)
Definition: m_hta.php:408
ListDir()
Returns the list of all user folder currently protected by a .htpasswd file.
Definition: m_hta.php:103
checkloginmail($mail)
Check a login mail, cf http://www.bortzmeyer.org/arreter-d-interdire-des-adresses-legales....
Definition: functions.php:233
_md5cr($pass, $salt="")
Hashe a password using proper crypto function.
Definition: functions.php:533
$user
Definition: bootstrap.php:84
$password
Definition: bootstrap.php:85
$bro
Definition: bootstrap.php:151
foreach($domaines_user as $domaine) $t
$i
if(empty($_POST['key'])||empty($_POST['val'])) $key
Definition: tempovars.php:14