* @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'); /** Format string for group lines in .htaccess files. */ define('GROUPLINEFORMAT', "require group %s\n"); /** Regular expression for finding group lines in .htaccess files. */ define('GROUPLINERE', '/^require\s+group\s+(.*)$/i'); /** Regular expression matching legal directory names. */ define('DIRNAMERE', '/^[a-zA-Z0-9 -_.]+$/'); /** * List of mandatory groups. These groups are assigned to every new or * changed directory. */ $mandatorygroups = array(ADMIN_GROUP); /** * Gets the group names allowed to access the given directory from its * .htaccess file. Mandatory groups are excluded. * * @param string $dirname a fully qualified directory name * @return array an array of group names */ function getDirGroupsFromHtaccess($dirname) { $htaccessname = getFullPath($dirname) . DIRECTORY_SEPARATOR . ".htaccess"; $groups = array(); if (false !== ($fh = fopen($htaccessname, "r"))) { while (!feof($fh)) { $buffer = fgets($fh); if (preg_match_all(GROUPLINERE, trim($buffer), $matches)) { $grouparr = explode(" ", $matches[1][0]); foreach ($grouparr as $group) { $group = trim($group); if (!in_array($group, $GLOBALS['mandatorygroups']) && !in_array($group, $groups)) { array_push($groups, $group); } } } } } fclose($fh); return $groups; } /** * Counts the visible files and their accumulated size in a directory * tree. * * @param string $dirname a fully qualified directory name * @param array $retval a two component array index 0 is the file * count, index 1 is the accumulated size of files * @return array array updated with the data from subdirectories and * files of $dirname */ function countFilesRecursive($dirname, $retval = array(0, 0)) { $dh = opendir($dirname); while (false !== ($entry = readdir($dh))) { $fullname = $dirname . DIRECTORY_SEPARATOR . $entry; if (strpos($entry, '.') !== 0) { if (is_dir($fullname)) { $retval = countFilesRecursive($fullname, $retval); } else if (is_file($fullname)) { $retval = array($retval[0] + 1, $retval[1] + filesize($fullname)); } } } closedir($dh); return $retval; } /** * Gets the data of a directory. * * @param string $dirname a fully qualified directory name * @return array array containing directory data */ function getDirectoryData($dirname) { $dir = array(); $dir['name'] = basename($dirname); $dir['groups'] = getDirGroupsFromHtaccess($dirname); list($dir['filecount'], $dir['filesize']) = countFilesRecursive(getFullPath($dirname)); $dir['maydelete'] = ($dir['filecount'] == 0) ? 1 : 0; $dir['filesize'] = sprintf(_("%d kBytes"), $dir['filesize'] / 1024); return $dir; } /** * Gets XML encoded data of a directory. * * @param string $dirname dirname relative to {@link $davconfig['dav.dir']} * @return string XML string */ function getDirectoryDataAsXml($dirname) { if (is_dir(getFullPath($dirname))) { $dirdata = getDirectoryData($dirname); header("Content-Type: text/xml; charset=UTF-8"); return sprintf('%s%s%d%s%d', $dirdata['name'], implode(", ", $dirdata['groups']), $dirdata['filecount'], $dirdata['filesize'], $dirdata['maydelete']); } else { errorAsXml(sprintf(_("Invalid directory name %s!"), $dirname)); } } /** * Gets XML encoded data of a deleted directory. * * @param string $dirname directory name relative to $davconfig['dav.dir'] * @return string XML string */ function getDeletedDirectoryData($dirname) { header("Content-Type: text/xml; charset=UTF-8"); return sprintf('%s', $dirname); } /** * Gets the list of directory data for all valid directories below * {@link $davconfig['dav.dir']}. * * @return array array of directory data arrays * @see #getDirectoryData(string) */ function getDirectories() { $dirs = array(); if (false !== ($entries = scandir($GLOBALS['davconfig']['dav.dir']))) { foreach ($entries as $entry) { if (is_dir(getFullPath($entry))) { if (strpos($entry, '.') !== 0) { if ($entry != ADMIN_DIR) { array_push($dirs, getDirectoryData($entry)); } } } } } return $dirs; } /** * Sets the groups of a directory in its .htaccess file. Mandatory * groups are added automatically. * * @param string $dirname directory name relative to {@link $davconfig['dav.dir']} * @param array &$groups reference to a list of group names */ function setGroups($dirname, &$groups) { $fullname = getFullPath($dirname); foreach ($groups as $key => $value) { $groups[$key] = trim($value); } foreach ($GLOBALS['mandatorygroups'] as $mgroup) { if (!in_array($mgroup, $groups)) { array_push($groups, $mgroup); } } $groups = array_unique($groups); asort($groups); $found = false; $lines = array(); $found = false; if (is_dir($fullname)) { $htaccess = $fullname . DIRECTORY_SEPARATOR . ".htaccess"; if (file_exists($htaccess) && (false !== ($fh = fopen($htaccess, "r")))) { while (!feof($fh)) { $buffer = fgets($fh); if (preg_match(GROUPLINERE, $buffer)) { array_push($lines, sprintf(GROUPLINEFORMAT, join(" ", $groups))); $found = true; } else { array_push($lines, $buffer); } } fclose($fh); } if (!$found) { array_push($lines, sprintf(GROUPLINEFORMAT, join(" ", $groups))); } if (false !== ($fh = fopen($htaccess, "w"))) { foreach ($lines as $line) { fwrite($fh, $line); } fclose($fh); } } } /** * Updates a directory to be accessible by the given list of * groups. The directory is created if it doesn't exist. * * @param string $dirname directory name relative to $davconfig['dav.dir'] * @param array $groups a list of group names */ function updateDirectory($dirname, $groups) { if (preg_match(DIRNAMERE, $dirname, $matches)) { if ($dirname != ADMIN_DIR) { $fullname = getFullPath($dirname); if (file_exists($fullname)) { if (!is_dir($fullname)) { errorAsXml(sprintf(_("There already is a directory entry named %s, but it's not a directory!"), $dirname)); } } else { mkdir($fullname); } setGroups($dirname, $groups); } return; } errorAsXml(sprintf(_("Invalid directory name %s!"), $dirname)); } /** * Recursively deletes the given directory. * * @param string $fullname fully qualified directory name */ function delrecursive($fullname) { $dh = opendir($fullname); while (false !== ($entry = readdir($dh))) { if ($entry != "." && $entry != "..") { $entryname = $fullname . DIRECTORY_SEPARATOR . $entry; if (is_dir($entryname)) { delrecursive($entryname); } else { unlink($entryname); } } } closedir($dh); rmdir($fullname); return true; } /** * Deletes the given directory if it has a valid name and is not the * administration interface directory. * * @param string $dirname directory name relative to $davconfig['dav.dir'] */ function deleteDirectory($dirname) { global $davconfig; if (preg_match(DIRNAMERE, $dirname, $matches)) { $fullname = $davconfig['dav.dir'] . DIRECTORY_SEPARATOR . $dirname; if (is_dir($fullname)) { return delrecursive($fullname); } } errorAsXml(sprintf(_("Invalid directory name %s!"), $dirname)); } if ($_GET) { // handle get requests with data if ($_GET['method']) { switch ($_GET['method']) { case 'getdirectorydata': print getDirectoryDataAsXml($_GET['dirname']); break; default: errorAsXml(sprintf(_("Unexpected values %s!"), serialize($_GET))); } } else if (isset($_GET["language"])) { header("Content-Type: text/html; charset=UTF-8"); $smarty->assign("directories", getDirectories()); $smarty->display("directories.html"); } else { invalidCall(); } } elseif ($_POST) { // handle post requests with data if ($_POST['method']) { switch ($_POST['method']) { case 'submitdirectory': updateDirectory($_POST['dirname'], explode(",", $_POST['groups'])); print getDirectoryDataAsXml($_POST['dirname']); break; case 'deletedirectory': if (deleteDirectory($_POST['dirname'])) { print getDeletedDirectoryData($_POST['dirname']); } else { errorAsXml(_("Delete failed!")); } break; default: errorAsXml(sprintf(_("Unexpected values %s!"), serialize($_POST))); } } else { invalidCall(); } } else { // handle requests without data header("Content-Type: text/html; charset=UTF-8"); $smarty->assign("directories", getDirectories()); $smarty->display("directories.html"); } ?>