diff --git a/config/dav.inc.php.sample b/config/dav.inc.php.sample index 9f56d50..7ec74ca 100644 --- a/config/dav.inc.php.sample +++ b/config/dav.inc.php.sample @@ -1,123 +1,120 @@ . | | | +-------------------------------------------------------------------------+ */ $config = array(); // The HTTP path to the iRony root directory. // Set to / if the service is registered as document root for a virtual host $config['base_uri'] = '/iRony/'; // User agent string written to kolab storage MIME messages $config['useragent'] = 'Kolab DAV Server libkolab/' . RCUBE_VERSION; // Type of Auth cache. Supported values: 'db', 'apc' and 'memcache'. // Note: This is only for username canonification map. $config['kolabdav_auth_cache'] = 'db'; // lifetime of the Auth cache, possible units: s, m, h, d, w $config['kolabdav_auth_cache_ttl'] = '1h'; // enable debug console showing the internal function calls triggered // by http requests. This will write log to /var/log/iRony/console $config['kolabdav_console'] = false; -// enable per-user debugging if /var/log/iRony// folder exists -$config['kolabdav_user_debug'] = false; - // enable logging of full HTTP payload // (bitmask of these values: 2 = HTTP Requests, 4 = HTTP Responses) $config['kolabdav_http_log'] = 0; // expose iTip invitations from email inbox in CalDAV scheduling inbox. // this will make capable CalDAV clients process event invitations and // as a result, the invitation messages are removed from the email inbox. // WARNING: this feature is still experimental and not fully implemented. // See https://git.kolab.org/T93 for details and implementation status. $config['kolabdav_caldav_inbox'] = false; // Enables the CardDAV Directory Gateway Extension by exposing an // LDAP-based address book in the pricipals address book collection. // Properties of this option are the same as for $config['ldap_public'] entries. /* $config['kolabdav_ldap_directory'] = array( 'name' => 'Global Address Book', 'hosts' => 'localhost', 'port' => 389, 'use_tls' => false, // If true the base_dn, bind_dn and bind_pass default to the user's credentials. 'user_specific' => false, // It's possible to bind with the current user's credentials for individual address books. // The login name is used to search for the DN to bind with 'search_base_dn' => 'ou=People,dc=example,dc=org', 'search_bind_dn' => 'uid=kolab-service,ou=Special Users,dc=example,dc=org', 'search_bind_pw' => 'Welcome2KolabSystems', 'search_filter' => '(&(objectClass=inetOrgPerson)(mail=%fu))', // When 'user_specific' is enabled following variables can be used in base_dn/bind_dn config: // %fu - The full username provided, assumes the username is an email // address, uses the username_domain value if not an email address. // %u - The username prior to the '@'. // %d - The domain name after the '@'. // %dc - The domain name hierarchal string e.g. "dc=test,dc=domain,dc=com" // %dn - DN found by ldap search when search_filter/search_base_dn are used 'base_dn' => 'ou=People,dc=example,dc=org', 'bind_dn' => 'uid=kolab-service,ou=Special Users,dc=example,dc=org', 'bind_pass' => 'Welcome2KolabSystems', 'ldap_version' => 3, 'filter' => '(objectClass=inetOrgPerson)', 'search_fields' => array('displayname', 'mail'), 'sort' => array('displayname', 'sn', 'givenname', 'cn'), 'scope' => 'sub', 'searchonly' => true, // Set to false to enable listing 'sizelimit' => '1000', 'timelimit' => '0', 'fieldmap' => array( // Roundcube => LDAP 'name' => 'displayName', 'surname' => 'sn', 'firstname' => 'givenName', 'middlename' => 'initials', 'prefix' => 'title', 'email:work' => 'mail', 'email:other' => 'alias', 'phone:main' => 'telephoneNumber', 'phone:work' => 'alternateTelephoneNumber', 'phone:mobile' => 'mobile', 'phone:work2' => 'blackberry', 'street' => 'street', 'zipcode' => 'postalCode', 'locality' => 'l', 'organization' => 'o', 'jobtitle' => 'title', 'photo' => 'jpegphoto', // required for internal handling and caching 'uid' => 'nsuniqueid', 'changed' => 'modifytimestamp', ), ); */ // Enable caching for LDAP directory data. // This is recommended with 'searchonly' => false to speed-up sychronization of multiple clients // $config['kolabdav_ldap_cache'] = 'memcache'; // $config['kolabdav_ldap_cache_ttl'] = 600; // in seconds diff --git a/lib/Kolab/Utils/DAVLogger.php b/lib/Kolab/Utils/DAVLogger.php index b35641c..f355a70 100644 --- a/lib/Kolab/Utils/DAVLogger.php +++ b/lib/Kolab/Utils/DAVLogger.php @@ -1,220 +1,203 @@ * * Copyright (C) 2013, Kolab Systems AG * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ namespace Kolab\Utils; use \rcube; use Sabre\DAV; use Kolab\DAV\Auth\HTTPBasic; /** * Utility class to log debug information about processed DAV requests */ class DAVLogger extends DAV\ServerPlugin { - const CONSOLE = 1; - const HTTP_REQUEST = 2; + const CONSOLE = 1; + const HTTP_REQUEST = 2; const HTTP_RESPONSE = 4; private $rcube; private $server; private $method; private $loglevel; /** * Default constructor */ public function __construct($level = 1) { - $this->rcube = rcube::get_instance(); + $this->rcube = rcube::get_instance(); $this->loglevel = $level; } /** * This initializes the plugin. * This method should set up the required event subscriptions. * * @param Server $server */ public function initialize(DAV\Server $server) { $this->server = $server; $server->on('beforeMethod', array($this, '_beforeMethod'), 15); $server->on('exception', array($this, '_exception')); $server->on('exit', array($this, '_exit')); // replace $server->httpResponse with a derived class that can do logging $server->httpResponse = new HTTPResponse(); } /** * Handler for 'beforeMethod' events */ public function _beforeMethod($request, $response) { $this->method = $request->getMethod(); // turn on per-user http logging if the destination file exists - if ($this->loglevel < 2 && $this->rcube->config->get('kolabdav_user_debug', false) - && ($log_dir = $this->user_log_dir()) && file_exists($log_dir . '/httpraw')) { + if ($this->loglevel < 2 && $this->rcube->config->get('per_user_logging', false) + && ($log_dir = $this->user_log_dir()) && file_exists($log_dir . '/httpraw') + ) { $this->loglevel |= (self::HTTP_REQUEST | self::HTTP_RESPONSE); } // log full HTTP request data if ($this->loglevel & self::HTTP_REQUEST) { $content_type = $request->getHeader('Content-Type'); if (strpos($content_type, 'text/') === 0 || strpos($content_type, 'application/xml') === 0) { $http_body = $request->getBodyAsString(); // Hack for reading php:://input because that stream can only be read once. // This is why we re-populate the request body with the existing data. $request->setBody($http_body); } else if (!empty($content_type)) { $http_body = '[binary data]'; } // catch all headers $http_headers = array(); foreach ($this->get_request_headers() as $hdr => $value) { if (strtolower($hdr) == 'authorization') { $method = preg_match('/^((basic|digest)\s+)/i', $value, $m) ? $m[1] : ''; $value = $method . str_repeat('*', strlen($value) - strlen($method)); } $http_headers[$hdr] = "$hdr: $value"; } - $this->write_log('httpraw', $request->getMethod() . ' ' . $request->getUrl() . ' ' . $_SERVER['SERVER_PROTOCOL'] . "\n" . + rcube::write_log('httpraw', $request->getMethod() . ' ' . $request->getUrl() . ' ' . $_SERVER['SERVER_PROTOCOL'] . "\n" . join("\n", $http_headers) . "\n\n" . $http_body); } // log to console if ($this->loglevel & self::CONSOLE) { - $this->write_log('console', $this->method . ' ' . $request->getUrl()); + rcube::write_log('console', $this->method . ' ' . $request->getUrl()); } } /* * Wrapper function in case apache_request_headers() is not available * * @return array */ public function get_request_headers() { if (function_exists('apache_request_headers')) { return apache_request_headers(); } $return = array(); foreach ($_SERVER as $key => $value) { if (preg_match('/^HTTP_(.*)/',$key,$regs)) { // restore original letter case $key = str_replace('_',' ',$regs[1]); $key = ucwords(strtolower($key)); $key = str_replace(' ','-',$key); // build return array $return[$key] = $value; } } return $return; } /** * Handler for 'exception' events */ public function _exception($e) { // log to console $this->console(get_class($e) . ' (EXCEPTION)', $e->getMessage() /*, $e->getTraceAsString()*/); } /** * Handler for 'exit' events */ public function _exit() { if ($this->loglevel & self::CONSOLE) { $time = microtime(true) - KOLAB_DAV_START; if (function_exists('memory_get_usage')) - $mem = round(memory_get_usage() / 1024 / 1024, 1) . 'MB'; + $mem = round(memory_get_usage() / 1024 / 1024, 1) . 'MB'; if (function_exists('memory_get_peak_usage')) - $mem .= '/' . round(memory_get_peak_usage() / 1024 / 1024, 1) . 'MB'; + $mem .= '/' . round(memory_get_peak_usage() / 1024 / 1024, 1) . 'MB'; - $this->write_log('console', sprintf("/%s: %0.4f sec; %s", $this->method, $time, $mem)); + rcube::write_log('console', sprintf("/%s: %0.4f sec; %s", $this->method, $time, $mem)); } // log full HTTP reponse if ($this->loglevel & self::HTTP_RESPONSE) { - $this->write_log('httpraw', "RESPONSE: " . $this->server->httpResponse->dump()); + rcube::write_log('httpraw', "RESPONSE: " . $this->server->httpResponse->dump()); } } /** * Wrapper for rcube::cosole() to write per-user logs */ public function console(/* ... */) { if ($this->loglevel & self::CONSOLE) { $msg = array(); foreach (func_get_args() as $arg) { $msg[] = !is_string($arg) ? var_export($arg, true) : $arg; } - $this->write_log('console', join(";\n", $msg)); + rcube::write_log('console', join(";\n", $msg)); } } - /** - * Wrapper for rcube::write_log() that can write per-user logs - */ - public function write_log($filename, $msg) - { - // dump data per user - if ($this->rcube->config->get('kolabdav_user_debug', false)) { - if ($this->user_log_dir()) { - $filename = HTTPBasic::$current_user . '/' . $filename; - } - else { - return; // don't log - } - } - - rcube::write_log($filename, $msg); - } - /** * Get the per-user log directory */ private function user_log_dir() { $log_dir = $this->rcube->config->get('log_dir', RCUBE_INSTALL_PATH . 'logs'); $user_log_dir = $log_dir . '/' . HTTPBasic::$current_user; return HTTPBasic::$current_user && is_writable($user_log_dir) ? $user_log_dir : false; } -} \ No newline at end of file +} diff --git a/public_html/index.php b/public_html/index.php index 2919763..dd81068 100644 --- a/public_html/index.php +++ b/public_html/index.php @@ -1,197 +1,197 @@ * * Copyright (C) 2013-2014, Kolab Systems AG * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ // define some environment variables used throughout the app and libraries define('KOLAB_DAV_ROOT', realpath('../')); define('KOLAB_DAV_VERSION', '0.4-dev'); define('KOLAB_DAV_START', microtime(true)); define('RCUBE_INSTALL_PATH', KOLAB_DAV_ROOT . '/'); define('RCUBE_CONFIG_DIR', KOLAB_DAV_ROOT . '/config/'); define('RCUBE_PLUGINS_DIR', KOLAB_DAV_ROOT . '/lib/plugins/'); // suppress error notices ini_set('error_reporting', E_ALL &~ E_NOTICE &~ E_STRICT); /** * Mapping PHP errors to exceptions. * * While this is not strictly needed, it makes a lot of sense to do so. If an * E_NOTICE or anything appears in your code, this allows SabreDAV to intercept * the issue and send a proper response back to the client (HTTP/1.1 500). */ function exception_error_handler($errno, $errstr, $errfile, $errline ) { throw new ErrorException($errstr, 0, $errno, $errfile, $errline); } //set_error_handler("exception_error_handler"); // use composer's autoloader for dependencies $loader = require_once(KOLAB_DAV_ROOT . '/vendor/autoload.php'); $loader->set('Kolab', array(KOLAB_DAV_ROOT . '/lib')); // register iRony namespace(s) $loader->setUseIncludePath(true); // enable include_path to load PEAR classes from their default location // load the Roundcube framework with its autoloader require_once KOLAB_DAV_ROOT . '/lib/Roundcube/bootstrap.php'; // Roundcube framework initialization $rcube = rcube::get_instance(rcube::INIT_WITH_DB | rcube::INIT_WITH_PLUGINS); $rcube->config->load_from_file(RCUBE_CONFIG_DIR . 'dav.inc.php'); // Load plugins $plugins = (array)$rcube->config->get('kolabdav_plugins', array('kolab_auth')); $required = array('libkolab', 'libcalendaring'); $rcube->plugins->init($rcube); $rcube->plugins->load_plugins($plugins, $required); // enable logger -if ($rcube->config->get('kolabdav_console') || $rcube->config->get('kolabdav_user_debug')) { +if ($rcube->config->get('kolabdav_console') || $rcube->config->get('per_user_logging')) { $logger = new \Kolab\Utils\DAVLogger((\Kolab\Utils\DAVLogger::CONSOLE | $rcube->config->get('kolabdav_http_log', 0))); } // convenience function, you know it well :-) function console() { global $logger; if ($logger) { call_user_func_array(array($logger, 'console'), func_get_args()); } } // Make sure this setting is turned on and reflects the root url of the *DAV server. $base_uri = $rcube->config->get('base_uri', slashify(substr(dirname($_SERVER['SCRIPT_FILENAME']), strlen($_SERVER['DOCUMENT_ROOT'])))); // add filename to base URI when called without mod_rewrite (e.g. /dav/index.php/calendar) if (strpos($_SERVER['REQUEST_URI'], 'index.php')) $base_uri .= 'index.php/'; // create the various backend instances $auth_backend = new \Kolab\DAV\Auth\HTTPBasic(); $principal_backend = new \Kolab\DAVACL\PrincipalBackend(); $services = array(); foreach (array('CALDAV','CARDDAV','WEBDAV') as $skey) { if (getenv($skey)) $services[$skey] = 1; } // no config means *all* services if (empty($services)) $services = array('CALDAV' => 1, 'CARDDAV' => 1, 'WEBDAV' => 1); // add chwala directories to include path for autoloading if ($services['WEBDAV']) { $include_path = ini_get('include_path') . PATH_SEPARATOR; $include_path .= KOLAB_DAV_ROOT . '/lib/FileAPI' . PATH_SEPARATOR; $include_path .= KOLAB_DAV_ROOT . '/lib/FileAPI/kolab' . PATH_SEPARATOR; $include_path .= KOLAB_DAV_ROOT . '/lib/FileAPI/ext'; set_include_path($include_path); } // Build the directory tree // This is an array which contains the 'top-level' directories in the WebDAV server. if ($services['CALDAV'] || $services['CARDDAV']) { $nodes = array( new \Kolab\CalDAV\Principal\Collection($principal_backend), ); if ($services['CALDAV']) { $caldav_backend = new \Kolab\CalDAV\CalendarBackend(); $caldav_backend->setUserAgent($_SERVER['HTTP_USER_AGENT']); $nodes[] = new \Kolab\CalDAV\CalendarRootNode($principal_backend, $caldav_backend); } if ($services['CARDDAV']) { $carddav_backend = new \Kolab\CardDAV\ContactsBackend(); $carddav_backend->setUserAgent($_SERVER['HTTP_USER_AGENT']); $nodes[] = new \Kolab\CardDAV\AddressBookRoot($principal_backend, $carddav_backend); } if ($services['WEBDAV']) { $nodes[] = new \Kolab\DAV\Collection(\Kolab\DAV\Collection::ROOT_DIRECTORY); } } // register WebDAV service as root else if ($services['WEBDAV']) { $nodes = new \Kolab\DAV\Collection(''); } // the object tree needs in turn to be passed to the server class $server = new \Sabre\DAV\Server($nodes); $server->setBaseUri($base_uri); // connect logger if (is_object($logger)) { $server->addPlugin($logger); } // register some plugins $server->addPlugin(new \Sabre\DAV\Auth\Plugin($auth_backend, 'KolabDAV')); $server->addPlugin(new \Sabre\DAVACL\Plugin()); if ($services['CALDAV']) { $server->addPlugin(new \Kolab\CalDAV\Plugin()); $server->addPlugin(new \Kolab\CalDAV\SchedulePlugin()); $server->addPlugin(new \Kolab\CalDAV\IMipPlugin('')); } if ($services['CARDDAV']) { $server->addPlugin(new \Kolab\CardDAV\Plugin()); } if ($services['WEBDAV']) { // the lock manager is responsible for making sure users don't overwrite each others changes. $locks_backend = new \Kolab\DAV\Locks\Chwala(\Kolab\DAV\Collection::ROOT_DIRECTORY); $server->addPlugin(new \Sabre\DAV\Locks\Plugin($locks_backend)); // intercept some of the garbage files operation systems tend to generate when mounting a WebDAV share $server->addPlugin(new \Kolab\DAV\TempFilesPlugin(KOLAB_DAV_ROOT . '/temp')); } // HTML UI for browser-based access (recommended only for development) if (getenv('DAVBROWSER')) { $server->addPlugin(new \Sabre\DAV\Browser\Plugin()); } // log exceptions in iRony error log $server->on('exception', function($e){ if (!($e instanceof \Sabre\DAV\Exception) || $e->getHTTPCode() == 500) { rcube::raise_error(array( 'code' => 500, 'type' => 'php', 'file' => $e->getFile(), 'line' => $e->getLine(), 'message' => $e->getMessage() . " (error 500)\n" . $e->getTraceAsString(), ), true, false); } }); // finally, process the request $server->exec(); // trigger log $server->emit('exit', array());