Rewritten to structure things differently. Untested as yet.

This commit is contained in:
Andrew McMillan 2007-05-25 23:36:52 +12:00
parent 08a88dbdae
commit 978cb43170

View File

@ -3,335 +3,164 @@
* A Class for connecting to a caldav server
*
* @package caldav
* @author Jeppe Bob Dyrby <jeppe.dyrby@gmail.com>
* @copyright Bob
* @author Andrew McMillan <debian@mcmillan.net.nz>
* @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 = '<?xml version="1.0"?><D:propfind xmlns:D="DAV:"><D:prop><D:resourcetype/></D:prop></D:propfind>';
$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 =
'<?xml version="1.0"?><D:propfind xmlns:D="DAV:"><D:prop><D:resourcetype/></D:prop></D:propfind>';
//$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;
}
}
?>