]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - modules/vCals/HTTP_WebDAV_Server_vCal.php
Release 6.5.11
[Github/sugarcrm.git] / modules / vCals / HTTP_WebDAV_Server_vCal.php
1 <?php
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.
6  * 
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.
13  * 
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
17  * details.
18  * 
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
22  * 02110-1301 USA.
23  * 
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.
26  * 
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.
30  * 
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  ********************************************************************************/
37
38
39
40
41
42
43 require_once 'modules/Calendar/Calendar.php';
44
45 require_once 'include/HTTP_WebDAV_Server/Server.php';
46
47
48     /**
49      * Filesystem access using WebDAV
50      *
51      * @access public
52      */
53     class HTTP_WebDAV_Server_vCal extends HTTP_WebDAV_Server
54     {
55         /**
56          * Root directory for WebDAV access
57          *
58          * Defaults to webserver document root (set by ServeRequest)
59          *
60          * @access private
61          * @var    string
62          */
63         var $base = "";
64         var $vcal_focus;
65         var $vcal_type = "";
66         var $source = "";
67         var $publish_key = "";
68
69         function HTTP_WebDAV_Server_vCal()
70         {
71            $this->vcal_focus = new vCal();
72            $this->user_focus = new User();
73         }
74
75
76         /**
77          * Serve a webdav request
78          *
79          * @access public
80          * @param  string
81          */
82         function ServeRequest($base = false)
83         {
84
85             global $sugar_config,$current_language;
86
87             if (!empty($sugar_config['session_dir']))
88             {
89                session_save_path($sugar_config['session_dir']);
90             }
91
92             session_start();
93
94             // clean_incoming_data();
95
96
97             $current_language = $sugar_config['default_language'];
98
99             // special treatment for litmus compliance test
100             // reply on its identifier header
101             // not needed for the test itself but eases debugging
102 /*
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);
107                 }
108             }
109 */
110
111             // set root directory, defaults to webserver document root if not set
112             if ($base) {
113                 $this->base = realpath($base); // TODO throw if not a directory
114             } else if(!$this->base) {
115                 $this->base = $_SERVER['DOCUMENT_ROOT'];
116             }
117
118
119             $query_arr =  array();
120              // set path
121             if ( empty($_SERVER["PATH_INFO"]))
122             {
123                                 $this->path = "/";
124                                 if(strtolower($_SERVER["REQUEST_METHOD"]) == 'get'){
125                                         $query_arr = $_REQUEST;
126                                 }else{
127                                         parse_str($_REQUEST['parms'],$query_arr);
128                                 }
129             } else{
130               $this->path = $this->_urldecode( $_SERVER["PATH_INFO"]);
131
132               if(ini_get("magic_quotes_gpc")) {
133                $this->path = stripslashes($this->path);
134               }
135
136               $query_str = preg_replace('/^\//','',$this->path);
137               $query_arr =  array();
138               parse_str($query_str,$query_arr);
139             }
140
141
142             if ( ! empty($query_arr['type']))
143             {
144               $this->vcal_type = $query_arr['type'];
145             }
146             else {
147               $this->vcal_type = 'vfb';
148             }
149
150             if ( ! empty($query_arr['source']))
151             {
152               $this->source = $query_arr['source'];
153             }
154             else {
155               $this->source = 'outlook';
156             }
157
158             if ( ! empty($query_arr['key']))
159             {
160               $this->publish_key = $query_arr['key'];
161             }
162
163             // select user by email
164             if ( ! empty($query_arr['email']))
165             {
166
167
168               // clean the string!
169               $query_arr['email'] = clean_string($query_arr['email']);
170               //get user info
171               $this->user_focus->retrieve_by_email_address( $query_arr['email']);
172
173             }
174             // else select user by user_name
175             else if ( ! empty($query_arr['user_name']))
176             {
177               // clean the string!
178               $query_arr['user_name'] = clean_string($query_arr['user_name']);
179
180               //get user info
181               $arr = array('user_name'=>$query_arr['user_name']);
182               $this->user_focus->retrieve_by_string_fields($arr);
183             }
184             // else select user by user id
185             else if ( ! empty($query_arr['user_id']))
186             {
187                 $this->user_focus->retrieve($query_arr['user_id']);
188             }
189
190             // if we haven't found a user, then return 404
191             if ( empty($this->user_focus->id) || $this->user_focus->id == -1)
192             {
193                 $this->http_status('401 Unauthorized');
194                 if (!isset($query_arr['noAuth'])) {
195                     header('WWW-Authenticate: Basic realm="'.($this->http_auth_realm).'"');
196                 }
197                 return;
198             }
199
200 //            if(empty($this->user_focus->user_preferences))
201 //            {
202                      $this->user_focus->loadPreferences();
203 //            }
204
205             // let the base class do all the work
206             parent::ServeRequest();
207         }
208
209         /**
210          * No authentication is needed here
211          *
212          * @access private
213          * @param  string  HTTP Authentication type (Basic, Digest, ...)
214          * @param  string  Username
215          * @param  string  Password
216          * @return bool    true on successful authentication
217          */
218         function check_auth($type, $user, $pass)
219         {
220             if(isset($_SESSION['authenticated_user_id'])) {
221                 // allow logged in users access to freebusy info
222                 return true;
223             }
224             if(!empty($this->publish_key) && !empty($this->user_focus) && $this->user_focus->getPreference('calendar_publish_key' ) == $this->publish_key) {
225                 return true;
226             }
227             return false;
228         }
229
230
231         function GET()
232         {
233             return true;
234         }
235
236         // {{{ http_GET()
237
238         /**
239         * GET method handler
240         *
241         * @param void
242         * @returns void
243         */
244         function http_GET()
245         {
246
247            if ($this->vcal_type == 'vfb')
248            {
249              $this->http_status("200 OK");
250              echo $this->vcal_focus->get_vcal_freebusy($this->user_focus);
251            } else {
252              $this->http_status("404 Not Found");
253            }
254
255         }
256         // }}}
257
258
259         // {{{ http_PUT()
260
261         /**
262         * PUT method handler
263         *
264         * @param  void
265         * @return void
266         */
267         function http_PUT()
268         {
269             $options = Array();
270             $options["path"] = $this->path;
271             $options["content_length"] = $_SERVER["CONTENT_LENGTH"];
272
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";
279                     return;
280                 }
281                 $options["content_type"] = $_SERVER["CONTENT_TYPE"];
282             } else {
283                 // default content type if none given
284                 $options["content_type"] = "application/octet-stream";
285             }
286
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."
291             */
292             foreach ($_SERVER as $key => $val) {
293                 if (strncmp($key, "HTTP_CONTENT", 11)) continue;
294                 switch ($key) {
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";
299                     return;
300
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;
305                     break;
306
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. */
311                     break;
312
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";
319                         return;
320
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";
325                     return;
326
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. */
331                     break;
332
333                 default:
334                     // any other unknown Content-* headers
335                     $this->http_status("501 not implemented");
336                     echo "The service does not support '$key'";
337                     return;
338                 }
339             }
340
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' ))
344             {
345                     $this->http_status("401 not authorized");
346                     return;
347
348             }
349
350             // retrieve
351             $arr = array('user_id'=>$this->user_focus->id,'type'=>'vfb','source'=>$this->source);
352             $this->vcal_focus->retrieve_by_string_fields($arr);
353
354             $isUpdate  = false;
355
356             if ( ! empty($this->vcal_focus->user_id ) &&
357                 $this->vcal_focus->user_id != -1 )
358             {
359               $isUpdate  = true;
360             }
361
362             // open input stream
363             $options["stream"] = fopen("php://input", "r");
364             $content = '';
365
366             // read in input stream
367             while (!feof($options["stream"]))
368             {
369                $content .= fread($options["stream"], 4096);
370             }
371
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();
379
380             if ( $isUpdate )
381             {
382                $this->http_status("204 No Content");
383             } else {
384                $this->http_status("201 Created");
385             }
386         }
387
388         /**
389          * PUT method handler
390          *
391          * @param  array  parameter passing array
392          * @return bool   true on success
393          */
394         function PUT(&$options)
395         {
396
397         }
398
399         /**
400          * LOCK method handler
401          *
402          * @param  array  general parameter passing array
403          * @return bool   true on success
404          */
405         function lock(&$options)
406         {
407
408             $options["timeout"] = time()+300; // 5min. hardcoded
409             return true;
410         }
411
412         /**
413          * UNLOCK method handler
414          *
415          * @param  array  general parameter passing array
416          * @return bool   true on success
417          */
418         function unlock(&$options)
419         {
420
421             return "200 OK";
422         }
423
424
425         /**
426          * checkLock() helper
427          *
428          * @param  string resource path to check for locks
429          * @return bool   true on success
430          */
431         function checkLock($path)
432         {
433             return false;
434
435         }
436
437     }
438
439
440 ?>