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