* @version $Id$ * @license GPL * @package DAVAdmin * * Copyright (c) 2007, 2008 Jan Dittberner * * This file is part of DAVAdmin. * * DAVAdmin is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * DAVAdmin is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with DAVAdmin; if not, see . */ /** Include common code. */ include_once('common.inc.php'); /** * Gets the names of the given users's groups from the group file. * * @param string $username user name * @return array array of group names * @access private */ function _getGroupNames($username) { $groupdata = file($GLOBALS['davconfig']['group.file']); $retval = array(); foreach ($groupdata as $line) { list($group, $users) = explode(":", $line); $group = trim($group); $users = explode(" ", $users); foreach ($users as $user) { if (trim($user) == $username) { array_push($retval, $group); } } } asort($retval); return $retval; } /** * Gets XML encoded data for a user. * * @param int $uid user id * @return string XML string */ function getUserData($uid) { if (!(is_numeric($uid) && array_key_exists($uid, $GLOBALS['namemap']))) { errorAsXml(sprintf(_("Invalid user id %s"), $uid)); } $row = $GLOBALS['namemap'][$uid]; $groups = _getGroupNames($row['username']); $retval = sprintf('%d%s%s%s%s0', $uid, $row['username'], $row['firstname'], $row['lastname'], implode(", ", $groups)); header("Content-Type: text/xml; charset=UTF-8"); return $retval; } /** * Gets XML encoded data for a deleted user. * * @param int $uid user id * @return string XML string */ function getDeletedUserData($uid) { if (!is_numeric($uid)) { errorAsXml(sprintf(_("Invalid user id %s"), $uid)); } return sprintf('%d', $uid); } /** * Validates the given user data array for correctness. * * @param array &$userdata reference to an user data array * @param boolean $forinsert if this is true the check skips field * that will be created during insert * @return array an array with validation error messages or an empty * array */ function validateUserData(&$userdata, $forinsert) { $errormsgs = array(); // normalize the user data array foreach ($userdata as $key => $value) { $userdata[$key] = trim($value); } if (!$forinsert) { if (!is_numeric($userdata['uid'])) { array_push($errormsgs, _("Uid must be numeric.")); } if (!empty($userdata['password']) && strlen($userdata['password']) < 8) { array_push($errormsgs, _("Password must be at least 8 characters long.")); } } else { if (!preg_match('/^[a-zA-Z0-9]{2,}$/', $userdata['username'])) { array_push($errormsgs, _("Username must be at least 2 characters long and must contain letters and digits only.")); } if (empty($userdata['password']) || strlen($userdata['password']) < 8) { array_push($errormsgs, _("Password must be at least 8 characters long.")); } } if (empty($userdata['firstname'])) { $userdata['firstname'] = null; } if (empty($userdata['lastname'])) { $userdata['lastname'] = null; } if (!preg_match('/^[0-9a-zA-z]+(\s*,\s*[0-9a-zA-z]+)*$/', $userdata['groups'])) { array_push($errormsgs, _('Groups must be a list of group names separated by commas. Group names must consist of letters and digits.')); } return $errormsgs; } /** * Create an entry for a digest authentication file. * * @param string $username user name * @param string $realm realm name * @param string $password password * @return string digest entry */ function createDigest($username, $realm, $password) { return sprintf("%s:%s:%s", $username, $realm, md5(sprintf("%s:%s:%s", $username, $realm, $password))); } /** * Update the user to name mapping file. */ function updateNameMap() { $fh = fopen($GLOBALS['davconfig']['namemap.file'], 'w'); fwrite($fh, json_encode($GLOBALS['namemap'])); fclose($fh); } /** * Update the digest file with the user information. * * @param &array reference to an associative array of user data */ function updateDigest(&$userdata) { if ($userdata['password']) { $digests = file($GLOBALS['davconfig']['digest.file']); $written = false; $fh = fopen($GLOBALS['davconfig']['digest.file'], 'w'); foreach ($digests as $digest) { list($username, $realm, $data) = explode(":", $digest); if ($username == $userdata['username'] && $realm == $GLOBALS['davconfig']['dav.realm']) { fwrite($fh, createDigest($userdata['username'], $GLOBALS['davconfig']['dav.realm'], $userdata['password']) . "\n"); $written = true; } else { fwrite($fh, $digest); } } if (!$written) { fwrite($fh, createDigest($userdata['username'], $GLOBALS['davconfig']['dav.realm'], $userdata['password']) . "\n"); } fclose($fh); } } /** * Update the group file with the group assignments of the user. * * @param &array reference to an associative array of user data */ function updateGroups(&$userdata) { if ($userdata['groups']) { $written = array(); foreach (explode(",", $userdata['groups']) as $group) { $written[trim($group)] = false; } $groupdata = file($GLOBALS['davconfig']['group.file']); $fh = fopen($GLOBALS['davconfig']['group.file'], 'w'); foreach ($groupdata as $groupline) { list ($group, $users) = explode(":", $groupline); $group = trim($group); $users = explode(" ", trim($users)); foreach ($users as $key => $user) { $users[$key] = trim($user); } if (array_key_exists($group, $written)) { if (!in_array($userdata['username'], $users)) { array_push($users, $userdata['username']); } fprintf($fh, "%s: %s\n", $group, implode(" ", $users)); $written[$group] = true; } else { if (in_array($userdata['username'], $users)) { array_splice($users, array_search($userdata['username'], $users),1); } if (count($users) > 0 ) { fprintf($fh, "%s: %s\n", $group, implode(" ", $users)); } } } foreach ($written as $group => $done) { if (!$done) { fprintf($fh, "%s: %s\n", $group, $userdata['username']); } } fclose($fh); } } /** * Updates the data of a user in the database. * * @param array &$userdata reference to a user data array */ function updateUser(&$userdata) { $validation = validateUserData($userdata, false); if (!empty($validation)) { errorAsXml(implode("\n", $validation)); } $GLOBALS['namemap'][$userdata['uid']] = array('uid' => $userdata['uid'], 'username' => $userdata['username'], 'firstname' => $userdata['firstname'], 'lastname' => $userdata['lastname']); // write name mapping data updateNameMap(); // write digest data updateDigest($userdata); // update group file updateGroups($userdata); } /** * Inserts a new user and its group assignments into the database. * * @param array &$userdata reference to an user data array */ function insertUser(&$userdata) { $validation = validateUserData($userdata, true); if (!empty($validation)) { errorAsXml(implode("\n", $validation)); } $uids = array_keys($GLOBALS['namemap']); if (empty($uids)) { $uid = 1; } else { foreach ($uids as $currentuid) { $uid = max($uid, $currentuid); } $uid += 1; } $GLOBALS['namemap'][$uid] = array('uid' => $uid, 'username' => $userdata['username'], 'firstname' => $userdata['firstname'], 'lastname' => $userdata['lastname']); // write name mapping data updateNameMap(); // write digest data updateDigest($userdata); // update group file updateGroups($userdata); return $uid; } /** * Remove the digest entries of the given user from the digest file. * * @param string $username */ function removeDigest($username) { $digests = file($GLOBALS['davconfig']['digest.file']); $fh = fopen($GLOBALS['davconfig']['digest.file'], 'w'); foreach ($digests as $digest) { list($user, $realm, $data) = explode(":", $digest); if (!($user == $username && $realm == $GLOBALS['davconfig']['dav.realm'])) { fwrite($fh, $digest); } } fclose($fh); } /** * Remove the given user from all groups in the group file. * * @param string $username */ function removeFromGroups($username) { $groupdata = file($GLOBALS['davconfig']['group.file']); $fh = fopen($GLOBALS['davconfig']['group.file'], 'w'); foreach ($groupdata as $groupline) { list ($group, $users) = explode(":", $groupline); $group = trim($group); $users = explode(" ", trim($users)); foreach ($users as $key => $user) { $users[$key] = trim($user); } if (in_array($username, $users)) { array_splice($users, array_search($username, $users),1); } if (count($users) > 0 ) { fprintf($fh, "%s: %s\n", $group, implode(" ", $users)); } } fclose($fh); } /** * Delete the user with the given user id and its group assignments * from the database. * * @param int $uid user id */ function deleteUser($uid) { if (!(is_numeric($uid) && array_key_exists($uid, $GLOBALS['namemap']))) { errorAsXml(sprintf(_("Invalid user id %s"), $uid)); } removeDigest($GLOBALS['namemap'][$uid]['username']); removeFromGroups($GLOBALS['namemap'][$uid]['username']); unset($GLOBALS['namemap'][$uid]); updateNameMap(); } if ($_GET) { if ($_GET['method']) { switch ($_GET['method']) { case 'getuserdata': if ($_GET['uid']) { print getUserData($_GET['uid']); } break; default: errorAsXml(sprintf(_("Unexpected values %s!"), serialize($_GET))); } } else if (isset($_GET['language'])) { header("Content-Type: text/html; charset=UTF-8"); $smarty->assign("users", $namemap); $smarty->display("users.html"); } else { invalidCall(); } } elseif ($_POST) { if ($_POST['method']) { switch ($_POST['method']) { case 'submituser': if ($_POST['uid']) { updateUser($_POST); print getUserData($_POST['uid']); } else { print getUserData(insertUser($_POST)); } break; case 'deleteuser': if ($_POST['uid']) { deleteUser($_POST['uid']); print getDeletedUserData($_POST['uid']); } else { errorAsXml(sprintf(_("Unexpected values %s!"), serialize($_POST))); } break; default: errorAsXml(sprintf(_("Unexpected values %s!"), serialize($_POST))); } } else { invalidCall(); } } else { header("Content-Type: text/html; charset=UTF-8"); $smarty->assign("users", $namemap); $smarty->display("users.html"); } ?>