| // +----------------------------------------------------------------------+ // // Manipulate standard UNIX passwd,.htpasswd and CVS pserver passwd files require_once 'PEAR.php'; /** * Class to manage passwd-style files * * @author Rasmus Lerdorf */ class File_Passwd { /** * Passwd file * @var string */ var $filename ; /** * Hash list of users * @var array */ var $users ; /** * hash list of csv-users * @var array */ var $cvs ; /** * filehandle for lockfile * @var int */ var $fplock ; /** * locking state * @var boolean */ var $locked ; /** * name of the lockfile * @var string */ var $lockfile = './passwd.lock'; /** * Constructor * Requires the name of the passwd file. This functions opens the file and read it. * Changes to this file will written first in the lock file, so it is still possible * to access the passwd file by another programs. The lock parameter controls the locking * oft the lockfile, not of the passwd file! ( Swapping $lock and $lockfile would * breaks bc to v1.3 and smaller). * Don't forget to call close() to save changes! * * @param $file name of the passwd file * @param $lock if 'true' $lockfile will be locked * @param $lockfile name of the temp file, where changes are saved * * @access public * @see close() */ function File_Passwd($file, $lock = 0, $lockfile = "") { $this->filename = $file; if( !empty( $lockfile) ) { $this->lockfile = $lockfile; } if ($lock) { //check if already locked, on some error or race condition or other user. //FIXME: implement timeout as with dba if (!empty($this->lockfile) and file_exists($this->lockfile)) { if (isset($GLOBALS['HTTP_GET_VARS']['force_unlock'])) { $this->fplock = fopen($this->lockfile, 'w'); flock($this->fplock, LOCK_UN); fclose($this->fplock); } else { trigger_error('File_Passwd lock conflict: Try &force_unlock=1',E_USER_NOTICE); } } $this->fplock = fopen($this->lockfile, 'w'); flock($this->fplock, LOCK_EX); $this->locked = true; } $fp = fopen($file,'r') ; if( !$fp ) { return new PEAR_Error( "Couldn't open '$file'!", 1, PEAR_ERROR_RETURN) ; } while(!feof($fp)) { $line = fgets($fp, 128); $array = explode(':', $line); if (count($array) and strlen(trim($array[0]))) { $user = trim($array[0]); if (in_array(substr($user,0,1),array('#',';'))) continue; if (empty($array[1])) $array[1]=''; $this->users[$user] = trim($array[1]); if (count($array) >= 3) $this->cvs[$user] = trim($array[2]); } } fclose($fp); } /** * Adds a user * * @param $user new user id * @param $pass password for new user * @param $cvs cvs user id (needed for pserver passwd files) * * @return mixed returns PEAR_Error, if the user already exists * @access public */ function addUser($user, $pass, $cvsuser = "") { if(!isset($this->users[$user]) && $this->locked) { $this->users[$user] = crypt($pass); $this->cvs[$user] = $cvsuser; return true; } else { return new PEAR_Error( "Couldn't add user '$user', because the user already exists!", 2, PEAR_ERROR_RETURN); } } // end func addUser() /** * Modifies a user * * @param $user user id * @param $pass new password for user * @param $cvs cvs user id (needed for pserver passwd files) * * @return mixed returns PEAR_Error, if the user doesn't exists * @access public */ function modUser($user, $pass, $cvsuser="") { if(isset($this->users[$user]) && $this->locked) { $this->users[$user] = crypt($pass); $this->cvs[$user] = $cvsuser; return true; } else { return new PEAR_Error( "Couldn't modify user '$user', because the user doesn't exists!", 3, PEAR_ERROR_RETURN) ; } } // end func modUser() /** * Deletes a user * * @param $user user id * * @return mixed returns PEAR_Error, if the user doesn't exists * @access public */ function delUser($user) { if (isset($this->users[$user]) && $this->locked) { unset($this->users[$user]); unset($this->cvs[$user]); } else { return new PEAR_Error( "Couldn't delete user '$user', because the user doesn't exists!", 3, PEAR_ERROR_RETURN) ; } } // end func delUser() /** * Verifies a user's password * * @param $user user id * @param $pass password for user * * @return boolean true if password is ok * @access public */ function verifyPassword($user, $pass) { //if ($this->users[$user] == crypt($pass, substr($this->users[$user], 0, 2))) // return true; if (isset($this->users[$user])) { $stored_password = $this->users[$user]; if (function_exists('crypt')) { if (crypt($pass, $stored_password) == $stored_password) return true; // matches encrypted password else return false; } else { if ($pass == $stored_password) return true; // matches plaintext password else { trigger_error(_("The crypt function is not available in this version of PHP."), E_USER_WARNING); return false; } } } return false; } /** * Return all users from passwd file * * @access public * @return array */ function listUsers() { return $this->users; } // end func listUsers() /** * Writes changes to passwd file and unlocks it * * @access public */ function close() { if ($this->locked) { foreach($this->users as $user => $pass) { if (isset($this->cvs[$user])) { fputs($this->fplock, "$user:$pass:" . $this->cvs[$user] . "\n"); } else { fputs($this->fplock, "$user:$pass\n"); } } @unlink($this->filename.'.bak'); if (isWindows()) { // windows doesn't allow renaming of open files flock($this->fplock, LOCK_UN); $this->locked = false; fclose($this->fplock); rename($this->filename,$this->filename.'.bak'); rename($this->lockfile, $this->filename); } else { rename($this->filename,$this->filename.'.bak'); rename($this->lockfile, $this->filename); flock($this->fplock, LOCK_UN); $this->locked = false; fclose($this->fplock); } if (file_exists($this->filename)) @unlink($this->filename.'.bak'); else { rename($this->filename.'.bak',$this->filename); } } } // end func close() } // Local Variables: // mode: php // tab-width: 8 // c-basic-offset: 4 // c-hanging-comment-ender-p: nil // indent-tabs-mode: nil // End: