2 if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
3 /*********************************************************************************
4 * SugarCRM Community Edition is a customer relationship management program developed by
5 * SugarCRM, Inc. Copyright (C) 2004-2013 SugarCRM Inc.
7 * This program is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU Affero General Public License version 3 as published by the
9 * Free Software Foundation with the addition of the following permission added
10 * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
11 * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
12 * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
14 * This program is distributed in the hope that it will be useful, but WITHOUT
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16 * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
19 * You should have received a copy of the GNU Affero General Public License along with
20 * this program; if not, see http://www.gnu.org/licenses or write to the Free
21 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
24 * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
25 * SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
27 * The interactive user interfaces in modified source and object code versions
28 * of this program must display Appropriate Legal Notices, as required under
29 * Section 5 of the GNU Affero General Public License version 3.
31 * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
32 * these Appropriate Legal Notices must retain the display of the "Powered by
33 * SugarCRM" logo. If the display of the logo is not reasonably feasible for
34 * technical reasons, the Appropriate Legal Notices must display the words
35 * "Powered by SugarCRM".
36 ********************************************************************************/
43 require_once 'modules/Calendar/Calendar.php';
45 require_once 'include/HTTP_WebDAV_Server/Server.php';
49 * Filesystem access using WebDAV
53 class HTTP_WebDAV_Server_vCal extends HTTP_WebDAV_Server
56 * Root directory for WebDAV access
58 * Defaults to webserver document root (set by ServeRequest)
67 var $publish_key = "";
69 function HTTP_WebDAV_Server_vCal()
71 $this->vcal_focus = new vCal();
72 $this->user_focus = new User();
77 * Serve a webdav request
82 function ServeRequest($base = false)
85 global $sugar_config,$current_language;
87 if (!empty($sugar_config['session_dir']))
89 session_save_path($sugar_config['session_dir']);
94 // clean_incoming_data();
97 $current_language = $sugar_config['default_language'];
99 // special treatment for litmus compliance test
100 // reply on its identifier header
101 // not needed for the test itself but eases debugging
103 foreach(apache_request_headers() as $key => $value) {
104 if(stristr($key,"litmus")) {
105 error_log("Litmus test $value");
106 header("X-Litmus-reply: ".$value);
111 // set root directory, defaults to webserver document root if not set
113 $this->base = realpath($base); // TODO throw if not a directory
114 } else if(!$this->base) {
115 $this->base = $_SERVER['DOCUMENT_ROOT'];
119 $query_arr = array();
121 if ( empty($_SERVER["PATH_INFO"]))
124 if(strtolower($_SERVER["REQUEST_METHOD"]) == 'get'){
125 $query_arr = $_REQUEST;
127 parse_str($_REQUEST['parms'],$query_arr);
130 $this->path = $this->_urldecode( $_SERVER["PATH_INFO"]);
132 if(ini_get("magic_quotes_gpc")) {
133 $this->path = stripslashes($this->path);
136 $query_str = preg_replace('/^\//','',$this->path);
137 $query_arr = array();
138 parse_str($query_str,$query_arr);
142 if ( ! empty($query_arr['type']))
144 $this->vcal_type = $query_arr['type'];
147 $this->vcal_type = 'vfb';
150 if ( ! empty($query_arr['source']))
152 $this->source = $query_arr['source'];
155 $this->source = 'outlook';
158 if ( ! empty($query_arr['key']))
160 $this->publish_key = $query_arr['key'];
163 // select user by email
164 if ( ! empty($query_arr['email']))
169 $query_arr['email'] = clean_string($query_arr['email']);
171 $this->user_focus->retrieve_by_email_address( $query_arr['email']);
174 // else select user by user_name
175 else if ( ! empty($query_arr['user_name']))
178 $query_arr['user_name'] = clean_string($query_arr['user_name']);
181 $arr = array('user_name'=>$query_arr['user_name']);
182 $this->user_focus->retrieve_by_string_fields($arr);
184 // else select user by user id
185 else if ( ! empty($query_arr['user_id']))
187 $this->user_focus->retrieve($query_arr['user_id']);
190 // if we haven't found a user, then return 404
191 if ( empty($this->user_focus->id) || $this->user_focus->id == -1)
193 $this->http_status('401 Unauthorized');
194 if (!isset($query_arr['noAuth'])) {
195 header('WWW-Authenticate: Basic realm="'.($this->http_auth_realm).'"');
200 // if(empty($this->user_focus->user_preferences))
202 $this->user_focus->loadPreferences();
205 // let the base class do all the work
206 parent::ServeRequest();
210 * No authentication is needed here
213 * @param string HTTP Authentication type (Basic, Digest, ...)
214 * @param string Username
215 * @param string Password
216 * @return bool true on successful authentication
218 function check_auth($type, $user, $pass)
220 if(isset($_SESSION['authenticated_user_id'])) {
221 // allow logged in users access to freebusy info
224 if(!empty($this->publish_key) && !empty($this->user_focus) && $this->user_focus->getPreference('calendar_publish_key' ) == $this->publish_key) {
247 if ($this->vcal_type == 'vfb')
249 $this->http_status("200 OK");
250 echo $this->vcal_focus->get_vcal_freebusy($this->user_focus);
252 $this->http_status("404 Not Found");
270 $options["path"] = $this->path;
271 $options["content_length"] = $_SERVER["CONTENT_LENGTH"];
273 // get the Content-type
274 if (isset($_SERVER["CONTENT_TYPE"])) {
275 // for now we do not support any sort of multipart requests
276 if (!strncmp($_SERVER["CONTENT_TYPE"], "multipart/", 10)) {
277 $this->http_status("501 not implemented");
278 echo "The service does not support mulipart PUT requests";
281 $options["content_type"] = $_SERVER["CONTENT_TYPE"];
283 // default content type if none given
284 $options["content_type"] = "application/octet-stream";
287 /* RFC 2616 2.6 says: "The recipient of the entity MUST NOT
288 ignore any Content-* (e.g. Content-Range) headers that it
289 does not understand or implement and MUST return a 501
290 (Not Implemented) response in such cases."
292 foreach ($_SERVER as $key => $val) {
293 if (strncmp($key, "HTTP_CONTENT", 11)) continue;
295 case 'HTTP_CONTENT_ENCODING': // RFC 2616 14.11
296 // TODO support this if ext/zlib filters are available
297 $this->http_status("501 not implemented");
298 echo "The service does not support '$val' content encoding";
301 case 'HTTP_CONTENT_LANGUAGE': // RFC 2616 14.12
302 // we assume it is not critical if this one is ignored
303 // in the actual PUT implementation ...
304 $options["content_language"] = $val;
307 case 'HTTP_CONTENT_LOCATION': // RFC 2616 14.14
308 /* The meaning of the Content-Location header in PUT
309 or POST requests is undefined; servers are free
310 to ignore it in those cases. */
313 case 'HTTP_CONTENT_RANGE': // RFC 2616 14.16
314 // single byte range requests are NOT supported
315 // the header format is also specified in RFC 2616 14.16
316 // TODO we have to ensure that implementations support this or send 501 instead
317 $this->http_status("400 bad request");
318 echo "The service does only support single byte ranges";
321 case 'HTTP_CONTENT_MD5': // RFC 2616 14.15
322 // TODO: maybe we can just pretend here?
323 $this->http_status("501 not implemented");
324 echo "The service does not support content MD5 checksum verification";
327 case 'HTTP_CONTENT_LENGTH': // RFC 2616 14.14
328 /* The meaning of the Content-Location header in PUT
329 or POST requests is undefined; servers are free
330 to ignore it in those cases. */
334 // any other unknown Content-* headers
335 $this->http_status("501 not implemented");
336 echo "The service does not support '$key'";
341 // DO AUTHORIZATION for publishing Free/busy to Sugar:
342 if ( empty($this->publish_key) ||
343 $this->publish_key != $this->user_focus->getPreference('calendar_publish_key' ))
345 $this->http_status("401 not authorized");
351 $arr = array('user_id'=>$this->user_focus->id,'type'=>'vfb','source'=>$this->source);
352 $this->vcal_focus->retrieve_by_string_fields($arr);
356 if ( ! empty($this->vcal_focus->user_id ) &&
357 $this->vcal_focus->user_id != -1 )
363 $options["stream"] = fopen("php://input", "r");
366 // read in input stream
367 while (!feof($options["stream"]))
369 $content .= fread($options["stream"], 4096);
372 // set freebusy members and save
373 $this->vcal_focus->content = $content;
374 $this->vcal_focus->type = 'vfb';
375 $this->vcal_focus->source = $this->source;
376 $focus->date_modified = null;
377 $this->vcal_focus->user_id = $this->user_focus->id;
378 $this->vcal_focus->save();
382 $this->http_status("204 No Content");
384 $this->http_status("201 Created");
391 * @param array parameter passing array
392 * @return bool true on success
394 function PUT(&$options)
400 * LOCK method handler
402 * @param array general parameter passing array
403 * @return bool true on success
405 function lock(&$options)
408 $options["timeout"] = time()+300; // 5min. hardcoded
413 * UNLOCK method handler
415 * @param array general parameter passing array
416 * @return bool true on success
418 function unlock(&$options)
428 * @param string resource path to check for locks
429 * @return bool true on success
431 function checkLock($path)