PageRenderTime 65ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/modules/vCals/HTTP_WebDAV_Server_vCal.php

https://gitlab.com/tjaafar/SuiteCRM
PHP | 443 lines | 210 code | 75 blank | 158 comment | 44 complexity | ed0b5aa478c1895eb846a2ac59d8190c MD5 | raw file
  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. * SuiteCRM is an extension to SugarCRM Community Edition developed by Salesagility Ltd.
  7. * Copyright (C) 2011 - 2014 Salesagility Ltd.
  8. *
  9. * This program is free software; you can redistribute it and/or modify it under
  10. * the terms of the GNU Affero General Public License version 3 as published by the
  11. * Free Software Foundation with the addition of the following permission added
  12. * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
  13. * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
  14. * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
  15. *
  16. * This program is distributed in the hope that it will be useful, but WITHOUT
  17. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  18. * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
  19. * details.
  20. *
  21. * You should have received a copy of the GNU Affero General Public License along with
  22. * this program; if not, see http://www.gnu.org/licenses or write to the Free
  23. * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  24. * 02110-1301 USA.
  25. *
  26. * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
  27. * SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
  28. *
  29. * The interactive user interfaces in modified source and object code versions
  30. * of this program must display Appropriate Legal Notices, as required under
  31. * Section 5 of the GNU Affero General Public License version 3.
  32. *
  33. * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
  34. * these Appropriate Legal Notices must retain the display of the "Powered by
  35. * SugarCRM" logo and "Supercharged by SuiteCRM" logo. If the display of the logos is not
  36. * reasonably feasible for technical reasons, the Appropriate Legal Notices must
  37. * display the words "Powered by SugarCRM" and "Supercharged by SuiteCRM".
  38. ********************************************************************************/
  39. require_once 'modules/Calendar/Calendar.php';
  40. require_once 'include/HTTP_WebDAV_Server/Server.php';
  41. /**
  42. * Filesystem access using WebDAV
  43. *
  44. * @access public
  45. */
  46. class HTTP_WebDAV_Server_vCal extends HTTP_WebDAV_Server
  47. {
  48. /**
  49. * Root directory for WebDAV access
  50. *
  51. * Defaults to webserver document root (set by ServeRequest)
  52. *
  53. * @access private
  54. * @var string
  55. */
  56. var $base = "";
  57. var $vcal_focus;
  58. var $vcal_type = "";
  59. var $source = "";
  60. var $publish_key = "";
  61. function HTTP_WebDAV_Server_vCal()
  62. {
  63. $this->vcal_focus = new vCal();
  64. $this->user_focus = new User();
  65. }
  66. /**
  67. * Serve a webdav request
  68. *
  69. * @access public
  70. * @param string
  71. */
  72. function ServeRequest($base = false)
  73. {
  74. global $sugar_config,$current_language;
  75. if (!empty($sugar_config['session_dir']))
  76. {
  77. session_save_path($sugar_config['session_dir']);
  78. }
  79. session_start();
  80. // clean_incoming_data();
  81. $current_language = $sugar_config['default_language'];
  82. // special treatment for litmus compliance test
  83. // reply on its identifier header
  84. // not needed for the test itself but eases debugging
  85. /*
  86. foreach(apache_request_headers() as $key => $value) {
  87. if(stristr($key,"litmus")) {
  88. error_log("Litmus test $value");
  89. header("X-Litmus-reply: ".$value);
  90. }
  91. }
  92. */
  93. // set root directory, defaults to webserver document root if not set
  94. if ($base) {
  95. $this->base = realpath($base); // TODO throw if not a directory
  96. } else if(!$this->base) {
  97. $this->base = $_SERVER['DOCUMENT_ROOT'];
  98. }
  99. $query_arr = array();
  100. // set path
  101. if ( empty($_SERVER["PATH_INFO"]))
  102. {
  103. $this->path = "/";
  104. if(strtolower($_SERVER["REQUEST_METHOD"]) == 'get'){
  105. $query_arr = $_REQUEST;
  106. }else{
  107. parse_str($_REQUEST['parms'],$query_arr);
  108. }
  109. } else{
  110. $this->path = $this->_urldecode( $_SERVER["PATH_INFO"]);
  111. if(ini_get("magic_quotes_gpc")) {
  112. $this->path = stripslashes($this->path);
  113. }
  114. $query_str = preg_replace('/^\//','',$this->path);
  115. $query_arr = array();
  116. parse_str($query_str,$query_arr);
  117. }
  118. if ( ! empty($query_arr['type']))
  119. {
  120. $this->vcal_type = $query_arr['type'];
  121. }
  122. else {
  123. $this->vcal_type = 'vfb';
  124. }
  125. if ( ! empty($query_arr['source']))
  126. {
  127. $this->source = $query_arr['source'];
  128. }
  129. else {
  130. $this->source = 'outlook';
  131. }
  132. if ( ! empty($query_arr['key']))
  133. {
  134. $this->publish_key = $query_arr['key'];
  135. }
  136. // select user by email
  137. if ( ! empty($query_arr['email']))
  138. {
  139. // clean the string!
  140. $query_arr['email'] = clean_string($query_arr['email']);
  141. //get user info
  142. $this->user_focus->retrieve_by_email_address( $query_arr['email']);
  143. }
  144. // else select user by user_name
  145. else if ( ! empty($query_arr['user_name']))
  146. {
  147. // clean the string!
  148. $query_arr['user_name'] = clean_string($query_arr['user_name']);
  149. //get user info
  150. $arr = array('user_name'=>$query_arr['user_name']);
  151. $this->user_focus->retrieve_by_string_fields($arr);
  152. }
  153. // else select user by user id
  154. else if ( ! empty($query_arr['user_id']))
  155. {
  156. $this->user_focus->retrieve($query_arr['user_id']);
  157. }
  158. // if we haven't found a user, then return 404
  159. if ( empty($this->user_focus->id) || $this->user_focus->id == -1)
  160. {
  161. $this->http_status('401 Unauthorized');
  162. if (!isset($query_arr['noAuth'])) {
  163. header('WWW-Authenticate: Basic realm="'.($this->http_auth_realm).'"');
  164. }
  165. return;
  166. }
  167. // if(empty($this->user_focus->user_preferences))
  168. // {
  169. $this->user_focus->loadPreferences();
  170. // }
  171. // let the base class do all the work
  172. parent::ServeRequest();
  173. }
  174. /**
  175. * No authentication is needed here
  176. *
  177. * @access private
  178. * @param string HTTP Authentication type (Basic, Digest, ...)
  179. * @param string Username
  180. * @param string Password
  181. * @return bool true on successful authentication
  182. */
  183. function check_auth($type, $user, $pass)
  184. {
  185. if(isset($_SESSION['authenticated_user_id'])) {
  186. // allow logged in users access to freebusy info
  187. return true;
  188. }
  189. if(!empty($this->publish_key) && !empty($this->user_focus) && $this->user_focus->getPreference('calendar_publish_key' ) == $this->publish_key) {
  190. return true;
  191. }
  192. return false;
  193. }
  194. function GET()
  195. {
  196. return true;
  197. }
  198. // {{{ http_GET()
  199. /**
  200. * GET method handler
  201. *
  202. * @param void
  203. * @returns void
  204. */
  205. function http_GET()
  206. {
  207. if ($this->vcal_type == 'vfb')
  208. {
  209. $this->http_status("200 OK");
  210. echo $this->vcal_focus->get_vcal_freebusy($this->user_focus);
  211. } else {
  212. $this->http_status("404 Not Found");
  213. }
  214. }
  215. // }}}
  216. // {{{ http_PUT()
  217. /**
  218. * PUT method handler
  219. *
  220. * @param void
  221. * @return void
  222. */
  223. function http_PUT()
  224. {
  225. $options = Array();
  226. $options["path"] = $this->path;
  227. $options["content_length"] = $_SERVER["CONTENT_LENGTH"];
  228. // get the Content-type
  229. if (isset($_SERVER["CONTENT_TYPE"])) {
  230. // for now we do not support any sort of multipart requests
  231. if (!strncmp($_SERVER["CONTENT_TYPE"], "multipart/", 10)) {
  232. $this->http_status("501 not implemented");
  233. echo "The service does not support mulipart PUT requests";
  234. return;
  235. }
  236. $options["content_type"] = $_SERVER["CONTENT_TYPE"];
  237. } else {
  238. // default content type if none given
  239. $options["content_type"] = "application/octet-stream";
  240. }
  241. /* RFC 2616 2.6 says: "The recipient of the entity MUST NOT
  242. ignore any Content-* (e.g. Content-Range) headers that it
  243. does not understand or implement and MUST return a 501
  244. (Not Implemented) response in such cases."
  245. */
  246. foreach ($_SERVER as $key => $val) {
  247. if (strncmp($key, "HTTP_CONTENT", 11)) continue;
  248. switch ($key) {
  249. case 'HTTP_CONTENT_ENCODING': // RFC 2616 14.11
  250. // TODO support this if ext/zlib filters are available
  251. $this->http_status("501 not implemented");
  252. echo "The service does not support '$val' content encoding";
  253. return;
  254. case 'HTTP_CONTENT_LANGUAGE': // RFC 2616 14.12
  255. // we assume it is not critical if this one is ignored
  256. // in the actual PUT implementation ...
  257. $options["content_language"] = $val;
  258. break;
  259. case 'HTTP_CONTENT_LOCATION': // RFC 2616 14.14
  260. /* The meaning of the Content-Location header in PUT
  261. or POST requests is undefined; servers are free
  262. to ignore it in those cases. */
  263. break;
  264. case 'HTTP_CONTENT_RANGE': // RFC 2616 14.16
  265. // single byte range requests are NOT supported
  266. // the header format is also specified in RFC 2616 14.16
  267. // TODO we have to ensure that implementations support this or send 501 instead
  268. $this->http_status("400 bad request");
  269. echo "The service does only support single byte ranges";
  270. return;
  271. case 'HTTP_CONTENT_MD5': // RFC 2616 14.15
  272. // TODO: maybe we can just pretend here?
  273. $this->http_status("501 not implemented");
  274. echo "The service does not support content MD5 checksum verification";
  275. return;
  276. case 'HTTP_CONTENT_LENGTH': // RFC 2616 14.14
  277. /* The meaning of the Content-Location header in PUT
  278. or POST requests is undefined; servers are free
  279. to ignore it in those cases. */
  280. break;
  281. default:
  282. // any other unknown Content-* headers
  283. $this->http_status("501 not implemented");
  284. echo "The service does not support '$key'";
  285. return;
  286. }
  287. }
  288. // DO AUTHORIZATION for publishing Free/busy to Sugar:
  289. if ( empty($this->publish_key) ||
  290. $this->publish_key != $this->user_focus->getPreference('calendar_publish_key' ))
  291. {
  292. $this->http_status("401 not authorized");
  293. return;
  294. }
  295. // retrieve
  296. $arr = array('user_id'=>$this->user_focus->id,'type'=>'vfb','source'=>$this->source);
  297. $this->vcal_focus->retrieve_by_string_fields($arr);
  298. $isUpdate = false;
  299. if ( ! empty($this->vcal_focus->user_id ) &&
  300. $this->vcal_focus->user_id != -1 )
  301. {
  302. $isUpdate = true;
  303. }
  304. // open input stream
  305. $options["stream"] = fopen("php://input", "r");
  306. $content = '';
  307. // read in input stream
  308. while (!feof($options["stream"]))
  309. {
  310. $content .= fread($options["stream"], 4096);
  311. }
  312. // set freebusy members and save
  313. $this->vcal_focus->content = $content;
  314. $this->vcal_focus->type = 'vfb';
  315. $this->vcal_focus->source = $this->source;
  316. $focus->date_modified = null;
  317. $this->vcal_focus->user_id = $this->user_focus->id;
  318. $this->vcal_focus->save();
  319. if ( $isUpdate )
  320. {
  321. $this->http_status("204 No Content");
  322. } else {
  323. $this->http_status("201 Created");
  324. }
  325. }
  326. /**
  327. * PUT method handler
  328. *
  329. * @param array parameter passing array
  330. * @return bool true on success
  331. */
  332. function PUT(&$options)
  333. {
  334. }
  335. /**
  336. * LOCK method handler
  337. *
  338. * @param array general parameter passing array
  339. * @return bool true on success
  340. */
  341. function lock(&$options)
  342. {
  343. $options["timeout"] = time()+300; // 5min. hardcoded
  344. return true;
  345. }
  346. /**
  347. * UNLOCK method handler
  348. *
  349. * @param array general parameter passing array
  350. * @return bool true on success
  351. */
  352. function unlock(&$options)
  353. {
  354. return "200 OK";
  355. }
  356. /**
  357. * checkLock() helper
  358. *
  359. * @param string resource path to check for locks
  360. * @return bool true on success
  361. */
  362. function checkLock($path)
  363. {
  364. return false;
  365. }
  366. }
  367. ?>