PageRenderTime 46ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/bennu/iCalendar_properties.php

https://bitbucket.org/kudutest1/moodlegit
PHP | 1481 lines | 1046 code | 306 blank | 129 comment | 187 complexity | e0d3116f8bdab3f070c8bd839567b49e MD5 | raw file
  1. <?php
  2. /**
  3. * BENNU - PHP iCalendar library
  4. * (c) 2005-2006 Ioannis Papaioannou (pj@moodle.org). All rights reserved.
  5. *
  6. * Released under the LGPL.
  7. *
  8. * See http://bennu.sourceforge.net/ for more information and downloads.
  9. *
  10. * @author Ioannis Papaioannou
  11. * @version $Id$
  12. * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
  13. */
  14. class iCalendar_property {
  15. // Properties can have parameters, but cannot have other properties or components
  16. var $parent_component = NULL;
  17. var $value = NULL;
  18. var $parameters = NULL;
  19. var $valid_parameters = NULL;
  20. // These are common for 95% of properties, so define them here and override as necessary
  21. var $val_multi = false;
  22. var $val_default = NULL;
  23. function __construct() {
  24. $this->parameters = array();
  25. }
  26. // If some property needs extra care with its parameters, override this
  27. // IMPORTANT: the parameter name MUST BE CAPITALIZED!
  28. function is_valid_parameter($parameter, $value) {
  29. if(is_array($value)) {
  30. if(!iCalendar_parameter::multiple_values_allowed($parameter)) {
  31. return false;
  32. }
  33. foreach($value as $item) {
  34. if(!iCalendar_parameter::is_valid_value($this, $parameter, $item)) {
  35. return false;
  36. }
  37. }
  38. return true;
  39. }
  40. return iCalendar_parameter::is_valid_value($this, $parameter, $value);
  41. }
  42. function invariant_holds() {
  43. return true;
  44. }
  45. // If some property is very picky about its values, it should do the work itself
  46. // Only data type validation is done here
  47. function is_valid_value($value) {
  48. if(is_array($value)) {
  49. if(!$this->val_multi) {
  50. return false;
  51. }
  52. else {
  53. foreach($value as $oneval) {
  54. if(!rfc2445_is_valid_value($oneval, $this->val_type)) {
  55. return false;
  56. }
  57. }
  58. }
  59. return true;
  60. }
  61. return rfc2445_is_valid_value($value, $this->val_type);
  62. }
  63. function default_value() {
  64. return $this->val_default;
  65. }
  66. function set_parent_component($componentname) {
  67. if(class_exists('iCalendar_'.strtolower(substr($componentname, 1)))) {
  68. $this->parent_component = strtoupper($componentname);
  69. return true;
  70. }
  71. return false;
  72. }
  73. function set_value($value) {
  74. if($this->is_valid_value($value)) {
  75. // This transparently formats any value type according to the iCalendar specs
  76. if(is_array($value)) {
  77. foreach($value as $key => $item) {
  78. $value[$key] = rfc2445_do_value_formatting($item, $this->val_type);
  79. }
  80. $this->value = implode(',', $value);
  81. }
  82. else {
  83. $this->value = rfc2445_do_value_formatting($value, $this->val_type);
  84. }
  85. return true;
  86. }
  87. return false;
  88. }
  89. function get_value() {
  90. // First of all, assume that we have multiple values
  91. $valarray = explode('\\,', $this->value);
  92. // Undo transparent formatting
  93. $replace_function = create_function('$a', 'return rfc2445_undo_value_formatting($a, '.$this->val_type.');');
  94. $valarray = array_map($replace_function, $valarray);
  95. // Now, if this property cannot have multiple values, don't return as an array
  96. if(!$this->val_multi) {
  97. return $valarray[0];
  98. }
  99. // Otherwise return an array even if it has one element, for uniformity
  100. return $valarray;
  101. }
  102. function set_parameter($name, $value) {
  103. // Uppercase
  104. $name = strtoupper($name);
  105. // Are we trying to add a valid parameter?
  106. $xname = false;
  107. if(!isset($this->valid_parameters[$name])) {
  108. // If not, is it an x-name as per RFC 2445?
  109. if(!rfc2445_is_xname($name)) {
  110. return false;
  111. }
  112. // No more checks -- all components are supposed to allow x-name parameters
  113. $xname = true;
  114. }
  115. if(!$this->is_valid_parameter($name, $value)) {
  116. return false;
  117. }
  118. if(is_array($value)) {
  119. foreach($value as $key => $element) {
  120. $value[$key] = iCalendar_parameter::do_value_formatting($name, $element);
  121. }
  122. }
  123. else {
  124. $value = iCalendar_parameter::do_value_formatting($name, $value);
  125. }
  126. $this->parameters[$name] = $value;
  127. // Special case: if we just changed the VALUE parameter, reflect this
  128. // in the object's status so that it only accepts correct type values
  129. if($name == 'VALUE') {
  130. // TODO: what if this invalidates an already-set value?
  131. $this->val_type = constant('RFC2445_TYPE_'.str_replace('-', '_', $value));
  132. }
  133. return true;
  134. }
  135. function get_parameter($name) {
  136. // Uppercase
  137. $name = strtoupper($name);
  138. if(isset($this->parameters[$name])) {
  139. // If there are any double quotes in the value, invisibly strip them
  140. if(is_array($this->parameters[$name])) {
  141. foreach($this->parameters[$name] as $key => $value) {
  142. if(substr($value, 0, 1) == '"') {
  143. $this->parameters[$name][$key] = substr($value, 1, strlen($value) - 2);
  144. }
  145. }
  146. return $this->parameters[$name];
  147. }
  148. else {
  149. if(substr($this->parameters[$name], 0, 1) == '"') {
  150. return substr($this->parameters[$name], 1, strlen($this->parameters[$name]) - 2);
  151. }
  152. }
  153. }
  154. return NULL;
  155. }
  156. function serialize() {
  157. $string = $this->name;
  158. if(!empty($this->parameters)) {
  159. foreach($this->parameters as $name => $value) {
  160. $string .= ';'.$name.'=';
  161. if(is_array($value)) {
  162. $string .= implode(',', $value);
  163. }
  164. else {
  165. $string .= $value;
  166. }
  167. }
  168. }
  169. $string .= ':'.$this->value;
  170. return rfc2445_fold($string) . RFC2445_CRLF;
  171. }
  172. }
  173. // 4.7 Calendar Properties
  174. // -----------------------
  175. class iCalendar_property_calscale extends iCalendar_property {
  176. var $name = 'CALSCALE';
  177. var $val_type = RFC2445_TYPE_TEXT;
  178. function __construct() {
  179. parent::__construct();
  180. $this->valid_parameters = array(
  181. RFC2445_XNAME => RFC2445_OPTIONAL
  182. );
  183. }
  184. function is_valid_value($value) {
  185. // This is case-sensitive
  186. return ($value === 'GREGORIAN');
  187. }
  188. }
  189. class iCalendar_property_method extends iCalendar_property {
  190. var $name = 'METHOD';
  191. var $val_type = RFC2445_TYPE_TEXT;
  192. function __construct() {
  193. parent::__construct();
  194. $this->valid_parameters = array(
  195. RFC2445_XNAME => RFC2445_OPTIONAL
  196. );
  197. }
  198. function is_valid_value($value) {
  199. // This is case-sensitive
  200. // Methods from RFC 2446
  201. $methods = array('PUBLISH', 'REQUEST', 'REPLY', 'ADD', 'CANCEL', 'REFRESH', 'COUNTER', 'DECLINECOUNTER');
  202. return in_array($value, $methods);
  203. }
  204. }
  205. class iCalendar_property_prodid extends iCalendar_property {
  206. var $name = 'PRODID';
  207. var $val_type = RFC2445_TYPE_TEXT;
  208. var $val_default = NULL;
  209. function __construct() {
  210. parent::__construct();
  211. $this->val_default = '-//John Papaioannou/NONSGML Bennu '._BENNU_VERSION.'//EN';
  212. $this->valid_parameters = array(
  213. RFC2445_XNAME => RFC2445_OPTIONAL
  214. );
  215. }
  216. }
  217. class iCalendar_property_version extends iCalendar_property {
  218. var $name = 'VERSION';
  219. var $val_type = RFC2445_TYPE_TEXT;
  220. var $val_default = '2.0';
  221. function __construct() {
  222. parent::__construct();
  223. $this->valid_parameters = array(
  224. RFC2445_XNAME => RFC2445_OPTIONAL
  225. );
  226. }
  227. function is_valid_value($value) {
  228. return($value === '2.0' || $value === 2.0);
  229. }
  230. }
  231. // 4.8.1 Descriptive Component Properties
  232. // --------------------------------------
  233. class iCalendar_property_attach extends iCalendar_property {
  234. var $name = 'ATTACH';
  235. var $val_type = RFC2445_TYPE_URI;
  236. function __construct() {
  237. parent::__construct();
  238. $this->valid_parameters = array(
  239. 'FMTTYPE' => RFC2445_OPTIONAL | RFC2445_ONCE,
  240. 'ENCODING' => RFC2445_OPTIONAL | RFC2445_ONCE,
  241. 'VALUE' => RFC2445_OPTIONAL | RFC2445_ONCE,
  242. RFC2445_XNAME => RFC2445_OPTIONAL
  243. );
  244. }
  245. function invariant_holds() {
  246. if(isset($this->parameters['ENCODING']) && !isset($this->parameters['VALUE'])) {
  247. return false;
  248. }
  249. if(isset($this->parameters['VALUE']) && !isset($this->parameters['ENCODING'])) {
  250. return false;
  251. }
  252. return true;
  253. }
  254. function is_valid_parameter($parameter, $value) {
  255. $parameter = strtoupper($parameter);
  256. if(!parent::is_valid_parameter($parameter, $value)) {
  257. return false;
  258. }
  259. if($parameter === 'ENCODING' && strtoupper($value) != 'BASE64') {
  260. return false;
  261. }
  262. if($parameter === 'VALUE' && strtoupper($value) != 'BINARY') {
  263. return false;
  264. }
  265. return true;
  266. }
  267. }
  268. class iCalendar_property_categories extends iCalendar_property {
  269. var $name = 'CATEGORIES';
  270. var $val_type = RFC2445_TYPE_TEXT;
  271. var $val_multi = true;
  272. function __construct() {
  273. parent::__construct();
  274. $this->valid_parameters = array(
  275. 'LANGUAGE' => RFC2445_OPTIONAL | RFC2445_ONCE,
  276. RFC2445_XNAME => RFC2445_OPTIONAL
  277. );
  278. }
  279. }
  280. class iCalendar_property_class extends iCalendar_property {
  281. var $name = 'CLASS';
  282. var $val_type = RFC2445_TYPE_TEXT;
  283. var $val_default = 'PUBLIC';
  284. function __construct() {
  285. parent::__construct();
  286. $this->valid_parameters = array(
  287. RFC2445_XNAME => RFC2445_OPTIONAL
  288. );
  289. }
  290. function is_valid_value($value) {
  291. // If this is not an xname, it is case-sensitive
  292. return ($value === 'PUBLIC' || $value === 'PRIVATE' || $value === 'CONFIDENTIAL' || rfc2445_is_xname(strtoupper($value)));
  293. }
  294. }
  295. class iCalendar_property_comment extends iCalendar_property {
  296. var $name = 'COMMENT';
  297. var $val_type = RFC2445_TYPE_TEXT;
  298. function __construct() {
  299. parent::__construct();
  300. $this->valid_parameters = array(
  301. 'ALTREP' => RFC2445_OPTIONAL | RFC2445_ONCE,
  302. 'LANGUAGE' => RFC2445_OPTIONAL | RFC2445_ONCE,
  303. RFC2445_XNAME => RFC2445_OPTIONAL
  304. );
  305. }
  306. }
  307. class iCalendar_property_description extends iCalendar_property {
  308. var $name = 'DESCRIPTION';
  309. var $val_type = RFC2445_TYPE_TEXT;
  310. function __construct() {
  311. parent::__construct();
  312. $this->valid_parameters = array(
  313. 'ALTREP' => RFC2445_OPTIONAL | RFC2445_ONCE,
  314. 'LANGUAGE' => RFC2445_OPTIONAL | RFC2445_ONCE,
  315. RFC2445_XNAME => RFC2445_OPTIONAL
  316. );
  317. }
  318. }
  319. class iCalendar_property_geo extends iCalendar_property {
  320. var $name = 'GEO';
  321. var $val_type = RFC2445_TYPE_TEXT;
  322. function __construct() {
  323. parent::__construct();
  324. $this->valid_parameters = array(
  325. 'ALTREP' => RFC2445_OPTIONAL | RFC2445_ONCE,
  326. 'LANGUAGE' => RFC2445_OPTIONAL | RFC2445_ONCE,
  327. RFC2445_XNAME => RFC2445_OPTIONAL
  328. );
  329. }
  330. function is_valid_value($value) {
  331. // This MUST be two floats separated by a semicolon
  332. if(!is_string($value)) {
  333. return false;
  334. }
  335. $floats = explode(';', $value);
  336. if(count($floats) != 2) {
  337. return false;
  338. }
  339. return rfc2445_is_valid_value($floats[0], RFC2445_TYPE_FLOAT) && rfc2445_is_valid_value($floats[1], RFC2445_TYPE_FLOAT);
  340. }
  341. function set_value($value) {
  342. // Must override this, otherwise the semicolon separating
  343. // the two floats would get auto-quoted, which is illegal
  344. if($this->is_valid_value($value)) {
  345. $this->value = $value;
  346. return true;
  347. }
  348. return false;
  349. }
  350. }
  351. class iCalendar_property_location extends iCalendar_property {
  352. var $name = 'LOCATION';
  353. var $val_type = RFC2445_TYPE_TEXT;
  354. function __construct() {
  355. parent::__construct();
  356. $this->valid_parameters = array(
  357. 'ALTREP' => RFC2445_OPTIONAL | RFC2445_ONCE,
  358. 'LANGUAGE' => RFC2445_OPTIONAL | RFC2445_ONCE,
  359. RFC2445_XNAME => RFC2445_OPTIONAL
  360. );
  361. }
  362. }
  363. class iCalendar_property_percent_complete extends iCalendar_property {
  364. var $name = 'PERCENT-COMPLETE';
  365. var $val_type = RFC2445_TYPE_INTEGER;
  366. function __construct() {
  367. parent::__construct();
  368. $this->valid_parameters = array(
  369. RFC2445_XNAME => RFC2445_OPTIONAL
  370. );
  371. }
  372. function is_valid_value($value) {
  373. // Only integers between 0 and 100 inclusive allowed
  374. if(!parent::is_valid_value($value)) {
  375. return false;
  376. }
  377. $value = intval($value);
  378. return ($value >= 0 && $value <= 100);
  379. }
  380. }
  381. class iCalendar_property_priority extends iCalendar_property {
  382. var $name = 'PRIORITY';
  383. var $val_type = RFC2445_TYPE_INTEGER;
  384. function __construct() {
  385. parent::__construct();
  386. $this->valid_parameters = array(
  387. RFC2445_XNAME => RFC2445_OPTIONAL
  388. );
  389. }
  390. function is_valid_value($value) {
  391. // Only integers between 0 and 9 inclusive allowed
  392. if(!parent::is_valid_value($value)) {
  393. return false;
  394. }
  395. $value = intval($value);
  396. return ($value >= 0 && $value <= 9);
  397. }
  398. }
  399. class iCalendar_property_resources extends iCalendar_property {
  400. var $name = 'RESOURCES';
  401. var $val_type = RFC2445_TYPE_TEXT;
  402. var $val_multi = true;
  403. function __construct() {
  404. parent::__construct();
  405. $this->valid_parameters = array(
  406. 'ALTREP' => RFC2445_OPTIONAL | RFC2445_ONCE,
  407. 'LANGUAGE' => RFC2445_OPTIONAL | RFC2445_ONCE,
  408. RFC2445_XNAME => RFC2445_OPTIONAL
  409. );
  410. }
  411. }
  412. class iCalendar_property_status extends iCalendar_property {
  413. var $name = 'STATUS';
  414. var $val_type = RFC2445_TYPE_TEXT;
  415. function __construct() {
  416. parent::__construct();
  417. $this->valid_parameters = array(
  418. RFC2445_XNAME => RFC2445_OPTIONAL
  419. );
  420. }
  421. function is_valid_value($value) {
  422. // This is case-sensitive
  423. switch ($this->parent_component) {
  424. case 'VEVENT':
  425. $allowed = array('TENTATIVE', 'CONFIRMED', 'CANCELLED');
  426. break;
  427. case 'VTODO':
  428. $allowed = array('NEEDS-ACTION', 'COMPLETED', 'IN-PROCESS', 'CANCELLED');
  429. break;
  430. case 'VJOURNAL':
  431. $allowed = array('DRAFT', 'FINAL', 'CANCELLED');
  432. break;
  433. }
  434. return in_array($value, $allowed);
  435. }
  436. }
  437. class iCalendar_property_summary extends iCalendar_property {
  438. var $name = 'SUMMARY';
  439. var $val_type = RFC2445_TYPE_TEXT;
  440. function __construct() {
  441. parent::__construct();
  442. $this->valid_parameters = array(
  443. 'ALTREP' => RFC2445_OPTIONAL | RFC2445_ONCE,
  444. 'LANGUAGE' => RFC2445_OPTIONAL | RFC2445_ONCE,
  445. RFC2445_XNAME => RFC2445_OPTIONAL
  446. );
  447. }
  448. }
  449. // 4.8.2 Date and Time Component Properties
  450. // ----------------------------------------
  451. class iCalendar_property_completed extends iCalendar_property {
  452. var $name = 'COMPLETED';
  453. var $val_type = RFC2445_TYPE_DATE_TIME;
  454. function __construct() {
  455. parent::__construct();
  456. $this->valid_parameters = array(
  457. RFC2445_XNAME => RFC2445_OPTIONAL
  458. );
  459. }
  460. function is_valid_value($value) {
  461. if(!parent::is_valid_value($value)) {
  462. return false;
  463. }
  464. // Time MUST be in UTC format
  465. return(substr($value, -1) == 'Z');
  466. }
  467. }
  468. class iCalendar_property_dtend extends iCalendar_property {
  469. var $name = 'DTEND';
  470. var $val_type = RFC2445_TYPE_DATE_TIME;
  471. function __construct() {
  472. parent::__construct();
  473. $this->valid_parameters = array(
  474. 'VALUE' => RFC2445_OPTIONAL | RFC2445_ONCE,
  475. 'TZID' => RFC2445_OPTIONAL | RFC2445_ONCE,
  476. RFC2445_XNAME => RFC2445_OPTIONAL
  477. );
  478. }
  479. function is_valid_value($value) {
  480. if(!parent::is_valid_value($value)) {
  481. return false;
  482. }
  483. // If present in a FREEBUSY component, must be in UTC format
  484. if($this->parent_component == 'VFREEBUSY' && substr($value, -1) != 'Z') {
  485. return false;
  486. }
  487. return true;
  488. }
  489. function is_valid_parameter($parameter, $value) {
  490. $parameter = strtoupper($parameter);
  491. if(!parent::is_valid_parameter($parameter, $value)) {
  492. return false;
  493. }
  494. if($parameter == 'VALUE' && !($value == 'DATE' || $value == 'DATE-TIME')) {
  495. return false;
  496. }
  497. return true;
  498. }
  499. }
  500. class iCalendar_property_due extends iCalendar_property {
  501. var $name = 'DUE';
  502. var $val_type = RFC2445_TYPE_DATE_TIME;
  503. function __construct() {
  504. parent::__construct();
  505. $this->valid_parameters = array(
  506. 'VALUE' => RFC2445_OPTIONAL | RFC2445_ONCE,
  507. 'TZID' => RFC2445_OPTIONAL | RFC2445_ONCE,
  508. RFC2445_XNAME => RFC2445_OPTIONAL
  509. );
  510. }
  511. function is_valid_value($value) {
  512. if(!parent::is_valid_value($value)) {
  513. return false;
  514. }
  515. // If present in a FREEBUSY component, must be in UTC format
  516. if($this->parent_component == 'VFREEBUSY' && substr($value, -1) != 'Z') {
  517. return false;
  518. }
  519. return true;
  520. }
  521. function is_valid_parameter($parameter, $value) {
  522. $parameter = strtoupper($parameter);
  523. if(!parent::is_valid_parameter($parameter, $value)) {
  524. return false;
  525. }
  526. if($parameter == 'VALUE' && !($value == 'DATE' || $value == 'DATE-TIME')) {
  527. return false;
  528. }
  529. return true;
  530. }
  531. }
  532. class iCalendar_property_dtstart extends iCalendar_property {
  533. var $name = 'DTSTART';
  534. var $val_type = RFC2445_TYPE_DATE_TIME;
  535. function __construct() {
  536. parent::__construct();
  537. $this->valid_parameters = array(
  538. 'VALUE' => RFC2445_OPTIONAL | RFC2445_ONCE,
  539. 'TZID' => RFC2445_OPTIONAL | RFC2445_ONCE,
  540. RFC2445_XNAME => RFC2445_OPTIONAL
  541. );
  542. }
  543. // TODO: unimplemented stuff when parent is a VTIMEZONE component
  544. function is_valid_value($value) {
  545. if(!parent::is_valid_value($value)) {
  546. return false;
  547. }
  548. // If present in a FREEBUSY component, must be in UTC format
  549. if($this->parent_component == 'VFREEBUSY' && substr($value, -1) != 'Z') {
  550. return false;
  551. }
  552. return true;
  553. }
  554. function is_valid_parameter($parameter, $value) {
  555. $parameter = strtoupper($parameter);
  556. if(!parent::is_valid_parameter($parameter, $value)) {
  557. return false;
  558. }
  559. if($parameter == 'VALUE' && !($value == 'DATE' || $value == 'DATE-TIME')) {
  560. return false;
  561. }
  562. return true;
  563. }
  564. }
  565. class iCalendar_property_duration extends iCalendar_property {
  566. var $name = 'DURATION';
  567. var $val_type = RFC2445_TYPE_DURATION;
  568. function __construct() {
  569. parent::__construct();
  570. $this->valid_parameters = array(
  571. RFC2445_XNAME => RFC2445_OPTIONAL
  572. );
  573. }
  574. function is_valid_value($value) {
  575. if(!parent::is_valid_value($value)) {
  576. return false;
  577. }
  578. // Value must be positive
  579. return ($value{0} != '-');
  580. }
  581. }
  582. class iCalendar_property_freebusy extends iCalendar_property {
  583. var $name = 'FREEBUSY';
  584. var $val_type = RFC2445_TYPE_PERIOD;
  585. var $val_multi = true;
  586. function __construct() {
  587. parent::__construct();
  588. $this->valid_parameters = array(
  589. 'FBTYPE' => RFC2445_OPTIONAL | RFC2445_ONCE,
  590. RFC2445_XNAME => RFC2445_OPTIONAL
  591. );
  592. }
  593. function is_valid_value($value) {
  594. if(!parent::is_valid_value($value)) {
  595. return false;
  596. }
  597. $pos = strpos($value, '/'); // We know there's only one / in there
  598. if($value{$pos - 1} != 'Z') {
  599. // Start time MUST be in UTC
  600. return false;
  601. }
  602. if($value{$pos + 1} != 'P' && substr($value, -1) != 'Z') {
  603. // If the second part is not a period, it MUST be in UTC
  604. return false;
  605. }
  606. return true;
  607. }
  608. // TODO: these properties SHOULD be shorted in ascending order (by start time and end time as tiebreak)
  609. }
  610. class iCalendar_property_transp extends iCalendar_property {
  611. var $name = 'TRANSP';
  612. var $val_type = RFC2445_TYPE_TEXT;
  613. var $val_default = 'OPAQUE';
  614. function __construct() {
  615. parent::__construct();
  616. $this->valid_parameters = array(
  617. RFC2445_XNAME => RFC2445_OPTIONAL
  618. );
  619. }
  620. function is_valid_value($value) {
  621. return ($value === 'TRANSPARENT' || $value === 'OPAQUE');
  622. }
  623. }
  624. // TODO: 4.8.3 timezone component properties
  625. // 4.8.4 Relationship Component Properties
  626. // ---------------------------------------
  627. class iCalendar_property_attendee extends iCalendar_property {
  628. var $name = 'ATTENDEE';
  629. var $val_type = RFC2445_TYPE_CAL_ADDRESS;
  630. // TODO: MUST NOT be specified when the calendar object has METHOD=PUBLISH
  631. // TODO: standard has lots of detail here, make triple sure that we eventually conform
  632. function __construct() {
  633. parent::__construct();
  634. $this->valid_parameters = array(
  635. 'LANGUAGE' => RFC2445_OPTIONAL | RFC2445_ONCE,
  636. 'CN' => RFC2445_OPTIONAL | RFC2445_ONCE,
  637. 'ROLE' => RFC2445_OPTIONAL | RFC2445_ONCE,
  638. 'PARTSTAT' => RFC2445_OPTIONAL | RFC2445_ONCE,
  639. 'RSVP' => RFC2445_OPTIONAL | RFC2445_ONCE,
  640. 'CUTYPE' => RFC2445_OPTIONAL | RFC2445_ONCE,
  641. 'MEMBER' => RFC2445_OPTIONAL | RFC2445_ONCE,
  642. 'DELEGATED-TO' => RFC2445_OPTIONAL | RFC2445_ONCE,
  643. 'DELEGATED-FROM' => RFC2445_OPTIONAL | RFC2445_ONCE,
  644. 'SENT-BY' => RFC2445_OPTIONAL | RFC2445_ONCE,
  645. 'DIR' => RFC2445_OPTIONAL | RFC2445_ONCE,
  646. RFC2445_XNAME => RFC2445_OPTIONAL
  647. );
  648. }
  649. function set_parent_component($componentname) {
  650. if(!parent::set_parent_component($componentname)) {
  651. return false;
  652. }
  653. if($this->parent_component == 'VFREEBUSY' || $this->parent_component == 'VALARM') {
  654. // Most parameters become invalid in this case, the full allowed set is now:
  655. $this->valid_parameters = array(
  656. 'LANGUAGE' => RFC2445_OPTIONAL | RFC2445_ONCE,
  657. RFC2445_XNAME => RFC2445_OPTIONAL
  658. );
  659. }
  660. return false;
  661. }
  662. }
  663. class iCalendar_property_contact extends iCalendar_property {
  664. var $name = 'CONTACT';
  665. var $val_type = RFC2445_TYPE_TEXT;
  666. function __construct() {
  667. parent::__construct();
  668. $this->valid_parameters = array(
  669. 'ALTREP' => RFC2445_OPTIONAL | RFC2445_ONCE,
  670. 'LANGUAGE' => RFC2445_OPTIONAL | RFC2445_ONCE,
  671. RFC2445_XNAME => RFC2445_OPTIONAL
  672. );
  673. }
  674. }
  675. class iCalendar_property_organizer extends iCalendar_property {
  676. var $name = 'ORGANIZER';
  677. var $val_type = RFC2445_TYPE_CAL_ADDRESS;
  678. function __construct() {
  679. parent::__construct();
  680. $this->valid_parameters = array(
  681. 'CN' => RFC2445_OPTIONAL | RFC2445_ONCE,
  682. 'DIR' => RFC2445_OPTIONAL | RFC2445_ONCE,
  683. 'SENT-BY' => RFC2445_OPTIONAL | RFC2445_ONCE,
  684. 'LANGUAGE' => RFC2445_OPTIONAL | RFC2445_ONCE,
  685. RFC2445_XNAME => RFC2445_OPTIONAL
  686. );
  687. }
  688. // TODO:
  689. /*
  690. Conformance: This property MUST be specified in an iCalendar object
  691. that specifies a group scheduled calendar entity. This property MUST
  692. be specified in an iCalendar object that specifies the publication of
  693. a calendar user's busy time. This property MUST NOT be specified in
  694. an iCalendar object that specifies only a time zone definition or
  695. that defines calendar entities that are not group scheduled entities,
  696. but are entities only on a single user's calendar.
  697. */
  698. }
  699. class iCalendar_property_recurrence_id extends iCalendar_property {
  700. // TODO: can only be specified when defining recurring components in the calendar
  701. /*
  702. Conformance: This property can be specified in an iCalendar object
  703. containing a recurring calendar component.
  704. Description: The full range of calendar components specified by a
  705. recurrence set is referenced by referring to just the "UID" property
  706. value corresponding to the calendar component. The "RECURRENCE-ID"
  707. property allows the reference to an individual instance within the
  708. recurrence set.
  709. */
  710. var $name = 'RECURRENCE-ID';
  711. var $val_type = RFC2445_TYPE_DATE_TIME;
  712. function __construct() {
  713. parent::__construct();
  714. $this->valid_parameters = array(
  715. 'RANGE' => RFC2445_OPTIONAL | RFC2445_ONCE,
  716. 'TZID' => RFC2445_OPTIONAL | RFC2445_ONCE,
  717. 'VALUE' => RFC2445_OPTIONAL | RFC2445_ONCE,
  718. RFC2445_XNAME => RFC2445_OPTIONAL
  719. );
  720. }
  721. function is_valid_parameter($parameter, $value) {
  722. $parameter = strtoupper($parameter);
  723. if(!parent::is_valid_parameter($parameter, $value)) {
  724. return false;
  725. }
  726. if($parameter == 'VALUE' && !($value == 'DATE' || $value == 'DATE-TIME')) {
  727. return false;
  728. }
  729. return true;
  730. }
  731. }
  732. class iCalendar_property_related_to extends iCalendar_property {
  733. var $name = 'RELATED-TO';
  734. var $val_type = RFC2445_TYPE_TEXT;
  735. // TODO: the value of this property must reference another component's UID
  736. function __construct() {
  737. parent::__construct();
  738. $this->valid_parameters = array(
  739. 'RELTYPE' => RFC2445_OPTIONAL | RFC2445_ONCE,
  740. RFC2445_XNAME => RFC2445_OPTIONAL
  741. );
  742. }
  743. }
  744. class iCalendar_property_url extends iCalendar_property {
  745. var $name = 'URL';
  746. var $val_type = RFC2445_TYPE_URI;
  747. function __construct() {
  748. parent::__construct();
  749. $this->valid_parameters = array(
  750. RFC2445_XNAME => RFC2445_OPTIONAL
  751. );
  752. }
  753. }
  754. class iCalendar_property_uid extends iCalendar_property {
  755. var $name = 'UID';
  756. var $val_type = RFC2445_TYPE_TEXT;
  757. function __construct() {
  758. parent::__construct();
  759. $this->valid_parameters = array(
  760. RFC2445_XNAME => RFC2445_OPTIONAL
  761. );
  762. // The exception to the rule: this is not a static value, so we
  763. // generate it on-the-fly here. Guaranteed to be different for
  764. // each instance of this property, too. Nice.
  765. $this->val_default = Bennu::generate_guid();
  766. }
  767. }
  768. // 4.8.5 Recurrence Component Properties
  769. // -------------------------------------
  770. class iCalendar_property_exdate extends iCalendar_property {
  771. var $name = 'EXDATE';
  772. var $val_type = RFC2445_TYPE_DATE_TIME;
  773. var $val_multi = true;
  774. function __construct() {
  775. parent::__construct();
  776. $this->valid_parameters = array(
  777. 'TZID' => RFC2445_OPTIONAL | RFC2445_ONCE,
  778. 'VALUE' => RFC2445_OPTIONAL | RFC2445_ONCE,
  779. RFC2445_XNAME => RFC2445_OPTIONAL
  780. );
  781. }
  782. function is_valid_parameter($parameter, $value) {
  783. $parameter = strtoupper($parameter);
  784. if(!parent::is_valid_parameter($parameter, $value)) {
  785. return false;
  786. }
  787. if($parameter == 'VALUE' && !($value == 'DATE' || $value == 'DATE-TIME')) {
  788. return false;
  789. }
  790. return true;
  791. }
  792. }
  793. class iCalendar_property_exrule extends iCalendar_property {
  794. var $name = 'EXRULE';
  795. var $val_type = RFC2445_TYPE_RECUR;
  796. function __construct() {
  797. parent::__construct();
  798. $this->valid_parameters = array(
  799. RFC2445_XNAME => RFC2445_OPTIONAL
  800. );
  801. }
  802. }
  803. class iCalendar_property_rdate extends iCalendar_property {
  804. var $name = 'RDATE';
  805. var $val_type = RFC2445_TYPE_DATE_TIME;
  806. var $val_multi = true;
  807. function __construct() {
  808. parent::__construct();
  809. $this->valid_parameters = array(
  810. 'TZID' => RFC2445_OPTIONAL | RFC2445_ONCE,
  811. 'VALUE' => RFC2445_OPTIONAL | RFC2445_ONCE,
  812. RFC2445_XNAME => RFC2445_OPTIONAL
  813. );
  814. }
  815. function is_valid_parameter($parameter, $value) {
  816. $parameter = strtoupper($parameter);
  817. if(!parent::is_valid_parameter($parameter, $value)) {
  818. return false;
  819. }
  820. if($parameter == 'VALUE' && !($value == 'DATE' || $value == 'DATE-TIME' || $value == 'PERIOD')) {
  821. return false;
  822. }
  823. return true;
  824. }
  825. }
  826. class iCalendar_property_rrule extends iCalendar_property {
  827. var $name = 'RRULE';
  828. var $val_type = RFC2445_TYPE_RECUR;
  829. function __construct() {
  830. parent::__construct();
  831. $this->valid_parameters = array(
  832. RFC2445_XNAME => RFC2445_OPTIONAL
  833. );
  834. }
  835. }
  836. // 4.8.6 Alarm Component Properties
  837. // -------------------------------------------
  838. class iCalendar_property_action extends iCalendar_property {
  839. var $name = 'ACTION';
  840. var $val_type = RFC2445_TYPE_TEXT;
  841. function __construct() {
  842. parent::__construct();
  843. $this->valid_parameters = array(
  844. RFC2445_XNAME => RFC2445_OPTIONAL
  845. );
  846. }
  847. function is_valid_value($value) {
  848. if(!parent::is_valid_value($value)) {
  849. return false;
  850. }
  851. // Value must be one of the following, or an x-name.
  852. $valid_values = array('ACTION', 'DISPLAY', 'EMAIL', 'PROCEDURE');
  853. return(in_array($value, $valid_values) || rfc2445_is_xname($value));
  854. }
  855. }
  856. class iCalendar_property_repeat extends iCalendar_property {
  857. var $name = 'REPEAT';
  858. var $val_type = RFC2445_TYPE_INTEGER;
  859. function __construct() {
  860. parent::__construct();
  861. $this->valid_parameters = array(
  862. RFC2445_XNAME => RFC2445_OPTIONAL
  863. );
  864. }
  865. }
  866. class iCalendar_property_trigger extends iCalendar_property {
  867. var $name = 'TRIGGER';
  868. var $val_type = RFC2445_TYPE_TEXT;
  869. function __construct() {
  870. parent::__construct();
  871. $this->valid_parameters = array(
  872. 'VALUE' => RFC2445_OPTIONAL | RFC2445_ONCE,
  873. 'RELATED' => RFC2445_OPTIONAL | RFC2445_ONCE,
  874. RFC2445_XNAME => RFC2445_OPTIONAL
  875. );
  876. }
  877. function is_valid_value($value) {
  878. if(!parent::is_valid_value($value)) {
  879. return false;
  880. }
  881. // Must either be DURATION or DATE_TIME type
  882. return(rfc2445_is_valid_value($value, RFC2445_TYPE_DURATION)
  883. || rfc2445_is_valid_value($value, RFC2445_TYPE_DATE_TIME));
  884. }
  885. }
  886. // 4.8.7 Change Management Component Properties
  887. // --------------------------------------------
  888. class iCalendar_property_created extends iCalendar_property {
  889. var $name = 'CREATED';
  890. var $val_type = RFC2445_TYPE_DATE_TIME;
  891. function __construct() {
  892. parent::__construct();
  893. $this->valid_parameters = array(
  894. RFC2445_XNAME => RFC2445_OPTIONAL
  895. );
  896. }
  897. function is_valid_value($value) {
  898. if(!parent::is_valid_value($value)) {
  899. return false;
  900. }
  901. // Time MUST be in UTC format
  902. return(substr($value, -1) == 'Z');
  903. }
  904. }
  905. class iCalendar_property_dtstamp extends iCalendar_property {
  906. var $name = 'DTSTAMP';
  907. var $val_type = RFC2445_TYPE_DATE_TIME;
  908. function __construct() {
  909. parent::__construct();
  910. $this->valid_parameters = array(
  911. RFC2445_XNAME => RFC2445_OPTIONAL
  912. );
  913. }
  914. function is_valid_value($value) {
  915. if(!parent::is_valid_value($value)) {
  916. return false;
  917. }
  918. // Time MUST be in UTC format
  919. return(substr($value, -1) == 'Z');
  920. }
  921. }
  922. class iCalendar_property_last_modified extends iCalendar_property {
  923. var $name = 'LAST-MODIFIED';
  924. var $val_type = RFC2445_TYPE_DATE_TIME;
  925. function __construct() {
  926. parent::__construct();
  927. $this->valid_parameters = array(
  928. RFC2445_XNAME => RFC2445_OPTIONAL
  929. );
  930. }
  931. function is_valid_value($value) {
  932. if(!parent::is_valid_value($value)) {
  933. return false;
  934. }
  935. // Time MUST be in UTC format
  936. return(substr($value, -1) == 'Z');
  937. }
  938. }
  939. class iCalendar_property_sequence extends iCalendar_property {
  940. var $name = 'SEQUENCE';
  941. var $val_type = RFC2445_TYPE_INTEGER;
  942. var $val_default = 0;
  943. function __construct() {
  944. parent::__construct();
  945. $this->valid_parameters = array(
  946. RFC2445_XNAME => RFC2445_OPTIONAL
  947. );
  948. }
  949. function is_valid_value($value) {
  950. if(!parent::is_valid_value($value)) {
  951. return false;
  952. }
  953. $value = intval($value);
  954. return ($value >= 0);
  955. }
  956. }
  957. // 4.8.8 Miscellaneous Component Properties
  958. // ----------------------------------------
  959. class iCalendar_property_x extends iCalendar_property {
  960. var $name = RFC2445_XNAME;
  961. var $val_type = NULL;
  962. function __construct() {
  963. parent::__construct();
  964. $this->valid_parameters = array(
  965. 'LANGUAGE' => RFC2445_OPTIONAL | RFC2445_ONCE,
  966. RFC2445_XNAME => RFC2445_OPTIONAL
  967. );
  968. }
  969. function set_name($name) {
  970. $name = strtoupper($name);
  971. if(rfc2445_is_xname($name)) {
  972. $this->name = $name;
  973. return true;
  974. }
  975. return false;
  976. }
  977. }
  978. class iCalendar_property_request_status extends iCalendar_property {
  979. // IMPORTANT NOTE: This property value includes TEXT fields
  980. // separated by semicolons. Unfortunately, auto-value-formatting
  981. // cannot be used in this case. As an exception, the value passed
  982. // to this property MUST be already escaped.
  983. var $name = 'REQUEST-STATUS';
  984. var $val_type = RFC2445_TYPE_TEXT;
  985. function __construct() {
  986. parent::__construct();
  987. $this->valid_parameters = array(
  988. 'LANGUAGE' => RFC2445_OPTIONAL | RFC2445_ONCE,
  989. RFC2445_XNAME => RFC2445_OPTIONAL
  990. );
  991. }
  992. function is_valid_value($value) {
  993. if(!is_string($value) || empty($value)) {
  994. return false;
  995. }
  996. $len = strlen($value);
  997. $parts = array();
  998. $from = 0;
  999. $escch = false;
  1000. for($i = 0; $i < $len; ++$i) {
  1001. if($value{$i} == ';' && !$escch) {
  1002. // Token completed
  1003. $parts[] = substr($value, $from, $i - $from);
  1004. $from = $i + 1;
  1005. continue;
  1006. }
  1007. $escch = ($value{$i} == '\\');
  1008. }
  1009. // Add one last token with the remaining text; if the value
  1010. // ended with a ';' it was illegal, so check that this token
  1011. // is not the empty string.
  1012. $parts[] = substr($value, $from);
  1013. $count = count($parts);
  1014. // May have 2 or 3 tokens (last one is optional)
  1015. if($count != 2 && $count != 3) {
  1016. return false;
  1017. }
  1018. // REMEMBER: if ANY part is empty, we have an illegal value
  1019. // First token must be hierarchical numeric status (3 levels max)
  1020. if(strlen($parts[0]) == 0) {
  1021. return false;
  1022. }
  1023. if($parts[0]{0} < '1' || $parts[0]{0} > '4') {
  1024. return false;
  1025. }
  1026. $len = strlen($parts[0]);
  1027. // Max 3 levels, and can't end with a period
  1028. if($len > 5 || $parts[0]{$len - 1} == '.') {
  1029. return false;
  1030. }
  1031. for($i = 1; $i < $len; ++$i) {
  1032. if(($i & 1) == 1 && $parts[0]{$i} != '.') {
  1033. // Even-indexed chars must be periods
  1034. return false;
  1035. }
  1036. else if(($i & 1) == 0 && ($parts[0]{$i} < '0' || $parts[0]{$i} > '9')) {
  1037. // Odd-indexed chars must be numbers
  1038. return false;
  1039. }
  1040. }
  1041. // Second and third tokens must be TEXT, and already escaped, so
  1042. // they are not allowed to have UNESCAPED semicolons, commas, slashes,
  1043. // or any newlines at all
  1044. for($i = 1; $i < $count; ++$i) {
  1045. if(strpos($parts[$i], "\n") !== false) {
  1046. return false;
  1047. }
  1048. $len = strlen($parts[$i]);
  1049. if($len == 0) {
  1050. // Cannot be empty
  1051. return false;
  1052. }
  1053. $parts[$i] .= '#'; // This guard token saves some conditionals in the loop
  1054. for($j = 0; $j < $len; ++$j) {
  1055. $thischar = $parts[$i]{$j};
  1056. $nextchar = $parts[$i]{$j + 1};
  1057. if($thischar == '\\') {
  1058. // Next char must now be one of ";,\nN"
  1059. if($nextchar != ';' && $nextchar != ',' && $nextchar != '\\' &&
  1060. $nextchar != 'n' && $nextchar != 'N') {
  1061. return false;
  1062. }
  1063. // OK, this escaped sequence is correct, bypass next char
  1064. ++$j;
  1065. continue;
  1066. }
  1067. if($thischar == ';' || $thischar == ',' || $thischar == '\\') {
  1068. // This wasn't escaped as it should
  1069. return false;
  1070. }
  1071. }
  1072. }
  1073. return true;
  1074. }
  1075. function set_value($value) {
  1076. // Must override this, otherwise the value would be quoted again
  1077. if($this->is_valid_value($value)) {
  1078. $this->value = $value;
  1079. return true;
  1080. }
  1081. return false;
  1082. }
  1083. }
  1084. class iCalendar_property_tzid extends iCalendar_property {
  1085. var $name = 'TZID';
  1086. var $val_type = RFC2445_TYPE_TEXT;
  1087. function __construct() {
  1088. parent::__construct();
  1089. $this->valid_parameters = array(
  1090. RFC2445_XNAME => RFC2445_OPTIONAL
  1091. );
  1092. }
  1093. function is_valid_value($value) {
  1094. if(!parent::is_valid_value($value)) {
  1095. return false;
  1096. } else {
  1097. return true;
  1098. }
  1099. }
  1100. }
  1101. class iCalendar_property_tzname extends iCalendar_property {
  1102. var $name = 'TZNAME';
  1103. var $val_type = RFC2445_TYPE_TEXT;
  1104. function __construct() {
  1105. parent::__construct();
  1106. $this->valid_parameters = array(
  1107. 'LANGUAGE' => RFC2445_OPTIONAL | RFC2445_ONCE,
  1108. RFC2445_XNAME => RFC2445_OPTIONAL
  1109. );
  1110. }
  1111. function is_valid_value($value) {
  1112. if(!parent::is_valid_value($value)) {
  1113. return false;
  1114. } else {
  1115. return true;
  1116. }
  1117. }
  1118. }
  1119. class iCalendar_property_tzoffsetfrom extends iCalendar_property {
  1120. var $name = 'TZOFFSETFROM';
  1121. var $val_type = RFC2445_TYPE_UTC_OFFSET;
  1122. function __construct() {
  1123. parent::__construct();
  1124. $this->valid_parameters = array(
  1125. RFC2445_XNAME => RFC2445_OPTIONAL
  1126. );
  1127. }
  1128. function is_valid_value($value) {
  1129. if(!parent::is_valid_value($value)) {
  1130. return false;
  1131. } else {
  1132. return true;
  1133. }
  1134. }
  1135. }
  1136. class iCalendar_property_tzoffsetto extends iCalendar_property {
  1137. var $name = 'TZOFFSETTO';
  1138. var $val_type = RFC2445_TYPE_UTC_OFFSET;
  1139. function __construct() {
  1140. parent::__construct();
  1141. $this->valid_parameters = array(
  1142. RFC2445_XNAME => RFC2445_OPTIONAL
  1143. );
  1144. }
  1145. function is_valid_value($value) {
  1146. if(!parent::is_valid_value($value)) {
  1147. return false;
  1148. } else {
  1149. return true;
  1150. }
  1151. }
  1152. }
  1153. #######################
  1154. /*
  1155. class iCalendar_property_class extends iCalendar_property {
  1156. var $name = 'CLASS';
  1157. var $val_type = RFC2445_TYPE_TEXT;
  1158. function __construct() {
  1159. parent::__construct();
  1160. $this->valid_parameters = array(
  1161. RFC2445_XNAME => RFC2445_OPTIONAL
  1162. );
  1163. }
  1164. }
  1165. */