summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/Kolab/Utils/DAVLogger.php175
-rw-r--r--lib/Kolab/Utils/HTTPResponse.php92
2 files changed, 232 insertions, 35 deletions
diff --git a/lib/Kolab/Utils/DAVLogger.php b/lib/Kolab/Utils/DAVLogger.php
index 982d87e..311fc8a 100644
--- a/lib/Kolab/Utils/DAVLogger.php
+++ b/lib/Kolab/Utils/DAVLogger.php
@@ -23,7 +23,9 @@
namespace Kolab\Utils;
+use \rcube;
use Sabre\DAV;
+use Kolab\DAV\Auth\HTTPBasic;
/**
@@ -31,8 +33,24 @@ use Sabre\DAV;
*/
class DAVLogger extends DAV\ServerPlugin
{
+ 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->loglevel = $level;
+ }
/**
* This initializes the plugin.
@@ -40,47 +58,134 @@ class DAVLogger extends DAV\ServerPlugin
*
* @param Server $server
*/
- public function initialize(DAV\Server $server)
- {
- $this->server = $server;
+ public function initialize(DAV\Server $server)
+ {
+ $this->server = $server;
- $server->subscribeEvent('beforeMethod', array($this, '_beforeMethod'));
- $server->subscribeEvent('exception', array($this, '_exception'));
- $server->subscribeEvent('exit', array($this, '_exit'));
+ $server->subscribeEvent('beforeMethod', array($this, '_beforeMethod'), 15);
+ $server->subscribeEvent('exception', array($this, '_exception'));
+ $server->subscribeEvent('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($method, $uri)
- {
- $this->method = $method;
+ /**
+ * Handler for 'beforeMethod' events
+ */
+ public function _beforeMethod($method, $uri)
+ {
+ $this->method = $method;
- // log to console
- console($method . ' ' . $uri);
- }
+ // 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')) {
+ $this->loglevel |= (self::HTTP_REQUEST | self::HTTP_RESPONSE);
+ }
- /**
- * Handler for 'exception' events
- */
- public function _exception($e)
- {
- // log to console
- console(get_class($e) . ' (EXCEPTION)', $e->getMessage() /*, $e->getTraceAsString()*/);
- }
+ // log full HTTP request data
+ if ($this->loglevel & self::HTTP_REQUEST) {
+ $request = $this->server->httpRequest;
+ $content_type = $request->getHeader('CONTENT_TYPE');
+ if (strpos($content_type, 'text/') === 0) {
+ $http_body = $request->getBody(true);
- /**
- * Handler for 'exit' events
- */
- public function _exit()
- {
- $time = microtime(true) - KOLAB_DAV_START;
+ // 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]';
+ }
- if (function_exists('memory_get_usage'))
- $mem = round(memory_get_usage() / 1024) . 'K';
- if (function_exists('memory_get_peak_usage'))
- $mem .= '/' . round(memory_get_peak_usage() / 1024) . 'K';
+ // catch all headers
+ $http_headers = array();
+ foreach (apache_request_headers() as $hdr => $value) {
+ $http_headers[$hdr] = "$hdr: $value";
+ }
- console(sprintf("/%s: %0.4f sec; %s", $this->method, $time, $mem));
- }
+ $this->write_log('httpraw', $request->getMethod() . ' ' . $request->getUri() . ' ' . $_SERVER['SERVER_PROTOCOL'] . "\n" .
+ join("\n", $http_headers) . "\n\n" . $http_body);
+ }
+
+ // log to console
+ if ($this->loglevel & self::CONSOLE) {
+ $this->write_log('console', $method . ' ' . $uri);
+ }
+ }
+
+ /**
+ * 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) . 'K';
+ if (function_exists('memory_get_peak_usage'))
+ $mem .= '/' . round(memory_get_peak_usage() / 1024) . 'K';
+
+ $this->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());
+ }
+ }
+
+ /**
+ * 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));
+ }
+ }
+
+ /**
+ * 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/lib/Kolab/Utils/HTTPResponse.php b/lib/Kolab/Utils/HTTPResponse.php
new file mode 100644
index 0000000..4f4e72d
--- /dev/null
+++ b/lib/Kolab/Utils/HTTPResponse.php
@@ -0,0 +1,92 @@
+<?php
+
+/**
+ * Utility class representing a HTTP response with logging capabilities
+ *
+ * @author Thomas Bruederli <bruederli@kolabsys.com>
+ *
+ * Copyright (C) 2013, Kolab Systems AG <contact@kolabsys.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+namespace Kolab\Utils;
+
+/**
+ * This class represents a HTTP response.
+ */
+class HTTPResponse extends \Sabre\HTTP\Response
+{
+ private $status;
+ private $body = '';
+ private $headers = array();
+
+ /**
+ * Sends an HTTP status header to the client.
+ *
+ * @param int $code HTTP status code
+ * @return bool
+ */
+ public function sendStatus($code)
+ {
+ $this->status = $this->getStatusMessage($code, $this->defaultHttpVersion);
+ return parent::sendStatus($code);
+ }
+
+ /**
+ * Sets an HTTP header for the response
+ *
+ * @param string $name
+ * @param string $value
+ * @param bool $replace
+ * @return bool
+ */
+ public function setHeader($name, $value, $replace = true) {
+ $this->headers[$name] = $value;
+ return parent::setHeader($name, $value, $replace);
+ }
+
+ /**
+ * Sends the entire response body
+ *
+ * This method can accept either an open filestream, or a string.
+ *
+ * @param mixed $body
+ * @return void
+ */
+ public function sendBody($body)
+ {
+ if (is_resource($body)) {
+ fpassthru($body);
+ $this->body = '[binary data]';
+ }
+ else {
+ echo $body;
+ $this->body .= $body;
+ }
+ }
+
+ /**
+ * Dump the response data for logging
+ */
+ public function dump()
+ {
+ $result_headers = '';
+ foreach ($this->headers as $hdr => $value) {
+ $result_headers .= "\n$hdr: " . $value;
+ }
+
+ return $this->status . $result_headers . "\n\n" . $this->body;
+ }
+} \ No newline at end of file