source: contrib/davical/inc/caldav-MKCOL.php @ 3733

Revision 3733, 10.1 KB checked in by gabriel.malheiros, 13 years ago (diff)

Ticket #1541 - <Davical customizado para o Expresso.Utiliza Caldav e CardDav?>

Line 
1<?php
2/**
3* CalDAV Server - handle MKCOL and MKCALENDAR method
4*
5* @package   davical
6* @subpackage   caldav
7* @author    Andrew McMillan <andrew@mcmillan.net.nz>
8* @copyright Catalyst IT Ltd, Morphoss Ltd - http://www.morphoss.com/
9* @license   http://gnu.org/copyleft/gpl.html GNU GPL v2
10*/
11dbg_error_log('MKCOL', 'method handler');
12require_once('AwlQuery.php');
13
14$request->NeedPrivilege('DAV::bind');
15$displayname = $request->path;
16
17// Enforce trailling '/' on collection name
18if ( ! preg_match( '#/$#', $request->path ) ) {
19  dbg_error_log( 'MKCOL', 'Add trailling "/" to "%s"', $request->path);
20  $request->path .= '/';
21}
22
23$parent_container = '/';
24if ( preg_match( '#^(.*/)([^/]+)(/)?$#', $request->path, $matches ) ) {
25  $parent_container = $matches[1];
26  $displayname = $matches[2];
27}
28
29require_once('DAVResource.php');
30$parent = new DAVResource( $parent_container );
31if ( $parent->IsSchedulingCollection( 'inbox' ) ) {
32  $request->PreconditionFailed(403, 'urn:ietf:params:xml:ns:caldav:no-mkcol-in-inbox' );
33}
34
35
36$request_type = $request->method;
37$is_calendar = ($request_type == 'MKCALENDAR');
38$is_addressbook = false;
39
40$resourcetypes = '<DAV::collection/>';
41if ($is_calendar) $resourcetypes .= '<urn:ietf:params:xml:ns:caldav:calendar/>';
42
43require_once('XMLDocument.php');
44$reply = new XMLDocument(array( 'DAV:' => '', 'urn:ietf:params:xml:ns:caldav' => 'C' ));
45
46$failure_code = null;
47
48$failure = array();
49$dav_properties = array();
50
51if ( isset($request->xml_tags) ) {
52  /**
53  * The MKCOL request may contain XML to set some DAV properties
54  */
55  $position = 0;
56  $xmltree = BuildXMLTree( $request->xml_tags, $position);
57  if ( $xmltree->GetTag() == 'DAV::mkcol' ) $request_type = 'extended-mkcol';
58
59  if ( $xmltree->GetTag() != 'urn:ietf:params:xml:ns:caldav:mkcalendar' && $request_type != 'extended-mkcol' ) {
60    $request->DoResponse( 406, sprintf('The XML is not a "DAV::mkcol" or "urn:ietf:params:xml:ns:caldav:mkcalendar" document (%s)', $xmltree->GetTag()) );
61  }
62  $setprops = $xmltree->GetContent();   // <set>
63  $setprops = $setprops[0]->GetContent();  // <prop>
64  $setprops = $setprops[0]->GetContent();  // the array of properties.
65
66  foreach( $setprops AS $k => $setting ) {
67    $tag = $setting->GetTag();
68    $content = $setting->RenderContent();
69
70    dbg_error_log( 'MKCOL', 'Processing tag "%s"', $tag);
71
72    switch( $tag ) {
73
74      case 'DAV::resourcetype':
75        /** Any value for resourcetype other than 'calendar' is ignored */
76        dbg_error_log( 'MKCOL', 'Extended MKCOL with resourcetype specified. "%s"', $content);
77        $is_addressbook = count($setting->GetPath('DAV::resourcetype/urn:ietf:params:xml:ns:carddav:addressbook'));
78        $is_calendar    = count($setting->GetPath('DAV::resourcetype/urn:ietf:params:xml:ns:caldav:calendar'));
79        if ( $is_addressbook && $is_calendar ) {
80          $failure['set-'.$tag] = new XMLElement( 'propstat', array(
81              new XMLElement( 'prop', new XMLElement($reply->Tag($tag))),
82              new XMLElement( 'status', 'HTTP/1.1 409 Conflict' ),
83              new XMLElement('responsedescription', translate('Collections may not be both CalDAV calendars and CardDAV addressbooks at the same time') )
84          ));
85        }
86        else {
87          $resourcetypes = $setting->GetPath('DAV::resourcetype/*');
88          $resourcetypes = str_replace( "\n", "", implode('',$resourcetypes));
89          $success[$tag] = 1;
90        }
91        break;
92
93      case 'DAV::displayname':
94        $displayname = $content;
95        /**
96        * @todo This is definitely a bug in SOHO Organizer and we probably should respond
97        * with an error, rather than silently doing what they *seem* to want us to do.
98        */
99        if ( preg_match( '/^SOHO.Organizer.6\./', $_SERVER['HTTP_USER_AGENT'] ) ) {
100          dbg_error_log( 'MKCOL', 'Displayname is "/" to "%s"', $request->path);
101          $parent_container = $request->path;
102          $request->path .= $content . '/';
103        }
104        $success[$tag] = 1;
105        break;
106
107      case 'urn:ietf:params:xml:ns:caldav:supported-calendar-component-set':  /** Ignored, since we will support all component types */
108      case 'urn:ietf:params:xml:ns:caldav:supported-calendar-data':  /** Ignored, since we will support iCalendar 2.0 */
109      case 'urn:ietf:params:xml:ns:caldav:calendar-data':  /** Ignored, since we will support iCalendar 2.0 */
110      case 'urn:ietf:params:xml:ns:caldav:max-resource-size':  /** Ignored, since we will support arbitrary size */
111      case 'urn:ietf:params:xml:ns:caldav:min-date-time':  /** Ignored, since we will support arbitrary time */
112      case 'urn:ietf:params:xml:ns:caldav:max-date-time':  /** Ignored, since we will support arbitrary time */
113      case 'urn:ietf:params:xml:ns:caldav:max-instances':  /** Ignored, since we will support arbitrary instances */
114        $success[$tag] = 1;
115        break;
116
117      /**
118      * The following properties are read-only, so they will cause the request to fail
119      */
120      case 'DAV::getetag':
121      case 'DAV::getcontentlength':
122      case 'DAV::getcontenttype':
123      case 'DAV::getlastmodified':
124      case 'DAV::creationdate':
125      case 'DAV::lockdiscovery':
126      case 'DAV::supportedlock':
127        $failure['set-'.$tag] = new XMLElement( 'propstat', array(
128            new XMLElement( 'prop', new XMLElement($reply->Tag($tag))),
129            new XMLElement( 'status', 'HTTP/1.1 409 Conflict' ),
130            new XMLElement('responsedescription', translate('Property is read-only') )
131        ));
132        if ( isset($failure_code) && $failure_code != 409 ) $failure_code = 207;
133        else if ( !isset($failure_code) ) $failure_code = 409;
134        break;
135
136      /**
137      * If we don't have any special processing for the property, we just store it verbatim (which will be an XML fragment).
138      */
139      default:
140        $dav_properties[$tag] = $content;
141        $success[$tag] = 1;
142        break;
143    }
144  }
145
146  /**
147  * If we have encountered any instances of failure, the whole damn thing fails.
148  */
149  if ( count($failure) > 0 ) {
150    $props = array();
151    $status = array();
152    foreach( $success AS $tag => $v ) {
153      // Unfortunately although these succeeded, we failed overall, so they didn't happen...
154      $props[] = new XMLElement($reply->Tag($tag));
155    }
156
157    $status[] = new XMLElement( 'propstat', array(
158      new XMLElement('prop', $props),
159      new XMLElement('status', 'HTTP/1.1 424 Failed Dependency' )
160    ));
161
162    if ( $request_type == 'extended-mkcol' ) {
163      $request->DoResponse( $failure_code, $reply->Render('mkcol-response', array_merge( $status, $failure ), 'text/xml; charset="utf-8"' ) );
164    }
165    else {
166      array_unshift( $failure, $reply->href( ConstructURL($request->path) ) );
167      $failure[] = new XMLElement('responsedescription', translate('Some properties were not able to be set.') );
168
169      $request->DoResponse( 207, $reply->Render('multistatus', new XMLElement( 'response', $failure )), 'text/xml; charset="utf-8"' );
170    }
171
172  }
173}
174
175$sql = 'SELECT * FROM collection WHERE dav_name = :dav_name';
176$qry = new AwlQuery( $sql, array( ':dav_name' => $request->path) );
177if ( ! $qry->Exec('MKCOL',__LINE__,__FILE__) ) {
178  $request->DoResponse( 500, translate('Error querying database.') );
179}
180if ( $qry->rows() != 0 ) {
181  $request->DoResponse( 405, translate('A collection already exists at that location.') );
182}
183
184$qry = new AwlQuery();
185$qry->Begin();
186
187if ( ! $qry->QDo( 'INSERT INTO collection ( user_no, parent_container, dav_name, dav_etag, dav_displayname,
188                                 is_calendar, is_addressbook, resourcetypes, created, modified )
189              VALUES( :user_no, :parent_container, :dav_name, :dav_etag, :dav_displayname,
190                      :is_calendar, :is_addressbook, :resourcetypes, current_timestamp, current_timestamp )',
191           array(
192              ':user_no'          => $request->user_no,
193              ':parent_container' => $parent_container,
194              ':dav_name'         => $request->path,
195              ':dav_etag'         => md5($request->user_no. $request->path),
196              ':dav_displayname'  => $displayname,
197              ':is_calendar'      => ($is_calendar ? 't' : 'f'),
198              ':is_addressbook'   => ($is_addressbook ? 't' : 'f'),
199              ':resourcetypes'    => $resourcetypes
200           ) ) ) {
201  $request->DoResponse( 500, translate('Error writing calendar details to database.') );
202}
203foreach( $dav_properties AS $k => $v ) {
204  if ( ! $qry->QDo('SELECT set_dav_property( :dav_name, :user_no, :tag::text, :value::text )',
205             array( ':dav_name' => $request->path, ':user_no' => $request->user_no, ':tag' => $k, ':value' => $v) ) ) {
206    $request->DoResponse( 500, translate('Error writing calendar properties to database.') );
207  }
208}
209if ( !$qry->Commit() ) {
210  $request->DoResponse( 500, translate('Error writing calendar details to database.') );
211}
212dbg_error_log( 'MKCOL', 'New calendar "%s" created named "%s" for user "%d" in parent "%s"', $request->path, $displayname, $session->user_no, $parent_container);
213header('Cache-Control: no-cache'); /** RFC4791 mandates this at 5.3.1 */
214$request->DoResponse( 201, '' );
215
216/**
217* @todo We could also respond to the request...
218*
219*   <?xml version="1.0" encoding="utf-8" ?>
220*   <C:mkcalendar xmlns:D="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav">
221*     <D:set>
222*       <D:prop>
223*         <D:displayname>Lisa's Events</D:displayname>
224*         <C:calendar-description xml:lang="en">Calendar restricted to events.</C:calendar-description>
225*         <C:supported-calendar-component-set>
226*           <C:comp name="VEVENT"/>
227*         </C:supported-calendar-component-set>
228*         <C:calendar-timezone><![CDATA[BEGIN:VCALENDAR
229*   PRODID:-//Example Corp.//CalDAV Client//EN
230*   VERSION:2.0
231*   BEGIN:VTIMEZONE
232*   TZID:US-Eastern
233*   LAST-MODIFIED:19870101T000000Z
234*   BEGIN:STANDARD
235*   DTSTART:19671029T020000
236*   RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
237*   TZOFFSETFROM:-0400
238*   TZOFFSETTO:-0500
239*   TZNAME:Eastern Standard Time (US & Canada)
240*   END:STANDARD
241*   BEGIN:DAYLIGHT
242*   DTSTART:19870405T020000
243*   RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4
244*   TZOFFSETFROM:-0500
245*   TZOFFSETTO:-0400
246*   TZNAME:Eastern Daylight Time (US & Canada)
247*   END:DAYLIGHT
248*   END:VTIMEZONE
249*   END:VCALENDAR
250*   ]]></C:calendar-timezone>
251*       </D:prop>
252*     </D:set>
253*   </C:mkcalendar>
254*
255*/
256
Note: See TracBrowser for help on using the repository browser.