Overview

Packages

  • awl
    • caldav-client-v2
    • RRule
  • davical
    • authentication
      • drivers
    • caldav
    • DAViCalSession
    • DAVTicket
    • external-bind
    • feed
    • HTTPAuthSession
    • iSchedule
    • iSchedule-POST
    • logging
    • metrics
    • Principal
    • propfind
    • PublicSession
    • Request
    • Resource
    • tzservice
  • None
  • PHP

Functions

  • add_failure
  • check_for_expansion
  • component_to_xml
  • controlRequestContainer
  • delete_collection
  • deliverItipCancel
  • display_status
  • do_scheduling_for_delete
  • do_scheduling_reply
  • do_scheduling_requests
  • doImipMessage
  • doItipAttendeeReply
  • doItipOrganizerCancel
  • export_iCalendar
  • GetItip
  • GetTZID
  • handle_cancel_request
  • handle_freebusy_request
  • handle_schedule_reply
  • handle_schedule_request
  • import_addressbook_collection
  • import_calendar_collection
  • import_collection
  • late_catch_fatal_error
  • logRequestHeaders
  • obfuscated_event
  • process_ace
  • processItipCancel
  • property_response
  • public_events_only
  • rollback
  • rollback_on_error
  • send_dav_header
  • simple_write_resource
  • write_alarms
  • write_attendees
  • write_resource
  • Overview
  • Package
  • Function
  • Tree
  • Deprecated
  • Todo
  1: <?php
  2: /**
  3: * CalDAV Server - handle sync-collection report (draft-daboo-webdav-sync-01)
  4: *
  5: * @package   davical
  6: * @subpackage   caldav
  7: * @author    Andrew McMillan <andrew@mcmillan.net.nz>
  8: * @copyright Morphoss Ltd - http://www.morphoss.com/
  9: * @license   http://gnu.org/copyleft/gpl.html GNU GPL v2 or later
 10: */
 11: 
 12: $responses = array();
 13: 
 14: /**
 15:  * Build the array of properties to include in the report output
 16:  */
 17: $sync_level = $xmltree->GetPath('/DAV::sync-collection/DAV::sync-level');
 18: if ( empty($sync_level) ) {
 19:   $sync_level = $request->depth;
 20: }
 21: else {
 22:   $sync_level = $sync_level[0]->GetContent();
 23:   if ( $sync_level == 'infinity' )
 24:     $sync_level = DEPTH_INFINITY;
 25:   else
 26:     $sync_level = 1;
 27: }
 28: 
 29: if ( $sync_level == DEPTH_INFINITY ) {
 30:   $request->PreconditionFailed(403, 'DAV::sync-traversal-supported','This server does not support sync-traversal');
 31: }
 32: 
 33: $sync_tokens = $xmltree->GetPath('/DAV::sync-collection/DAV::sync-token');
 34: if ( isset($sync_tokens[0]) ) $sync_token = $sync_tokens[0]->GetContent();
 35: if ( !isset($sync_token) ) $sync_token = 0;
 36: $sync_token = intval(str_replace('data:,', '', strtolower($sync_token) ));
 37: dbg_error_log( 'sync', " sync-token: %s", $sync_token );
 38: 
 39: $proplist = array();
 40: $props = $xmltree->GetPath('/DAV::sync-collection/DAV::prop/*');
 41: if ( !empty($props) ) {
 42:   foreach( $props AS $k => $v ) {
 43:     $proplist[] = $v->GetNSTag();
 44:   }
 45: }
 46: 
 47: function display_status( $status_code ) {
 48:   return sprintf( 'HTTP/1.1 %03d %s', intval($status_code), getStatusMessage($status_code) );
 49: }
 50: 
 51: $collection = new DAVResource( $request->path );
 52: if ( !$collection->Exists() ) {
 53:   $request->DoResponse( 404 );
 54: }
 55: $bound_from = $collection->bound_from();
 56: $collection_path = $collection->dav_name();
 57: $request_via_binding = ($bound_from != $collection_path);
 58: 
 59: $params = array( ':collection_id' => $collection->GetProperty('collection_id'), ':sync_token' => $sync_token );
 60: $sql = "SELECT new_sync_token( :sync_token, :collection_id)";
 61: $qry = new AwlQuery($sql, $params);
 62: if ( !$qry->Exec("REPORT",__LINE__,__FILE__) || $qry->rows() <= 0 ) {
 63:   $request->DoResponse( 500, translate("Database error") );
 64: }
 65: $row = $qry->Fetch();
 66: 
 67: if ( !isset($row->new_sync_token) ) {
 68:   /** If we got a null back then they gave us a sync token we know not of, so provide a full sync */
 69:   $sync_token = 0;
 70:   $params[':sync_token'] = $sync_token;
 71:   if ( !$qry->QDo($sql, $params) || $qry->rows() <= 0 ) {
 72:     $request->DoResponse( 500, translate("Database error") );
 73:   }
 74:   $row = $qry->Fetch();
 75: }
 76: $new_token = $row->new_sync_token;
 77: 
 78: if ( $sync_token == $new_token ) {
 79:   // No change, so we just re-send the old token.
 80:   $responses[] = new XMLElement( 'sync-token', 'data:,'.$new_token );
 81: }
 82: else {
 83:   $hide_older = '';
 84:   if ( isset($c->hide_older_than) && intval($c->hide_older_than) > 0 )
 85:     $hide_older = " AND (CASE WHEN caldav_data.caldav_type<>'VEVENT' OR calendar_item.dtstart IS NULL OR calendar_item.rrule IS NOT NULL THEN true ELSE calendar_item.dtstart > (now() - interval '".intval($c->hide_older_than)." days') END)";
 86: 
 87:   $hide_todo = '';
 88:   if ( isset($c->hide_TODO) && ($c->hide_TODO === true || (is_string($c->hide_TODO) && preg_match($c->hide_TODO, $_SERVER['HTTP_USER_AGENT']))) && ! $collection->HavePrivilegeTo('all') )
 89:     $hide_todo = " AND (caldav_data.caldav_type NOT IN ('VTODO') OR caldav_data.caldav_type IS NULL) ";
 90: 
 91:   if ( $sync_token == 0 ) {
 92:     $sql = <<<EOSQL
 93:   SELECT collection.*, calendar_item.*, caldav_data.*, addressbook_resource.*, 201 AS sync_status FROM collection
 94:               LEFT JOIN caldav_data USING (collection_id)
 95:               LEFT JOIN calendar_item USING (dav_id)
 96:                            LEFT JOIN addressbook_resource USING (dav_id)
 97:               WHERE collection.collection_id = :collection_id $hide_older $hide_todo
 98:      ORDER BY collection.collection_id, caldav_data.dav_id
 99: EOSQL;
100:     unset($params[':sync_token']);
101:   }
102:   else {
103:     $sql = <<<EOSQL
104:   SELECT collection.*, calendar_item.*, caldav_data.*, addressbook_resource.*, sync_changes.*
105:     FROM collection LEFT JOIN sync_changes USING(collection_id)
106:                            LEFT JOIN caldav_data USING (collection_id,dav_id)
107:                            LEFT JOIN calendar_item USING (collection_id,dav_id)
108:                            LEFT JOIN addressbook_resource USING (dav_id)
109:                            WHERE collection.collection_id = :collection_id $hide_older $hide_todo
110:          AND sync_time >= (SELECT modification_time FROM sync_tokens WHERE sync_token = :sync_token)
111: EOSQL;
112:     if ( isset($c->strict_result_ordering) && $c->strict_result_ordering ) {
113:       $sql .= " ORDER BY collection.collection_id, lower(sync_changes.dav_name), sync_changes.sync_time";
114:     }
115:     else {
116:       $sql .= " ORDER BY collection.collection_id, sync_changes.dav_name, sync_changes.sync_time";
117:     }
118:   }
119:   $qry = new AwlQuery($sql, $params );
120: 
121:   $last_dav_name = '';
122:   $first_status = 0;
123: 
124:   if ( $qry->Exec("REPORT",__LINE__,__FILE__) ) {
125:     if ( $qry->rows() > 50 ) {
126:       // If there are more than 50 rows to send we should not send full data in response ...
127:       $c->sync_resource_data_ok = false;
128:     }
129:     while( $object = $qry->Fetch() ) {
130:       if ( $request_via_binding )
131:         $object->dav_name = str_replace( $bound_from, $collection_path, $object->dav_name);
132: 
133:       if ( $object->dav_name == $last_dav_name ) {
134:         /** The complex case: this is the second or subsequent for this dav_id */
135:         if ( $object->sync_status == 404 ) {
136:           array_pop($responses);
137:           $resultset = array(
138:             new XMLElement( 'href', ConstructURL($object->dav_name) ),
139:             new XMLElement( 'status', display_status($object->sync_status) )
140:           );
141:           $responses[] = new XMLElement( 'response', $resultset );
142:           $first_status = 404;
143:         }
144:         else if ( $object->sync_status == 201 && $first_status == 404 ) {
145:           // ... Delete ... Create ... is indicated as a create, but don't forget we started with a delete
146:           array_pop($responses);
147:           $dav_resource = new DAVResource($object);
148:           $resultset = $dav_resource->GetPropStat($proplist,$reply);
149:           array_unshift($resultset, new XMLElement( 'href', ConstructURL($object->dav_name)));
150:           $responses[] = new XMLElement( 'response', $resultset );
151:         }
152:         /** Else:
153:          *    the object existed at start and we have multiple modifications,
154:          *  or,
155:          *    the object didn't exist at start and we have subsequent modifications,
156:          *  but:
157:          *    in either case we simply stick with our existing report.
158:          */
159:       }
160:       else {
161:         /** The simple case: this is the first one for this dav_id */
162:         if ( $object->sync_status == 404 ) {
163:           $resultset = array(
164:             new XMLElement( 'href', ConstructURL($object->dav_name) ),
165:             new XMLElement( 'status', display_status($object->sync_status) )
166:           );
167:           $first_status = 404;
168:         }
169:         else {
170:           $dav_resource = new DAVResource($object);
171:           $resultset = $dav_resource->GetPropStat($proplist,$reply);
172:           array_unshift($resultset, new XMLElement( 'href', ConstructURL($object->dav_name)));
173:           $first_status = $object->sync_status;
174:         }
175:         $responses[] = new XMLElement( 'response', $resultset );
176:         $last_dav_name  = $object->dav_name;
177:       }
178:     }
179:     $responses[] = new XMLElement( 'sync-token', 'data:,'.$new_token );
180:   }
181:   else {
182:     $request->DoResponse( 500, translate("Database error") );
183:   }
184: }
185: 
186: $multistatus = new XMLElement( "multistatus", $responses, $reply->GetXmlNsArray() );
187: 
188: $request->XMLResponse( 207, $multistatus );
189: 
DAViCal API documentation generated by ApiGen 2.8.0