1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11:
12:
13: require_once('XMLDocument.php');
14:
15: 16: 17: 18:
19: class CalendarInfo {
20: public $url;
21: public $displayname;
22: public $getctag;
23:
24: function __construct( $url, $displayname = null, $getctag = null ) {
25: $this->url = $url;
26: $this->displayname = $displayname;
27: $this->getctag = $getctag;
28: }
29:
30: function __toString() {
31: return( '(URL: '.$this->url.' Ctag: '.$this->getctag.' Displayname: '.$this->displayname .')'. "\n" );
32: }
33: }
34:
35: if(!defined("_FSOCK_TIMEOUT")){
36: define("_FSOCK_TIMEOUT", 10);
37: }
38:
39: 40: 41: 42: 43:
44: class CalDAVClient {
45: 46: 47: 48: 49:
50: protected $base_url, $user, $pass, $entry, $protocol, $server, $port;
51:
52: 53: 54:
55: protected $principal_url;
56:
57: 58: 59:
60: protected $calendar_url;
61:
62: 63: 64:
65: protected $calendar_home_set;
66:
67: 68: 69:
70: protected $calendar_urls;
71:
72: 73: 74: 75: 76:
77: public $user_agent = 'DAViCalClient';
78:
79: protected = array();
80: protected $body = "";
81: protected $requestMethod = "GET";
82: protected $httpRequest = "";
83: protected $xmlRequest = "";
84: protected $xmlResponse = "";
85: protected $httpResponseCode = 0;
86: protected = "";
87: protected ;
88: protected $httpResponseBody = "";
89:
90: protected $parser;
91:
92: private $debug = false;
93:
94: 95: 96: 97: 98: 99: 100:
101: function __construct( $base_url, $user, $pass ) {
102: $this->user = $user;
103: $this->pass = $pass;
104: $this->headers = array();
105:
106: if ( preg_match( '#^(https?)://([a-z0-9.-]+)(:([0-9]+))?(/.*)$#', $base_url, $matches ) ) {
107: $this->server = $matches[2];
108: $this->base_url = $matches[5];
109: if ( $matches[1] == 'https' ) {
110: $this->protocol = 'ssl';
111: $this->port = 443;
112: }
113: else {
114: $this->protocol = 'tcp';
115: $this->port = 80;
116: }
117: if ( $matches[4] != '' ) {
118: $this->port = intval($matches[4]);
119: }
120: }
121: else {
122: trigger_error("Invalid URL: '".$base_url."'", E_USER_ERROR);
123: }
124: }
125:
126:
127: 128: 129: 130: 131:
132: function SetDebug( $new_value ) {
133: $old_value = $this->debug;
134: if ( $new_value )
135: $this->debug = true;
136: else
137: $this->debug = false;
138: return $old_value;
139: }
140:
141:
142:
143: 144: 145: 146: 147: 148:
149: function SetMatch( $match, $etag = '*' ) {
150: $this->headers['match'] = sprintf( "%s-Match: \"%s\"", ($match ? "If" : "If-None"), trim($etag,'"'));
151: }
152:
153: 154: 155: 156: 157:
158: function SetDepth( $depth = '0' ) {
159: $this->headers['depth'] = 'Depth: '. ($depth == '1' ? "1" : ($depth == 'infinity' ? $depth : "0") );
160: }
161:
162: 163: 164: 165: 166:
167: function SetUserAgent( $user_agent = null ) {
168: if ( !isset($user_agent) ) $user_agent = $this->user_agent;
169: $this->user_agent = $user_agent;
170: }
171:
172: 173: 174: 175: 176:
177: function SetContentType( $type ) {
178: $this->headers['content-type'] = "Content-type: $type";
179: }
180:
181: 182: 183: 184: 185:
186: function SetCalendar( $url ) {
187: $this->calendar_url = $url;
188: }
189:
190: 191: 192: 193: 194:
195: function ParseResponse( $response ) {
196: $pos = strpos($response, '<?xml');
197: if ($pos !== false) {
198: $this->xmlResponse = trim(substr($response, $pos));
199: $this->xmlResponse = preg_replace('{>[^>]*$}s', '>',$this->xmlResponse );
200: $parser = xml_parser_create_ns('UTF-8');
201: xml_parser_set_option ( $parser, XML_OPTION_SKIP_WHITE, 1 );
202: xml_parser_set_option ( $parser, XML_OPTION_CASE_FOLDING, 0 );
203:
204: if ( xml_parse_into_struct( $parser, $this->xmlResponse, $this->xmlnodes, $this->xmltags ) === 0 ) {
205: printf( "XML parsing error: %s - %s\n", xml_get_error_code($parser), xml_error_string(xml_get_error_code($parser)) );
206:
207:
208:
209: printf( "\nXML Reponse:\n%s\n", $this->xmlResponse );
210: }
211:
212: xml_parser_free($parser);
213: }
214: }
215:
216: 217: 218: 219: 220:
221: function () {
222: if ( empty($this->httpResponseHeaders) ) return array();
223: if ( !isset($this->httpParsedHeaders) ) {
224: $this->httpParsedHeaders = array();
225: $headers = str_replace("\r\n", "\n", $this->httpResponseHeaders);
226: $ar_headers = explode("\n", $headers);
227: $last_header = '';
228: foreach ($ar_headers as $cur_headers) {
229: if( preg_match( '{^\s*\S}', $cur_headers) ) $header_name = $last_header;
230: else if ( preg_match( '{^(\S*):', $cur_headers, $matches) ) {
231: $header_name = $matches[1];
232: $last_header = $header_name;
233: if ( empty($this->httpParsedHeaders[$header_name]) ) $this->httpParsedHeaders[$header_name] = array();
234: }
235: $this->httpParsedHeaders[$header_name][] = $cur_headers;
236: }
237: }
238: return $this->httpParsedHeaders;
239: }
240:
241: 242: 243: 244: 245:
246: function GetHttpRequest() {
247: return $this->httpRequest;
248: }
249: 250: 251: 252: 253:
254: function () {
255: return $this->httpResponseHeaders;
256: }
257: 258: 259: 260: 261:
262: function GetResponseBody() {
263: return $this->httpResponseBody;
264: }
265: 266: 267: 268: 269:
270: function GetXmlRequest() {
271: return $this->xmlRequest;
272: }
273: 274: 275: 276: 277:
278: function GetXmlResponse() {
279: return $this->xmlResponse;
280: }
281:
282: 283: 284: 285: 286: 287: 288:
289: function DoRequest( $url = null ) {
290: $headers = array();
291:
292: if ( !isset($url) ) $url = $this->base_url;
293: $this->request_url = $url;
294: $url = preg_replace('{^https?://[^/]+}', '', $url);
295:
296: if ( preg_match( '{[^%?&=+,.-_/a-z0-9]}', $url ) ) {
297: $url = str_replace(rawurlencode('/'),'/',rawurlencode($url));
298: $url = str_replace(rawurlencode('?'),'?',$url);
299: $url = str_replace(rawurlencode('&'),'&',$url);
300: $url = str_replace(rawurlencode('='),'=',$url);
301: $url = str_replace(rawurlencode('+'),'+',$url);
302: $url = str_replace(rawurlencode(','),',',$url);
303: }
304: $headers[] = $this->requestMethod." ". $url . " HTTP/1.1";
305: $headers[] = "Authorization: Basic ".base64_encode($this->user .":". $this->pass );
306: $headers[] = "Host: ".$this->server .":".$this->port;
307:
308: if ( !isset($this->headers['content-type']) ) $this->headers['content-type'] = "Content-type: text/plain";
309: foreach( $this->headers as $ii => $head ) {
310: $headers[] = $head;
311: }
312: $headers[] = "Content-Length: " . strlen($this->body);
313: $headers[] = "User-Agent: " . $this->user_agent;
314: $headers[] = 'Connection: close';
315: $this->httpRequest = join("\r\n",$headers);
316: $this->xmlRequest = $this->body;
317:
318: $this->xmlResponse = '';
319:
320: $fip = fsockopen( $this->protocol . '://' . $this->server, $this->port, $errno, $errstr, _FSOCK_TIMEOUT);
321: if ( !(get_resource_type($fip) == 'stream') ) return false;
322: if ( !fwrite($fip, $this->httpRequest."\r\n\r\n".$this->body) ) { fclose($fip); return false; }
323: $response = "";
324: while( !feof($fip) ) { $response .= fgets($fip,8192); }
325: fclose($fip);
326:
327: list( $this->httpResponseHeaders, $this->httpResponseBody ) = preg_split( '{\r?\n\r?\n}s', $response, 2 );
328: if ( preg_match( '{Transfer-Encoding: chunked}i', $this->httpResponseHeaders ) ) $this->Unchunk();
329: if ( preg_match('/HTTP\/\d\.\d (\d{3})/', $this->httpResponseHeaders, $status) )
330: $this->httpResponseCode = intval($status[1]);
331: else
332: $this->httpResponseCode = 0;
333:
334: $this->headers = array();
335: $this->ParseResponse($this->httpResponseBody);
336: return $response;
337: }
338:
339:
340: 341: 342:
343: function Unchunk() {
344: $content = '';
345: $chunks = $this->httpResponseBody;
346:
347: do {
348: $bytes = 0;
349: if ( preg_match('{^((\r\n)?\s*([ 0-9a-fA-F]+)(;[^\n]*)?\r?\n)}', $chunks, $matches ) ) {
350: $octets = $matches[3];
351: $bytes = hexdec($octets);
352: $pos = strlen($matches[1]);
353:
354: if ( $bytes > 0 ) {
355:
356: $content .= substr($chunks,$pos,$bytes);
357: $chunks = substr($chunks,$pos + $bytes + 2);
358:
359: }
360: }
361: else {
362: $content .= $chunks;
363: }
364: }
365: while( $bytes > 0 );
366: $this->httpResponseBody = $content;
367:
368: }
369:
370:
371: 372: 373: 374: 375: 376: 377:
378: function DoOptionsRequest( $url = null ) {
379: $this->requestMethod = "OPTIONS";
380: $this->body = "";
381: $this->DoRequest($url);
382: $this->ParseResponseHeaders();
383: $allowed = '';
384: foreach( $this->httpParsedHeaders['Allow'] as $allow_header ) {
385: $allowed .= preg_replace( '/^(Allow:)?\s+([a-z, ]+)\r?\n.*/is', '$1,', $allow_header );
386: }
387: $options = array_flip( preg_split( '/[, ]+/', trim($allowed, ', ') ));
388: return $options;
389: }
390:
391:
392:
393: 394: 395: 396: 397: 398: 399: 400: 401:
402: function DoXMLRequest( $request_method, $xml, $url = null ) {
403: $this->body = $xml;
404: $this->requestMethod = $request_method;
405: $this->SetContentType("text/xml");
406: return $this->DoRequest($url);
407: }
408:
409:
410:
411: 412: 413: 414: 415:
416: function DoGETRequest( $url ) {
417: $this->body = "";
418: $this->requestMethod = "GET";
419: return $this->DoRequest( $url );
420: }
421:
422:
423: 424: 425: 426: 427:
428: function DoHEADRequest( $url ) {
429: $this->body = "";
430: $this->requestMethod = "HEAD";
431: return $this->DoRequest( $url );
432: }
433:
434:
435: 436: 437: 438: 439: 440: 441: 442: 443:
444: function DoPUTRequest( $url, $icalendar, $etag = null ) {
445: $this->body = $icalendar;
446:
447: $this->requestMethod = "PUT";
448: if ( $etag != null ) {
449: $this->SetMatch( ($etag != '*'), $etag );
450: }
451: $this->SetContentType('text/calendar; charset="utf-8"');
452: $this->DoRequest($url);
453:
454: $etag = null;
455: if ( preg_match( '{^ETag:\s+"([^"]*)"\s*$}im', $this->httpResponseHeaders, $matches ) ) $etag = $matches[1];
456: if ( !isset($etag) || $etag == '' ) {
457: if ( $this->debug ) printf( "No etag in:\n%s\n", $this->httpResponseHeaders );
458: $save_request = $this->httpRequest;
459: $save_response_headers = $this->httpResponseHeaders;
460: $this->DoHEADRequest( $url );
461: if ( preg_match( '{^Etag:\s+"([^"]*)"\s*$}im', $this->httpResponseHeaders, $matches ) ) $etag = $matches[1];
462: if ( !isset($etag) || $etag == '' ) {
463: if ( $this->debug ) printf( "Still No etag in:\n%s\n", $this->httpResponseHeaders );
464: }
465: $this->httpRequest = $save_request;
466: $this->httpResponseHeaders = $save_response_headers;
467: }
468: return $etag;
469: }
470:
471:
472: 473: 474: 475: 476: 477: 478: 479:
480: function DoDELETERequest( $url, $etag = null ) {
481: $this->body = "";
482:
483: $this->requestMethod = "DELETE";
484: if ( $etag != null ) {
485: $this->SetMatch( true, $etag );
486: }
487: $this->DoRequest($url);
488: return $this->httpResponseCode;
489: }
490:
491:
492: 493: 494: 495: 496:
497: function DoPROPFINDRequest( $url, $props, $depth = 0 ) {
498: $this->SetDepth($depth);
499: $xml = new XMLDocument( array( 'DAV:' => '', 'urn:ietf:params:xml:ns:caldav' => 'C' ) );
500: $prop = new XMLElement('prop');
501: foreach( $props AS $v ) {
502: $xml->NSElement($prop,$v);
503: }
504:
505: $this->body = $xml->Render('propfind',$prop );
506:
507: $this->requestMethod = "PROPFIND";
508: $this->SetContentType("text/xml");
509: $this->DoRequest($url);
510: return $this->GetXmlResponse();
511: }
512:
513:
514: 515: 516: 517: 518:
519: function PrincipalURL( $url = null ) {
520: if ( isset($url) ) {
521: $this->principal_url = $url;
522: }
523: return $this->principal_url;
524: }
525:
526:
527: 528: 529: 530: 531:
532: function CalendarHomeSet( $urls = null ) {
533: if ( isset($urls) ) {
534: if ( ! is_array($urls) ) $urls = array($urls);
535: $this->calendar_home_set = $urls;
536: }
537: return $this->calendar_home_set;
538: }
539:
540:
541: 542: 543: 544: 545:
546: function CalendarUrls( $urls = null ) {
547: if ( isset($urls) ) {
548: if ( ! is_array($urls) ) $urls = array($urls);
549: $this->calendar_urls = $urls;
550: }
551: return $this->calendar_urls;
552: }
553:
554:
555: 556: 557: 558: 559:
560: function HrefValueInside( $tagname ) {
561: foreach( $this->xmltags[$tagname] AS $k => $v ) {
562: $j = $v + 1;
563: if ( $this->xmlnodes[$j]['tag'] == 'DAV::href' ) {
564: return rawurldecode($this->xmlnodes[$j]['value']);
565: }
566: }
567: return null;
568: }
569:
570:
571: 572: 573: 574: 575: 576:
577: function HrefForProp( $tagname, $i = 0 ) {
578: if ( isset($this->xmltags[$tagname]) && isset($this->xmltags[$tagname][$i]) ) {
579: $j = $this->xmltags[$tagname][$i];
580: while( $j-- > 0 && $this->xmlnodes[$j]['tag'] != 'DAV::href' ) {
581:
582: if ( $this->xmlnodes[$j]['tag'] == 'DAV::status' && $this->xmlnodes[$j]['value'] != 'HTTP/1.1 200 OK' ) return null;
583: }
584:
585: if ( $j > 0 && isset($this->xmlnodes[$j]['value']) ) {
586:
587: return rawurldecode($this->xmlnodes[$j]['value']);
588: }
589: }
590: else {
591: if ( $this->debug ) printf( "xmltags[$tagname] or xmltags[$tagname][$i] is not set\n");
592: }
593: return null;
594: }
595:
596:
597: 598: 599: 600: 601: 602:
603: function HrefForResourcetype( $tagname, $i = 0 ) {
604: if ( isset($this->xmltags[$tagname]) && isset($this->xmltags[$tagname][$i]) ) {
605: $j = $this->xmltags[$tagname][$i];
606: while( $j-- > 0 && $this->xmlnodes[$j]['tag'] != 'DAV::resourcetype' );
607: if ( $j > 0 ) {
608: while( $j-- > 0 && $this->xmlnodes[$j]['tag'] != 'DAV::href' );
609: if ( $j > 0 && isset($this->xmlnodes[$j]['value']) ) {
610: return rawurldecode($this->xmlnodes[$j]['value']);
611: }
612: }
613: }
614: return null;
615: }
616:
617:
618: 619: 620: 621: 622:
623: function GetOKProps( $nodenum ) {
624: $props = null;
625: $level = $this->xmlnodes[$nodenum]['level'];
626: $status = '';
627: while ( $this->xmlnodes[++$nodenum]['level'] >= $level ) {
628: if ( $this->xmlnodes[$nodenum]['tag'] == 'DAV::propstat' ) {
629: if ( $this->xmlnodes[$nodenum]['type'] == 'open' ) {
630: $props = array();
631: $status = '';
632: }
633: else {
634: if ( $status == 'HTTP/1.1 200 OK' ) break;
635: }
636: }
637: elseif ( !isset($this->xmlnodes[$nodenum]) || !is_array($this->xmlnodes[$nodenum]) ) {
638: break;
639: }
640: elseif ( $this->xmlnodes[$nodenum]['tag'] == 'DAV::status' ) {
641: $status = $this->xmlnodes[$nodenum]['value'];
642: }
643: else {
644: $props[] = $this->xmlnodes[$nodenum];
645: }
646: }
647: return $props;
648: }
649:
650:
651: 652: 653: 654: 655:
656: function FindPrincipal( $url=null ) {
657: $xml = $this->DoPROPFINDRequest( $url, array('resourcetype', 'current-user-principal', 'owner', 'principal-URL',
658: 'urn:ietf:params:xml:ns:caldav:calendar-home-set'), 1);
659:
660: $principal_url = $this->HrefForProp('DAV::principal');
661:
662: if ( !isset($principal_url) ) {
663: foreach( array('DAV::current-user-principal', 'DAV::principal-URL', 'DAV::owner') AS $href ) {
664: if ( !isset($principal_url) ) {
665: $principal_url = $this->HrefValueInside($href);
666: }
667: }
668: }
669:
670: return $this->PrincipalURL($principal_url);
671: }
672:
673:
674: 675: 676: 677: 678:
679: function FindCalendarHome( $recursed=false ) {
680: if ( !isset($this->principal_url) ) {
681: $this->FindPrincipal();
682: }
683: if ( $recursed ) {
684: $this->DoPROPFINDRequest( $this->principal_url, array('urn:ietf:params:xml:ns:caldav:calendar-home-set'), 0);
685: }
686:
687: $calendar_home = array();
688: foreach( $this->xmltags['urn:ietf:params:xml:ns:caldav:calendar-home-set'] AS $k => $v ) {
689: if ( $this->xmlnodes[$v]['type'] != 'open' ) continue;
690: while( $this->xmlnodes[++$v]['type'] != 'close' && $this->xmlnodes[$v]['tag'] != 'urn:ietf:params:xml:ns:caldav:calendar-home-set' ) {
691:
692: if ( $this->xmlnodes[$v]['tag'] == 'DAV::href' && isset($this->xmlnodes[$v]['value']) )
693: $calendar_home[] = rawurldecode($this->xmlnodes[$v]['value']);
694: }
695: }
696:
697: if ( !$recursed && count($calendar_home) < 1 ) {
698: $calendar_home = $this->FindCalendarHome(true);
699: }
700:
701: return $this->CalendarHomeSet($calendar_home);
702: }
703:
704:
705: 706: 707:
708: function FindCalendars( $recursed=false ) {
709: if ( !isset($this->calendar_home_set[0]) ) {
710: $this->FindCalendarHome();
711: }
712: $this->DoPROPFINDRequest( $this->calendar_home_set[0], array('resourcetype','displayname','http://calendarserver.org/ns/:getctag'), 1);
713:
714: $calendars = array();
715: if ( isset($this->xmltags['urn:ietf:params:xml:ns:caldav:calendar']) ) {
716: $calendar_urls = array();
717: foreach( $this->xmltags['urn:ietf:params:xml:ns:caldav:calendar'] AS $k => $v ) {
718: $calendar_urls[$this->HrefForProp('urn:ietf:params:xml:ns:caldav:calendar', $k)] = 1;
719: }
720:
721: foreach( $this->xmltags['DAV::href'] AS $i => $hnode ) {
722: $href = rawurldecode($this->xmlnodes[$hnode]['value']);
723:
724: if ( !isset($calendar_urls[$href]) ) continue;
725:
726:
727:
728: $calendar = new CalendarInfo($href);
729: $ok_props = $this->GetOKProps($hnode);
730: foreach( $ok_props AS $v ) {
731:
732: switch( $v['tag'] ) {
733: case 'http://calendarserver.org/ns/:getctag':
734: $calendar->getctag = $v['value'];
735: break;
736: case 'DAV::displayname':
737: $calendar->displayname = $v['value'];
738: break;
739: }
740: }
741: $calendars[] = $calendar;
742: }
743: }
744:
745: return $this->CalendarUrls($calendars);
746: }
747:
748:
749: 750: 751:
752: function GetCalendarDetails( $url = null ) {
753: if ( isset($url) ) $this->SetCalendar($url);
754:
755: $calendar_properties = array( 'resourcetype', 'displayname', 'http://calendarserver.org/ns/:getctag', 'urn:ietf:params:xml:ns:caldav:calendar-timezone', 'supported-report-set' );
756: $this->DoPROPFINDRequest( $this->calendar_url, $calendar_properties, 0);
757:
758: $hnode = $this->xmltags['DAV::href'][0];
759: $href = rawurldecode($this->xmlnodes[$hnode]['value']);
760:
761: $calendar = new CalendarInfo($href);
762: $ok_props = $this->GetOKProps($hnode);
763: foreach( $ok_props AS $k => $v ) {
764: $name = preg_replace( '{^.*:}', '', $v['tag'] );
765: if ( isset($v['value'] ) ) {
766: $calendar->{$name} = $v['value'];
767: }
768: 769: 770:
771: }
772:
773: return $calendar;
774: }
775:
776:
777: 778: 779:
780: function GetCollectionETags( $url = null ) {
781: if ( isset($url) ) $this->SetCalendar($url);
782:
783: $this->DoPROPFINDRequest( $this->calendar_url, array('getetag'), 1);
784:
785: $etags = array();
786: if ( isset($this->xmltags['DAV::getetag']) ) {
787: foreach( $this->xmltags['DAV::getetag'] AS $k => $v ) {
788: $href = $this->HrefForProp('DAV::getetag', $k);
789: if ( isset($href) && isset($this->xmlnodes[$v]['value']) ) $etags[$href] = $this->xmlnodes[$v]['value'];
790: }
791: }
792:
793: return $etags;
794: }
795:
796:
797: 798: 799:
800: function CalendarMultiget( $event_hrefs, $url = null ) {
801:
802: if ( isset($url) ) $this->SetCalendar($url);
803:
804: $hrefs = '';
805: foreach( $event_hrefs AS $k => $href ) {
806: $href = str_replace( rawurlencode('/'),'/',rawurlencode($href));
807: $hrefs .= '<href>'.$href.'</href>';
808: }
809: $this->body = <<<EOXML
810: <?xml version="1.0" encoding="utf-8" ?>
811: <C:calendar-multiget xmlns="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav">
812: <prop><getetag/><C:calendar-data/></prop>
813: $hrefs
814: </C:calendar-multiget>
815: EOXML;
816:
817: $this->requestMethod = "REPORT";
818: $this->SetContentType("text/xml");
819: $this->DoRequest( $this->calendar_url );
820:
821: $events = array();
822: if ( isset($this->xmltags['urn:ietf:params:xml:ns:caldav:calendar-data']) ) {
823: foreach( $this->xmltags['urn:ietf:params:xml:ns:caldav:calendar-data'] AS $k => $v ) {
824: $href = $this->HrefForProp('urn:ietf:params:xml:ns:caldav:calendar-data', $k);
825:
826: $events[$href] = $this->xmlnodes[$v]['value'];
827: }
828: }
829: else {
830: foreach( $event_hrefs AS $k => $href ) {
831: $this->DoGETRequest($href);
832: $events[$href] = $this->httpResponseBody;
833: }
834: }
835:
836: return $events;
837: }
838:
839:
840: 841: 842: 843: 844: 845: 846: 847: 848: 849: 850: 851: 852:
853: function DoCalendarQuery( $filter, $url = '' ) {
854:
855: if ( !empty($url) ) $this->SetCalendar($url);
856:
857: $this->body = <<<EOXML
858: <?xml version="1.0" encoding="utf-8" ?>
859: <C:calendar-query xmlns:D="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav">
860: <D:prop>
861: <C:calendar-data/>
862: <D:getetag/>
863: </D:prop>$filter
864: </C:calendar-query>
865: EOXML;
866:
867: $this->requestMethod = "REPORT";
868: $this->SetContentType("text/xml");
869: $this->DoRequest( $this->calendar_url );
870:
871: $report = array();
872: foreach( $this->xmlnodes as $k => $v ) {
873: switch( $v['tag'] ) {
874: case 'DAV::response':
875: if ( $v['type'] == 'open' ) {
876: $response = array();
877: }
878: elseif ( $v['type'] == 'close' ) {
879: $report[] = $response;
880: }
881: break;
882: case 'DAV::href':
883: $response['href'] = basename( rawurldecode($v['value']) );
884: break;
885: case 'DAV::getetag':
886: $response['etag'] = preg_replace('/^"?([^"]+)"?/', '$1', $v['value']);
887: break;
888: case 'urn:ietf:params:xml:ns:caldav:calendar-data':
889: $response['data'] = $v['value'];
890: break;
891: }
892: }
893: return $report;
894: }
895:
896:
897: 898: 899: 900: 901: 902: 903: 904: 905: 906: 907: 908: 909:
910: function GetEvents( $start = null, $finish = null, $relative_url = '' ) {
911: $filter = "";
912: if ( isset($start) && isset($finish) )
913: $range = "<C:time-range start=\"$start\" end=\"$finish\"/>";
914: else
915: $range = '';
916:
917: $filter = <<<EOFILTER
918: <C:filter>
919: <C:comp-filter name="VCALENDAR">
920: <C:comp-filter name="VEVENT">
921: $range
922: </C:comp-filter>
923: </C:comp-filter>
924: </C:filter>
925: EOFILTER;
926:
927: return $this->DoCalendarQuery($filter, $relative_url);
928: }
929:
930:
931: 932: 933: 934: 935: 936: 937: 938: 939: 940: 941: 942: 943: 944: 945:
946: function GetTodos( $start, $finish, $completed = false, $cancelled = false, $relative_url = "" ) {
947:
948: if ( $start && $finish ) {
949: $time_range = <<<EOTIME
950: <C:time-range start="$start" end="$finish"/>
951: EOTIME;
952: }
953:
954:
955: $neg_cancelled = ( $cancelled === true ? "no" : "yes" );
956: $neg_completed = ( $cancelled === true ? "no" : "yes" );
957:
958: $filter = <<<EOFILTER
959: <C:filter>
960: <C:comp-filter name="VCALENDAR">
961: <C:comp-filter name="VTODO">
962: <C:prop-filter name="STATUS">
963: <C:text-match negate-condition="$neg_completed">COMPLETED</C:text-match>
964: </C:prop-filter>
965: <C:prop-filter name="STATUS">
966: <C:text-match negate-condition="$neg_cancelled">CANCELLED</C:text-match>
967: </C:prop-filter>$time_range
968: </C:comp-filter>
969: </C:comp-filter>
970: </C:filter>
971: EOFILTER;
972:
973: return $this->DoCalendarQuery($filter, $relative_url);
974: }
975:
976:
977: 978: 979: 980: 981: 982: 983: 984: 985:
986: function GetEntryByUid( $uid, $relative_url = '', $component_type = 'VEVENT' ) {
987: $filter = "";
988: if ( $uid ) {
989: $filter = <<<EOFILTER
990: <C:filter>
991: <C:comp-filter name="VCALENDAR">
992: <C:comp-filter name="$component_type">
993: <C:prop-filter name="UID">
994: <C:text-match icollation="i;octet">$uid</C:text-match>
995: </C:prop-filter>
996: </C:comp-filter>
997: </C:comp-filter>
998: </C:filter>
999: EOFILTER;
1000: }
1001:
1002: return $this->DoCalendarQuery($filter, $relative_url);
1003: }
1004:
1005:
1006: 1007: 1008: 1009: 1010: 1011: 1012:
1013: function GetEntryByHref( $href ) {
1014: $href = str_replace( rawurlencode('/'),'/',rawurlencode($href));
1015: return $this->DoGETRequest( $href );
1016: }
1017:
1018: }
1019: