new config to disallow webdav clients using user agent strings
[tine20] / tine20 / Tinebase / Server / WebDAV.php
1 <?php
2 /**
3  * Tine 2.0
4  * 
5  * @package     Tinebase
6  * @subpackage  Server
7  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
8  * @copyright   Copyright (c) 2011-2015 Metaways Infosystems GmbH (http://www.metaways.de)
9  * @author      Lars Kneschke <l.kneschke@metaways.de>
10  */
11
12 /**
13  * webdav Server class with handle() function
14  * 
15  * @package     Tinebase
16  * @subpackage  Server
17  */
18 class Tinebase_Server_WebDAV extends Tinebase_Server_Abstract implements Tinebase_Server_Interface
19 {
20     const REQUEST_TYPE = 'WebDAV';
21     
22    /**
23     * @var \Sabre\DAV\Server
24     */
25     protected static $_server;
26     
27     /**
28      * (non-PHPdoc)
29      * @see Tinebase_Server_Interface::handle()
30      */
31     public function handle(\Zend\Http\Request $request = null, $body = null)
32     {
33         $this->_request = $request instanceof \Zend\Http\Request ? $request : Tinebase_Core::get(Tinebase_Core::REQUEST);
34         if ($body !== null) {
35             $this->_body = $body;
36         } else if ($this->_request instanceof \Zend\Http\Request) {
37             $this->_body = fopen('php://temp', 'r+');
38             fwrite($this->_body, $request->getContent());
39             rewind($this->_body);
40         }
41         
42         try {
43             list($loginName, $password) = $this->_getAuthData($this->_request);
44             
45         } catch (Tinebase_Exception_NotFound $tenf) {
46             header('WWW-Authenticate: Basic realm="WebDAV for Tine 2.0"');
47             header('HTTP/1.1 401 Unauthorized');
48             
49             return;
50         }
51         
52         if (Tinebase_Core::isLogLevel(Zend_Log::INFO))
53             Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ .' is CalDav, CardDAV or WebDAV request.');
54         
55         Tinebase_Core::initFramework();
56
57         if (null !== ($denyList = Tinebase_Config::getInstance()->get(Tinebase_Config::DENY_WEBDAV_CLIENT_LIST)) &&
58                 is_array($denyList)) {
59             foreach ($denyList as $deny) {
60                 if (preg_match($deny, $_SERVER['HTTP_USER_AGENT'])) {
61                     header('HTTP/1.1 420 Policy Not Fulfilled User Agent Not Accepted');
62                     return;
63                 }
64             }
65         }
66         
67         if (Tinebase_Controller::getInstance()->login(
68             $loginName,
69             $password,
70             $this->_request,
71             self::REQUEST_TYPE
72         ) !== true) {
73             header('WWW-Authenticate: Basic realm="WebDAV for Tine 2.0"');
74             header('HTTP/1.1 401 Unauthorized');
75             
76             return;
77         }
78         
79         if (Tinebase_Core::isLogLevel(Zend_Log::INFO))
80             Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ .' requestUri:' . $this->_request->getRequestUri());
81         
82         self::$_server = new \Sabre\DAV\Server(new Tinebase_WebDav_Root());
83         \Sabre\DAV\Server::$exposeVersion = false;
84         
85         if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) {
86             self::$_server->debugExceptions = true;
87             $contentType = self::$_server->httpRequest->getHeader('Content-Type');
88             Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . " requestContentType: " . $contentType);
89             
90             if (stripos($contentType, 'text') === 0 || stripos($contentType, '/xml') !== false) {
91                 // NOTE inputstream can not be rewinded
92                 $debugStream = fopen('php://temp','r+');
93                 stream_copy_to_stream($this->_body, $debugStream);
94                 rewind($debugStream);
95                 $this->_body = $debugStream;
96                 
97                 Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . " <<< *DAV request\n" . stream_get_contents($this->_body));
98                 rewind($this->_body);
99             } else {
100                 Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . " <<< *DAV request\n -- BINARY DATA --");
101             }
102         }
103         
104         self::$_server->httpRequest->setBody($this->_body);
105         
106         // compute base uri
107         self::$_server->setBaseUri($this->_request->getBaseUrl() . '/');
108         
109         $tempDir = Tinebase_Core::getTempDir();
110         if (!empty($tempDir)) {
111             self::$_server->addPlugin(
112                 new \Sabre\DAV\Locks\Plugin(new \Sabre\DAV\Locks\Backend\File($tempDir . '/webdav.lock'))
113             );
114         }
115         
116         self::$_server->addPlugin(
117             new \Sabre\DAV\Auth\Plugin(new Tinebase_WebDav_Auth(), null)
118         );
119         
120         $aclPlugin = new \Sabre\DAVACL\Plugin();
121         $aclPlugin->defaultUsernamePath    = Tinebase_WebDav_PrincipalBackend::PREFIX_USERS;
122         $aclPlugin->principalCollectionSet = array (Tinebase_WebDav_PrincipalBackend::PREFIX_USERS, Tinebase_WebDav_PrincipalBackend::PREFIX_GROUPS, Tinebase_WebDav_PrincipalBackend::PREFIX_INTELLIGROUPS);
123         
124         $aclPlugin->principalSearchPropertySet = array(
125             '{DAV:}displayname'                                                   => 'Display name',
126             '{' . \Sabre\DAV\Server::NS_SABREDAV . '}email-address'               => 'Email address',
127             '{' . \Sabre\CalDAV\Plugin::NS_CALENDARSERVER . '}email-address-set'  => 'Email addresses',
128             '{' . \Sabre\CalDAV\Plugin::NS_CALENDARSERVER . '}first-name'         => 'First name',
129             '{' . \Sabre\CalDAV\Plugin::NS_CALENDARSERVER . '}last-name'          => 'Last name',
130             '{' . \Sabre\CalDAV\Plugin::NS_CALDAV         . '}calendar-user-address-set' => 'Calendar user address set',
131             '{' . \Sabre\CalDAV\Plugin::NS_CALDAV         . '}calendar-user-type' => 'Calendar user type'
132         );
133         
134         self::$_server->addPlugin($aclPlugin);
135         
136         self::$_server->addPlugin(new \Sabre\CardDAV\Plugin());
137         self::$_server->addPlugin(new Calendar_Frontend_CalDAV_SpeedUpPlugin); // this plugin must be loaded before CalDAV plugin
138         self::$_server->addPlugin(new Calendar_Frontend_CalDAV_FixMultiGet404Plugin()); // replacement for new \Sabre\CalDAV\Plugin());
139         self::$_server->addPlugin(new \Sabre\CalDAV\SharingPlugin());
140         self::$_server->addPlugin(new Calendar_Frontend_CalDAV_PluginAutoSchedule());
141         self::$_server->addPlugin(new Calendar_Frontend_CalDAV_PluginDefaultAlarms());
142         self::$_server->addPlugin(new Calendar_Frontend_CalDAV_PluginManagedAttachments());
143         self::$_server->addPlugin(new Calendar_Frontend_CalDAV_PluginPrivateEvents());
144         self::$_server->addPlugin(new Tinebase_WebDav_Plugin_Inverse());
145         self::$_server->addPlugin(new Tinebase_WebDav_Plugin_OwnCloud());
146         self::$_server->addPlugin(new Tinebase_WebDav_Plugin_PrincipalSearch());
147         self::$_server->addPlugin(new Tinebase_WebDav_Plugin_ExpandedPropertiesReport());
148         self::$_server->addPlugin(new \Sabre\DAV\Browser\Plugin());
149         self::$_server->addPlugin(new Tinebase_WebDav_Plugin_SyncToken());
150         self::$_server->addPlugin(new Calendar_Frontend_CalDAV_SpeedUpPropfindPlugin());
151
152         $contentType = self::$_server->httpRequest->getHeader('Content-Type');
153         $logOutput = Tinebase_Core::isLogLevel(Zend_Log::DEBUG) && (stripos($contentType, 'text') === 0 || stripos($contentType, '/xml') !== false);
154
155         if ($logOutput) {
156             ob_start();
157         }
158         
159         self::$_server->exec();
160         
161         if ($logOutput) {
162             Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . " >>> *DAV response:\n" . ob_get_contents());
163             ob_end_flush();
164         } else {
165
166             Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . " <<< *DAV response\n -- BINARY DATA --");
167         }
168
169         Tinebase_Controller::getInstance()->logout($this->_request->getServer('REMOTE_ADDR'));
170     }
171     
172    /**
173     * helper to return request
174     *
175     * @return Sabre\HTTP\Request
176     */
177     public static function getRequest()
178     {
179         return self::$_server ? self::$_server->httpRequest : new Sabre\HTTP\Request();
180     }
181
182     /**
183      * helper to return response
184      *
185      * @return Sabre\HTTP\Response
186      */
187     public static function getResponse()
188     {
189         return self::$_server ? self::$_server->httpResponse : new Sabre\HTTP\Response();
190     }
191
192     /**
193     * returns request method
194     *
195     * @return string
196     */
197     public function getRequestMethod()
198     {
199         return self::getRequest()->getMethod();
200     }
201 }