From ac78cbbdaead9ff36f0ce8ce5d9593a616765c8c Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Wed, 23 Nov 2011 16:37:43 +0000 Subject: [PATCH] * CalDAV/CardDAV/GroupDAV new user preference to log requests and responses to Apache error-log --- egw-pear/HTTP/WebDAV/Server.php | 70 +++++++++++++++---- .../HTTP/WebDAV/Tools/_parse_propfind.php | 12 +++- .../HTTP/WebDAV/Tools/_parse_proppatch.php | 43 +++++++----- phpgwapi/inc/class.groupdav.inc.php | 43 +++++++++++- phpgwapi/inc/class.groupdav_hooks.inc.php | 10 +-- 5 files changed, 141 insertions(+), 37 deletions(-) diff --git a/egw-pear/HTTP/WebDAV/Server.php b/egw-pear/HTTP/WebDAV/Server.php index d52aecd39f..7e43ba6ebf 100644 --- a/egw-pear/HTTP/WebDAV/Server.php +++ b/egw-pear/HTTP/WebDAV/Server.php @@ -599,6 +599,19 @@ class HTTP_WebDAV_Server // {{{ http_PROPFIND() + /** + * Should the whole PROPFIND request (xml) be stored + * + * @var boolean + */ + var $store_request = false; + /** + * Content of (last) PROPFIND request + * + * @var string + */ + var $request; + /** * PROPFIND method handler * @@ -620,7 +633,8 @@ class HTTP_WebDAV_Server } // analyze request payload - $propinfo = new _parse_propfind("php://input"); + $propinfo = new _parse_propfind("php://input", $this->store_request); + if ($this->store_request) $this->request = $propinfo->request; if (!$propinfo->success) { $this->http_status("400 Error"); return; @@ -1052,7 +1066,7 @@ class HTTP_WebDAV_Server $options["path"] = $this->path; - $propinfo = new _parse_proppatch("php://input"); + $propinfo = new _parse_proppatch("php://input", $this->store_request); if (!$propinfo->success) { $this->http_status("400 Error"); @@ -1409,6 +1423,26 @@ class HTTP_WebDAV_Server } $options['stream'] = fopen('php://input', 'r'); + switch($this->_SERVER['HTTP_CONTENT_ENCODING']) + { + case 'gzip': + case 'deflate': //zlib + if (extension_loaded('zlib')) + { + stream_filter_append($options['stream'], 'zlib.inflate', STREAM_FILTER_READ); + } + } + // store request in $this->request, if requested via $this->store_request + if ($this->store_request) + { + $options['content'] = ''; + while(!feof($options['stream'])) + { + $options['content'] .= fread($options['stream'],8192); + } + $this->request =& $options['content']; + unset($options['stream']); + } /* RFC 2616 2.6 says: "The recipient of the entity MUST NOT ignore any Content-* (e.g. Content-Range) headers that it @@ -1423,11 +1457,7 @@ class HTTP_WebDAV_Server { case 'gzip': case 'deflate': //zlib - if (extension_loaded('zlib')) - { - stream_filter_append($options['stream'], 'zlib.inflate', STREAM_FILTER_READ); - break; - } + if (extension_loaded('zlib')) break; // fall through for no zlib support default: $this->http_status('415 Unsupported Media Type'); @@ -1568,6 +1598,26 @@ class HTTP_WebDAV_Server } $options["stream"] = fopen("php://input", "r"); + switch($this->_SERVER['HTTP_CONTENT_ENCODING']) + { + case 'gzip': + case 'deflate': //zlib + if (extension_loaded('zlib')) + { + stream_filter_append($options['stream'], 'zlib.inflate', STREAM_FILTER_READ); + } + } + // store request in $this->request, if requested via $this->store_request + if ($this->store_request) + { + $options['content'] = ''; + while(!feof($options['stream'])) + { + $options['content'] .= fread($options['stream'],8192); + } + $this->request =& $options['content']; + unset($options['stream']); + } /* RFC 2616 2.6 says: "The recipient of the entity MUST NOT ignore any Content-* (e.g. Content-Range) headers that it @@ -1582,11 +1632,7 @@ class HTTP_WebDAV_Server { case 'gzip': case 'deflate': //zlib - if (extension_loaded('zlib')) - { - stream_filter_append($options['stream'], 'zlib.inflate', STREAM_FILTER_READ); - break; - } + if (extension_loaded('zlib')) break; // fall through for no zlib support default: $this->http_status('415 Unsupported Media Type'); diff --git a/egw-pear/HTTP/WebDAV/Tools/_parse_propfind.php b/egw-pear/HTTP/WebDAV/Tools/_parse_propfind.php index 2d1f4ccc37..20092c4201 100644 --- a/egw-pear/HTTP/WebDAV/Tools/_parse_propfind.php +++ b/egw-pear/HTTP/WebDAV/Tools/_parse_propfind.php @@ -97,13 +97,21 @@ class _parse_propfind */ var $depth = 0; + /** + * On return whole request, if $store_request == true was specified in constructor + * + * @var string + */ + var $request; /** * constructor * * @access public + * @param string $path + * @param boolean $store_request=false if true whole request data will be made available in $this->request */ - function _parse_propfind($path) + function _parse_propfind($path, $store_request=false) { // success state flag $this->success = true; @@ -140,10 +148,10 @@ class _parse_propfind xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, false); - // parse input while ($this->success && !feof($f_in)) { $line = fgets($f_in); + if ($store_request) $this->request .= $line; if (is_string($line)) { $had_input = true; $this->success &= xml_parse($xml_parser, $line, false); diff --git a/egw-pear/HTTP/WebDAV/Tools/_parse_proppatch.php b/egw-pear/HTTP/WebDAV/Tools/_parse_proppatch.php index 3cd951db48..25a0bbef9a 100644 --- a/egw-pear/HTTP/WebDAV/Tools/_parse_proppatch.php +++ b/egw-pear/HTTP/WebDAV/Tools/_parse_proppatch.php @@ -36,16 +36,16 @@ /** * helper class for parsing PROPPATCH request bodies - * + * * @package HTTP_WebDAV_Server * @author Hartmut Holzgraefe * @version @package-version@ */ -class _parse_proppatch +class _parse_proppatch { /** * - * + * * @var * @access */ @@ -53,7 +53,7 @@ class _parse_proppatch /** * - * + * * @var * @access */ @@ -61,7 +61,7 @@ class _parse_proppatch /** * - * + * * @var * @access */ @@ -69,7 +69,7 @@ class _parse_proppatch /** * - * + * * @var * @access */ @@ -77,19 +77,27 @@ class _parse_proppatch /** * - * + * * @var * @access */ var $current; + /** + * On return whole request, if $store_request == true was specified in constructor + * + * @var string + */ + var $request; + /** * constructor - * - * @param string path of input stream + * + * @param string path of input stream + * @param boolean $store_request=false if true whole request data will be made available in $this->request * @access public */ - function _parse_proppatch($path) + function _parse_proppatch($path, $store_request=false) { $this->success = true; @@ -117,12 +125,13 @@ class _parse_proppatch while($this->success && !feof($f_in)) { $line = fgets($f_in); + if ($store_request) $this->request .= $line; if (is_string($line)) { $had_input = true; $this->success &= xml_parse($xml_parser, $line, false); } - } - + } + if($had_input) { $this->success &= xml_parse($xml_parser, "", true); } @@ -141,7 +150,7 @@ class _parse_proppatch * @return void * @access private */ - function _startElement($parser, $name, $attrs) + function _startElement($parser, $name, $attrs) { if (strstr($name, " ")) { list($ns, $tag) = explode(" ", $name); @@ -154,7 +163,7 @@ class _parse_proppatch if ($this->depth == 1) { $this->mode = $tag; - } + } if ($this->depth == 3) { $prop = array("name" => $tag); @@ -174,7 +183,7 @@ class _parse_proppatch $this->current["val"] .= ">"; } - + $this->depth++; } @@ -187,7 +196,7 @@ class _parse_proppatch * @return void * @access private */ - function _endElement($parser, $name) + function _endElement($parser, $name) { if (strstr($name, " ")) { list($ns, $tag) = explode(" ", $name); @@ -220,7 +229,7 @@ class _parse_proppatch * @return void * @access private */ - function _data($parser, $data) + function _data($parser, $data) { if (isset($this->current)) { $this->current["val"] .= $data; diff --git a/phpgwapi/inc/class.groupdav.inc.php b/phpgwapi/inc/class.groupdav.inc.php index e1eafe3d12..d4c38bdfb9 100644 --- a/phpgwapi/inc/class.groupdav.inc.php +++ b/phpgwapi/inc/class.groupdav.inc.php @@ -351,7 +351,7 @@ class groupdav extends HTTP_WebDAV_Server { // principals collection $files['files'][] = $this->add_collection('/principals/', array( - 'displayname' => lang('Accounts'), + 'displayname' => lang('Accounts'), )); // todo: account_selection owngroups and none!!! foreach($this->accounts->search(array('type' => 'both')) as $account) @@ -1235,7 +1235,7 @@ class groupdav extends HTTP_WebDAV_Server } if ($path[0] == '/') { - $path = substr($path, 1); + $path = substr($path, 1); } $parts = explode('/', $this->_unslashify($path)); @@ -1293,4 +1293,43 @@ class groupdav extends HTTP_WebDAV_Server self::mkprop('write-content',''), ))); } + + /** + * Serve WebDAV HTTP request + * + * Reimplemented to add logging, currently only to Apache error-log + */ + function ServeRequest() + { + if (($debug_level=$GLOBALS['egw_info']['user']['preferences']['groupdav']['debug_level']) === 'r' || + $debug_level === 'f' || $this->debug) + { + $starttime = microtime(true); + $this->store_request = true; + ob_start(); + } + parent::ServeRequest(); + + if ($starttime) + { + error_log($_SERVER['REQUEST_METHOD'].' '.$_SERVER['PATH_INFO'].' HTTP/1.1'); + // reconstruct headers + foreach($_SERVER as $name => $value) + { + list($type,$name) = explode('_',$name,2); + if ($type == 'HTTP' || $type == 'CONTENT') error_log(str_replace(' ','-',ucwords(strtolower(($type=='HTTP'?'':$type.' ').str_replace('_',' ',$name)))).': '.$value); + } + if ($this->request) + { + error_log(''); + foreach(explode("\n",$this->request) as $line) error_log($line); + } + error_log('HTTP/1.1 '.$this->_http_status); + foreach(headers_list() as $line) error_log($line); + if (($content = ob_get_flush())) error_log(''); + if (strlen($content) > 1024 && $debug_level !== 'f') $content = substr($content,0,1024)."\n*** LOG TRUNKATED "; + $content .= sprintf('*** %s --> "%s" took %5.3f s',$_SERVER['REQUEST_METHOD'].($_SERVER['REQUEST_METHOD']=='REPORT'?' '.$this->propfind_options['root']['name']:'').' '.$_SERVER['PATH_INFO'],$this->_http_status,microtime(true)-$starttime)."\n"; + foreach(explode("\n",$content) as $line) error_log($line); + } + } } diff --git a/phpgwapi/inc/class.groupdav_hooks.inc.php b/phpgwapi/inc/class.groupdav_hooks.inc.php index dbd2989e4d..d26febedf3 100644 --- a/phpgwapi/inc/class.groupdav_hooks.inc.php +++ b/phpgwapi/inc/class.groupdav_hooks.inc.php @@ -120,10 +120,12 @@ class groupdav_hooks 'name' => 'debug_level', 'help' => 'Enables debug-messages to Apache/PHP error-log, allowing to diagnose problems on a per user basis.', 'values' => array( - '0' => '0 - off', - '1' => '1 - function calls', - '2' => '2 - more info', - '3' => '3 - complete $_SERVER array', + '0' => 'Off', + 'r' => 'Requests and truncated responses', + 'f' => 'Requests and full responses', + '1' => 'Debug 1 - function calls', + '2' => 'Debug 2 - more info', + '3' => 'Debug 3 - complete $_SERVER array', ), 'xmlrpc' => true, 'admin' => false,