- initial import

This commit is contained in:
Jan Dittberner 2007-08-28 08:18:04 +00:00
commit 5e60bf4a9a
37 changed files with 2204 additions and 0 deletions

5
AUTHORS Normal file
View file

@ -0,0 +1,5 @@
======================
Authors of WebDAVAdmin
======================
Jan Dittberner <jan@dittberner.info>

211
INSTALL Normal file
View file

@ -0,0 +1,211 @@
=======================================
WebDAVAdmin installation instructions
=======================================
:Author: Jan Dittberner
:Contact: jan@dittberner.info
:Version: 0.1
:Revision: $Revision$
:Date: $Date$
:Copyright: Copyright (C) 2007 Jan Dittberner
.. contents::
Unpack WebDAVAdmin
==================
1. unpack the WebDAVAdmin distribution file somewhere::
cd ~/tmp/
tar xjf webdavadmin-0.1.tar.bz2
``~/tmp/`` is just an example to be able to reference it in these
installation instructions
Setup PostgreSQL and your database
==================================
1. Install PostgreSQL by the means of your operating system. For
Debian GNU/Linux 4.0 Etch execute::
sudo aptitude install postgresql-8.1
2. Switch to user postgres::
sudo su - postgres
and
1) create a user for WebDAVAdmin::
createuser -SDRP myuser
when prompted type the password for the database user twice
2) create a database::
createdb --owner=myuser --encoding=UTF-8 mydb
3) exit the postgres shell
You may skip theese steps if you want to use an existing database
3. Import the schema for WebDAVAdmin::
psql -h localhost -U myuser mydb < ~/tmp/webdavadmin-0.1/setup/schema.sql
when prompted type the password for your database user.
Setup Apache
============
1. Install, enable and configure apache and the apache modules
- mod_dav
- mod_dav_fs
- mod_auth_pgsql
- libphp5
by the means of your operating system vendor. For Debian GNU/Linux 4.0
Etch this means [1]_::
sudo aptitude install apache2-mpm-prefork libapache2-mod-php5 libapache2-mod-auth-pgsql
sudo a2enmod php5
sudo a2enmod auth_pgsql
sudo a2enmod dav
sudo a2enmod dav_fs
.. [1] if you don't want to use ``sudo`` you may also switch to root.
2. Configure a VirtualHost to use WebDAV and PostgreSQL
authentication, this VirtualHost configuration could look like::
<VirtualHost *:80>
ServerName davhost.yourdomain.net
DavLockDb /var/run/apache2/davlock/davhost.yourdomain.net
DocumentRoot /var/www
<Directory /var/www/dav>
Options Indexes
Order allow,deny
allow from all
Dav on
# Authentication/Authorization
AuthType Basic
AuthName "WebDAVAdmin example"
AuthBasicAuthoritative Off
AuthUserFile /etc/apache2/auth/davhost.yourdomain.net.passwd
Auth_PG_host localhost
Auth_PG_port 5432
Auth_PG_user myuser
Auth_PG_pwd secret
Auth_PG_database mydb
Auth_PG_pwd_table dav_password
Auth_PG_uid_field username
Auth_PG_pwd_field password
Auth_PG_grp_table dav_group
Auth_PG_grp_user_field username
Auth_PG_grp_group_field groupname
Auth_PG_hash_type MD5
#Auth_PG_log_table dav_log
#Auth_PG_log_uname_field username
#Auth_PG_log_date_field reqdate
#Auth_PG_log_uri_field uri
#Auth_PG_log_addrs_field ipaddr
Auth_PG_authoritative on
require group davroot
</Directory>
ErrorLog /var/log/apache2/davhost.yourdomain.net_error.log
CustomLog /var/log/apache2/davhost.yourdomain.net_access.log combined
</VirtualHost>
The directory specified for ``DavLockDb`` must be writable for the
user your apache processes run as. The ``AuthUserFile`` is
specified as a fallback if your PostgreSQL database is not
available.
Install required php modules and classes
========================================
WebDAVAdmin needs Smarty and a PostgreSQL PDO driver for PHP5. To
install these requirements perform the following step::
sudo aptitude install smarty php5-pgsql
on operating systems other then Debian GNU/Linux consult your system
documentation.
Copy WebDAVAdmin files
======================
2. create a new document root directory or a subdirectory inside an
existing one
3. create a subdirectory which you'll later use for WebDAVAdmin::
mkdir /var/www/dav
4. copy the admin subdirectory of the unpacked webdavadmin distribution
file to the directory just created::
cp -R webdavadmin-0.1/admin /var/www/dav/
5. set the filesystem permissions of the dav directory to allow the
user apache is running as to write to the directory
Configure WebDAVAdmin
=====================
The WebDAVAdmin distribution contains a directory ``config`` with
configuration templates that you need to customize for your
environment.
1. ``dbsettings.inc.php``
This file contains the settings for your database connection. The
file should be placed outside the document root for security
reasons. A customized version of this file may look like::
<?php
/** Data source name. */
$dsn = "pgsql:host=localhost port=5432 dbname=mydb";
/** Database user. */
$dbuser = "myuser";
/** Database password. */
$dbpass = "secret";
?>
2. ``config.inc.php``
This file contains the absolute path to your WebDAVAdmin
installation and to your ``dbsettings.inc.php``. A customized
version of this file could be::
<?php
/** DAV area root directory. */
define(DAV_ROOT, '/var/www/dav');
/** Include the database settings. */
include_once('/etc/webdavadmin/dbsettings.inc.php');
?>
After adapting the contents to your environment put this file into
your WebDAVAdmin directory. For example::
cp config.inc.php /var/www/dav/admin/
Be sure to make the subdirectory templates_c of your WebDAVAdmin
directory writable for your apache user [2]_.
.. [2] you could use chown, chmod and/or ACLs to perform this task
Now you should be able to use your installation of WebDAVAdmin by
opening the URL http://davhost.yourdomain.net/dav/admin/ (if you just
followed this instructions).

18
Makefile Normal file
View file

@ -0,0 +1,18 @@
.PHONY: apidoc
VERSION=0.1
PROJECT=webdavadmin
SRCFILES=admin/common.inc.php,admin/directories.php,admin/getgroups.php,admin/index.php,admin/users.php
apidoc:
if [ -d apidoc ]; then rm -r apidoc; fi
phpdoc -f $(SRCFILES) -t apidoc -s
clean:
find -name '*~' -type f -exec rm {} \;
distclean: clean
if [ -d apidoc ]; then rm -r apidoc; fi
dist: distclean
cd .. ; tar cjf $(PROJECT)-$(VERSION).tar.bz2 $(PROJECT)-$(VERSION)

71
README Normal file
View file

@ -0,0 +1,71 @@
=============
WebDAVAdmin
=============
:Author: Jan Dittberner
:Contact: jan@dittberner.info
:Version: 0.1
:Revision: $Revision$
:Date: $Date$
:Copyright: Copyright (C) 2007 Jan Dittberner
.. contents::
Goals
=====
The goal of this software is to provide an easy to use administration
interface for a WebDAV repository using mod-auth-pgsql as its
authentication and authorization source.
Requirements
============
To use this software you need an Apache webserver configured with the
dav module and mod-auth-pgsql, PHP 5 with PostgreSQL PDO driver, the
Smarty_ template engine and a PostgreSQL database. The software has
been developed using the versions contained in Debian GNU/Linux 4.0
Etch.
- Apache 2.2.3
- PostgreSQL 8.1.8
- mod-auth-pgsql 2.0.3
- PHP 5.2.0
- Smarty 2.6.14
.. _Smarty: http://smarty.php.net/
Installation
============
Please see INSTALL_ for installation instructions.
.. _INSTALL: INSTALL
Used third party code
=====================
WebDAVAdmin uses the JQuery Javascript library for DOM manipulations
and AJAX calls.
.. _JQuery: http://www.jquery.com/
Version 1.1.3 of JQuery has been used for developing this software and
is bundled in the ``scripts`` directory as ``jquery.js``.
License
=======
This program 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 2 of the License, or (at
your option) any later version.
This program 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 this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA.

6
TODO Normal file
View file

@ -0,0 +1,6 @@
TODO
====
- create an installer
- setup admin user during installation
- better integration into existing databases

2
admin/.htaccess Normal file
View file

@ -0,0 +1,2 @@
DirectoryIndex index.php
require group davadmin

75
admin/common.inc.php Normal file
View file

@ -0,0 +1,75 @@
<?php
/**
* Common code for WebDAVAdmin.
*
* @author Jan Dittberner <jan@dittberner.info>
* @version $Id$
* @license GPL
* @package WebDAVAdmin
*
* Copyright (c) 2007 Jan Dittberner
*
* This file is part of WebDAVAdmin.
*
* This program 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 2 of the
* License, or (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
/** Include configuration information. */
require_once('config.inc.php');
/** DAV administrator group name. */
define(ADMIN_GROUP, 'davadmin');
/** DAV administration application subdirectory. */
define(ADMIN_DIR, 'admin');
/** Include the Smarty template engine. */
require_once("smarty/libs/Smarty.class.php");
/** Global Smarty template engine instance. */
$smarty = new Smarty();
/** Handle invalid requests to the application. */
function invalidCall() {
header("Content-Type: text/plain; charset=UTF-8");
print("Ungültiger Aufruf!");
die();
}
/** Handle errors with an XML message. */
function errorAsXml($errormsg) {
header("Content-Type: text/xml; charset=UTF-8");
$GLOBALS['smarty']->assign("errormsg", $errormsg);
$GLOBALS['smarty']->display("error.xml");
die();
}
/** Handle errors with an HTML error page. */
function errorAsHtml($errormsg) {
header("Content-Type: text/html; charset=UTF-8");
$GLOBALS['smarty']->assign("errormsg", $errormsg);
$GLOBALS['smarty']->display("error.html");
die();
}
/**
* Handle a PDO statement error.
*
* @param PDOStatement $sth statement handle
*/
function statementErrorAsXml(&$sth) {
errorAsXml(utf8_encode(implode("\n", $sth->errorInfo())));
}
?>

331
admin/directories.php Normal file
View file

@ -0,0 +1,331 @@
<?php
/**
* Directory administration code.
*
* @author Jan Dittberner <jan@dittberner.info>
* @version $Id$
* @license GPL
* @package WebDAVAdmin
*
* Copyright (c) 2007 Jan Dittberner
*
* This file is part of WebDAVAdmin.
*
* This program 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 2 of the
* License, or (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
/** 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 an array of group names
*/
function getDirGroupsFromHtaccess($dirname) {
$htaccessname = $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'])) {
array_push($groups, $group);
}
}
}
}
}
fclose($fh);
return $groups;
}
/**
* Gets the names of groups for a directory.
*
* @param string $dirname directory name relative to {@link DAV_ROOT}
* @return an array of group names
* @see #getDirGroupsFromHtaccess(string)
*/
function getDirGroups($dirname) {
return getDirGroupsFromHtaccess(DAV_ROOT . DIRECTORY_SEPARATOR . $dirname);
}
/**
* 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 2 is the accumulated size of files
* @return 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 of containing directory data
*/
function getDirectoryData($dirname) {
$dir = array();
$dir['name'] = basename($dirname);
$dir['groups'] = getDirGroupsFromHtaccess($dirname);
list($dir['filecount'], $dir['filesize']) = countFilesRecursive($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 DAV_ROOT}
* @return XML string
*/
function getDirectoryDataAsXml($dirname) {
if (is_dir(DAV_ROOT . $dirname)) {
$dirdata = getDirectoryData(DAV_ROOT . $dirname);
header("Content-Type: text/xml; charset=UTF-8");
return sprintf('<?xml version="1.0" encoding="utf8"?><directory><dirname>%s</dirname><groups>%s</groups><filecount>%d</filecount><filesize>%s</filesize><maydelete>%d</maydelete></directory>', $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 {@link DAV_ROOT}
* @return XML string
*/
function getDeletedDirectoryData($dirname) {
header("Content-Type: text/xml; charset=UTF-8");
return sprintf('<?xml version="1.0" encoding="utf8"?><directory><dirname>%s</dirname></directory>', $dirname);
}
/**
* Gets the list of directory data for all valid directories below
* {@link DAV_ROOT}.
*
* @return array of directory data arrays
* @see #getDirectoryData(string)
*/
function getDirectories() {
$dirs = array();
if (false !== ($entries = scandir(DAV_ROOT))) {
foreach ($entries as $entry) {
if (is_dir(DAV_ROOT . $entry)) {
if (strpos($entry, '.') !== 0) {
if ($entry != ADMIN_DIR) {
array_push($dirs, getDirectoryData(DAV_ROOT . $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 DAV_ROOT}
* @param array &$groups reference to a list of group names
*/
function setGroups($dirname, &$groups) {
$fullname = DAV_ROOT . $dirname;
foreach ($groups as $key => $value) {
$groups[$key] = trim($value);
}
foreach ($GLOBALS['mandatorygroups'] as $mgroup) {
if (!in_array($mgroup, $groups)) {
array_push($groups, $mgroup);
}
}
$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 {@link DAV_ROOT}
* @param array $groups a list of group names
*/
function updateDirectory($dirname, $groups) {
if (preg_match(DIRNAMERE, $dirname, $matches)) {
if ($dirname != ADMIN_DIR) {
$fullname = DAV_ROOT . $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 {@link DAV_ROOT}
*/
function deleteDirectory($dirname) {
if (preg_match(DIRNAMERE, $dirname, $matches)) {
if ($dirname != ADMIN_DIR) {
$fullname = DAV_ROOT . $dirname;
if (is_dir($fullname)) {
return delrecursive($fullname);
}
}
errorAsXml(_("Tried to delete the administration interface directory!"));
}
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 {
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");
}
?>

47
admin/dynaform.css Normal file
View file

@ -0,0 +1,47 @@
#usereditor {
display:none;
z-index:10;
position:absolute;
top:10px;
margin-left:auto;
margin-right:auto;
}
.dynaform {
border:2px solid black;
background-color:#fffbed;
color:#2b2b26;
width:380px;
padding:2px;
}
.dynaform .formelement {
padding:2px;
}
.dynaform .formelement label {
display:block;
width:171px;
float:left;
}
.dynaform .formactions {
margin-top:5px;
border-top:1px solid #ffd436;
padding:5px;
text-align:right;
}
.dynaform input, .dynaform select
{
padding:0;
width:200px;
background-color:#fffbed;
color:#2b2b26;
border:1px solid #ffd436;
clear:both;
}
.dynaform input[type=submit] {
width:auto;
}

88
admin/format.css Normal file
View file

@ -0,0 +1,88 @@
html
{
font-family: Verdana,Helvetica,Arial,sans-serif;
font-size: 10pt;
background-color:white;
color:black;
}
#usertable, #dirtable {
border-collapse:collapse;
}
#usertable #hcol-username {
width:120px;
}
#usertable #hcol-name {
width:264px;
}
#dirtable #hcol-directory {
width:120px;
}
#dirtable #hcol-groups {
width:200px;
}
#dirtable #hcol-files {
width:150px;
}
#usertable th, #usertable td, #dirtable th, #dirtable td {
border:1px solid black;
text-align:left;
padding:2px;
}
dt {
display:block;
float:left;
width:20px;
clear:both;
}
dd {
display:block;
margin:0;
}
/*
* Autocomplete styles
*/
/* Suggestion list */
#autocomplete {
position: absolute;
border: 1px solid;
overflow: hidden;
z-index: 100;
}
#autocomplete ul {
margin: 0;
padding: 0;
list-style: none;
}
#autocomplete li {
background: #fff;
color: #000;
white-space: pre;
cursor: default;
}
#autocomplete li.selected {
background: #0072b9;
color: #fff;
}
/* Animated throbber */
input.form-autocomplete {
background-image: url(images/throbber.gif);
background-repeat: no-repeat;
background-position: 100% 2px;
}
input.throbbing {
background-position: 100% -18px;
}
a img.actionicon {
border:0;
}

69
admin/getgroups.php Normal file
View file

@ -0,0 +1,69 @@
<?php
/**
* AJAX autocompletion code for group names.
*
* @author Jan Dittberner <jan@dittberner.info>
* @version $Id$
* @license GPL
* @package WebDAVAdmin
*
* Copyright (c) 2007 Jan Dittberner
*
* This file is part of WebDAVAdmin.
*
* This program 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 2 of the
* License, or (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
/** Include configuration information. */
require_once('config.inc.php');
// output is plain text (JSON or an error message)
header("Content-Type: text/plain; charset=UTF-8");
/**
* Gets group names for autocompletion.
*
* @param string $part Comma separated list of groups and beginning of
* a new group entry
* @return list of Comma separated lists of groups
*/
function getGroups($part) {
$regexp = '%(?:^|,\ *)("(?>[^"]*)(?>""[^"]* )*"|(?: [^",]*))%x';
preg_match_all($regexp, $part, $matches);
$array = $matches[1];
$last_string = trim(array_pop($array));
if ($last_string != '') {
try {
$dbh = new PDO($GLOBALS['dsn'], $GLOBALS['dbuser'], $GLOBALS['dbpass']);
$sth = $dbh->prepare("SELECT DISTINCT groupname FROM dav_group WHERE LOWER(groupname) LIKE LOWER(?) ORDER BY groupname");
$sth->execute(array("%" . $last_string . "%"));
$prefix = count($array) ? implode(",", $array) . ", " : '';
$retval = array();
foreach ($sth->fetchAll(PDO::FETCH_ASSOC) as $row) {
$retval[$prefix . $row['groupname']] = $row['groupname'];
}
$dbh = null;
} catch (PDOException $e) {
return $e->getMessage();
}
}
return json_encode($retval);
}
// split group list part from requested URL.
$parts = substr($_SERVER['PHP_SELF'], strrpos($_SERVER['PHP_SELF'], "/") + 1);
print getGroups($parts);
?>

BIN
admin/images/assign.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 876 B

BIN
admin/images/delete.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 841 B

BIN
admin/images/directory.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 575 B

BIN
admin/images/edit.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 710 B

BIN
admin/images/groups.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 847 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 600 B

BIN
admin/images/newuser.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 751 B

BIN
admin/images/throbber.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
admin/images/x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 752 B

48
admin/index.php Normal file
View file

@ -0,0 +1,48 @@
<?php
/**
* Start page code.
*
* @author Jan Dittberner <jan@dittberner.info>
* @version $Id$
* @license GPL
* @package WebDAVAdmin
*
* Copyright (c) 2007 Jan Dittberner
*
* This file is part of WebDAVAdmin.
*
* This program 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 2 of the
* License, or (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
/** Include common code. */
include_once('common.inc.php');
header("Content-Type: text/html; charset=UTF-8");
try {
$dbh = new PDO($dsn, $dbuser, $dbpass);
$query = $dbh->prepare("SELECT firstname, lastname FROM dav_password WHERE username=:username");
$currentuser = $_SERVER['PHP_AUTH_USER'];
$query->execute(array(":username" => $currentuser));
$row = $query->fetch(PDO::FETCH_ASSOC);
$smarty->assign("firstname", $row['firstname']);
$smarty->assign("lastname", $row['lastname']);
$smarty->display("start.html");
$dbh = null;
} catch (PDOException $e) {
$smarty->setErrorMsg($e->getMessage());
$smarty->display("error.html");
}
?>

View file

@ -0,0 +1,305 @@
// $Id$
/*
* Autocompletion for input fields, ideas from Drupal autocompletion
*/
/**
* Attaches the autocomplete behaviour to all required fields
*/
DAV.autocompleteAutoAttach = function () {
var acdb = [];
$('input.autocomplete').each(function () {
var uri = this.value;
if (!acdb[uri]) {
acdb[uri] = new DAV.ACDB(uri);
}
var input = $('#' + this.id.substr(0, this.id.length - 13))
.attr('autocomplete', 'OFF')[0];
$(input.form).submit(DAV.autocompleteSubmit);
new DAV.jsAC(input, acdb[uri]);
});
}
/**
* Prevents the form from submitting if the suggestions popup is open
* and closes the suggestions popup when doing so.
*/
DAV.autocompleteSubmit = function () {
return $('#autocomplete').each(function () {
this.owner.hidePopup();
}).size() == 0;
}
/**
* An AutoComplete object
*/
DAV.jsAC = function (input, db) {
var ac = this;
this.input = input;
this.db = db;
$(this.input)
.keydown(function (event) { return ac.onkeydown(this, event); })
.keyup(function (event) { ac.onkeyup(this, event) })
.blur(function () { ac.hidePopup(); ac.db.cancel(); });
};
/**
* Handler for the "keydown" event
*/
DAV.jsAC.prototype.onkeydown = function (input, e) {
if (!e) {
e = window.event;
}
switch (e.keyCode) {
case 40: // down arrow
this.selectDown();
return false;
case 38: // up arrow
this.selectUp();
return false;
default: // all other keys
return true;
}
}
/**
* Handler for the "keyup" event
*/
DAV.jsAC.prototype.onkeyup = function (input, e) {
if (!e) {
e = window.event;
}
switch (e.keyCode) {
case 16: // shift
case 17: // ctrl
case 18: // alt
case 20: // caps lock
case 33: // page up
case 34: // page down
case 35: // end
case 36: // home
case 37: // left arrow
case 38: // up arrow
case 39: // right arrow
case 40: // down arrow
return true;
case 9: // tab
case 13: // enter
case 27: // esc
this.hidePopup(e.keyCode);
return true;
default: // all other keys
if (input.value.length > 0)
this.populatePopup();
else
this.hidePopup(e.keyCode);
return true;
}
}
/**
* Puts the currently highlighted suggestion into the autocomplete field
*/
DAV.jsAC.prototype.select = function (node) {
this.input.value = node.autocompleteValue;
}
/**
* Highlights the next suggestion
*/
DAV.jsAC.prototype.selectDown = function () {
if (this.selected && this.selected.nextSibling) {
this.highlight(this.selected.nextSibling);
}
else {
var lis = $('li', this.popup);
if (lis.size() > 0) {
this.highlight(lis.get(0));
}
}
}
/**
* Highlights the previous suggestion
*/
DAV.jsAC.prototype.selectUp = function () {
if (this.selected && this.selected.previousSibling) {
this.highlight(this.selected.previousSibling);
}
}
/**
* Highlights a suggestion
*/
DAV.jsAC.prototype.highlight = function (node) {
if (this.selected) {
$(this.selected).removeClass('selected');
}
$(node).addClass('selected');
this.selected = node;
}
/**
* Unhighlights a suggestion
*/
DAV.jsAC.prototype.unhighlight = function (node) {
$(node).removeClass('selected');
this.selected = false;
}
/**
* Hides the autocomplete suggestions
*/
DAV.jsAC.prototype.hidePopup = function (keycode) {
// Select item if the right key or mousebutton was pressed
if (this.selected && ((keycode && keycode != 46 && keycode != 8 && keycode != 27) || !keycode)) {
this.input.value = this.selected.autocompleteValue;
}
// Hide popup
var popup = this.popup;
if (popup) {
this.popup = null;
$(popup).fadeOut('fast', function() { $(popup).remove(); });
}
this.selected = false;
}
/**
* Positions the suggestions popup and starts a search
*/
DAV.jsAC.prototype.populatePopup = function () {
// Show popup
if (this.popup) {
$(this.popup).remove();
}
this.selected = false;
this.popup = document.createElement('div');
this.popup.id = 'autocomplete';
this.popup.owner = this;
$(this.popup).css({
marginTop: this.input.offsetHeight +'px',
width: (this.input.offsetWidth - 4) +'px',
display: 'none'
});
$(this.input).before(this.popup);
// Do search
this.db.owner = this;
this.db.search(this.input.value);
}
/**
* Fills the suggestion popup with any matches received
*/
DAV.jsAC.prototype.found = function (matches) {
// If no value in the textfield, do not show the popup.
if (!this.input.value.length) {
return false;
}
// Prepare matches
var ul = document.createElement('ul');
var ac = this;
for (key in matches) {
var li = document.createElement('li');
$(li)
.html('<div>'+ matches[key] +'</div>')
.mousedown(function () { ac.select(this); })
.mouseover(function () { ac.highlight(this); })
.mouseout(function () { ac.unhighlight(this); });
li.autocompleteValue = key;
$(ul).append(li);
}
// Show popup with matches, if any
if (this.popup) {
if (ul.childNodes.length > 0) {
$(this.popup).empty().append(ul).show();
}
else {
$(this.popup).css({visibility: 'hidden'});
this.hidePopup();
}
}
}
DAV.jsAC.prototype.setStatus = function (status) {
switch (status) {
case 'begin':
$(this.input).addClass('throbbing');
break;
case 'cancel':
case 'error':
case 'found':
$(this.input).removeClass('throbbing');
break;
}
}
/**
* An AutoComplete DataBase object
*/
DAV.ACDB = function (uri) {
this.uri = uri;
this.delay = 300;
this.cache = {};
}
/**
* Performs a cached and delayed search
*/
DAV.ACDB.prototype.search = function (searchString) {
var db = this;
this.searchString = searchString;
// See if this key has been searched for before
if (this.cache[searchString]) {
return this.owner.found(this.cache[searchString]);
}
// Initiate delayed search
if (this.timer) {
clearTimeout(this.timer);
}
this.timer = setTimeout(function() {
db.owner.setStatus('begin');
// Ajax GET request for autocompletion
$.ajax({
type: "GET",
url: db.uri +'/'+ DAV.encodeURIComponent(searchString),
success: function (data) {
// Parse back result
var matches = DAV.parseJson(data);
if (typeof matches['status'] == 'undefined' || matches['status'] != 0) {
db.cache[searchString] = matches;
// Verify if these are still the matches the user wants to see
if (db.searchString == searchString) {
db.owner.found(matches);
}
db.owner.setStatus('found');
}
},
error: function (xmlhttp) {
alert('An HTTP error '+ xmlhttp.status +' occured.\n'+ db.uri);
}
});
}, this.delay);
}
/**
* Cancels the current autocomplete request
*/
DAV.ACDB.prototype.cancel = function() {
if (this.owner) this.owner.setStatus('cancel');
if (this.timer) clearTimeout(this.timer);
this.searchString = '';
}
// Assign autocomplete
$(document).ready(DAV.autocompleteAutoAttach);

View file

@ -0,0 +1,148 @@
function handleerror(xmldata) {
if ($('error', xmldata).size()) {
var msg = 'Es ist ein Fehler aufgetreten:\n';
$('error', xmldata).find('errormsg').each(function(i) {
msg += $(this).text();
});
alert(msg);
return true;
}
return false;
$('#content').show();
}
function updateDirectories(xmldata) {
var dirname = $('dirname', xmldata).text();
var groups = $('groups', xmldata).text();
var filecount = $('filecount', xmldata).text();
var filesize = $('filesize', xmldata).text();
var maydelete = $('maydelete', xmldata).text();
var htmltext = '<td>' + dirname + '</td><td>' + groups + '</td><td>' + filecount + ', ' + filesize + '</td><td><a id="edit' + dirname + '" class="editlink" href="#" title="Die Gruppenzuordnungen dieses Verzeichnisses bearbeiten"><img class="actionicon" src="images/groups.png" width="16" height="16" alt="Gruppen"/></a>';
if (maydelete == '1') {
htmltext = htmltext + '<a id="delete' + dirname + '" class="deletelink" href="#" title="Dieses Verzeichnis löschen"><img class="actionicon" src="images/delete.png" width="16" height="16" alt="löschen" /></a>';
}
htmltext = htmltext + '</td>';
$('#dirtable').find('tr#dir' + dirname).empty().append(htmltext);
if (!($('#dirtable').find('tr#dir' + dirname).size())) {
var rows = $('#dirtable').find('tr');
var inserted = false;
for (var i = 0; !inserted && i < rows.length; i++) {
if ($(rows[i]).find('td:first').text() > dirname) {
$(rows[i]).before(
'<tr id="dir' + dirname + '">' + htmltext + '</tr>');
inserted = true;
}
}
if (!inserted) {
$(rows[rows.length-1]).before(
'<tr id="dir' + dirname + '">' + htmltext + '</tr>');
}
}
$('#dirtable').find('tr#dir' + dirname).find('a.editlink').click(function() {
editdirectory(this.id.substr(4));
});
$('#dirtable').find('tr#dir' + dirname).find('a.deletelink').click(function() {
removedirectory(this.id.substr(6));
});
}
function getDirectoryForm(title, dirname, groups) {
return '<form action="#" id="dirform"><fieldset class="dynaform"><legend><img id="closer" src="images/x.png" width="16" height="16" alt="Schließen" /> ' + title + '</legend><div class="formelement"><label for="dirname">Verzeichnisname:</label><input type="text" name="dirname" id="input-dirname" value="' + dirname + '" /></div><div class="formelement"><label for="groups">Gruppen:</label><input type="text" name="groups" class="form-autocomplete" id="input-groups" value="' + groups + '" /><input type="hidden" class="autocomplete" id="input-groups-autocomplete" value="getgroups.php" /></div><div class="formactions"><input type="submit" name="submit" value="Absenden" /></div></div></fieldset></form>';
}
function displaydirectoryeditor(title, dirname, groups) {
$('#content').hide();
$('#direditor').hide().empty().append(getDirectoryForm(title, dirname,
groups)).show();
DAV.autocompleteAutoAttach();
$('#dirform').find('#input-dirname').focus();
$('#closer').click(function() {
$('#direditor').hide().empty();
$('#content').show();
});
$('#dirform').submit(function() {
if (!this.dirname.value.match(/^[a-zA-Z0-9 -_.]+$/)) {
alert("Ungültiger Verzeichnisname.");
this.dirname.focus();
return false;
}
$.post(
"/dav/admin/directories.php",
{method : 'submitdirectory',
dirname : this.dirname.value,
groups : this.groups.value},
function(retval) {
$('div#direditor').hide().empty();
if (!handleerror(retval)) {
updateDirectories(retval);
$('#content').show();
}
});
return false;
});
}
function editdirectory(dirname) {
$.get(
"directories.php",
{dirname : dirname,
method : 'getdirectorydata'},
function(retval) {
if (!handleerror(retval)) {
var dirname, groups;
dirname = $("dirname:first", retval).text();
groups = $("groups:first", retval).text();
displaydirectoryeditor("Verzeichnisdaten bearbeiten",
dirname, groups);
}
});
}
function newdirectory() {
displaydirectoryeditor('Neues Verzeichnis anlegen', '', '');
}
function deletedirectorydialog(dirname) {
$("#direditor").hide().empty();
var msg = 'Soll das Verzeichnis ' + dirname +
' wirklich gelöscht werden?';
if (confirm(msg) == true) {
$.post(
"directories.php",
{method : 'deletedirectory',
dirname : dirname},
function(retval) {
if (!handleerror(retval)) {
var dirname = $('dirname:first', retval).text();
$('#dirtable').find('tr#dir' + dirname).remove();
}
});
}
}
function removedirectory(dirname) {
$.get(
"directories.php",
{dirname : dirname,
method : 'getdirectorydata'},
function(retval) {
if (!handleerror(retval)) {
var username, lastname, firstname;
var dirname, groups;
dirname = $("dirname:first", retval).text();
deletedirectorydialog(dirname);
}
});
}
$(function() {
$("a.editlink").each(function(i) {
$(this).click(function() { editdirectory(this.id.substr(4)); });
});
$("a.newlink").each(function(i) {
$(this).click(function() { newdirectory(); });
});
$("a.deletelink").each(function(i) {
$(this).click(function() { removedirectory(this.id.substr(6)); });
});
});

29
admin/scripts/helper.js Normal file
View file

@ -0,0 +1,29 @@
// $Id$
/**
* Helper code. Ideas from Drupal.
*/
var DAV = DAV || {};
/**
* Parse a JSON response.
*
* The result is either the JSON object, or an object with 'status' 0 and
* 'data' an error message.
*/
DAV.parseJson = function (data) {
if ((data.substring(0, 1) != '{') && (data.substring(0, 1) != '[')) {
return { status: 0, data: data.length ? data : 'Unspecified error' };
}
return eval('(' + data + ');');
};
/**
* Wrapper to address the mod_rewrite url encoding bug.
*/
DAV.encodeURIComponent = function (item, uri) {
uri = uri || location.href;
item = encodeURIComponent(item).replace('%2F', '/');
return uri.indexOf('?q=') ? item : item.replace('%26', '%2526').replace('%23', '%2523');
};

1
admin/scripts/jquery.js vendored Normal file

File diff suppressed because one or more lines are too long

189
admin/scripts/users.js Normal file
View file

@ -0,0 +1,189 @@
function handleerror(xmldata) {
if ($('error', xmldata).size()) {
var msg = 'Es ist ein Fehler aufgetreten:\n';
$('error', xmldata).find('errormsg').each(function(i) {
msg += $(this).text();
});
alert(msg);
$('#content').show();
return true;
}
return false;
}
function updateusers(xmldata) {
var uid = $(xmldata).find('uid').text();
var username = $(xmldata).find('username').text();
var firstname = $(xmldata).find('firstname').text();
var lastname = $(xmldata).find('lastname').text();
var loggedin = $(xmldata).find('loggedin').text();
var htmltext = '<td>' + username + '</td><td>' + lastname + ', ' + firstname + '</td><td><a id="edit' + uid + '" class="editlink" href="#" title="Die Daten dieses Nutzers bearbeiten"><img class="actionicon" src="images/edit.png" width="16" height="16" alt="bearbeiten"/></a>';
if (loggedin == '0') {
htmltext = htmltext + '<a id="delete' + uid + '" class="deletelink" href="#" title="Die Daten dieses Nutzers löschen"><img class="actionicon" src="images/delete.png" width="16" height="16" alt="löschen" /></a>';
}
htmltext = htmltext + '</td>';
$('#usertable').find('tr#uid' + uid).empty().append(htmltext);
if (!($('#usertable').find('tr#uid' + uid).size())) {
var rows = $('#usertable').find('tr');
var inserted = false;
for (var i = 0; !inserted && i < rows.length; i++) {
if ($(rows[i]).find('td:first').text() > username) {
$(rows[i]).before(
'<tr id="uid' + uid + '">' + htmltext + '</tr>');
inserted = true;
}
}
if (!inserted) {
$(rows[rows.length-1]).before(
'<tr id="uid' + uid + '">' + htmltext + '</tr>');
}
}
$('#usertable').find('tr#uid' + uid).find('a.editlink').click(function() {
edituser(this.id.substr(4));
});
$('#usertable').find('tr#uid' + uid).find('a.deletelink').click(function() {
deleteuser(this.id.substr(6));
});
$('#content').show();
}
function getEditUserForm(title, username, firstname, lastname, groups, userid) {
var retval;
retval = '<form action="#" id="userform"><fieldset class="dynaform"><legend><img id="closer" src="images/x.png" width="16" height="16" alt="Schließen" /> ' + title + '</legend><div class="formelement"><label for="input-username">Nutzername:</label><input id="input-username" type="text" name="username" value="' + username + '" ';
if (userid != null) {
retval = retval + ' readonly="readonly"';
}
retval = retval + '/></div><div class="formelement"><label for="input-firstname">Vorname:</label><input id="input-firstname" type="text" name="firstname" value="' + firstname + '" /></div><div class="formelement"><label for="input-lastname">Nachname:</label><input id="input-lastname" type="text" name="lastname" value="' + lastname + '" /></div><div class="formelement"><label for="pwd1">Passwort:</label><input type="password" name="pwd1" /></div><div class="formelement"><label for="pwd2">Wiederholung:</label><input type="password" name="pwd2" /></div><div class="formelement"><label for="groups">Gruppen:</label><input type="text" name="groups" class="form-autocomplete" id="input-groups" value="' + groups + '" /><input type="hidden" class="autocomplete" id="input-groups-autocomplete" value="getgroups.php" /></div><div class="formactions"><input type="submit" name="submit" value="Absenden" /></div></fieldset></form>';
return retval;
}
function displayusereditor(title, userid, username, firstname, lastname, groups) {
$('#content').hide();
$('#usereditor').hide().empty().append(getEditUserForm(title, username, firstname, lastname, groups, userid)).show();
DAV.autocompleteAutoAttach();
$('#closer').click(function() {
$('#usereditor').hide().empty();
$('#content').show();
});
$('#userform').find('#input-username').focus();
$('#userform').submit(function() {
var params;
if (userid == null) {
if (!this.username.value.match(/^[a-zA-Z0-9]{2,}$/)) {
alert('Der Nutzername muss aus mindestens 2 Buchstaben oder Ziffern bestehen und darf keine sonstigen Zeichen enthalten.');
this.username.focus();
return false;
}
}
if (userid == null || this.pwd1.value.length > 0) {
if (this.pwd1.value.length < 8) {
alert('Das Passwort muss mindestens 8 Zeichen lang sein!');
this.pwd1.focus();
return false;
}
if (this.pwd1.value != this.pwd2.value) {
alert('Passwort und Wiederholung müssen übereinstimmen!');
this.pwd2.focus();
return false;
}
}
if (!this.groups.value.match(/^([0-9a-zA-z]+[,\s]*)+$/)) {
alert('Die Gruppenangabe muss eine durch Kommata getrennte Liste von Gruppennamen, die aus Buchstaben und Ziffern zusammengesetzt sein können, sein.');
this.groups.focus();
return false;
}
if (userid == null) {
params = {method : 'submituser',
username : this.username.value,
firstname : this.firstname.value,
lastname : this.lastname.value,
password : this.pwd1.value,
groups : this.groups.value};
} else {
params = {method : 'submituser',
uid : userid,
username : this.username.value,
firstname : this.firstname.value,
lastname : this.lastname.value,
password : this.pwd1.value,
groups : this.groups.value};
}
$.post(
"users.php", params,
function(retval) {
$('div#usereditor').hide().empty();
if (!handleerror(retval)) {
updateusers(retval);
}
});
return false;
});
}
function deleteuserdialog(userid, username, firstname, lastname) {
$("#usereditor").hide().empty();
var msg = 'Soll der Nutzer ' + firstname + ' ' + lastname +
' mit dem Login ' + username + ' wirklich gelöscht werden?';
if (confirm(msg) == true) {
$.post(
"users.php",
{method : 'deleteuser',
uid : userid},
function(retval) {
if (!handleerror(retval)) {
var deluid = $('uid:first', retval).text();
$('#usertable').find('tr#uid' + deluid).remove();
}
});
}
}
function deleteuser(userid) {
$.get(
"users.php",
{uid : userid,
method : 'getuserdata'},
function(retval) {
if (!handleerror(retval)) {
var username, lastname, firstname;
username = $("username:first", retval).text();
lastname = $("lastname:first", retval).text();
firstname = $("firstname:first", retval).text();
deleteuserdialog(userid, username, firstname, lastname);
}
});
}
function edituser(userid) {
$.get(
"users.php",
{uid : userid,
method : 'getuserdata'},
function(retval) {
if (!handleerror(retval)) {
var username, lastname, firstname, groups;
username = $("username:first", retval).text();
lastname = $("lastname:first", retval).text();
firstname = $("firstname:first", retval).text();
groups = $("groups:first", retval).text();
displayusereditor('Nutzerdaten bearbeiten', userid, username,
firstname, lastname, groups);
}
});
}
function newuser() {
displayusereditor('Neuen Nutzer anlegen', null, '', '', '', '');
}
$(function() {
$("a.editlink").each(function(i) {
$(this).click(function() { edituser(this.id.substr(4)); });
});
$("a.newlink").each(function(i) {
$(this).click(function() { newuser(); });
});
$("a.deletelink").each(function(i) {
$(this).click(function() { deleteuser(this.id.substr(6)); });
});
});

View file

@ -0,0 +1,39 @@
{include file="header.html" title="Verzeichnisverwaltung"}
<script type="text/javascript" src="/dav/admin/scripts/helper.js"></script>
<script type="text/javascript" src="/dav/admin/scripts/autocomplete.js"></script>
<script type="text/javascript" src="/dav/admin/scripts/directories.js"></script><div id="content">
<h1>WebDAV-Verwaltung</h1>
<h2>Verzeichnisverwaltung</h2>
<table id="dirtable">
<thead>
<tr><th id="hcol-directory">Verzeichnis</th><th id="hcol-groups">Gruppen</th><th id="hcol-files">enthaltene Dateien (Anzahl und Größe)</th><th /></tr>
</thead>
<tbody>
{foreach item=dir from=$directories}
<tr id="dir{$dir.name}">
<td>{$dir.name}</td>
<td>{foreach item=group from=$dir.groups name=dirloop}{$group}{if !$smarty.foreach.dirloop.last}, {/if}{/foreach}</td>
<td>{$dir.filecount}, {$dir.filesize}</td>
<td><a id="edit{$dir.name}" class="editlink" href="#" title="Die Gruppenzuordnungen dieses Verzeichnisses bearbeiten"><img class="actionicon" src="images/groups.png" width="16" height="16" alt="Gruppen"/></a>{if $dir.maydelete}<a id="delete{$dir.name}" class="deletelink" href="#" title="Dieses Verzeichnis löschen"><img class="actionicon" src="images/delete.png" width="16" height="16" alt="löschen" /></a>{/if}</td>
</tr>
{foreachelse}
<tr><td colspan="4">keine Verzeichnisse vorhanden</td></tr>
{/foreach}
</tbody>
<tfoot>
<tr><td colspan="4"><a id="newdir" class="newlink" href="#" title="Neues Verzeichnis anlegen"><img class="actionicon" src="images/newdirectory.png" width="16" height="16" alt="neu" /></a></td></tr>
</tfoot>
</table>
<h3>Symbolerklärung</h3>
<dl>
<dt><img src="images/groups.png" width="16" height="16" alt="Gruppen" /></dt>
<dd>Die Gruppenberechtigungen dieses Verzeichnisses bearbeiten</dd>
<dt><img src="images/delete.png" width="16" height="16" alt="löschen" /></dt>
<dd>Diese Verzeichnis löschen</dd>
<dt><img src="images/newdirectory.png" width="16" height="16" alt="neu" /></dt>
<dd>Neues Verzeichnis anlegen</dd>
</dl>
<div id="footer"><a href="index.php">Zurück</a></div>
</div>
<div id="direditor"></div>
{include file="footer.html"}

View file

@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8"?><error><errormsg>{$errormsg}</errormsg></error>

View file

@ -0,0 +1 @@
</body></html>

View file

@ -0,0 +1,8 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="de">
<head><title>{$smarty.server.SERVER_NAME} - WebDAV Verwaltung{if $title} - {$title}{/if}</title></head>
<link rel="stylesheet" type="text/css" href="format.css" />
<link rel="stylesheet" type="text/css" href="dynaform.css" />
<script type="text/javascript" src="scripts/jquery.js"></script>
<body>

View file

@ -0,0 +1,10 @@
{include file="header.html"}
<h1>WebDAV-Verwaltung</h1>
<p>Hallo {$firstname} {$lastname},<br />
willkommen zur WebDAV-Verwaltung für {$smarty.server.SERVER_NAME}. Ihnen
stehen folgende Möglichkeiten zur Verfügung.</p>
<ul>
<li><a href="directories.php">Verzeichnisse verwalten</a></li>
<li><a href="users.php">Nutzeraccounts verwalten</a></li>
</ul>
{include file="footer.html"}

View file

@ -0,0 +1,37 @@
{include file="header.html" title="Nutzerverwaltung"}
<script type="text/javascript" src="/dav/admin/scripts/helper.js"></script>
<script type="text/javascript" src="/dav/admin/scripts/autocomplete.js"></script>
<script type="text/javascript" src="/dav/admin/scripts/users.js"></script>
<div id="content">
<h1>WebDAV-Verwaltung</h1>
<h2>Nutzerverwaltung</h2>
<table id="usertable">
<thead>
<tr><th id="hcol-username">Nutzername</th><th id="hcol-name">Name, Vorname</th><th /></tr>
</thead>
<tbody>
{foreach item=user from=$users}
<tr id="uid{$user.uid}">
<td>{$user.username}</td>
<td>{$user.lastname}, {$user.firstname}</td>
<td><a id="edit{$user.uid}" class="editlink" href="#" title="Die Daten dieses Nutzers bearbeiten"><img class="actionicon" src="images/edit.png" width="16" height="16" alt="bearbeiten"/></a>{if !$user.loggedin}<a id="delete{$user.uid}" class="deletelink" href="#" title="Die Daten dieses Nutzers löschen"><img class="actionicon" src="images/delete.png" width="16" height="16" alt="löschen" /></a>{/if}</td>
</tr>
{/foreach}
</tbody>
<tfoot>
<tr><td colspan="3"><a id="newuser" class="newlink" href="#" title="Neuen Nutzer anlegen"><img class="actionicon" src="images/newuser.png" width="16" height="16" alt="neu" /></a></td></tr>
</tfoot>
</table>
<h3>Symbolerklärung</h3>
<dl>
<dt><img src="images/edit.png" width="16" height="16" alt="bearbeiten" /></dt>
<dd>Die Daten dieses Nutzers bearbeiten</dd>
<dt><img src="images/delete.png" width="16" height="16" alt="löschen" /></dt>
<dd>Die Daten dieses Nutzers löschen</dd>
<dt><img src="images/newuser.png" width="16" height="16" alt="neu" /></dt>
<dd>Neuen Nutzer anlegen</dd>
</dl>
<div id="footer"><a href="index.php">Zurück</a></div>
</div>
<div id="usereditor"></div>
{include file="footer.html"}

317
admin/users.php Normal file
View file

@ -0,0 +1,317 @@
<?php
/**
* User administration code.
*
* @author Jan Dittberner <jan@dittberner.info>
* @version $Id$
* @license GPL
* @package WebDAVAdmin
*
* Copyright (c) 2007 Jan Dittberner
*
* This file is part of WebDAVAdmin.
*
* This program 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 2 of the
* License, or (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
/** Include common code. */
include_once('common.inc.php');
/**
* Gets XML encoded data for a user.
*
* @param int $uid user id
* @return XML string
*/
function getUserData($uid) {
if (!is_numeric($uid)) {
errorAsXml(sprintf(_("Invalid user id %s"), $uid));
}
try {
$currentuser = $_SERVER['PHP_AUTH_USER'];
$dbh = new PDO($GLOBALS['dsn'], $GLOBALS['dbuser'], $GLOBALS['dbpass']);
$sth = $dbh->prepare("SELECT groupname FROM dav_group, dav_password WHERE dav_group.username=dav_password.username AND dav_password.uid=:uid");
if (!$sth->execute(array(':uid' => $uid))) {
statementErrorAsXml($sth);
}
$groups = array();
while ($grouprow = $sth->fetch(PDO::FETCH_ASSOC)) {
array_push($groups, $grouprow['groupname']);
}
$sth = $dbh->prepare("SELECT username, firstname, lastname FROM dav_password WHERE uid=:uid");
if (!$sth->execute(array(':uid' => $uid))) {
statementErrorAsXml($sth);
}
$row = $sth->fetch(PDO::FETCH_ASSOC);
$retval = sprintf('<?xml version="1.0" encoding="utf8"?><userdata><uid>%d</uid><username>%s</username><firstname>%s</firstname><lastname>%s</lastname><groups>%s</groups><loggedin>%d</loggedin></userdata>',
$uid, $row['username'], $row['firstname'],
$row['lastname'], implode(", ", $groups),
($currentuser == $row['username']) ? 1 : 0);
$dbh = null;
} catch (PDOException $e) {
errorAsXml($e->getMessage());
}
header("Content-Type: text/xml; charset=UTF-8");
return $retval;
}
/**
* Gets XML encoded data for a deleted user.
*
* @param int $uid user id
* @return XML string
*/
function getDeletedUserData($uid) {
if (!is_numeric($uid)) {
errorAsXml(sprintf(_("Invalid user id %s"), $uid));
}
return sprintf('<?xml version="1.0" encoding="utf8"?><userdata><uid>%d</uid></userdata>', $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 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]*)+$/', $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;
}
/**
* 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));
}
try {
$dbh = new PDO($GLOBALS['dsn'], $GLOBALS['dbuser'], $GLOBALS['dbpass']);
$dbh->beginTransaction();
if ($userdata['password']) {
$sth = $dbh->prepare("UPDATE dav_password SET firstname=:firstname, lastname=:lastname, password=md5(:password) WHERE uid=:uid");
if (!$sth->execute(array(':firstname' => $userdata['firstname'],
':lastname' => $userdata['lastname'],
':password' => $userdata['password'],
':uid' => $userdata['uid']))) {
$dbh->rollback();
statementErrorAsXml($sth);
}
} else {
$sth = $dbh->prepare("UPDATE dav_password SET firstname=:firstname, lastname=:lastname WHERE uid=:uid");
if (!$sth->execute(array(':firstname' => $userdata['firstname'],
':lastname' => $userdata['lastname'],
':uid' => $userdata['uid']))) {
$dbh->rollback();
statementErrorAsXml($sth);
}
}
if ($userdata['groups']) {
$groups = array();
$sth = $dbh->prepare("DELETE FROM dav_group WHERE username=:username");
if (!$sth->execute(array(':username' => $userdata['username']))) {
$dbh->rollback();
statementErrorAsXml($sth);
}
$sth = $dbh->prepare("INSERT INTO dav_group (username, groupname) VALUES (:username, :groupname)");
foreach (explode(",", $userdata['groups']) as $group) {
$group = trim($group);
if (!in_array($group, $groups)) {
array_push($groups, $group);
if (!$sth->execute(array(':username' => $userdata['username'],
':groupname' => $group))) {
$dbh->rollback();
statementErrorAsXml($sth);
}
$sth->closeCursor();
}
}
}
$dbh->commit();
$dbh = null;
} catch (PDOException $e) {
errorAsXml($e->getMessage());
}
}
/**
* 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));
}
try {
$dbh = new PDO($GLOBALS['dsn'], $GLOBALS['dbuser'], $GLOBALS['dbpass']);
$dbh->beginTransaction();
$sth = $dbh->prepare("INSERT INTO dav_password (username, firstname, lastname, password) VALUES (:username, :firstname, :lastname, md5(:password))");
if (!$sth->execute(array(':username' => $userdata['username'],
':firstname' => $userdata['firstname'],
':lastname' => $userdata['lastname'],
':password' => $userdata['password']))) {
$dbh->rollback();
statementErrorAsXml($sth);
}
$uid = $dbh->lastInsertId('dav_password_uid_seq');
$groups = array();
$sth = $dbh->prepare("INSERT INTO dav_group (username, groupname) VALUES (:username, :groupname)");
foreach (split(",", $userdata['groups']) as $group) {
$group = trim($group);
if (!in_array($group, $groups)) {
array_push($groups, $group);
if (!$sth->execute(array(':username' => $userdata['username'],
':groupname' => $group))) {
$dbh->rollback();
statementErrorAsXml($sth);
}
$sth->closeCursor();
}
}
$dbh->commit();
$dbh = null;
} catch (PDOException $e) {
errorAsXml($e->getMessage);
}
return $uid;
}
/**
* 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)) {
errorAsXml(sprintf(_("Invalid user id %s"), $uid));
}
try {
$dbh = new PDO($GLOBALS['dsn'], $GLOBALS['dbuser'], $GLOBALS['dbpass']);
$dbh->beginTransaction();
$query = $dbh->prepare("DELETE FROM dav_group WHERE username IN (SELECT username FROM dav_password WHERE uid=:uid)");
if (!$query->execute(array(':uid' => $uid))) {
$dbh->rollback();
statementErrorAsXml($query);
}
$query = $dbh->prepare("DELETE FROM dav_password WHERE uid=:uid");
if (!$query->execute(array(':uid' => $uid))) {
$dbh->rollback();
statementErrorAsXml($query);
}
$dbh->commit();
$dbh = null;
} catch (PDOException $e) {
errorAsXml($e->getMessage());
}
}
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 {
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 {
$currentuser = $_SERVER['PHP_AUTH_USER'];
header("Content-Type: text/html; charset=UTF-8");
try {
$dbh = new PDO($dsn, $dbuser, $dbpass);
$query = $dbh->prepare("SELECT uid, username, firstname, lastname FROM dav_password ORDER BY username");
$query->execute();
$rows = $query->fetchall(PDO::FETCH_ASSOC);
foreach ($rows as $key => $value) {
$value['loggedin'] = ($value['username'] == $currentuser);
$rows[$key] = $value;
}
$smarty->assign("users", $rows);
$smarty->display("users.html");
} catch (PDOException $e) {
errorAsHtml($e->getMessage());
}
}
?>

35
config/config.inc.php Normal file
View file

@ -0,0 +1,35 @@
<?php
/**
* Global configuration for WebDAVAdmin.
*
* @author Jan Dittberner <jan@dittberner.info>
* @version $Id$
* @license GPL
* @package WebDAVAdmin
*
* Copyright (c) 2007 Jan Dittberner
*
* This file is part of WebDAVAdmin.
*
* This program 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 2 of the
* License, or (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
/** Absolute path to DAV area root directory with a trailing slash. */
define(DAV_ROOT, '@davrootdirectory@');
/** Include the database settings. */
require_once('@path.to.dbsettings@/dbsettings.inc.php');
?>

36
config/dbsettings.inc.php Normal file
View file

@ -0,0 +1,36 @@
<?php
/**
* Database settings.
*
* @author Jan Dittberner <jan@dittberner.info>
* @version $Id$
* @license GPL
* @package WebDAVAdmin
*
* Copyright (c) 2007 Jan Dittberner
*
* This file is part of WebDAV administration.
*
* This program 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 2 of the
* License, or (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
/** Data source name. */
$dsn = "pgsql:host=localhost port=5432 dbname=@dbname@";
/** Database user. */
$dbuser = "@dbuser@";
/** Database password. */
$dbpass = "@dbpass@";
?>

25
setup/schema.sql Normal file
View file

@ -0,0 +1,25 @@
CREATE TABLE dav_password (
uid SERIAL PRIMARY KEY,
username VARCHAR(16) NOT NULL UNIQUE,
password VARCHAR(34) NOT NULL,
firstname VARCHAR(64),
lastname VARCHAR(64)
);
CREATE TABLE dav_group (
gid SERIAL PRIMARY KEY,
username VARCHAR(16) NOT NULL REFERENCES dav_password(username),
groupname VARCHAR(32) NOT NULL,
UNIQUE(username, groupname)
);
CREATE TABLE dav_log (
logid SERIAL PRIMARY KEY,
username VARCHAR(16),
reqdate VARCHAR(20),
uri TEXT,
ipaddr VARCHAR(16)
);
INSERT INTO dav_password (username, password) VALUES ('admin', md5('secret'));
INSERT INTO dav_group (username, groupname) VALUES ('admin', 'davadmin');

52
setup/webdavadmin.vhost Normal file
View file

@ -0,0 +1,52 @@
<VirtualHost *:80>
ServerAdmin webmaster@davhost.yourdomain.net
ServerName davhost.yourdomain.net
DavLockDb /var/run/apache2/davlock/davhost.yourdomain.net
DocumentRoot /home/www/usr29/html
php_admin_value allow_call_time_pass_reference 1
<Directory /var/www/dav>
Dav on
AllowOverride AuthConfig Indexes
Order Allow,Deny
allow from all
AuthType Basic
AuthName "WebDAV on davhost"
AuthBasicAuthoritative Off
AuthUserFile /etc/apache2/auth/davhost.yourdomain.net
Auth_PG_host localhost
Auth_PG_port 5432
Auth_PG_user @dbuser@
Auth_PG_pwd @dbpassword@
Auth_PG_database @dbname@
Auth_PG_pwd_table dav_password
Auth_PG_uid_field username
Auth_PG_pwd_field password
Auth_PG_grp_table dav_group
Auth_PG_grp_user_field username
Auth_PG_grp_group_field groupname
Auth_PG_hash_type MD5
#Auth_PG_log_table dav_log
#Auth_PG_log_uname_field username
#Auth_PG_log_date_field reqdate
#Auth_PG_log_uri_field uri
#Auth_PG_log_addrs_field ipaddr
Auth_PG_authoritative on
require group davroot
</Directory>
ErrorLog /var/log/apache2/davhost.yourdomain.net_error.log
# Possible values include: debug, info, notice, warn, error, crit,
# alert, emerg.
LogLevel warn
CustomLog /var/log/apache2/davhost.yourdomain.net_access.log combined
</VirtualHost>