2 /* vim: set ts=4 sw=4: */
3 // +----------------------------------------------------------------------+
5 // +----------------------------------------------------------------------+
6 // | Copyright (c) 1997-2002 The PHP Group |
7 // +----------------------------------------------------------------------+
8 // | This source file is subject to version 2.0 of the PHP license, |
9 // | that is bundled with this package in the file LICENSE, and is |
10 // | available at through the world-wide-web at |
11 // | http://www.php.net/license/2_02.txt. |
12 // | If you did not receive a copy of the PHP license and are unable to |
13 // | obtain it through the world-wide-web, please send a note to |
14 // | license@php.net so we can mail you a copy immediately. |
15 // +----------------------------------------------------------------------+
16 // | Author: Rasmus Lerdorf <rasmus@php.net> |
17 // +----------------------------------------------------------------------+
19 // Manipulate standard UNIX passwd,.htpasswd and CVS pserver passwd files
21 require_once 'PEAR.php';
24 * Class to manage passwd-style files
26 * @author Rasmus Lerdorf <rasmus@php.net>
43 * hash list of csv-users
49 * filehandle for lockfile
61 * name of the lockfile
64 var $lockfile = './passwd.lock';
68 * Requires the name of the passwd file. This functions opens the file and read it.
69 * Changes to this file will written first in the lock file, so it is still possible
70 * to access the passwd file by another programs. The lock parameter controls the locking
71 * oft the lockfile, not of the passwd file! ( Swapping $lock and $lockfile would
72 * breaks bc to v1.3 and smaller).
73 * Don't forget to call close() to save changes!
75 * @param $file name of the passwd file
76 * @param $lock if 'true' $lockfile will be locked
77 * @param $lockfile name of the temp file, where changes are saved
83 function File_Passwd($file, $lock = 0, $lockfile = "") {
84 $this->filename = $file;
85 if( !empty( $lockfile) ) {
86 $this->lockfile = $lockfile;
90 //check if already locked, on some error or race condition or other user.
91 //FIXME: implement timeout as with dba
92 if (!empty($this->lockfile) and file_exists($this->lockfile)) {
93 if (isset($GLOBALS['HTTP_GET_VARS']['force_unlock'])) {
94 $this->fplock = fopen($this->lockfile, 'w');
95 flock($this->fplock, LOCK_UN);
96 fclose($this->fplock);
98 trigger_error('File_Passwd lock conflict: Try &force_unlock=1',E_USER_NOTICE);
101 $this->fplock = fopen($this->lockfile, 'w');
102 flock($this->fplock, LOCK_EX);
103 $this->locked = true;
106 $fp = fopen($file,'r') ;
108 return new PEAR_Error( "Couldn't open '$file'!", 1, PEAR_ERROR_RETURN) ;
111 $line = fgets($fp, 128);
112 $array = explode(':', $line);
113 if (count($array) and strlen(trim($array[0]))) {
114 $user = trim($array[0]);
115 if (in_array(substr($user,0,1),array('#',';'))) continue;
116 if (empty($array[1])) $array[1]='';
117 $this->users[$user] = trim($array[1]);
118 if (count($array) >= 3)
119 $this->cvs[$user] = trim($array[2]);
128 * @param $user new user id
129 * @param $pass password for new user
130 * @param $cvs cvs user id (needed for pserver passwd files)
132 * @return mixed returns PEAR_Error, if the user already exists
135 function addUser($user, $pass, $cvsuser = "") {
136 if(!isset($this->users[$user]) && $this->locked) {
137 $this->users[$user] = crypt($pass);
138 $this->cvs[$user] = $cvsuser;
141 return new PEAR_Error( "Couldn't add user '$user', because the user already exists!", 2, PEAR_ERROR_RETURN);
143 } // end func addUser()
148 * @param $user user id
149 * @param $pass new password for user
150 * @param $cvs cvs user id (needed for pserver passwd files)
152 * @return mixed returns PEAR_Error, if the user doesn't exists
156 function modUser($user, $pass, $cvsuser="") {
157 if(isset($this->users[$user]) && $this->locked) {
158 $this->users[$user] = crypt($pass);
159 $this->cvs[$user] = $cvsuser;
162 return new PEAR_Error( "Couldn't modify user '$user', because the user doesn't exists!", 3, PEAR_ERROR_RETURN) ;
164 } // end func modUser()
169 * @param $user user id
171 * @return mixed returns PEAR_Error, if the user doesn't exists
175 function delUser($user) {
176 if (isset($this->users[$user]) && $this->locked) {
177 unset($this->users[$user]);
178 unset($this->cvs[$user]);
180 return new PEAR_Error( "Couldn't delete user '$user', because the user doesn't exists!", 3, PEAR_ERROR_RETURN) ;
182 } // end func delUser()
185 * Verifies a user's password
187 * @param $user user id
188 * @param $pass password for user
190 * @return boolean true if password is ok
193 function verifyPassword($user, $pass) {
194 //if ($this->users[$user] == crypt($pass, substr($this->users[$user], 0, 2)))
196 if (isset($this->users[$user])) {
197 $stored_password = $this->users[$user];
198 if (function_exists('crypt')) {
199 if (crypt($pass, $stored_password) == $stored_password)
200 return true; // matches encrypted password
204 if ($pass == $stored_password)
205 return true; // matches plaintext password
207 trigger_error(_("The crypt function is not available in this version of PHP."),
217 * Return all users from passwd file
222 function listUsers() {
224 } // end func listUsers()
227 * Writes changes to passwd file and unlocks it
233 foreach($this->users as $user => $pass) {
234 if (isset($this->cvs[$user])) {
235 fputs($this->fplock, "$user:$pass:" . $this->cvs[$user] . "\n");
237 fputs($this->fplock, "$user:$pass\n");
240 @unlink($this->filename.'.bak');
242 // windows doesn't allow renaming of open files
243 flock($this->fplock, LOCK_UN);
244 $this->locked = false;
245 fclose($this->fplock);
246 rename($this->filename,$this->filename.'.bak');
247 rename($this->lockfile, $this->filename);
249 rename($this->filename,$this->filename.'.bak');
250 rename($this->lockfile, $this->filename);
251 flock($this->fplock, LOCK_UN);
252 $this->locked = false;
253 fclose($this->fplock);
255 if (file_exists($this->filename))
256 @unlink($this->filename.'.bak');
258 rename($this->filename.'.bak',$this->filename);
261 } // end func close()
268 // c-hanging-comment-ender-p: nil
269 // indent-tabs-mode: nil