]> git.datanom.net - webcal.git/blob - caldav/ical.class.php
Initial upload
[webcal.git] / caldav / ical.class.php
1 <?php
2 /* $Id$ */
3
4 $msg = <<<EOF
5 BEGIN:VCALENDAR
6 PRODID:-//davical.org//NONSGML AWL Calendar//EN
7 VERSION:2.0
8 CALSCALE:GREGORIAN
9 BEGIN:VEVENT
10 DTSTAMP:20070417T195323Z
11 ORGANIZER:MAILTO:
12 X-PILOTID:12451851
13 X-PILOTSTAT:0
14 CREATED:20050814T160951Z
15 UID:libkcal-1123041440.757
16 SEQUENCE:0
17 LAST-MODIFIED:20070124T213448Z
18 SUMMARY:møde carl christian\, hovedopgave
19 CLASS:PUBLIC
20 PRIORITY:3
21 RRULE:FREQ=DAILY;UNTIL=20020528T153000Z;INTERVAL=14
22 EXDATE;VALUE=DATE:20020402
23 DTSTART:20020219T163000Z
24 DTEND:20020219T180000Z
25 TRANSP:OPAQUE
26 BEGIN:VALARM
27 DESCRIPTION:
28 ACTION:DISPLAY
29 TRIGGER;VALUE=DURATION:-P1D
30 END:VALARM
31 END:VEVENT
32 END:VCALENDAR
33 BEGIN:VCALENDAR
34 PRODID:-//davical.org//NONSGML AWL Calendar//EN
35 VERSION:2.0
36 CALSCALE:GREGORIAN
37 BEGIN:VEVENT
38 DTSTAMP:20070417T195323Z
39 ORGANIZER:MAILTO:
40 X-PILOTID:12451851
41 X-PILOTSTAT:0
42 CREATED:20050814T160951Z
43 UID:libkcal-1123041440.757
44 SEQUENCE:0
45 LAST-MODIFIED:20070124T213448Z
46 SUMMARY:event number 2
47 CLASS:PUBLIC
48 PRIORITY:3
49 RRULE:FREQ=DAILY;UNTIL=20020528T153000Z;INTERVAL=14
50 EXDATE;VALUE=DATE:20020402
51 DTSTART:20020219T163000Z
52 DTEND:20020219T180000Z
53 TRANSP:OPAQUE
54 BEGIN:VALARM
55 DESCRIPTION:
56 ACTION:DISPLAY
57 TRIGGER;VALUE=DURATION:-P1D
58 END:VALARM
59 END:VEVENT
60 END:VCALENDAR
61 EOF;
62
63 require_once 'caldav-client.php';
64
65 interface ICalendar {
66 function getProperty($name);
67 function setProperty($name, $value);
68 function getAllProperties();
69 function setAllProperties(array $assoc_array);
70 function isChanged();
71 }
72
73 class VEvent implements ICalendar {
74 private $attendee;
75 private $class;
76 private $created;
77 private $dtstamp;
78 private $description;
79 private $dtend;
80 private $dtstart;
81 private $duration;
82 private $exdate;
83 private $lastmodified;
84 private $location;
85 private $organizer;
86 private $priority;
87 private $rrule;
88 private $sequence;
89 private $status;
90 private $summary;
91 private $transp;
92 private $uid;
93 private $xprop = array();
94 // Properties must not contain hypens in their name
95 private static $translate = array(
96 'lastmodified' => 'last-modified',
97 'xprop' => 'x-prop'
98 );
99 private $changed;
100
101 function __construct($values = array()) {
102 if (! is_array($values))
103 throw new Exception("Attribute to constructor must be an array");
104 $this->setProperties($values);
105 $this->changed = FALSE;
106 }
107
108 private function setProperties(array $props) {
109 foreach ($props as $k => $v) {
110 switch (strtoupper($k)) {
111 case 'DTSTAMP':
112 $this->dtstamp = $v;
113 break;
114 case 'ORGANIZER':
115 $this->organizer = $v;
116 break;
117 case 'CREATED':
118 $this->created = $v;
119 break;
120 case 'UID':
121 $this->uid = $v;
122 break;
123 case 'SEQUENCE':
124 $this->sequence = $v;
125 break;
126 case 'LAST-MODIFIED':
127 $this->lastmodified = $v;
128 break;
129 case 'SUMMARY':
130 $this->summary = $v;
131 break;
132 case 'CLASS':
133 $this->class = $v;
134 break;
135 case 'PRIORITY':
136 $this->priority = $v;
137 break;
138 case 'RRULE':
139 $this->rrule = $v;
140 break;
141 case 'EXDATE':
142 $this->exdate = $v;
143 break;
144 case 'DTSTART':
145 $this->dtstart = $v;
146 break;
147 case 'DTEND':
148 $this->dtend = $v;
149 break;
150 case 'TRANSP':
151 $this->transp = $v;
152 break;
153 case 'ATTENDEE':
154 $this->attendee = $v;
155 break;
156 case 'DURATION':
157 $this->duration = $v;
158 break;
159 case 'LOCATION':
160 $this->location = $v;
161 break;
162 case 'STATUS':
163 $this->status = $v;
164 break;
165 case 'DESCRIPTION':
166 $this->description = $v;
167 break;
168 default:
169 if (($k[0] == 'x' || $k[0] == 'X') && $k[1] == '-')
170 $this->xprop[$k] = $v;
171 else
172 throw new Exception("[$k,$v]: Unknown attribute");
173 }
174 }
175 }
176
177 public function getProperty($name) {
178 $prop = strtolower($name);
179 if (($trans = array_search($prop, self::$translate)) !== false)
180 $prop = $trans;
181 if (! property_exists($this, $prop)) {
182 // Support for PHP 5 < 5.3
183 $obj = new ReflectionClass(get_class($this));
184 if (! $obj->hasProperty($prop))
185 throw new Exception("$name: Unknown property");
186 }
187 return $this->$prop;
188 }
189
190 public function setProperty($name, $value) {
191 $prop = strtolower($name);
192 if (($trans = array_search($prop, self::$translate)) !== false)
193 $prop = $trans;
194 if (! property_exists($this, $prop)) {
195 // Support for PHP 5 < 5.3
196 $obj = new ReflectionClass(get_class($this));
197 if (! $obj->hasProperty($prop))
198 throw new Exception("$name: Unknown property");
199 }
200 $this->$prop = $value;
201 $this->changed = TRUE;
202 }
203
204 function getAllProperties() {
205 $props = array();
206 $p = get_object_vars($this);
207 foreach ($p as $k => $v){
208 if ($k == 'changed')
209 continue;
210 if (array_key_exists($k, self::$translate))
211 $k = strtoupper(self::$translate[$k]);
212 else
213 $k = strtoupper($k);
214 $props[$k] = $v;
215 }
216 return $props;
217 }
218
219 function setAllProperties(array $assoc_array) {
220 $props = array();
221 foreach ($assoc_array as $k => $v){
222 $k = strtolower($k);
223 if (is_array($v)) {
224 foreach ($v as $k1 => $v1)
225 $props[$k1] = $v1;
226 }
227 else
228 $props[$k] = $v;
229 }
230 $this->setProperties($props);
231 $this->changed = TRUE;
232 }
233
234 function isChanged() {
235 return $this->changed;
236 }
237
238 }
239
240 class VAlarm implements ICalendar {
241 private $action;
242 private $description;
243 private $duration;
244 private $repeat;
245 private $trigger;
246 // Properties must not contain hypens in their name
247 private static $translate = array();
248 private $changed;
249
250 function __construct($values = array()) {
251 if (! is_array($values))
252 throw new Exception("Attribute to constructor must be an array");
253 $this->setProperties($values);
254 $this->changed = FALSE;
255 }
256
257 private function setProperties(array $props) {
258 foreach ($props as $k => $v) {
259 switch (strtoupper($k)) {
260 case 'ACTION':
261 $this->action = $v;
262 break;
263 case 'DESCRIPTION':
264 $this->description = $v;
265 break;
266 case 'DURATION':
267 $this->duration = $v;
268 break;
269 case 'REPEAT':
270 $this->repeat = $v;
271 break;
272 case 'TRIGGER':
273 $this->trigger = $v;
274 break;
275 default:
276 throw new Exception("$k: Unknown attribute");
277 }
278 }
279 }
280
281 public function getProperty($name) {
282 $prop = strtolower($name);
283 if (($trans = array_search($prop, self::$translate)) !== false)
284 $prop = $trans;
285 if (! property_exists($this, $prop)) {
286 // Support for PHP 5 < 5.3
287 $obj = new ReflectionClass(get_class($this));
288 if (! $obj->hasProperty($prop))
289 throw new Exception("$name: Unknown property");
290 }
291 return $this->$prop;
292 }
293
294 public function setProperty($name, $value) {
295 $prop = strtolower($name);
296 if (($trans = array_search($prop, self::$translate)) !== false)
297 $prop = $trans;
298 if (! property_exists($this, $prop)) {
299 // Support for PHP 5 < 5.3
300 $obj = new ReflectionClass(get_class($this));
301 if (! $obj->hasProperty($prop))
302 throw new Exception("$name: Unknown property");
303 }
304 $this->$prop = $value;
305 $this->changed = TRUE;
306 }
307
308 function getAllProperties() {
309 $props = array();
310 $p = get_object_vars($this);
311 foreach ($p as $k => $v){
312 if ($k == 'changed')
313 continue;
314 if (array_key_exists($k, self::$translate))
315 $k = strtoupper(self::$translate[$k]);
316 else
317 $k = strtoupper($k);
318 $props[$k] = $v;
319 }
320 return $props;
321 }
322
323 function setAllProperties(array $assoc_array) {
324 $props = array();
325 foreach ($assoc_array as $k => $v){
326 $k = strtolower($k);
327 if (is_array($v)) {
328 foreach ($v as $k1 => $v1)
329 $props[$k1] = $v1;
330 }
331 else
332 $props[$k] = $v;
333 }
334 $this->setProperties($props);
335 $this->changed = TRUE;
336 }
337
338 function isChanged() {
339 return $this->changed;
340 }
341
342 }
343
344 class VTimezone implements ICalendar {
345
346 private $tzid;
347 private $xprop;
348 private $standard;
349 private $daylight;
350
351 // Properties must not contain hypens in their name
352 private static $translate = array();
353 private $changed;
354
355 function __construct($values = array()) {
356 if (! is_array($values))
357 throw new Exception("Attribute to constructor must be an array");
358 $this->setProperties($values);
359 $this->changed = FALSE;
360 }
361
362 private function setProperties(array $props) {
363 foreach ($props as $k => $v) {
364 switch (strtoupper($k)) {
365 case 'TZID':
366 $this->tzid = $v;
367 break;
368 default:
369 if (($k[0] == 'x' || $k[0] == 'X') && $k[1] == '-')
370 $this->xprop[$k] = $v;
371 else
372 throw new Exception("[$k,$v]: Unknown attribute");
373 }
374 }
375 }
376
377 function getProperty($name){}
378 function setProperty($name, $value) {
379 $prop = strtolower($name);
380 if (($trans = array_search($prop, self::$translate)) !== false)
381 $prop = $trans;
382 if (! property_exists($this, $prop)) {
383 // Support for PHP 5 < 5.3
384 $obj = new ReflectionClass(get_class($this));
385 if (! $obj->hasProperty($prop))
386 throw new Exception("$name: Unknown property");
387 }
388 $this->$prop = $value;
389 if (
390 $this->$prop == 'standard' && ! isset($this->standard) ||
391 $this->$prop == 'daylight' && ! isset($this->daylight)
392 ) {
393 // we are constructing the object
394 }
395 else
396 $this->changed = TRUE;
397 }
398 function getAllProperties(){}
399 function setAllProperties(array $assoc_array){}
400 function isChanged(){}
401 }
402
403 class VCalendar {
404 private $calscale;
405 private $prodid;
406 private $version;
407 private $objects;
408
409 function __construct($values = array()) {
410 if (! is_array($values))
411 throw new Exception("Attribute to constructor must be an array");
412 $this->objects = array();
413 //print_r($values);
414 foreach ($values as $k => $v) {
415 switch (strtoupper($k)) {
416 case 'CALSCALE':
417 $this->calscale = $v;
418 break;
419 case 'PRODID':
420 $this->prodid = $v;
421 break;
422 case 'VERSION':
423 $this->version = $v;
424 break;
425 default:
426 throw new Exception("$k: Unknown attribute");
427 }
428 }
429 }
430
431 public function addObject(ICalendar $object) {
432 $this->objects[get_class($object)] = $object;
433 }
434
435 public function getObject($name) {
436 if (isset($this->objects[$name]))
437 return $this->objects[$name];
438 else
439 return null;
440 }
441
442 public function getObjects() {
443 return $this->objects;
444 }
445
446 }
447
448 class IcalParserIterator implements Iterator {
449 private $icalParser;
450 private $pos;
451
452 function __construct(IcalParser $icalParser) {
453 if (! $icalParser instanceof IcalParser)
454 throw new Exception(get_class($icalParser) . ': Can only handle instances of class IcalParser');
455 $this->icalParser = $icalParser;
456 $this->pos = 0;
457 }
458
459 public function current() {
460 return $this->icalParser->getIcal($this->pos);
461 }
462
463 public function key() {
464 return $this->pos;
465 }
466
467 public function next() {
468 $this->pos++;
469 }
470
471 public function rewind() {
472 $this->pos = 0;
473 }
474
475 public function valid() {
476 return $this->icalParser->peek($this->pos);
477 }
478
479 }
480
481 class IcalParser implements IteratorAggregate {
482 // private $elements = array('VCALENDAR', 'VEVENT', 'VALARM', 'VTIMEZONE');
483 // private $subelem = array('VTIMEZONE' => array('STANDARD', 'DAYLIGHT'));
484 private $items;
485
486 function __construct($message = NULL) {
487 $this->items = array();
488 if ($message)
489 $this->setMessage($message);
490 }
491
492 function setMessage($message) {
493 $this->split_message($message);
494 }
495
496 private function remove_value(array &$array, $key) {
497 $tmp = array();
498 $val;
499
500 foreach ($array as $k => $v) {
501 if ($k == $key)
502 $val = $v;
503 else
504 $tmp[$k] = $v;
505 }
506 $array = $tmp;
507 return $val;
508 }
509
510 private function newObject($name, $props = array()) {
511 try {
512 $obj = new ReflectionClass($name);
513 }
514 catch (ReflectionException $ex) {
515 print $ex->getMessage();
516 throw new Exception("$name: Class not found");
517 }
518 $obj = NULL;
519 return new $name($props);
520 }
521
522 private function split_message($message) {
523 $data = null;
524 $elem_data = array();
525 $data_list = array();
526 $elem_list = array();
527
528 if (empty($message))
529 return;
530 //print "$message\n";
531 $lines = explode("\n", $message);
532 foreach ($lines as $line) {
533 $a = explode(":", $line);
534 //print_r($a);
535 $item = strtoupper($a[0]);
536 if (count($a) > 2) {
537 $elem = '';
538 for ($i = 1; $i < count($a); $i++) {
539 if ($elem != '')
540 $elem .= ':';
541 $elem .= $a[$i];
542 }
543 //print "elem: $elem\n";
544 }
545 else
546 $elem = $a[1];
547 switch ($item) {
548 case 'BEGIN':
549 /*if (! in_array($elem, $this->elements)) {
550 $found = TRUE;
551 if (($sub = end($elem_list)) !== FALSE) {
552 if (! array_key_exists($sub, $this->subelem))
553 $found = FALSE;
554 }
555 else
556 $found = FALSE;
557 reset($elem_list);
558 if (! $found)
559 break;
560 }*/
561 if (count($data) > 0) {
562 if (count($elem_list) > 0) {
563 /*$name = array_pop($elem_list);
564 if (array_key_exists($name, $this->subelem)) {
565 print "BEGIN: $name\n";
566 print_r($data);
567 }*/
568 $elem_data[array_pop($elem_list)] = $data;
569 }
570 else
571 $elem_data[$elem] = $data;
572 }
573 array_push($elem_list, $elem);
574 $data = array();
575 break;
576 case 'END':
577 if (count($elem_list) > 0) {
578 /*$name = array_pop($elem_list);
579 if (array_key_exists($name, $this->subelem)) {
580 print "END: $name\n";
581 print_r($data);
582 }*/
583 $elem_data[array_pop($elem_list)] = $data;
584 }
585 $data = array();
586 if ($elem == 'VCALENDAR' && count($elem_data) > 1) {
587 array_push($data_list, $elem_data);
588 $elem_data = array();
589 $elem_list = array();
590 }
591 break;
592 default:
593 if (! is_array($data))
594 throw new Exception("Message is not valid [missing 'BEGIN']");
595 if (($pos = strpos($item, ';')) !== false) {
596 $head = substr($item, 0, $pos);
597 $elem = substr($item, $pos + 1) . ';' . $elem;
598 $item = $head;
599 //print "Ny elem: $item:$elem\n";
600 }
601 $data[$item] = $elem;
602 }
603 }
604 //print_r($data_list);
605 foreach ($data_list as $item) {
606 $c = $this->remove_value($item, 'VCALENDAR');
607 $calendar = new VCalendar($c);
608 foreach ($item as $k => $v) {
609 if ($k == 'DAYLIGHT' || $k == 'STANDARD') {
610 $object = $calendar->getObject('VTimezone');
611 if (! is_object($object)) {
612 $object = $this->newObject('VTimezone');
613 $calendar->addObject($object);
614 }
615 $object->setProperty($k, $v);
616 }
617 else {
618 if (($object = $calendar->getObject($k)) == NULL) {
619 $object = $this->newObject($k, $v);
620 $calendar->addObject($object);
621 }
622 else {
623 $object->setAllProperties($v);
624 }
625 }
626 }
627 array_push($this->items, $calendar);
628 }
629 }
630
631 public function getIterator() {
632 return new IcalParserIterator($this);
633 }
634
635 public function getIcal($id) {
636 if ($this->peek($id))
637 return $this->items[$id];
638 else
639 throw new Exception("Index out of bounds");
640 }
641
642 public function peek($id) {
643 return isset($this->items[$id]);
644 }
645
646 }
647
648 ?>
This page took 0.119978 seconds and 6 git commands to generate.