From 978cb43170df9e030b8a86fd076c33056046aa4b Mon Sep 17 00:00:00 2001 From: Andrew McMillan Date: Fri, 25 May 2007 23:36:52 +1200 Subject: [PATCH] Rewritten to structure things differently. Untested as yet. --- inc/caldav-client.php | 481 ++++++++++++++---------------------------- 1 file changed, 155 insertions(+), 326 deletions(-) diff --git a/inc/caldav-client.php b/inc/caldav-client.php index ca1ca58a..cc81a934 100644 --- a/inc/caldav-client.php +++ b/inc/caldav-client.php @@ -3,335 +3,164 @@ * A Class for connecting to a caldav server * * @package caldav -* @author Jeppe Bob Dyrby -* @copyright Bob +* @author Andrew McMillan +* @copyright Andrew McMillan * @license http://gnu.org/copyleft/gpl.html GNU GPL v2 */ -class caldav { - /** - * Server, username, password, calendar - * - * @var string - */ - var $server, $user, $pass, $calendar; - /** - * Port number - * - * @var int - */ - var $port; - /** - * socket - * - * @var socket - */ - var $stream; - /** - * The useragent which is send to the caldav server - * - * @var string - */ - var $user_agent = 'ARTICMEDIA PULP Calendaring System'; - /** - * array of options, what is allowed on the server - * - * @var array - */ - var $options = array(); - /** - * Is access granted on this server? if false, - * we wont try to log in. - * This is automatic set, if the first login failes. - * - * @var bool - */ - var $granted = true; - /** - * Timeout for connection - * - * @var int - */ - var $timeout = 10; - /** - * The last error description - * - * @var mixed - */ - var $last_error = ""; - - var $header = ""; - var $body = ""; - - /** - * Constructer, sets the server, username etc, and connects for the first time - * to check for permissions and options - * - * @param string $server - * @param string $calendar - * @return bool - */ - function caldav($server,$calendar) { - return $this->__construct($server,$calendar); - } - - /** - * Constructer, sets the server, username etc, and connects for the first time - * to check for permissions and options - * - * @param string $server - * @param string $calendar - * @return bool - */ - function __construct($server,$calendar) { - $this->server = parse_url($server); - $this->user = $this->server['user']; - $this->pass = $this->server['pass']; - $this->port = $port = isset($this->server['port']) ? $this->server['port'] : 80; - $this->setCalendar($calendar); - $test = $this->sendRequest('OPTIONS'); - if ($test) { - $this->granted = true; - } else { - $this->granted = false; - } - return true; - } - - /** - * Sets the calender, if it is changed - * - * @param string $calendar - */ - function setCalendar($calendar) { - $this->calendar = $calendar; - } - - /** - * Initiases the connection - * - * @param string $do - * @param string $content - * @param string $mime - * @return mixed - */ - function sendRequest($do,$content=null,$mime=null) { - $headers = $this->createHeader($do,$content,$mime); - //echo $headers; - $return = $this->open($headers); - if ($return != false) { - $return = split("\r\n\r\n",$return); - $headers = $return[0]; - unset($return[0]); - $body = implode("\r\n\r\n",$return); - $result = $this->parseResult($do,$headers,$body); - $this->close(); - if (!is_array($result)) return false; - $this->header = $result[0]; - $this->body = $result[1]; - return true; - } else { - $this->close(); - return false; - } - } - function sendPROPFIND() { - $xml = ''; - $this->sendRequest('PROPFIND',$xml,'text/xml'); - $urlList = Array(); - $xml = new SimpleXMLElement($this->body,LIBXML_NOWARNING); - foreach ($xml->response as $rep) { - $urlList[] = (string)$rep->href; - } - return $urlList; - } - function sendPUT_CREATION($content) { - $uid = $this->getUniqueUID(); - $this->setCalendar((string)($this->calendar.$uid.'.ics')); - echo $this->calendar; - $this->sendRequest('PUT',$content,'text/icalendar'); - } - function sendPUT_UPDATE($content,$uid) { - $this->setCalendar((string)($this->calendar.$uid)); - echo $this->calendar; - $this->sendRequest('PUT',$content,'text/icalendar'); +/** +* I be you find this hard to believe, but having to write this hack really +* annoys the crap out of me. WTF! Why does PHP/Curl not have a function which +* simply accepts a string as what the request will contain. Oh no. They only +* think of "POST" and "PUT a file". Crap. +* +* So the following PoS code accepts that it will be called, and asked for $length +* bites of the $fd (which we ignore, because we get it all from the $_...data variable) +* and so it will eat it's way through the data. +*/ +$__curl_read_callback_pos = 0; +$__curl_read_callback_data = ""; +function __curl_init_callback( $data ) { + global $__curl_read_callback_pos, $__curl_read_callback_data; + $__curl_read_callback_pos = 0; + $__curl_read_callback_data = $data; +} +/** +* As documented in the comments on this page(!) +* http://nz2.php.net/curl_setopt +*/ +function __curl_read_callback( $ch, $fd, $length) { + global $__curl_read_callback_pos, $__curl_read_callback_data; - } - function getUniqueUID(){ - //return (string)('uuid'.time().rand(100,999)); - return md5(uniqid('',true)); - } - /** - * Trims array values - * - * @param unknown_type $item - * @param unknown_type $key - */ - function aTrim(&$item,$key) { - if (is_array($item)) { - array_walk($item,array(&$this,'aTrim')); - } else { - $item = trim($item); - } - } - - /** - * Parses the returned headers, and the body - * - * @param string $do - * @param string $headers - * @param string $body - * @return mixed - */ - function parseResult($do,$headers,$body) { - //echo "\r\n".$headers."\r\n\r\n"; - $headers = explode("\r\n",$headers); - if (!strpos($headers[0],'401') && !strpos($headers[0],'404')) { - $nh = array(); - foreach ($headers as $line) { - $tmp = explode(":",$line); - $nh[$tmp[0]] = $tmp[1]; - } - $headers = $nh; - $headers['Content-Type'] = explode("; ",$headers['Content-Type']); - if (strtoupper($do) == 'OPTIONS') $headers['Allow'] = explode(",",$headers['Allow']); - $t = $this; - if (function_exists("array_walk_recursive")) { - array_walk_recursive($headers,array(&$this,'aTrim')); - } else { - array_walk($headers,array(&$this,'aTrim')); - } - if ($headers['Allow']) { - $this->options = $headers['Allow']; - } - //print_r($headers); - $body = utf8_decode($body); - return array($headers,$body); - } else { - $this->last_error = $headers[0] ." - ".$body; - return false; - } - } - - /** - * Opens the connection, and writes to the server - * - * @param string $headers - * @return string - */ - function open($headers) { - if ($this->granted == true) { - $this->stream = fsockopen($this->server['host'],$this->port,&$errno,&$errstr,$this->timeout); - if (!$this->stream) { - $this->last_error = "$errno:\t$errstr"; - $this->granted = false; - $this->close(); - return false; - } - if (function_exists("stream_get_contents")) - stream_set_timeout($this->stream, $this->timeout); - $test = fwrite($this->stream, $headers, strlen($headers)); - if ($test != strlen($headers)) { - $this->close(); - return false; - } else { - if (function_exists("stream_get_contents")) { - return stream_get_contents($this->stream); - } else { - $c = ""; - while (!feof($this->stream)) { - $c .= fread($this->stream, 1024); - } - return $c; - } - } - } else { - return false; - } - } - - /** - * Closes connection, should clean up too - * - */ - function close() { - fclose($this->stream); - } - - /** - * Creates headers for the Caldav server, including content - * - * @param string $do - * @param string $content - * @param string $mime - * @return string - */ - function createHeader($do,$content=null,$mime=null) { - if (strtoupper($do) != 'OPTIONS') { - if (!in_array(strtoupper($do),$this->options)) return; - } - $headers = array(); - $headers[] = strtoupper($do)." ".$this->calendar." HTTP/1.0"; - $headers[] = "Host: ".$this->server['host']; - $headers[] = "User-Agent: ".$this->user_agent; - if ($mime) $headers[] = "Content-Type: ".$mime."; charset=\"utf-8\""; - if ($content) { - $content .= "\r\n"; - $headers[] .= "Content-Length: ".strlen($content); - } - if ($this->user && $this->pass) $headers[] = "Authorization: Basic ".base64_encode("$this->user:$this->pass"); - $headers[] = "Connection: close"; - $headers[] = ""; - $headers[] = ""; - if ($content) $headers[] = $content; - //return wordwrap(implode("\r\n",$headers),76); - return implode("\r\n",$headers); - } - + if ( $__curl_read_callback_pos < 0 ) return false; + + $answer = substr($__curl_read_callback_data, $__curl_read_callback_pos, $length ); + if ( strlen($answer) < $length ) $__curl_read_callback_pos = -1; + + return $answer; } -$c = new caldav('http://jonathan:phoenix@10.0.1.30','/caldav/caldav.php/jonathan/'); -$icsStream = -"BEGIN:VCALENDAR -PRODID:-//Mozilla Calendar//NONSGML Sunbird//EN -VERSION:2.0 -BEGIN:VEVENT -CREATED:20070524T090157Z -LAST-MODIFIED:20070524T090208Z -DTSTAMP:20070524T090208Z -UID:uuid1179997317576 -SUMMARY:nox updated -DTSTART;TZID=/mozilla.org/20070129_1/Africa/Ceuta:20070524T090000 -DTEND;TZID=/mozilla.org/20070129_1/Africa/Ceuta:20070524T100000 -X-MOZ-LOCATIONPATH:uuid1179997317576.ics -LOCATION;LANGUAGE=fr;ENCODING=QUOTED-PRINTABLE:ii -END:VEVENT -BEGIN:VTIMEZONE -TZID:/mozilla.org/20070129_1/Africa/Ceuta -X-LIC-LOCATION:Africa/Ceuta -BEGIN:DAYLIGHT -TZOFFSETFROM:+0100 -TZOFFSETTO:+0200 -TZNAME:CEST -DTSTART:19700329T020000 -RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=-1SU;BYMONTH=3 -END:DAYLIGHT -BEGIN:STANDARD -TZOFFSETFROM:+0200 -TZOFFSETTO:+0100 -TZNAME:CET -DTSTART:19701025T030000 -RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=-1SU;BYMONTH=10 -END:STANDARD -END:VTIMEZONE -END:VCALENDAR"; - $xml = -''; -//$c->sendRequest('PUT',$icsStream,'text/icalendar'); -//$c->sendRequest('PUT',$icsStream,'text/icalendar'); -//echo(print_r($c->header).'****'.$c->body); -$c->sendPUT_UPDATE($icsStream,'uuid1180017338347.ics'); -//$c->sendRequest('PUT',$icsStream,'text/icalendar'); -//$c->sendPUT() -//echo($c->getUniqueUID()); -?> + +/** +* A class for accessing RSCDS via CalDAV, as a client +* +* @package rscds +*/ +class CalDAVClient { + /** + * Server, username, password, calendar, $entry + * + * @var string + */ + var $base_url, $user, $pass, $calendar, $entry; + + /** + * The useragent which is send to the caldav server + * + * @var string + */ + var $user_agent = 'RSCDSClient'; + + var $headers = array(); + var $body = ""; + + /** + * Our cURL connection + * + * @var resource + */ + var $curl; + + + /** + * Constructor, initialises the class + * + * @param string $base_url + * @param string $user + * @param string $pass + * @param string $calendar + */ + function CalDAVClient( $base_url, $user, $pass, $calendar ) { + $this->base_url = $base_url; + $this->user = $user; + $this->pass = $pass; + $this->calendar = $calendar; + + $this->curl = curl_init(); + curl_setopt($this->curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); + curl_setopt($this->curl, CURLOPT_USERPWD, "$user:$pass" ); + curl_setopt($this->curl, CURLOPT_RETURNTRANSFER, true ); + curl_setopt($this->curl, CURLOPT_BINARYTRANSFER, true ); + } + + /** + * Adds an If-Match or If-None-Match header + * + * @param bool $match to Match or Not to Match, that is the question! + * @param string $etag The etag to match / not match against. + */ + function SetMatch( $match, $etag = '*' ) { + $this->headers[] = sprintf( "%s-Match: %s", ($match ? "If" : "If-None"), $etag); + } + + /** + * Add a Depth: header. Valid values are 1 or infinity + * + * @param int $depth The depth, default to infinity + */ + function SetDepth( $depth = 'infinity' ) { + $this->headers[] = "Depth: ". ($depth == 1 ? "1" : "infinity" ); + } + + /** + * Add a Depth: header. Valid values are 1 or infinity + * + * @param int $depth The depth, default to infinity + */ + function SetUserAgent( $user_agent = null ) { + if ( !isset($user_agent) ) $user_agent = $this->user_agent; + $this->user_agent = $user_agent; + } + + + /** + * Send a request to the server + * + * @param string The URL to make the request to + * + * @return string The content of the response from the server + */ + function DoRequest( $url ) { + + curl_setopt($this->curl, CURLOPT_URL, $url ); + curl_setopt($this->curl, CURLOPT_HEADER, 0); + curl_setopt($this->curl, CURLOPT_USERAGENT, $this->user_agent ); + curl_setopt($this->curl, CURLOPT_HTTPHEADER, $this->headers ); + + /** + * So we don't get annoyed at self-signed certificates. Should be a setup + * configuration thing really. + */ + curl_setopt($this->curl, CURLOPT_SSL_VERIFYPEER, false ); + + /** + * Our magic write the data function. You'd think there would be a + * simple setopt call where we could set the data to be written, but no, + * we have to pass a function, which passes the data. + */ + __curl_init_callback($this->body); + curl_setopt($this->curl, CURLOPT_READFUNCTION, '__curl_read_callback' ); + + $content = curl_exec($this->curl); + + } + + + function DoXMLRequest( $url, $xml, $request_method ) { + curl_setopt($this->curl, CURLOPT_CUSTOMREQUEST, $request_method ); + $this->body = $xml; + } + +} + +?> \ No newline at end of file