mirror of
https://gitlab.com/davical-project/davical.git
synced 2026-05-21 01:54:23 +00:00
Some updates to the caldav client library and an example script.
This commit is contained in:
parent
cef7ecf74d
commit
701e96e1b2
@ -120,7 +120,7 @@ class CalDAVClient {
|
|||||||
* @param string $etag The etag to match / not match against.
|
* @param string $etag The etag to match / not match against.
|
||||||
*/
|
*/
|
||||||
function SetMatch( $match, $etag = '*' ) {
|
function SetMatch( $match, $etag = '*' ) {
|
||||||
$this->headers[] = sprintf( "%s-Match: %s", ($match ? "If" : "If-None"), $etag);
|
$this->headers['match'] = sprintf( "%s-Match: %s", ($match ? "If" : "If-None"), $etag);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -129,7 +129,7 @@ class CalDAVClient {
|
|||||||
* @param int $depth The depth, default to infinity
|
* @param int $depth The depth, default to infinity
|
||||||
*/
|
*/
|
||||||
function SetDepth( $depth = '0' ) {
|
function SetDepth( $depth = '0' ) {
|
||||||
$this->headers[] = 'Depth: '. ($depth == '1' ? "1" : ($depth == 'infinity' ? $depth : "0") );
|
$this->headers['depth'] = 'Depth: '. ($depth == '1' ? "1" : ($depth == 'infinity' ? $depth : "0") );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -145,10 +145,10 @@ class CalDAVClient {
|
|||||||
/**
|
/**
|
||||||
* Add a Content-type: header.
|
* Add a Content-type: header.
|
||||||
*
|
*
|
||||||
* @param int $type The content type
|
* @param string $type The content type
|
||||||
*/
|
*/
|
||||||
function SetContentType( $type ) {
|
function SetContentType( $type ) {
|
||||||
$this->headers[] = "Content-type: $type";
|
$this->headers['content-type'] = "Content-type: $type";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -173,20 +173,19 @@ class CalDAVClient {
|
|||||||
else {
|
else {
|
||||||
$this->httpResponse = trim(substr($response, 0, $pos));
|
$this->httpResponse = trim(substr($response, 0, $pos));
|
||||||
$this->xmlResponse = trim(substr($response, $pos));
|
$this->xmlResponse = trim(substr($response, $pos));
|
||||||
|
$this->xmlResponse = preg_replace('{>[^>]*$}s', '>',$this->xmlResponse );
|
||||||
$parser = xml_parser_create_ns('UTF-8');
|
$parser = xml_parser_create_ns('UTF-8');
|
||||||
xml_parser_set_option ( $parser, XML_OPTION_SKIP_WHITE, 1 );
|
xml_parser_set_option ( $parser, XML_OPTION_SKIP_WHITE, 1 );
|
||||||
xml_parser_set_option ( $parser, XML_OPTION_CASE_FOLDING, 0 );
|
xml_parser_set_option ( $parser, XML_OPTION_CASE_FOLDING, 0 );
|
||||||
|
|
||||||
if ( 0 == xml_parse_into_struct( $parser, $this->xmlResponse, $this->xmlnodes, $this->xmltags ) ) {
|
if ( xml_parse_into_struct( $parser, $this->xmlResponse, $this->xmlnodes, $this->xmltags ) === 0 ) {
|
||||||
printf( "XML parsing error: %s - %s\n", xml_get_error_code($this->parser), xml_error_string(xml_get_error_code($this->parser)) );
|
printf( "XML parsing error: %s - %s\n", xml_get_error_code($parser), xml_error_string(xml_get_error_code($parser)) );
|
||||||
|
// debug_print_backtrace();
|
||||||
|
// echo "\nNodes array............................................................\n"; print_r( $this->xmlnodes );
|
||||||
|
// echo "\nTags array............................................................\n"; print_r( $this->xmltags );
|
||||||
|
printf( "\nXML Reponse:\n%s\n", $this->xmlResponse );
|
||||||
}
|
}
|
||||||
/* else {
|
|
||||||
echo "\nNodes array............................................................\n";
|
|
||||||
print_r( $this->xmlnodes );
|
|
||||||
echo "\nTags array............................................................\n";
|
|
||||||
print_r( $this->xmltags );
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
xml_parser_free($parser);
|
xml_parser_free($parser);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -204,8 +203,16 @@ class CalDAVClient {
|
|||||||
*
|
*
|
||||||
* @return HTTP headers
|
* @return HTTP headers
|
||||||
*/
|
*/
|
||||||
function GetHttpResponse() {
|
function GetResponseHeaders() {
|
||||||
return $this->httpResponse;
|
return $this->httpResponseHeaders;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Output http response body
|
||||||
|
*
|
||||||
|
* @return HTTP body
|
||||||
|
*/
|
||||||
|
function GetResponseBody() {
|
||||||
|
return $this->httpResponseBody;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Output xml request
|
* Output xml request
|
||||||
@ -237,10 +244,16 @@ class CalDAVClient {
|
|||||||
|
|
||||||
if ( !isset($url) ) $url = $this->base_url;
|
if ( !isset($url) ) $url = $this->base_url;
|
||||||
$this->request_url = $url;
|
$this->request_url = $url;
|
||||||
$headers[] = $this->requestMethod." ". $this->request_url . " HTTP/1.1";
|
$url = preg_replace('{^https?://[^/]+}', '', $url);
|
||||||
|
// URLencode if it isn't already
|
||||||
|
/* if ( !preg_match( '{(%\x\x)}', $url) && preg_match( '{[^.-_/a-z0-9]}', $url ) ) {
|
||||||
|
$url = str_replace(rawurlencode('/'),'/',rawurlencode($url));
|
||||||
|
}*/
|
||||||
|
$headers[] = $this->requestMethod." ". $url . " HTTP/1.1";
|
||||||
$headers[] = "Authorization: Basic ".base64_encode($this->user .":". $this->pass );
|
$headers[] = "Authorization: Basic ".base64_encode($this->user .":". $this->pass );
|
||||||
$headers[] = "Host: ".$this->server .":".$this->port;
|
$headers[] = "Host: ".$this->server .":".$this->port;
|
||||||
|
|
||||||
|
if ( !isset($this->headers['content-type']) ) $this->headers['content-type'] = "Content-type: text/plain";
|
||||||
foreach( $this->headers as $ii => $head ) {
|
foreach( $this->headers as $ii => $head ) {
|
||||||
$headers[] = $head;
|
$headers[] = $head;
|
||||||
}
|
}
|
||||||
@ -250,16 +263,29 @@ class CalDAVClient {
|
|||||||
$this->httpRequest = join("\r\n",$headers);
|
$this->httpRequest = join("\r\n",$headers);
|
||||||
$this->xmlRequest = $this->body;
|
$this->xmlRequest = $this->body;
|
||||||
|
|
||||||
|
$this->httpResponse = '';
|
||||||
|
$this->xmlResponse = '';
|
||||||
|
|
||||||
$fip = fsockopen( $this->protocol . '://' . $this->server, $this->port, $errno, $errstr, _FSOCK_TIMEOUT); //error handling?
|
$fip = fsockopen( $this->protocol . '://' . $this->server, $this->port, $errno, $errstr, _FSOCK_TIMEOUT); //error handling?
|
||||||
if ( !(get_resource_type($fip) == 'stream') ) return false;
|
if ( !(get_resource_type($fip) == 'stream') ) return false;
|
||||||
if ( !fwrite($fip, $this->httpRequest."\r\n\r\n".$this->body) ) { fclose($fip); return false; }
|
if ( !fwrite($fip, $this->httpRequest."\r\n\r\n".$this->body) ) { fclose($fip); return false; }
|
||||||
$rsp = "";
|
$response = "";
|
||||||
while( !feof($fip) ) { $rsp .= fgets($fip,8192); }
|
while( !feof($fip) ) { $response .= fgets($fip,8192); }
|
||||||
fclose($fip);
|
fclose($fip);
|
||||||
|
|
||||||
|
$pos = strpos($response, "\n\n");
|
||||||
|
if ( $pos === false ) {
|
||||||
|
$this->httpResponseHeaders = $response;
|
||||||
|
$this->httpResponseBody = '';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$this->httpResponseHeaders = substr($response,0,pos+1);
|
||||||
|
$this->httpResponseBody = substr($response, $pos + 2);
|
||||||
|
}
|
||||||
|
|
||||||
$this->headers = array(); // reset the headers array for our next request
|
$this->headers = array(); // reset the headers array for our next request
|
||||||
$this->ParseResponse($rsp);
|
$this->ParseResponse($response);
|
||||||
return $rsp;
|
return $response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -311,6 +337,18 @@ class CalDAVClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the HEAD of a single item from the server.
|
||||||
|
*
|
||||||
|
* @param string $url The URL to HEAD
|
||||||
|
*/
|
||||||
|
function DoHEADRequest( $url ) {
|
||||||
|
$this->body = "";
|
||||||
|
$this->requestMethod = "HEAD";
|
||||||
|
return $this->DoRequest( $url );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PUT a text/icalendar resource, returning the etag
|
* PUT a text/icalendar resource, returning the etag
|
||||||
*
|
*
|
||||||
@ -327,14 +365,19 @@ class CalDAVClient {
|
|||||||
if ( $etag != null ) {
|
if ( $etag != null ) {
|
||||||
$this->SetMatch( ($etag != '*'), $etag );
|
$this->SetMatch( ($etag != '*'), $etag );
|
||||||
}
|
}
|
||||||
$this->SetContentType("text/icalendar");
|
$this->SetContentType('text/icalendar; encoding="utf-8"');
|
||||||
$headers = $this->DoRequest($url);
|
$this->DoRequest($url);
|
||||||
|
|
||||||
/**
|
$etag = null;
|
||||||
* RSCDS will always return the real etag on PUT. Other CalDAV servers may need
|
if ( preg_match( '{^ETag:\s+"([^"]*)"\s*$}im', $this->httpResponseHeaders, $matches ) ) $etag = $matches[1];
|
||||||
* more work, but we are assuming we are running against RSCDS in this case.
|
if ( !isset($etag) || $etag == '' ) {
|
||||||
*/
|
printf( "No etag in:\n%s\n", $this->httpResponseHeaders );
|
||||||
$etag = preg_replace( '/^.*Etag: "?([^"\r\n]+)"?\r?\n.*/is', '$1', $headers );
|
$this->DoHEADRequest( $url );
|
||||||
|
if ( preg_match( '{^Etag:\s+"([^"]*)"\s*$}im', $this->httpResponseHeaders, $matches ) ) $etag = $matches[1];
|
||||||
|
if ( !isset($etag) || $etag == '' ) {
|
||||||
|
printf( "Still No etag in:\n%s\n", $this->httpResponseHeaders );
|
||||||
|
}
|
||||||
|
}
|
||||||
return $etag;
|
return $etag;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -431,7 +474,7 @@ class CalDAVClient {
|
|||||||
foreach( $this->xmltags[$tagname] AS $k => $v ) {
|
foreach( $this->xmltags[$tagname] AS $k => $v ) {
|
||||||
$j = $v + 1;
|
$j = $v + 1;
|
||||||
if ( $this->xmlnodes[$j]['tag'] == 'DAV::href' ) {
|
if ( $this->xmlnodes[$j]['tag'] == 'DAV::href' ) {
|
||||||
return $this->xmlnodes[$j]['value'];
|
return rawurldecode($this->xmlnodes[$j]['value']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@ -439,7 +482,7 @@ class CalDAVClient {
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the href containing this property
|
* Return the href containing this property. Except only if it's inside a status != 200
|
||||||
*
|
*
|
||||||
* @param string $tagname The tag name of the property to find the href for
|
* @param string $tagname The tag name of the property to find the href for
|
||||||
* @param integer $which Which instance of the tag should we use
|
* @param integer $which Which instance of the tag should we use
|
||||||
@ -447,13 +490,18 @@ class CalDAVClient {
|
|||||||
function HrefForProp( $tagname, $i = 0 ) {
|
function HrefForProp( $tagname, $i = 0 ) {
|
||||||
if ( isset($this->xmltags[$tagname]) && isset($this->xmltags[$tagname][$i]) ) {
|
if ( isset($this->xmltags[$tagname]) && isset($this->xmltags[$tagname][$i]) ) {
|
||||||
$j = $this->xmltags[$tagname][$i];
|
$j = $this->xmltags[$tagname][$i];
|
||||||
while( $j-- > 0 && $this->xmlnodes[$j]['tag'] != 'DAV::prop' );
|
while( $j-- > 0 && $this->xmlnodes[$j]['tag'] != 'DAV::href' ) {
|
||||||
if ( $j > 0 ) {
|
// printf( "Node[$j]: %s\n", $this->xmlnodes[$j]['tag']);
|
||||||
while( $j-- > 0 && $this->xmlnodes[$j]['tag'] != 'DAV::href' );
|
if ( $this->xmlnodes[$j]['tag'] == 'DAV::status' && $this->xmlnodes[$j]['value'] != 'HTTP/1.1 200 OK' ) return null;
|
||||||
if ( $j > 0 && isset($this->xmlnodes[$j]['value']) ) {
|
|
||||||
return $this->xmlnodes[$j]['value'];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
// printf( "Node[$j]: %s\n", $this->xmlnodes[$j]['tag']);
|
||||||
|
if ( $j > 0 && isset($this->xmlnodes[$j]['value']) ) {
|
||||||
|
// printf( "Value[$j]: %s\n", $this->xmlnodes[$j]['value']);
|
||||||
|
return rawurldecode($this->xmlnodes[$j]['value']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
printf( "xmltags[$tagname] or xmltags[$tagname][$i] is not set\n");
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -472,7 +520,7 @@ class CalDAVClient {
|
|||||||
if ( $j > 0 ) {
|
if ( $j > 0 ) {
|
||||||
while( $j-- > 0 && $this->xmlnodes[$j]['tag'] != 'DAV::href' );
|
while( $j-- > 0 && $this->xmlnodes[$j]['tag'] != 'DAV::href' );
|
||||||
if ( $j > 0 && isset($this->xmlnodes[$j]['value']) ) {
|
if ( $j > 0 && isset($this->xmlnodes[$j]['value']) ) {
|
||||||
return $this->xmlnodes[$j]['value'];
|
return rawurldecode($this->xmlnodes[$j]['value']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -552,7 +600,7 @@ class CalDAVClient {
|
|||||||
while( $this->xmlnodes[++$v]['type'] != 'close' && $this->xmlnodes[$v]['tag'] != 'urn:ietf:params:xml:ns:caldav:calendar-home-set' ) {
|
while( $this->xmlnodes[++$v]['type'] != 'close' && $this->xmlnodes[$v]['tag'] != 'urn:ietf:params:xml:ns:caldav:calendar-home-set' ) {
|
||||||
// printf( "Tag: '%s' = '%s'\n", $this->xmlnodes[$v]['tag'], $this->xmlnodes[$v]['value']);
|
// printf( "Tag: '%s' = '%s'\n", $this->xmlnodes[$v]['tag'], $this->xmlnodes[$v]['value']);
|
||||||
if ( $this->xmlnodes[$v]['tag'] == 'DAV::href' && isset($this->xmlnodes[$v]['value']) )
|
if ( $this->xmlnodes[$v]['tag'] == 'DAV::href' && isset($this->xmlnodes[$v]['value']) )
|
||||||
$calendar_home[] = $this->xmlnodes[$v]['value'];
|
$calendar_home[] = rawurldecode($this->xmlnodes[$v]['value']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -581,7 +629,7 @@ class CalDAVClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
foreach( $this->xmltags['DAV::href'] AS $i => $hnode ) {
|
foreach( $this->xmltags['DAV::href'] AS $i => $hnode ) {
|
||||||
$href = $this->xmlnodes[$hnode]['value'];
|
$href = rawurldecode($this->xmlnodes[$hnode]['value']);
|
||||||
|
|
||||||
if ( !isset($calendar_urls[$href]) ) continue;
|
if ( !isset($calendar_urls[$href]) ) continue;
|
||||||
|
|
||||||
@ -608,18 +656,47 @@ class CalDAVClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the calendars, from the calendar_home_set
|
||||||
|
*/
|
||||||
|
function GetCalendarDetails( $url = null ) {
|
||||||
|
if ( isset($url) ) $this->SetCalendar($url);
|
||||||
|
|
||||||
|
$calendar_properties = array( 'resourcetype', 'displayname', 'http://calendarserver.org/ns/:getctag', 'urn:ietf:params:xml:ns:caldav:calendar-timezone', 'supported-report-set' );
|
||||||
|
$this->DoPROPFINDRequest( $this->calendar_url, $calendar_properties, 0);
|
||||||
|
|
||||||
|
$hnode = $this->xmltags['DAV::href'][0];
|
||||||
|
$href = rawurldecode($this->xmlnodes[$hnode]['value']);
|
||||||
|
|
||||||
|
$calendar = new CalendarInfo($href);
|
||||||
|
$ok_props = $this->GetOKProps($hnode);
|
||||||
|
foreach( $ok_props AS $k => $v ) {
|
||||||
|
$name = preg_replace( '{^.*:}', '', $v['tag'] );
|
||||||
|
if ( isset($v['value'] ) ) {
|
||||||
|
$calendar->{$name} = $v['value'];
|
||||||
|
}
|
||||||
|
/* else {
|
||||||
|
printf( "Calendar property '%s' has no text content\n", $v['tag'] );
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
||||||
|
return $calendar;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all etags for a calendar
|
* Get all etags for a calendar
|
||||||
*/
|
*/
|
||||||
function GetCollectionETags( $url = null ) {
|
function GetCollectionETags( $url = null ) {
|
||||||
if ( isset($url) ) $this->SetCalendar($url);
|
if ( isset($url) ) $this->SetCalendar($url);
|
||||||
|
|
||||||
$this->DoPROPFINDRequest( $this->calendar_url, array('getetag'), 1);
|
$this->DoPROPFINDRequest( $this->calendar_url, array('getetag','supported-report-set'), 1);
|
||||||
|
|
||||||
$etags = array();
|
$etags = array();
|
||||||
if ( isset($this->xmltags['DAV::getetag']) ) {
|
if ( isset($this->xmltags['DAV::getetag']) ) {
|
||||||
foreach( $this->xmltags['DAV::getetag'] AS $k => $v ) {
|
foreach( $this->xmltags['DAV::getetag'] AS $k => $v ) {
|
||||||
$events[$this->HrefForProp('DAV::getetag', $k)] = $v['value'];
|
$href = $this->HrefForProp('DAV::getetag', $k);
|
||||||
|
if ( isset($href) ) $etags[$href] = $this->xmlnodes[$v]['value'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -634,30 +711,33 @@ class CalDAVClient {
|
|||||||
|
|
||||||
if ( isset($url) ) $this->SetCalendar($url);
|
if ( isset($url) ) $this->SetCalendar($url);
|
||||||
|
|
||||||
$hrefs = array();
|
$hrefs = '';
|
||||||
foreach( $event_hrefs AS $k => $href ) {
|
foreach( $event_hrefs AS $k => $href ) {
|
||||||
$hrefs .= '<href>'.rawurlencode($url).'</href>';
|
$href = str_replace( rawurlencode('/'),'/',rawurlencode($href));
|
||||||
|
$hrefs .= '<href>'.$href.'</href>';
|
||||||
}
|
}
|
||||||
$this->body = <<<EOXML
|
$this->body = <<<EOXML
|
||||||
<?xml version="1.0" encoding="utf-8" ?>
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
<C:calendar-multiget xmlns="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav">
|
<C:calendar-multiget xmlns="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav">
|
||||||
<prop><getetag/><C:calendar-data/></prop>
|
<prop><getetag/><C:calendar-data/></prop>
|
||||||
$hrefs
|
$hrefs
|
||||||
</C:calendar-query>
|
</C:calendar-multiget>
|
||||||
EOXML;
|
EOXML;
|
||||||
|
|
||||||
$this->requestMethod = "REPORT";
|
$this->requestMethod = "REPORT";
|
||||||
$this->SetContentType("text/xml");
|
$this->SetContentType("text/xml");
|
||||||
$this->DoRequest( $this->calendar_url );
|
$this->DoRequest( $this->calendar_url );
|
||||||
|
|
||||||
$etags = array();
|
$events = array();
|
||||||
if ( isset($this->xmltags['DAV::getetag']) ) {
|
if ( isset($this->xmltags['urn:ietf:params:xml:ns:caldav:calendar-data']) ) {
|
||||||
foreach( $this->xmltags['DAV::getetag'] AS $k => $v ) {
|
foreach( $this->xmltags['urn:ietf:params:xml:ns:caldav:calendar-data'] AS $k => $v ) {
|
||||||
$events[$this->HrefForProp('DAV::getetag', $k)] = $v['value'];
|
$href = $this->HrefForProp('urn:ietf:params:xml:ns:caldav:calendar-data', $k);
|
||||||
|
// echo "Calendar-data:\n"; print_r($this->xmlnodes[$v]);
|
||||||
|
$events[$href] = $this->xmlnodes[$v]['value'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $etags;
|
return $events;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -704,7 +784,7 @@ EOXML;
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'DAV::href':
|
case 'DAV::href':
|
||||||
$response['href'] = basename( $v['value'] );
|
$response['href'] = basename( rawurldecode($v['value']) );
|
||||||
break;
|
break;
|
||||||
case 'DAV::getetag':
|
case 'DAV::getetag':
|
||||||
$response['etag'] = preg_replace('/^"?([^"]+)"?/', '$1', $v['value']);
|
$response['etag'] = preg_replace('/^"?([^"]+)"?/', '$1', $v['value']);
|
||||||
@ -830,12 +910,12 @@ EOFILTER;
|
|||||||
* Get the calendar entry by HREF
|
* Get the calendar entry by HREF
|
||||||
*
|
*
|
||||||
* @param string $href The href from a call to GetEvents or GetTodos etc.
|
* @param string $href The href from a call to GetEvents or GetTodos etc.
|
||||||
* @param string $relative_url The URL relative to the base_url specified when the calendar was opened. Default ''.
|
|
||||||
*
|
*
|
||||||
* @return string The iCalendar of the calendar entry
|
* @return string The iCalendar of the calendar entry
|
||||||
*/
|
*/
|
||||||
function GetEntryByHref( $href, $relative_url = '' ) {
|
function GetEntryByHref( $href ) {
|
||||||
return $this->DoGETRequest( $relative_url . $href );
|
$href = str_replace( rawurlencode('/'),'/',rawurlencode($href));
|
||||||
|
return $this->DoGETRequest( $href );
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,174 +0,0 @@
|
|||||||
#!/usr/bin/php
|
|
||||||
<?php
|
|
||||||
|
|
||||||
if ( @file_exists('../../awl/inc/AWLUtilities.php') ) {
|
|
||||||
set_include_path('../inc:../../awl/inc');
|
|
||||||
}
|
|
||||||
else if ( @file_exists('../awl/inc/AWLUtilities.php') ) {
|
|
||||||
set_include_path('inc:../awl/inc:.');
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
set_include_path('../inc:/usr/share/awl/inc');
|
|
||||||
}
|
|
||||||
include('always.php');
|
|
||||||
require_once('AwlQuery.php');
|
|
||||||
require_once('caldav-PUT-functions.php');
|
|
||||||
|
|
||||||
include('caldav-client-v2.php');
|
|
||||||
|
|
||||||
$args = (object) null;
|
|
||||||
$args->sync_all = false;
|
|
||||||
|
|
||||||
function parse_arguments() {
|
|
||||||
global $args;
|
|
||||||
|
|
||||||
$opts = getopt( 'u:U:p:a' );
|
|
||||||
foreach( $opts AS $k => $v ) {
|
|
||||||
switch( $k ) {
|
|
||||||
case 'u': $args->url = $v; break;
|
|
||||||
case 'U': $args->user = $v; break;
|
|
||||||
case 'p': $args->pass = $v; break;
|
|
||||||
case 'a': $args->sync_all = 1; break;
|
|
||||||
case 'c': $args->local_collection_path = $v; break;
|
|
||||||
default: $args->{$k} = $v;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
parse_arguments();
|
|
||||||
|
|
||||||
// E.g.
|
|
||||||
// sync-pull.php -U andrew@example.net -p 53cret -u https://www.google.com/calendar/dav/andrew@example.net/events -c /andrew/gsync/
|
|
||||||
//
|
|
||||||
|
|
||||||
if ( !preg_match( '{/$}', $args->url ) ) $args->url .= '/';
|
|
||||||
|
|
||||||
$caldav = new CalDAVClient( $args->url, $args->user, $args->pass );
|
|
||||||
|
|
||||||
// This will find the 'Principal URL' which we can query for user-related
|
|
||||||
// properties.
|
|
||||||
$principal_url = $caldav->FindPrincipal($args->url);
|
|
||||||
|
|
||||||
// This will find the 'Calendar Home URL' which will be the folder(s) which
|
|
||||||
// contain all of the user's calendars
|
|
||||||
$calendar_home_set = $caldav->FindCalendarHome();
|
|
||||||
|
|
||||||
$calendar = null;
|
|
||||||
|
|
||||||
// This will go through the calendar_home_set and find all of the users
|
|
||||||
// calendars on the remote server.
|
|
||||||
$calendars = $caldav->FindCalendars();
|
|
||||||
if ( count($calendars) < 1 ) {
|
|
||||||
printf( "No calendars found based on '%s'\n", $args->url );
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now we have all of the remote calendars, we will look for the URL that
|
|
||||||
// matches what we were originally supplied. While this seems laborious
|
|
||||||
// because we already have it, it means we could provide a match in some
|
|
||||||
// other way (e.g. on displayname) and we could also present a list to
|
|
||||||
// the user which is built from following the above process.
|
|
||||||
foreach( $calendars AS $k => $a_calendar ) {
|
|
||||||
if ( rawurldecode($a_calendar->url) == rawurldecode($args->url) ) $calendar = $a_calendar;
|
|
||||||
}
|
|
||||||
if ( !isset($calendar) ) $calendar = $calendars[0];
|
|
||||||
|
|
||||||
// In reality we could have omitted all of the above parts, If we really do
|
|
||||||
// know the correct URL at the start.
|
|
||||||
printf( "Calendar '%s' is at %s\n", $calendar->displayname, $calendar->url );
|
|
||||||
|
|
||||||
// Generate a consistent filename for our synchronisation cache
|
|
||||||
$sync_cache_filename = md5($args->url . $args->user . $calendar->url);
|
|
||||||
|
|
||||||
// Do we just need to sync everything across and overwrite all the local stuff?
|
|
||||||
$sync_all = ( !file_exists($sync_cache_filename) || $args->sync_all);
|
|
||||||
|
|
||||||
if ( ! $sync_all ) {
|
|
||||||
/**
|
|
||||||
* Read a structure out of the cache file containing:
|
|
||||||
* server_getctag - A collection tag (string)
|
|
||||||
* server_etags - An array of event tags (strings) keyed on filename, from the server
|
|
||||||
* local_etags - An array of event tags (strings) keyed on filename, from local DAViCal
|
|
||||||
*/
|
|
||||||
$cache = unserialize( file_get_contents($sync_cache_filename) );
|
|
||||||
|
|
||||||
// First compare the ctag for the calendar
|
|
||||||
if ( isset($cache) && isset($cache->server_ctag) && isset($calendar->getctag) && $calendar->getctag == $cache->server_ctag ) {
|
|
||||||
printf( 'No changes to calendar "%s"'."\n", $args->url );
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( !isset($cache) || !isset($cache->server_ctag) ) $sync_all = true;
|
|
||||||
|
|
||||||
// Everything now will be at our calendar URL
|
|
||||||
$caldav->SetCalendar($calendar->url);
|
|
||||||
|
|
||||||
// So it seems we do need to sync. We now need to check each individual event
|
|
||||||
// which might have changed, so we pull a list of event etags from the server.
|
|
||||||
$server_etags = $caldav->GetCollectionETags($calendar->url);
|
|
||||||
|
|
||||||
$newcache = (object) array( 'server_ctag' => $calendar->getctag, 'server_etags' => array(), 'local_etags' => array() );
|
|
||||||
|
|
||||||
if ( $sync_all ) {
|
|
||||||
// The easy case. Sync them all, delete nothing
|
|
||||||
$insert_urls = array_flip($server_etags);
|
|
||||||
$update_urls = array();
|
|
||||||
$delete_urls = array();
|
|
||||||
foreach( $server_etags AS $href => $etag ) {
|
|
||||||
$fname = preg_replace('{^.*/}', '', $href);
|
|
||||||
$newcache->server_etags[$fname] = $etag;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Only sync the ones where the etag has changed. Delete any that are no
|
|
||||||
// longer present at the remote end.
|
|
||||||
$insert_urls = array();
|
|
||||||
$update_urls = array();
|
|
||||||
foreach( $server_etags AS $href => $etag ) {
|
|
||||||
$fname = preg_replace('{^.*/}', '', $href);
|
|
||||||
$newcache->server_etags[$fname] = $etag;
|
|
||||||
if ( isset($cache->server_etags[$fname]) ) {
|
|
||||||
$cache_etag = $cache->server_etags[$fname];
|
|
||||||
unset($cache->server_etags[$fname]);
|
|
||||||
if ( $cache_etag == $etag ) continue;
|
|
||||||
$update_urls[] = $href;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$insert_urls[] = $href;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$delete_urls = array_flip($cache->server_etags);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Fetch the calendar data
|
|
||||||
$events = $caldav->CalendarMultiget( array_merge( $insert_urls, $update_urls) );
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @TODO: We should really check for collisions locally in case the local ETag is
|
|
||||||
* also different to the one we saved earlier.
|
|
||||||
*/
|
|
||||||
// Update the local system with these events
|
|
||||||
foreach( $events AS $href => $event ) {
|
|
||||||
// Do what we need to write $v into the local calendar we are syncing to
|
|
||||||
// at the
|
|
||||||
$fname = preg_replace('{^.*/}', '', $href);
|
|
||||||
$local_fname = $args->local_collection_path . $fname;
|
|
||||||
simple_write_resource( $local_fname, $event, (isset($insert_urls[$href]) ? 'INSERT' : 'UPDATE') );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @TODO: We should not delete locally in the case that the local ETag is different
|
|
||||||
* to the one we saved earlier.
|
|
||||||
*/
|
|
||||||
// Delete any events which were present in our cache, but are not on the master server
|
|
||||||
foreach( $delete_urls AS $k => $v ) {
|
|
||||||
$fname = preg_replace('{^.*/}', '', $href);
|
|
||||||
$local_fname = $args->local_collection_path . $fname;
|
|
||||||
$qry = new AwlQuery('DELETE FROM caldav_data WHERE dav_name = :dav_name', array( ':dav_name' => $local_fname ) );
|
|
||||||
$qry->Exec('sync_pull',__LINE__,__FILE__);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now (re)write the cache file reflecting the current state.
|
|
||||||
$cache_file = fopen($sync_cache_filename, 'w');
|
|
||||||
fwrite( $cache_file, serialize($newcache) );
|
|
||||||
fclose($cache_file);
|
|
||||||
346
scripts/sync-remote-caldav.php
Executable file
346
scripts/sync-remote-caldav.php
Executable file
@ -0,0 +1,346 @@
|
|||||||
|
#!/usr/bin/php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
if ( @file_exists('../../awl/inc/AWLUtilities.php') ) {
|
||||||
|
set_include_path('../inc:../../awl/inc');
|
||||||
|
}
|
||||||
|
else if ( @file_exists('../awl/inc/AWLUtilities.php') ) {
|
||||||
|
set_include_path('inc:../awl/inc:.');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
set_include_path('../inc:/usr/share/awl/inc');
|
||||||
|
}
|
||||||
|
include('always.php');
|
||||||
|
require_once('AwlQuery.php');
|
||||||
|
require_once('caldav-PUT-functions.php');
|
||||||
|
|
||||||
|
include('caldav-client-v2.php');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call with something like e.g.:
|
||||||
|
*
|
||||||
|
* scripts/sync-remote-caldav.php -U andrew@example.net -p 53cret -u https://www.google.com/calendar/dav/andrew@example.net/events -c /andrew/gsync/
|
||||||
|
*
|
||||||
|
* Optionally also:
|
||||||
|
* Add '-a' to sync everything, rather than checking if getctag has changed. (DON'T USE THIS)
|
||||||
|
* Add '-w remote' to make the remote end win arguments when there is a change to the same event in both places.
|
||||||
|
* Add '-i' to only sync inwards, from the remote server into DAViCal
|
||||||
|
* Add '-o' to only sync outwards, from DAViCal to the remote server
|
||||||
|
*
|
||||||
|
* Note that this script is ugly (though it works, at least with Google) and should really be rewritten
|
||||||
|
* with better structuring. As it is it's more like one long stream of consciousness novel.
|
||||||
|
*
|
||||||
|
* One bug that would be better solved through restructuring is that if you supply -a and have changed an
|
||||||
|
* event locally, it will be overwritten by the remote server's copy while we then overwrite the remote
|
||||||
|
* server with our version! These will then end up swapping each time thereafter in all likelihood...
|
||||||
|
* Recommendation: don't use '-a', except possibly for the very first sync (but why then, even?)
|
||||||
|
*
|
||||||
|
* Other improvements would be to not use command-line parameters, but a configuration file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
$args = (object) null;
|
||||||
|
$args->sync_all = false; // Back to basics and sync everything into one mess
|
||||||
|
$args->local_changes_win = true; // If true, and something has changed at both places, our local update will overwrite the remote
|
||||||
|
|
||||||
|
$args->sync_in = false; // If true, remote changes will be applied locally
|
||||||
|
$args->sync_out = false; // If true, local changes will be applied remotely
|
||||||
|
|
||||||
|
$args->cache_directory = '.sync-cache';
|
||||||
|
|
||||||
|
function parse_arguments() {
|
||||||
|
global $args;
|
||||||
|
|
||||||
|
$opts = getopt( 'u:U:p:c:w:ioa' );
|
||||||
|
foreach( $opts AS $k => $v ) {
|
||||||
|
switch( $k ) {
|
||||||
|
case 'u': $args->url = $v; break;
|
||||||
|
case 'U': $args->user = $v; break;
|
||||||
|
case 'p': $args->pass = $v; break;
|
||||||
|
case 'a': $args->sync_all = true; break;
|
||||||
|
case 'c': $args->local_collection_path = $v; break;
|
||||||
|
case 'w': $args->local_changes_win = (strtolower($v) != 'remote' ); break;
|
||||||
|
case 'i': $args->sync_in = true; break;
|
||||||
|
case 'o': $args->sync_out = true; break;
|
||||||
|
default: $args->{$k} = $v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
parse_arguments();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if ( !preg_match('{/$}', $args->local_collection_path) ) $args->local_collection_path .= '/';
|
||||||
|
if ( !preg_match('{^/[^/]+/[^/]+/$}', $args->local_collection_path) ) {
|
||||||
|
printf( "The local URL of '%s' looks wrong. It should be formed as '/username/collection/'\n", $args->local_collection_path );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !preg_match('{/$}', $args->url) ) $args->url .= '/';
|
||||||
|
|
||||||
|
$caldav = new CalDAVClient( $args->url, $args->user, $args->pass );
|
||||||
|
|
||||||
|
// // This will find the 'Principal URL' which we can query for user-related
|
||||||
|
// // properties.
|
||||||
|
// $principal_url = $caldav->FindPrincipal($args->url);
|
||||||
|
//
|
||||||
|
// // This will find the 'Calendar Home URL' which will be the folder(s) which
|
||||||
|
// // contain all of the user's calendars
|
||||||
|
// $calendar_home_set = $caldav->FindCalendarHome();
|
||||||
|
//
|
||||||
|
// $calendar = null;
|
||||||
|
//
|
||||||
|
// // This will go through the calendar_home_set and find all of the users
|
||||||
|
// // calendars on the remote server.
|
||||||
|
// $calendars = $caldav->FindCalendars();
|
||||||
|
// if ( count($calendars) < 1 ) {
|
||||||
|
// printf( "No calendars found based on '%s'\n", $args->url );
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Now we have all of the remote calendars, we will look for the URL that
|
||||||
|
// // matches what we were originally supplied. While this seems laborious
|
||||||
|
// // because we already have it, it means we could provide a match in some
|
||||||
|
// // other way (e.g. on displayname) and we could also present a list to
|
||||||
|
// // the user which is built from following the above process.
|
||||||
|
// foreach( $calendars AS $k => $a_calendar ) {
|
||||||
|
// if ( $a_calendar->url == $args->url ) $calendar = $a_calendar;
|
||||||
|
// }
|
||||||
|
// if ( !isset($calendar) ) $calendar = $calendars[0];
|
||||||
|
|
||||||
|
// In reality we could have omitted all of the above parts, If we really do
|
||||||
|
// know the correct URL at the start.
|
||||||
|
|
||||||
|
// Everything now will be at our calendar URL
|
||||||
|
$caldav->SetCalendar($args->url);
|
||||||
|
|
||||||
|
$calendar = $caldav->GetCalendarDetails();
|
||||||
|
|
||||||
|
printf( "Remote calendar '%s' is at %s\n", $calendar->displayname, $calendar->url );
|
||||||
|
|
||||||
|
// Generate a consistent filename for our synchronisation cache
|
||||||
|
if ( ! file_exists($args->cache_directory) && ! is_dir($args->cache_directory) ) {
|
||||||
|
mkdir($args->cache_directory, 0750 ); // Not incredibly sensitive file contents - URLs and ETags
|
||||||
|
}
|
||||||
|
$sync_cache_filename = $args->cache_directory .'/'. md5($args->user . $calendar->url);
|
||||||
|
|
||||||
|
// Do we just need to sync everything across and overwrite all the local stuff?
|
||||||
|
$sync_all = ( !file_exists($sync_cache_filename) || $args->sync_all);
|
||||||
|
$sync_in = false;
|
||||||
|
$sync_out = false;
|
||||||
|
if ( $args->sync_in || !$args->sync_out ) $sync_in = true;
|
||||||
|
if ( $args->sync_out || !$args->sync_in ) $sync_out = true;
|
||||||
|
|
||||||
|
|
||||||
|
if ( ! $sync_all ) {
|
||||||
|
/**
|
||||||
|
* Read a structure out of the cache file containing:
|
||||||
|
* server_getctag - A collection tag (string) from the remote server
|
||||||
|
* local_getctag - A collection tag (string) from the local DB
|
||||||
|
* server_etags - An array of event tags (strings) keyed on filename, from the server
|
||||||
|
* local_etags - An array of event tags (strings) keyed on filename, from local DAViCal
|
||||||
|
*/
|
||||||
|
$cache = unserialize( file_get_contents($sync_cache_filename) );
|
||||||
|
|
||||||
|
// First compare the ctag for the calendar
|
||||||
|
if ( isset($cache) && isset($cache->server_ctag) && isset($calendar->getctag) && $calendar->getctag == $cache->server_ctag ) {
|
||||||
|
printf( 'No changes to remote calendar "%s" at "%s"'."\n", $calendar->displayname, $calendar->url );
|
||||||
|
$sync_in = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$qry = new AwlQuery('SELECT collection_id, dav_displayname AS displayname, dav_etag AS getctag FROM collection WHERE dav_name = :collection_dav_name', array(':collection_dav_name' => $args->local_collection_path) );
|
||||||
|
if ( $qry->Exec('sync-pull',__LINE__,__FILE__) && $qry->rows() > 0 ) {
|
||||||
|
$local_calendar = $qry->Fetch();
|
||||||
|
|
||||||
|
// First compare the ctag for the calendar
|
||||||
|
if ( isset($cache) && isset($cache->local_ctag) && isset($local_calendar->getctag) && $local_calendar->getctag == $cache->local_ctag ) {
|
||||||
|
printf( 'No changes to local calendar "%s" at "%s"'."\n", $local_calendar->displayname, $args->local_collection_path );
|
||||||
|
$sync_out = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( !isset($cache) || !isset($cache->server_ctag) ) $sync_all = true;
|
||||||
|
|
||||||
|
$remote_event_prefix = preg_replace('{^https?://[^/]+/}', '/', $calendar->url);
|
||||||
|
$insert_urls = array();
|
||||||
|
$update_urls = array();
|
||||||
|
$local_delete_urls = array();
|
||||||
|
$server_delete_urls = array();
|
||||||
|
$push_urls = array();
|
||||||
|
$push_events = array();
|
||||||
|
|
||||||
|
$newcache = (object) array( 'server_ctag' => $calendar->getctag,
|
||||||
|
'local_ctag' => (isset($local_calendar->getctag) ? $local_calendar->getctag : null),
|
||||||
|
'server_etags' => array(), 'local_etags' => array() );
|
||||||
|
if ( isset($cache) ) {
|
||||||
|
if ( !$sync_in && isset($cache->server_etags) ) $newcache->server_etags = $cache->server_etags;
|
||||||
|
if ( !$sync_out && isset($cache->local_etags) ) $newcache->local_etags = $cache->local_etags;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $sync_in ) {
|
||||||
|
// So it seems we do need to sync. We now need to check each individual event
|
||||||
|
// which might have changed, so we pull a list of event etags from the server.
|
||||||
|
$server_etags = $caldav->GetCollectionETags();
|
||||||
|
// printf( "\nGetCollectionEtags Response:\n%s\n", $caldav->GetXmlResponse() );
|
||||||
|
// print_r( $server_etags );
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if ( $sync_all ) {
|
||||||
|
// The easy case. Sync them all, delete nothing
|
||||||
|
$insert_urls = $server_etags;
|
||||||
|
foreach( $server_etags AS $href => $etag ) {
|
||||||
|
$fname = preg_replace('{^.*/}', '', $href);
|
||||||
|
$newcache->server_etags[$fname] = $etag;
|
||||||
|
printf( 'Need to pull "%s"'."\n", $href );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Only sync the ones where the etag has changed. Delete any that are no
|
||||||
|
// longer present at the remote end.
|
||||||
|
foreach( $server_etags AS $href => $etag ) {
|
||||||
|
$fname = preg_replace('{^.*/}', '', $href);
|
||||||
|
$newcache->server_etags[$fname] = $etag;
|
||||||
|
if ( isset($cache->server_etags[$fname]) ) {
|
||||||
|
$cache_etag = $cache->server_etags[$fname];
|
||||||
|
unset($cache->server_etags[$fname]);
|
||||||
|
if ( $cache_etag == $etag ) continue;
|
||||||
|
$update_urls[$href] = 1;
|
||||||
|
printf( 'Need to pull to update "%s"'."\n", $href );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$insert_urls[$href] = 1;
|
||||||
|
printf( 'Need to pull to insert "%s"'."\n", $href );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$local_delete_urls = $cache->server_etags;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Fetch the calendar data
|
||||||
|
$events = $caldav->CalendarMultiget( array_merge( array_keys($insert_urls), array_keys($update_urls)) );
|
||||||
|
// printf( "\nCalendarMultiget Request:\n%s\n Response:\n%s\n", $caldav->GetXmlRequest(), $caldav->GetXmlResponse() );
|
||||||
|
// print_r($events);
|
||||||
|
|
||||||
|
if ( !preg_match( '{/$}', $remote_event_prefix) ) $remote_event_prefix .= '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $sync_out ) {
|
||||||
|
/**
|
||||||
|
* This is a fairly tricky bit. We find local changes and check to see if they
|
||||||
|
* are collisions. We actually have to check the data for a collision, since the
|
||||||
|
* real data may in fact be identical, e.g. because of the -a option or something.
|
||||||
|
*
|
||||||
|
* Once we have verified that the target objects actually *are* different, then:
|
||||||
|
* Change vs No change => The change is propagated to the other server
|
||||||
|
* DELETE vs UPDATE/INSERT => DELETE always loses
|
||||||
|
* UPDATE vs UPDATE => pick the winner according to arbitrary setting (see top of file)
|
||||||
|
* INSERT vs INSERT => pick the winner according to arbitrary setting (see top of file) v. unlikely
|
||||||
|
*/
|
||||||
|
// Read the local ETag from DAViCal.
|
||||||
|
$qry = new AwlQuery( 'SELECT dav_name, dav_etag, caldav_data FROM caldav_data WHERE collection_id = (SELECT collection_id FROM collection WHERE dav_name = :collection_dav_name)',
|
||||||
|
array(':collection_dav_name' => $args->local_collection_path) );
|
||||||
|
if ( $qry->Exec('sync-pull',__LINE__,__FILE__) && $qry->rows() > 0 ) {
|
||||||
|
$local_etags = array();
|
||||||
|
while( $local = $qry->Fetch() ) {
|
||||||
|
$fname = preg_replace('{^.*/}', '', $local->dav_name);
|
||||||
|
$newcache->local_etags[$fname] = $local->dav_etag;
|
||||||
|
if ( !$sync_all && isset($cache->local_etags[$fname]) ) {
|
||||||
|
$cache_etag = $cache->local_etags[$fname];
|
||||||
|
unset($cache->local_etags[$fname]);
|
||||||
|
if ( $cache_etag == $local->dav_etag ) continue;
|
||||||
|
}
|
||||||
|
if ( isset($insert_urls[$remote_event_prefix.$fname]) ) {
|
||||||
|
if ( $local->caldav_data == $events[$remote_event_prefix.$fname] ) {
|
||||||
|
// Not actually changed. Ignore it at *both* ends!
|
||||||
|
unset($insert_urls[$remote_event_prefix.$fname]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ( $args->local_changes_win )
|
||||||
|
unset($insert_urls[$remote_event_prefix.$fname]);
|
||||||
|
else
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ( isset($update_urls[$remote_event_prefix.$fname]) ) {
|
||||||
|
if ( $local->caldav_data == $events[$remote_event_prefix.$fname] ) {
|
||||||
|
// Not actually changed. Ignore it at *both* ends!
|
||||||
|
unset($update_urls[$remote_event_prefix.$fname]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ( $args->local_changes_win )
|
||||||
|
unset($update_urls[$remote_event_prefix.$fname]);
|
||||||
|
else
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$push_urls[$fname] = (isset($cache->server_etags[$remote_event_prefix.$fname]) ? $cache->server_etags[$remote_event_prefix.$fname] : '*');
|
||||||
|
$push_events[$fname] = $local->caldav_data;
|
||||||
|
printf( 'Need to push "%s"'."\n", $local->dav_name );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !$sync_all ) {
|
||||||
|
foreach( $cache->local_etags AS $href => $etag ) {
|
||||||
|
$fname = preg_replace('{^.*/}', '', $local->dav_name);
|
||||||
|
if ( !isset($insert_urls[$remote_event_prefix.$fname])
|
||||||
|
&& !isset($update_urls[$remote_event_prefix.$fname])
|
||||||
|
&& isset($cache->server_etags[$remote_event_prefix.$fname]) ) {
|
||||||
|
$server_delete_urls[$fname] = $cache->server_etags[$remote_event_prefix.$fname];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if ( $sync_in ) {
|
||||||
|
// Delete any local events which have been removed from the remote server
|
||||||
|
foreach( $local_delete_urls AS $k => $v ) {
|
||||||
|
$fname = preg_replace('{^.*/}', '', $href);
|
||||||
|
$local_fname = $args->local_collection_path . $fname;
|
||||||
|
$qry = new AwlQuery('DELETE FROM caldav_data WHERE dav_name = :dav_name', array( ':dav_name' => $local_fname ) );
|
||||||
|
$qry->Exec('sync_pull',__LINE__,__FILE__);
|
||||||
|
unset($newcache->local_etags[$fname]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
unset($c->dbg['querystring']);
|
||||||
|
// Update the local system with events that are new or updated on the remote server
|
||||||
|
foreach( $events AS $href => $event ) {
|
||||||
|
// Do what we need to write $v into the local calendar we are syncing to
|
||||||
|
// at the
|
||||||
|
$fname = preg_replace('{^.*/}', '', $href);
|
||||||
|
$local_fname = $args->local_collection_path . $fname;
|
||||||
|
simple_write_resource( $local_fname, $event, (isset($insert_urls[$href]) ? 'INSERT' : 'UPDATE') );
|
||||||
|
$newcache->local_etags[$fname] = md5($event);
|
||||||
|
}
|
||||||
|
|
||||||
|
$qry = new AwlQuery('SELECT collection_id, dav_displayname AS displayname, dav_etag AS getctag FROM collection WHERE dav_name = :collection_dav_name', array(':collection_dav_name' => $args->local_collection_path) );
|
||||||
|
if ( $qry->Exec('sync-pull',__LINE__,__FILE__) && $qry->rows() > 0 ) {
|
||||||
|
$local_calendar = $qry->Fetch();
|
||||||
|
if ( isset($local_calendar->getctag) ) $newcache->local_ctag = $local_calendar->getctag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $sync_out ) {
|
||||||
|
// Delete any remote events which have been removed from the local server
|
||||||
|
foreach( $server_delete_urls AS $href => $etag ) {
|
||||||
|
$caldav->DoDELETERequest( $args->url . $href, $etag );
|
||||||
|
printf( "\nDELETE Response:\n%s\n", $caldav->GetResponseHeaders() );
|
||||||
|
unset($newcache->server_etags[$fname]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Push locally updated events to the remote server
|
||||||
|
foreach( $push_urls AS $href => $etag ) {
|
||||||
|
$new_etag = $caldav->DoPUTRequest( $args->url . $href, $push_events[$href], $etag );
|
||||||
|
printf( "\nPUT:\n%s\nResponse:\n%s\n", $caldav->GetHttpRequest(), $caldav->GetResponseHeaders() );
|
||||||
|
$newcache->server_etags[$fname] = $new_etag;
|
||||||
|
}
|
||||||
|
|
||||||
|
$calendar = $caldav->GetCalendarDetails();
|
||||||
|
if ( isset($calendar->getctag) ) $newcache->server_ctag = $calendar->getctag;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now (re)write the cache file reflecting the current state.
|
||||||
|
$cache_file = fopen($sync_cache_filename, 'w');
|
||||||
|
fwrite( $cache_file, serialize($newcache) );
|
||||||
|
fclose($cache_file);
|
||||||
|
|
||||||
|
print_r($newcache);
|
||||||
Loading…
x
Reference in New Issue
Block a user