1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15:
16:
17: require_once("AwlCache.php");
18: require_once("XMLDocument.php");
19: require_once("DAVPrincipal.php");
20: require_once("DAVTicket.php");
21:
22: define('DEPTH_INFINITY', 9999);
23:
24:
25: 26: 27: 28: 29:
30: class CalDAVRequest
31: {
32: var $options;
33:
34: 35: 36:
37: var $raw_post;
38:
39: 40: 41:
42: var $method;
43:
44: 45: 46: 47:
48: var $depth;
49:
50: 51: 52: 53:
54: var $principal;
55:
56: 57: 58: 59:
60: var $current_user_principal_xml;
61:
62: 63: 64:
65: var $user_agent;
66:
67: 68: 69:
70: var $collection_id;
71:
72: 73: 74:
75: var $collection_path;
76:
77: 78: 79: 80:
81: var $collection_type;
82:
83: 84: 85: 86:
87: protected $exists;
88:
89: 90: 91:
92: var $destination;
93:
94: 95: 96:
97: protected $privileges;
98:
99: 100: 101:
102: var $supported_privileges;
103:
104: 105: 106:
107: public $ticket;
108:
109: 110: 111: 112:
113: private $prefer;
114:
115: 116: 117:
118: function __construct( $options = array() ) {
119: global $session, $c, $debugging;
120:
121: $this->options = $options;
122: if ( !isset($this->options['allow_by_email']) ) $this->options['allow_by_email'] = false;
123:
124: if ( isset($_SERVER['HTTP_PREFER']) ) {
125: $this->prefer = explode( ',', $_SERVER['HTTP_PREFER']);
126: }
127: else if ( isset($_SERVER['HTTP_BRIEF']) && (strtoupper($_SERVER['HTTP_BRIEF']) == 'T') ) {
128: $this->prefer = array( 'return=minimal');
129: }
130: else
131: $this->prefer = array();
132:
133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145:
146: if ( isset($_SERVER['PATH_INFO']) ) {
147: $this->path = $_SERVER['PATH_INFO'];
148: }
149: else {
150: $this->path = '/';
151: if ( isset($_SERVER['REQUEST_URI']) ) {
152: if ( preg_match( '{^(.*?\.php)([^?]*)}', $_SERVER['REQUEST_URI'], $matches ) ) {
153: $this->path = $matches[2];
154: if ( substr($this->path,0,1) != '/' )
155: $this->path = '/'.$this->path;
156: }
157: else if ( $_SERVER['REQUEST_URI'] != '/' ) {
158: dbg_error_log('LOG', 'Server is not supplying PATH_INFO and REQUEST_URI does not include a PHP program. Wildly guessing "/"!!!');
159: }
160: }
161: }
162: $this->path = rawurldecode($this->path);
163:
164:
165: if ( preg_match( '#^(/[^/]+/[^/]+).ics$#', $this->path, $matches ) ) {
166: $this->path = $matches[1]. '/';
167: }
168:
169: if ( isset($c->replace_path) && isset($c->replace_path['from']) && isset($c->replace_path['to']) ) {
170: $this->path = preg_replace($c->replace_path['from'], $c->replace_path['to'], $this->path);
171: }
172:
173:
174: $bad_chars_regex = '/[\\^\\[\\(\\\\]/';
175: if ( preg_match( $bad_chars_regex, $this->path ) ) {
176: $this->DoResponse( 400, translate("The calendar path contains illegal characters.") );
177: }
178: if ( strstr($this->path,'//') ) $this->path = preg_replace( '#//+#', '/', $this->path);
179:
180: if ( !isset($c->raw_post) ) $c->raw_post = file_get_contents( 'php://input');
181: if ( isset($_SERVER['HTTP_CONTENT_ENCODING']) ) {
182: $encoding = $_SERVER['HTTP_CONTENT_ENCODING'];
183: @dbg_error_log('caldav', 'Content-Encoding: %s', $encoding );
184: $encoding = preg_replace('{[^a-z0-9-]}i','',$encoding);
185: if ( ! ini_get('open_basedir') && (isset($c->dbg['ALL']) || isset($c->dbg['caldav'])) ) {
186: $fh = fopen('/var/log/davical/encoded_data.debug'.$encoding,'w');
187: if ( $fh ) {
188: fwrite($fh,$c->raw_post);
189: fclose($fh);
190: }
191: }
192: switch( $encoding ) {
193: case 'gzip':
194: $this->raw_post = @gzdecode($c->raw_post);
195: break;
196: case 'deflate':
197: $this->raw_post = @gzinflate($c->raw_post);
198: break;
199: case 'compress':
200: $this->raw_post = @gzuncompress($c->raw_post);
201: break;
202: default:
203: }
204: if ( empty($this->raw_post) && !empty($c->raw_post) ) {
205: $this->PreconditionFailed(415, 'content-encoding', sprintf('Unable to decode "%s" content encoding.', $_SERVER['HTTP_CONTENT_ENCODING']));
206: }
207: $c->raw_post = $this->raw_post;
208: }
209: else {
210: $this->raw_post = $c->raw_post;
211: }
212:
213: if ( isset($debugging) && isset($_GET['method']) ) {
214: $_SERVER['REQUEST_METHOD'] = $_GET['method'];
215: }
216: else if ( $_SERVER['REQUEST_METHOD'] == 'POST' && isset($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE']) ){
217: $_SERVER['REQUEST_METHOD'] = $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'];
218: }
219: $this->method = $_SERVER['REQUEST_METHOD'];
220: $this->content_type = (isset($_SERVER['CONTENT_TYPE']) ? $_SERVER['CONTENT_TYPE'] : null);
221: if ( preg_match( '{^(\S+/\S+)\s*(;.*)?$}', $this->content_type, $matches ) ) {
222: $this->content_type = $matches[1];
223: }
224: if ( strlen($c->raw_post) > 0 ) {
225: if ( $this->method == 'PROPFIND' || $this->method == 'REPORT' || $this->method == 'PROPPATCH' || $this->method == 'BIND' || $this->method == 'MKTICKET' || $this->method == 'ACL' ) {
226: if ( !preg_match( '{^(text|application)/xml$}', $this->content_type ) ) {
227: @dbg_error_log( "LOG request", 'Request is "%s" but client set content-type to "%s". Assuming they meant XML!',
228: $this->method, $this->content_type );
229: $this->content_type = 'text/xml';
230: }
231: }
232: else if ( $this->method == 'PUT' || $this->method == 'POST' ) {
233: $this->CoerceContentType();
234: }
235: }
236: else {
237: $this->content_type = 'text/plain';
238: }
239: $this->user_agent = ((isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : "Probably Mulberry"));
240:
241: 242: 243:
244: if ( isset($_SERVER['HTTP_DEPTH']) ) {
245: $this->depth = $_SERVER['HTTP_DEPTH'];
246: }
247: else {
248: 249: 250: 251: 252:
253: switch( $this->method ) {
254: case 'DELETE':
255: case 'MOVE':
256: case 'COPY':
257: case 'LOCK':
258: $this->depth = 'infinity';
259: break;
260:
261: case 'REPORT':
262: $this->depth = 0;
263: break;
264:
265: case 'PROPFIND':
266: default:
267: $this->depth = 0;
268: }
269: }
270: if ( !is_int($this->depth) && "infinity" == $this->depth ) $this->depth = DEPTH_INFINITY;
271: $this->depth = intval($this->depth);
272:
273: 274: 275:
276: if ( isset($_SERVER['HTTP_DESTINATION']) ) {
277: $this->destination = $_SERVER['HTTP_DESTINATION'];
278: if ( preg_match('{^(https?)://([a-z.-]+)(:[0-9]+)?(/.*)$}', $this->destination, $matches ) ) {
279: $this->destination = $matches[4];
280: }
281: }
282: $this->overwrite = ( isset($_SERVER['HTTP_OVERWRITE']) && ($_SERVER['HTTP_OVERWRITE'] == 'F') ? false : true );
283:
284: 285: 286:
287: if ( isset($_SERVER['HTTP_IF']) ) $this->if_clause = $_SERVER['HTTP_IF'];
288: if ( isset($_SERVER['HTTP_LOCK_TOKEN']) && preg_match( '#[<]opaquelocktoken:(.*)[>]#', $_SERVER['HTTP_LOCK_TOKEN'], $matches ) ) {
289: $this->lock_token = $matches[1];
290: }
291:
292: 293: 294:
295: if ( isset($_GET['ticket']) ) {
296: $this->ticket = new DAVTicket($_GET['ticket']);
297: }
298: else if ( isset($_SERVER['HTTP_TICKET']) ) {
299: $this->ticket = new DAVTicket($_SERVER['HTTP_TICKET']);
300: }
301:
302: 303: 304:
305: if ( isset($_SERVER['HTTP_TIMEOUT']) ) {
306: $timeouts = explode( ',', $_SERVER['HTTP_TIMEOUT'] );
307: foreach( $timeouts AS $k => $v ) {
308: if ( strtolower($v) == 'infinite' ) {
309: $this->timeout = (isset($c->maximum_lock_timeout) ? $c->maximum_lock_timeout : 86400 * 100);
310: break;
311: }
312: elseif ( strtolower(substr($v,0,7)) == 'second-' ) {
313: $this->timeout = min( intval(substr($v,7)), (isset($c->maximum_lock_timeout) ? $c->maximum_lock_timeout : 86400 * 100) );
314: break;
315: }
316: }
317: if ( ! isset($this->timeout) || $this->timeout == 0 ) $this->timeout = (isset($c->default_lock_timeout) ? $c->default_lock_timeout : 900);
318: }
319:
320: $this->principal = new Principal('path',$this->path);
321:
322: 323: 324: 325: 326: 327: 328: 329: 330: 331: 332:
333: $sql = "SELECT * FROM collection WHERE dav_name = :exact_name";
334: $params = array( ':exact_name' => $this->path );
335: if ( !preg_match( '#/$#', $this->path ) ) {
336: $sql .= " OR dav_name = :truncated_name OR dav_name = :trailing_slash_name";
337: $params[':truncated_name'] = preg_replace( '#[^/]*$#', '', $this->path);
338: $params[':trailing_slash_name'] = $this->path."/";
339: }
340: $sql .= " ORDER BY LENGTH(dav_name) DESC LIMIT 1";
341: $qry = new AwlQuery( $sql, $params );
342: if ( $qry->Exec('caldav',__LINE__,__FILE__) && $qry->rows() == 1 && ($row = $qry->Fetch()) ) {
343: if ( $row->dav_name == $this->path."/" ) {
344: $this->path = $row->dav_name;
345: dbg_error_log( "caldav", "Path is actually a collection - sending Content-Location header." );
346: header( "Content-Location: ".ConstructURL($this->path) );
347: }
348:
349: $this->collection_id = $row->collection_id;
350: $this->collection_path = $row->dav_name;
351: $this->collection_type = ($row->is_calendar == 't' ? 'calendar' : 'collection');
352: $this->collection = $row;
353: if ( preg_match( '#^((/[^/]+/)\.(in|out)/)[^/]*$#', $this->path, $matches ) ) {
354: $this->collection_type = 'schedule-'. $matches[3]. 'box';
355: }
356: $this->collection->type = $this->collection_type;
357: }
358: else if ( preg_match( '{^( ( / ([^/]+) / ) \.(in|out)/ ) [^/]*$}x', $this->path, $matches ) ) {
359:
360: $params = array( ':username' => $matches[3], ':parent_container' => $matches[2], ':dav_name' => $matches[1] );
361: $params[':boxname'] = ($matches[4] == 'in' ? ' Inbox' : ' Outbox');
362: $this->collection_type = 'schedule-'. $matches[4]. 'box';
363: $params[':resourcetypes'] = sprintf('<DAV::collection/><urn:ietf:params:xml:ns:caldav:%s/>', $this->collection_type );
364: $sql = <<<EOSQL
365: INSERT INTO collection ( user_no, parent_container, dav_name, dav_displayname, is_calendar, created, modified, dav_etag, resourcetypes )
366: VALUES( (SELECT user_no FROM usr WHERE username = text(:username)),
367: :parent_container, :dav_name,
368: (SELECT fullname FROM usr WHERE username = text(:username)) || :boxname,
369: FALSE, current_timestamp, current_timestamp, '1', :resourcetypes )
370: EOSQL;
371:
372: $qry = new AwlQuery( $sql, $params );
373: $qry->Exec('caldav',__LINE__,__FILE__);
374: dbg_error_log( 'caldav', 'Created new collection as "%s".', trim($params[':boxname']) );
375:
376:
377: $cache = getCacheInstance();
378: $cache->delete( 'collection-'.$params[':dav_name'], null );
379: $cache->delete( 'principal-'.$params[':parent_container'], null );
380:
381: $qry = new AwlQuery( "SELECT * FROM collection WHERE dav_name = :dav_name", array( ':dav_name' => $matches[1] ) );
382: if ( $qry->Exec('caldav',__LINE__,__FILE__) && $qry->rows() == 1 && ($row = $qry->Fetch()) ) {
383: $this->collection_id = $row->collection_id;
384: $this->collection_path = $matches[1];
385: $this->collection = $row;
386: $this->collection->type = $this->collection_type;
387: }
388: }
389: else if ( preg_match( '#^((/[^/]+/)calendar-proxy-(read|write))/?[^/]*$#', $this->path, $matches ) ) {
390: $this->collection_type = 'proxy';
391: $this->_is_proxy_request = true;
392: $this->proxy_type = $matches[3];
393: $this->collection_path = $matches[1].'/';
394: if ( $this->collection_path == $this->path."/" ) {
395: $this->path .= '/';
396: dbg_error_log( "caldav", "Path is actually a (proxy) collection - sending Content-Location header." );
397: header( "Content-Location: ".ConstructURL($this->path) );
398: }
399: }
400: else if ( $this->options['allow_by_email'] && preg_match( '#^/(\S+@\S+[.]\S+)/?$#', $this->path) ) {
401:
402: $this->collection_id = -1;
403: $this->collection_type = 'email';
404: $this->collection_path = $this->path;
405: $this->_is_principal = true;
406: }
407: else if ( preg_match( '#^(/[^/?]+)/?$#', $this->path, $matches) || preg_match( '#^(/principals/[^/]+/[^/]+)/?$#', $this->path, $matches) ) {
408: $this->collection_id = -1;
409: $this->collection_path = $matches[1].'/';
410: $this->collection_type = 'principal';
411: $this->_is_principal = true;
412: if ( $this->collection_path == $this->path."/" ) {
413: $this->path .= '/';
414: dbg_error_log( "caldav", "Path is actually a collection - sending Content-Location header." );
415: header( "Content-Location: ".ConstructURL($this->path) );
416: }
417: if ( preg_match( '#^(/principals/[^/]+/[^/]+)/?$#', $this->path, $matches) ) {
418:
419: $this->depth = 0;
420: }
421: }
422: else if ( $this->path == '/' ) {
423: $this->collection_id = -1;
424: $this->collection_path = '/';
425: $this->collection_type = 'root';
426: }
427:
428: if ( $this->collection_path == $this->path ) $this->_is_collection = true;
429: dbg_error_log( "caldav", " Collection '%s' is %d, type %s", $this->collection_path, $this->collection_id, $this->collection_type );
430:
431: 432: 433:
434: $this->principal = new DAVPrincipal( array( "path" => $this->path, "options" => $this->options ) );
435: $this->user_no = $this->principal->user_no();
436: $this->username = $this->principal->username();
437: $this->by_email = $this->principal->byEmail();
438: $this->principal_id = $this->principal->principal_id();
439:
440: if ( $this->collection_type == 'principal' || $this->collection_type == 'email' || $this->collection_type == 'proxy' ) {
441: $this->collection = $this->principal->AsCollection();
442: if( $this->collection_type == 'proxy' ) {
443: $this->collection->is_proxy = 't';
444: $this->collection->type = 'proxy';
445: $this->collection->proxy_type = $this->proxy_type;
446: $this->collection->dav_displayname = sprintf('Proxy %s for %s', $this->proxy_type, $this->principal->username() );
447: }
448: }
449: elseif( $this->collection_type == 'root' ) {
450: $this->collection = (object) array(
451: 'collection_id' => 0,
452: 'dav_name' => '/',
453: 'dav_etag' => md5($c->system_name),
454: 'is_calendar' => 'f',
455: 'is_addressbook' => 'f',
456: 'is_principal' => 'f',
457: 'user_no' => 0,
458: 'dav_displayname' => $c->system_name,
459: 'type' => 'root',
460: 'created' => date('Ymd\THis')
461: );
462: }
463:
464: 465: 466:
467: $this->setPermissions();
468:
469: $this->supported_methods = array(
470: 'OPTIONS' => '',
471: 'PROPFIND' => '',
472: 'REPORT' => '',
473: 'DELETE' => '',
474: 'LOCK' => '',
475: 'UNLOCK' => '',
476: 'MOVE' => '',
477: 'ACL' => ''
478: );
479: if ( $this->IsCollection() ) {
480: switch ( $this->collection_type ) {
481: case 'root':
482: case 'email':
483:
484: $this->supported_methods = array(
485: 'OPTIONS' => '',
486: 'PROPFIND' => '',
487: 'REPORT' => ''
488: );
489: break;
490: case 'schedule-inbox':
491: case 'schedule-outbox':
492: $this->supported_methods = array_merge(
493: $this->supported_methods,
494: array(
495: 'POST' => '', 'GET' => '', 'PUT' => '', 'HEAD' => '', 'PROPPATCH' => ''
496: )
497: );
498: break;
499: case 'calendar':
500: $this->supported_methods['GET'] = '';
501: $this->supported_methods['PUT'] = '';
502: $this->supported_methods['HEAD'] = '';
503: break;
504: case 'collection':
505: case 'principal':
506: $this->supported_methods['GET'] = '';
507: $this->supported_methods['PUT'] = '';
508: $this->supported_methods['HEAD'] = '';
509: $this->supported_methods['MKCOL'] = '';
510: $this->supported_methods['MKCALENDAR'] = '';
511: $this->supported_methods['PROPPATCH'] = '';
512: $this->supported_methods['BIND'] = '';
513: break;
514: }
515: }
516: else {
517: $this->supported_methods = array_merge(
518: $this->supported_methods,
519: array(
520: 'GET' => '',
521: 'HEAD' => '',
522: 'PUT' => ''
523: )
524: );
525: }
526:
527: $this->supported_reports = array(
528: 'DAV::principal-property-search' => '',
529: 'DAV::expand-property' => '',
530: 'DAV::sync-collection' => ''
531: );
532: if ( isset($this->collection) && $this->collection->is_calendar ) {
533: $this->supported_reports = array_merge(
534: $this->supported_reports,
535: array(
536: 'urn:ietf:params:xml:ns:caldav:calendar-query' => '',
537: 'urn:ietf:params:xml:ns:caldav:calendar-multiget' => '',
538: 'urn:ietf:params:xml:ns:caldav:free-busy-query' => ''
539: )
540: );
541: }
542: if ( isset($this->collection) && $this->collection->is_addressbook ) {
543: $this->supported_reports = array_merge(
544: $this->supported_reports,
545: array(
546: 'urn:ietf:params:xml:ns:carddav:addressbook-query' => '',
547: 'urn:ietf:params:xml:ns:carddav:addressbook-multiget' => ''
548: )
549: );
550: }
551:
552:
553: 554: 555: 556:
557: if ( isset($this->content_type) && preg_match( '#(application|text)/xml#', $this->content_type ) ) {
558: if ( !isset($this->raw_post) || $this->raw_post == '' ) {
559: $this->XMLResponse( 400, new XMLElement( 'error', new XMLElement('missing-xml'), array( 'xmlns' => 'DAV:') ) );
560: }
561: $xml_parser = xml_parser_create_ns('UTF-8');
562: $this->xml_tags = array();
563: xml_parser_set_option ( $xml_parser, XML_OPTION_SKIP_WHITE, 1 );
564: xml_parser_set_option ( $xml_parser, XML_OPTION_CASE_FOLDING, 0 );
565: $rc = xml_parse_into_struct( $xml_parser, $this->raw_post, $this->xml_tags );
566: if ( $rc == false ) {
567: dbg_error_log( 'ERROR', 'XML parsing error: %s at line %d, column %d',
568: xml_error_string(xml_get_error_code($xml_parser)),
569: xml_get_current_line_number($xml_parser), xml_get_current_column_number($xml_parser) );
570: $this->XMLResponse( 400, new XMLElement( 'error', new XMLElement('invalid-xml'), array( 'xmlns' => 'DAV:') ) );
571: }
572: xml_parser_free($xml_parser);
573: if ( count($this->xml_tags) ) {
574: dbg_error_log( "caldav", " Parsed incoming XML request body." );
575: }
576: else {
577: $this->xml_tags = null;
578: dbg_error_log( "ERROR", "Incoming request sent content-type XML with no XML request body." );
579: }
580: }
581:
582: 583: 584:
585: if ( isset($_SERVER["HTTP_IF_NONE_MATCH"]) ) {
586: $this->etag_none_match = $_SERVER["HTTP_IF_NONE_MATCH"];
587: if ( $this->etag_none_match == '' ) unset($this->etag_none_match);
588: }
589: if ( isset($_SERVER["HTTP_IF_MATCH"]) ) {
590: $this->etag_if_match = $_SERVER["HTTP_IF_MATCH"];
591: if ( $this->etag_if_match == '' ) unset($this->etag_if_match);
592: }
593: }
594:
595:
596: 597: 598: 599: 600: 601: 602: 603: 604: 605: 606: 607:
608: function setPermissions() {
609: global $c, $session;
610:
611: if ( $this->path == '/' || $this->path == '' ) {
612: $this->privileges = privilege_to_bits( array('read','read-free-busy','read-acl'));
613: dbg_error_log( "caldav", "Full read permissions for user accessing /" );
614: }
615: else if ( $session->AllowedTo("Admin") || $session->principal->user_no() == $this->user_no ) {
616: $this->privileges = privilege_to_bits('all');
617: dbg_error_log( "caldav", "Full permissions for %s", ( $session->principal->user_no() == $this->user_no ? "user accessing their own hierarchy" : "a systems administrator") );
618: }
619: else {
620: $this->privileges = 0;
621: if ( $this->IsPublic() ) {
622: $this->privileges = privilege_to_bits(array('read','read-free-busy'));
623: dbg_error_log( "caldav", "Basic read permissions for user accessing a public collection" );
624: }
625: else if ( isset($c->public_freebusy_url) && $c->public_freebusy_url ) {
626: $this->privileges = privilege_to_bits('read-free-busy');
627: dbg_error_log( "caldav", "Basic free/busy permissions for user accessing a public free/busy URL" );
628: }
629:
630: 631: 632:
633: $params = array( ':session_principal_id' => $session->principal->principal_id(), ':scan_depth' => $c->permission_scan_depth );
634: if ( isset($this->by_email) && $this->by_email ) {
635: $sql ='SELECT pprivs( :session_principal_id::int8, :request_principal_id::int8, :scan_depth::int ) AS perm';
636: $params[':request_principal_id'] = $this->principal_id;
637: }
638: else {
639: $sql = 'SELECT path_privs( :session_principal_id::int8, :request_path::text, :scan_depth::int ) AS perm';
640: $params[':request_path'] = $this->path;
641: }
642: $qry = new AwlQuery( $sql, $params );
643: if ( $qry->Exec('caldav',__LINE__,__FILE__) && $permission_result = $qry->Fetch() )
644: $this->privileges |= bindec($permission_result->perm);
645:
646: dbg_error_log( 'caldav', 'Restricted permissions for user accessing someone elses hierarchy: %s', decbin($this->privileges) );
647: if ( isset($this->ticket) && $this->ticket->MatchesPath($this->path) ) {
648: $this->privileges |= $this->ticket->privileges();
649: dbg_error_log( 'caldav', 'Applying permissions for ticket "%s" now: %s', $this->ticket->id(), decbin($this->privileges) );
650: }
651: }
652:
653:
654: $this->permissions = array();
655: $privs = bits_to_privilege($this->privileges);
656: foreach( $privs AS $k => $v ) {
657: switch( $v ) {
658: case 'DAV::all': $type = 'abstract'; break;
659: case 'DAV::write': $type = 'aggregate'; break;
660: default: $type = 'real';
661: }
662: $v = str_replace('DAV::', '', $v);
663: $this->permissions[$v] = $type;
664: }
665:
666: }
667:
668:
669: 670: 671: 672: 673: 674: 675:
676: function IsLocked() {
677: if ( !isset($this->_locks_found) ) {
678: $this->_locks_found = array();
679:
680: $sql = 'DELETE FROM locks WHERE (start + timeout) < current_timestamp';
681: $qry = new AwlQuery($sql);
682: $qry->Exec('caldav',__LINE__,__FILE__);
683:
684: 685: 686:
687: $sql = 'SELECT * FROM locks WHERE :dav_name::text ~ (\'^\'||dav_name||:pattern_end_match)::text';
688: $qry = new AwlQuery($sql, array( ':dav_name' => $this->path, ':pattern_end_match' => ($this->IsInfiniteDepth() ? '' : '$') ) );
689: if ( $qry->Exec('caldav',__LINE__,__FILE__) ) {
690: while( $lock_row = $qry->Fetch() ) {
691: $this->_locks_found[$lock_row->opaquelocktoken] = $lock_row;
692: }
693: }
694: else {
695: $this->DoResponse(500,translate("Database Error"));
696:
697: }
698: }
699:
700: foreach( $this->_locks_found AS $lock_token => $lock_row ) {
701: if ( $lock_row->depth == DEPTH_INFINITY || $lock_row->dav_name == $this->path ) {
702: return $lock_token;
703: }
704: }
705:
706: return false;
707: }
708:
709:
710: 711: 712:
713: function IsPublic() {
714: if ( isset($this->collection) && isset($this->collection->publicly_readable) && $this->collection->publicly_readable == 't' ) {
715: return true;
716: }
717: return false;
718: }
719:
720:
721: private static function supportedPrivileges() {
722: return array(
723: 'all' => array(
724: 'read' => translate('Read the content of a resource or collection'),
725: 'write' => array(
726: 'bind' => translate('Create a resource or collection'),
727: 'unbind' => translate('Delete a resource or collection'),
728: 'write-content' => translate('Write content'),
729: 'write-properties' => translate('Write properties')
730: ),
731: 'urn:ietf:params:xml:ns:caldav:read-free-busy' => translate('Read the free/busy information for a calendar collection'),
732: 'read-acl' => translate('Read ACLs for a resource or collection'),
733: 'read-current-user-privilege-set' => translate('Read the details of the current user\'s access control to this resource.'),
734: 'write-acl' => translate('Write ACLs for a resource or collection'),
735: 'unlock' => translate('Remove a lock'),
736:
737: 'urn:ietf:params:xml:ns:caldav:schedule-deliver' => array(
738: 'urn:ietf:params:xml:ns:caldav:schedule-deliver-invite'=> translate('Deliver scheduling invitations from an organiser to this scheduling inbox'),
739: 'urn:ietf:params:xml:ns:caldav:schedule-deliver-reply' => translate('Deliver scheduling replies from an attendee to this scheduling inbox'),
740: 'urn:ietf:params:xml:ns:caldav:schedule-query-freebusy' => translate('Allow free/busy enquiries targeted at the owner of this scheduling inbox')
741: ),
742:
743: 'urn:ietf:params:xml:ns:caldav:schedule-send' => array(
744: 'urn:ietf:params:xml:ns:caldav:schedule-send-invite' => translate('Send scheduling invitations as an organiser from the owner of this scheduling outbox.'),
745: 'urn:ietf:params:xml:ns:caldav:schedule-send-reply' => translate('Send scheduling replies as an attendee from the owner of this scheduling outbox.'),
746: 'urn:ietf:params:xml:ns:caldav:schedule-send-freebusy' => translate('Send free/busy enquiries')
747: )
748: )
749: );
750: }
751:
752: 753: 754:
755: function dav_name() {
756: if ( isset($this->path) ) return $this->path;
757: return null;
758: }
759:
760:
761: 762: 763:
764: function GetDepthName( ) {
765: if ( $this->IsInfiniteDepth() ) return 'infinity';
766: return $this->depth;
767: }
768:
769: 770: 771: 772:
773: function DepthRegexTail( $for_collection_report = false) {
774: if ( $this->IsInfiniteDepth() ) return '';
775: if ( $this->depth == 0 && $for_collection_report ) return '[^/]+$';
776: if ( $this->depth == 0 ) return '$';
777: return '[^/]*/?$';
778: }
779:
780: 781: 782: 783: 784:
785: function GetLockRow( $lock_token ) {
786: if ( isset($this->_locks_found) && isset($this->_locks_found[$lock_token]) ) {
787: return $this->_locks_found[$lock_token];
788: }
789:
790: $qry = new AwlQuery('SELECT * FROM locks WHERE opaquelocktoken = :lock_token', array( ':lock_token' => $lock_token ) );
791: if ( $qry->Exec('caldav',__LINE__,__FILE__) ) {
792: $lock_row = $qry->Fetch();
793: $this->_locks_found = array( $lock_token => $lock_row );
794: return $this->_locks_found[$lock_token];
795: }
796: else {
797: $this->DoResponse( 500, translate("Database Error") );
798: }
799:
800: return false;
801: }
802:
803:
804: 805: 806: 807: 808: 809:
810: function ValidateLockToken( $lock_token ) {
811: if ( isset($this->lock_token) && $this->lock_token == $lock_token ) {
812: dbg_error_log( "caldav", "They supplied a valid lock token. Great!" );
813: return true;
814: }
815: if ( isset($this->if_clause) ) {
816: dbg_error_log( "caldav", "Checking lock token '%s' against '%s'", $lock_token, $this->if_clause );
817: $tokens = preg_split( '/[<>]/', $this->if_clause );
818: foreach( $tokens AS $k => $v ) {
819: dbg_error_log( "caldav", "Checking lock token '%s' against '%s'", $lock_token, $v );
820: if ( 'opaquelocktoken:' == substr( $v, 0, 16 ) ) {
821: if ( substr( $v, 16 ) == $lock_token ) {
822: dbg_error_log( "caldav", "Lock token '%s' validated OK against '%s'", $lock_token, $v );
823: return true;
824: }
825: }
826: }
827: }
828: else {
829: @dbg_error_log( "caldav", "Invalid lock token '%s' - not in Lock-token (%s) or If headers (%s) ", $lock_token, $this->lock_token, $this->if_clause );
830: }
831:
832: return false;
833: }
834:
835:
836: 837: 838: 839: 840:
841: function GetLockDetails( $lock_token ) {
842: if ( !isset($this->_locks_found) && false === $this->IsLocked() ) return false;
843: if ( isset($this->_locks_found[$lock_token]) ) return $this->_locks_found[$lock_token];
844: return false;
845: }
846:
847:
848: 849: 850: 851: 852: 853: 854:
855: function FailIfLocked() {
856: if ( $existing_lock = $this->IsLocked() ) {
857: dbg_error_log( "caldav", "There is a lock on '%s'", $this->path);
858: if ( ! $this->ValidateLockToken($existing_lock) ) {
859: $lock_row = $this->GetLockRow($existing_lock);
860: 861: 862:
863: $response[] = new XMLElement( 'response', array(
864: new XMLElement( 'href', $lock_row->dav_name ),
865: new XMLElement( 'status', 'HTTP/1.1 423 Resource Locked')
866: ));
867: if ( $lock_row->dav_name != $this->path ) {
868: $response[] = new XMLElement( 'response', array(
869: new XMLElement( 'href', $this->path ),
870: new XMLElement( 'propstat', array(
871: new XMLElement( 'prop', new XMLElement( 'lockdiscovery' ) ),
872: new XMLElement( 'status', 'HTTP/1.1 424 Failed Dependency')
873: ))
874: ));
875: }
876: $response = new XMLElement( "multistatus", $response, array('xmlns'=>'DAV:') );
877: $xmldoc = $response->Render(0,'<?xml version="1.0" encoding="utf-8" ?>');
878: $this->DoResponse( 207, $xmldoc, 'text/xml; charset="utf-8"' );
879:
880: }
881: return $existing_lock;
882: }
883: return false;
884: }
885:
886:
887: 888: 889:
890: function CoerceContentType() {
891: if ( isset($this->content_type) ) {
892: $type = explode( '/', $this->content_type, 2);
893:
894: if ( $type[0] == 'text' ) {
895: if ( !empty($type[1]) && ($type[1] == 'vcard' || $type[1] == 'calendar' || $type[1] == 'x-vcard') ) {
896: return;
897: }
898: }
899: }
900:
901:
902: $first_word = trim(substr( $this->raw_post, 0, 30));
903: $first_word = strtoupper( preg_replace( '/\s.*/s', '', $first_word ) );
904: switch( $first_word ) {
905: case '<?XML':
906: dbg_error_log( 'LOG WARNING', 'Application sent content-type of "%s" instead of "text/xml"',
907: (isset($this->content_type)?$this->content_type:'(null)') );
908: $this->content_type = 'text/xml';
909: break;
910: case 'BEGIN:VCALENDAR':
911: dbg_error_log( 'LOG WARNING', 'Application sent content-type of "%s" instead of "text/calendar"',
912: (isset($this->content_type)?$this->content_type:'(null)') );
913: $this->content_type = 'text/calendar';
914: break;
915: case 'BEGIN:VCARD':
916: dbg_error_log( 'LOG WARNING', 'Application sent content-type of "%s" instead of "text/vcard"',
917: (isset($this->content_type)?$this->content_type:'(null)') );
918: $this->content_type = 'text/vcard';
919: break;
920: default:
921: dbg_error_log( 'LOG NOTICE', 'Unusual content-type of "%s" and first word of content is "%s"',
922: (isset($this->content_type)?$this->content_type:'(null)'), $first_word );
923: }
924: if ( empty($this->content_type) ) $this->content_type = 'text/plain';
925: }
926:
927:
928: 929: 930:
931: function PreferMinimal() {
932: if ( empty($this->prefer) ) return false;
933: foreach( $this->prefer AS $v ) {
934: if ( $v == 'return=minimal' ) return true;
935: if ( $v == 'return-minimal' ) return true;
936: }
937: return false;
938: }
939:
940: 941: 942:
943: function IsCollection( ) {
944: if ( !isset($this->_is_collection) ) {
945: $this->_is_collection = preg_match( '#/$#', $this->path );
946: }
947: return $this->_is_collection;
948: }
949:
950:
951: 952: 953:
954: function IsCalendar( ) {
955: if ( !$this->IsCollection() || !isset($this->collection) ) return false;
956: return $this->collection->is_calendar == 't';
957: }
958:
959:
960: 961: 962:
963: function IsAddressBook( ) {
964: if ( !$this->IsCollection() || !isset($this->collection) ) return false;
965: return $this->collection->is_addressbook == 't';
966: }
967:
968:
969: 970: 971:
972: function IsPrincipal( ) {
973: if ( !isset($this->_is_principal) ) {
974: $this->_is_principal = preg_match( '#^/[^/]+/$#', $this->path );
975: }
976: return $this->_is_principal;
977: }
978:
979:
980: 981: 982:
983: function IsProxyRequest( ) {
984: if ( !isset($this->_is_proxy_request) ) {
985: $this->_is_proxy_request = preg_match( '#^/[^/]+/calendar-proxy-(read|write)/?[^/]*$#', $this->path );
986: }
987: return $this->_is_proxy_request;
988: }
989:
990:
991: 992: 993:
994: function IsInfiniteDepth( ) {
995: return ($this->depth == DEPTH_INFINITY);
996: }
997:
998:
999: 1000: 1001:
1002: function CollectionId( ) {
1003: return $this->collection_id;
1004: }
1005:
1006:
1007: 1008: 1009:
1010: function BuildSupportedPrivileges( &$reply, $privs = null ) {
1011: $privileges = array();
1012: if ( $privs === null ) $privs = self::supportedPrivileges();
1013: foreach( $privs AS $k => $v ) {
1014: dbg_error_log( 'caldav', 'Adding privilege "%s" which is "%s".', $k, $v );
1015: $privilege = new XMLElement('privilege');
1016: $reply->NSElement($privilege,$k);
1017: $privset = array($privilege);
1018: if ( is_array($v) ) {
1019: dbg_error_log( 'caldav', '"%s" is a container of sub-privileges.', $k );
1020: $privset = array_merge($privset, $this->BuildSupportedPrivileges($reply,$v));
1021: }
1022: else if ( $v == 'abstract' ) {
1023: dbg_error_log( 'caldav', '"%s" is an abstract privilege.', $v );
1024: $privset[] = new XMLElement('abstract');
1025: }
1026: else if ( strlen($v) > 1 ) {
1027: $privset[] = new XMLElement('description', $v);
1028: }
1029: $privileges[] = new XMLElement('supported-privilege',$privset);
1030: }
1031: return $privileges;
1032: }
1033:
1034:
1035: 1036: 1037: 1038: 1039: 1040: 1041: 1042: 1043: 1044: 1045: 1046: 1047:
1048: function AllowedTo( $activity ) {
1049: global $session;
1050: dbg_error_log('caldav', 'Checking whether "%s" is allowed to "%s"', $session->principal->username(), $activity);
1051: if ( isset($this->permissions['all']) ) return true;
1052: switch( $activity ) {
1053: case 'all':
1054: return false;
1055: break;
1056:
1057: case "CALDAV:schedule-send-freebusy":
1058: return isset($this->permissions['read']) || isset($this->permissions['urn:ietf:params:xml:ns:caldav:read-free-busy']);
1059: break;
1060:
1061: case "CALDAV:schedule-send-invite":
1062: return isset($this->permissions['read']) || isset($this->permissions['urn:ietf:params:xml:ns:caldav:read-free-busy']);
1063: break;
1064:
1065: case "CALDAV:schedule-send-reply":
1066: return isset($this->permissions['read']) || isset($this->permissions['urn:ietf:params:xml:ns:caldav:read-free-busy']);
1067: break;
1068:
1069: case 'freebusy':
1070: return isset($this->permissions['read']) || isset($this->permissions['urn:ietf:params:xml:ns:caldav:read-free-busy']);
1071: break;
1072:
1073: case 'delete':
1074: return isset($this->permissions['write']) || isset($this->permissions['unbind']);
1075: break;
1076:
1077: case 'proppatch':
1078: return isset($this->permissions['write']) || isset($this->permissions['write-properties']);
1079: break;
1080:
1081: case 'modify':
1082: return isset($this->permissions['write']) || isset($this->permissions['write-content']);
1083: break;
1084:
1085: case 'create':
1086: return isset($this->permissions['write']) || isset($this->permissions['bind']);
1087: break;
1088:
1089: case 'mkcalendar':
1090: case 'mkcol':
1091: if ( !isset($this->permissions['write']) || !isset($this->permissions['bind']) ) return false;
1092: if ( $this->is_principal ) return false;
1093: if ( $this->path == '/' ) return false;
1094: break;
1095:
1096: default:
1097: $test_bits = privilege_to_bits( $activity );
1098:
1099:
1100:
1101: return (($this->privileges & $test_bits) > 0 );
1102: break;
1103: }
1104:
1105: return false;
1106: }
1107:
1108:
1109:
1110: 1111: 1112:
1113: function Privileges() {
1114: return $this->privileges;
1115: }
1116:
1117:
1118: 1119: 1120: 1121: 1122: 1123:
1124: function CheckEtagMatch( $exists, $dest_etag ) {
1125: global $c;
1126:
1127: if ( ! $exists ) {
1128: if ( (isset($this->etag_if_match) && $this->etag_if_match != '') ) {
1129: 1130: 1131: 1132: 1133: 1134:
1135: $this->PreconditionFailed(412, 'if-match', translate('No resource exists at the destination.'));
1136: }
1137: }
1138: else {
1139:
1140: if ( isset($c->strict_etag_checking) && $c->strict_etag_checking )
1141: $trim_chars = '\'\\" ';
1142: else
1143: $trim_chars = ' ';
1144:
1145: if ( isset($this->etag_if_match) && $this->etag_if_match != '' && trim( $this->etag_if_match, $trim_chars) != trim( $dest_etag, $trim_chars ) ) {
1146: 1147: 1148: 1149: 1150: 1151:
1152: $this->PreconditionFailed(412,'if-match',sprintf('Existing resource ETag of <<%s>> does not match <<%s>>', $dest_etag, $this->etag_if_match) );
1153: }
1154: else if ( isset($this->etag_none_match) && $this->etag_none_match != ''
1155: && ($this->etag_none_match == $dest_etag || $this->etag_none_match == '*') ) {
1156: 1157: 1158: 1159: 1160: 1161: 1162: 1163:
1164: $this->PreconditionFailed(412,'if-none-match', translate( 'Existing resource matches "If-None-Match" header - not accepted.'));
1165: }
1166: }
1167:
1168: }
1169:
1170:
1171: 1172: 1173:
1174: function HavePrivilegeTo( $do_what ) {
1175: $test_bits = privilege_to_bits( $do_what );
1176:
1177:
1178:
1179: return ($this->privileges & $test_bits) > 0;
1180: }
1181:
1182:
1183: 1184: 1185: 1186:
1187: function UnsupportedRequest( $unsupported ) {
1188: if ( isset($unsupported) && count($unsupported) > 0 ) {
1189: $badprops = new XMLElement( "prop" );
1190: foreach( $unsupported AS $k => $v ) {
1191:
1192: dbg_error_log("ERROR", " %s: Support for $v:$k properties is not implemented yet", $this->method );
1193: $badprops->NewElement(strtolower($k),false,array("xmlns" => strtolower($v)));
1194: }
1195: $error = new XMLElement("error", $badprops, array("xmlns" => "DAV:") );
1196:
1197: $this->XMLResponse( 422, $error );
1198: }
1199: }
1200:
1201:
1202: 1203: 1204: 1205: 1206: 1207: 1208: 1209:
1210: function NeedPrivilege( $privileges, $href=null ) {
1211: if ( is_string($privileges) ) $privileges = array( $privileges );
1212: if ( !isset($href) ) {
1213: if ( $this->HavePrivilegeTo($privileges) ) return;
1214: $href = $this->path;
1215: }
1216:
1217: $reply = new XMLDocument( array('DAV:' => '') );
1218: $privnodes = array( $reply->href(ConstructURL($href)), new XMLElement( 'privilege' ) );
1219:
1220: $reply->NSElement( $privnodes[1], $privileges[0] );
1221: $xml = new XMLElement( 'need-privileges', new XMLElement( 'resource', $privnodes) );
1222: $xmldoc = $reply->Render('error',$xml);
1223: $this->DoResponse( 403, $xmldoc, 'text/xml; charset="utf-8"' );
1224: exit(0);
1225: }
1226:
1227:
1228: 1229: 1230: 1231: 1232: 1233: 1234:
1235: function PreconditionFailed( $status, $precondition, $explanation = '', $xmlns='DAV:') {
1236: $xmldoc = sprintf('<?xml version="1.0" encoding="utf-8" ?>
1237: <error xmlns="%s">
1238: <%s/>%s
1239: </error>', $xmlns, str_replace($xmlns.':', '', $precondition), $explanation );
1240:
1241: $this->DoResponse( $status, $xmldoc, 'text/xml; charset="utf-8"' );
1242: exit(0);
1243: }
1244:
1245:
1246: 1247: 1248: 1249: 1250:
1251: function MalformedRequest( $text = 'Bad request' ) {
1252: $this->DoResponse( 400, $text );
1253: exit(0);
1254: }
1255:
1256:
1257: 1258: 1259: 1260: 1261: 1262:
1263: function XMLResponse( $status, $xmltree ) {
1264: $xmldoc = $xmltree->Render(0,'<?xml version="1.0" encoding="utf-8" ?>');
1265: $etag = md5($xmldoc);
1266: if ( !headers_sent() ) header("ETag: \"$etag\"");
1267: $this->DoResponse( $status, $xmldoc, 'text/xml; charset="utf-8"' );
1268: exit(0);
1269: }
1270:
1271: public static function kill_on_exit() {
1272: posix_kill( getmypid(), 28 );
1273: }
1274:
1275: 1276: 1277: 1278: 1279: 1280: 1281:
1282: function DoResponse( $status, $message="", $content_type="text/plain; charset=\"utf-8\"" ) {
1283: global $session, $c;
1284: if ( !headers_sent() ) @header( sprintf("HTTP/1.1 %d %s", $status, getStatusMessage($status)) );
1285: if ( !headers_sent() ) @header( sprintf("X-DAViCal-Version: DAViCal/%d.%d.%d; DB/%d.%d.%d", $c->code_major, $c->code_minor, $c->code_patch, $c->schema_major, $c->schema_minor, $c->schema_patch) );
1286: if ( !headers_sent() ) header( "Content-type: ".$content_type );
1287:
1288: if ( (isset($c->dbg['ALL']) && $c->dbg['ALL']) || (isset($c->dbg['response']) && $c->dbg['response'])
1289: || $status == 400 || $status == 402 || $status == 403 || $status > 404 ) {
1290: @dbg_error_log( "LOG ", 'Response status %03d for %s %s', $status, $this->method, $_SERVER['REQUEST_URI'] );
1291: $lines = headers_list();
1292: dbg_error_log( "LOG ", "***************** Response Header ****************" );
1293: foreach( $lines AS $v ) {
1294: dbg_error_log( "LOG headers", "-->%s", $v );
1295: }
1296: dbg_error_log( "LOG ", "******************** Response ********************" );
1297:
1298: $lines = preg_split( '#[\r\n]+#', $message);
1299: foreach( $lines AS $v ) {
1300: dbg_error_log( "LOG response", "-->%s", $v );
1301: }
1302: }
1303:
1304: $script_finish = microtime(true);
1305: $script_time = $script_finish - $c->script_start_time;
1306: $message_length = strlen($message);
1307: if ( $message != '' ) {
1308: if ( !headers_sent() ) header( "Content-Length: ".$message_length );
1309: echo $message;
1310: }
1311:
1312: if ( isset($c->dbg['caldav']) && $c->dbg['caldav'] ) {
1313: if ( $message_length > 100 || strstr($message, "\n") ) {
1314: $message = substr( preg_replace("#\s+#m", ' ', $message ), 0, 100) . ($message_length > 100 ? "..." : "");
1315: }
1316:
1317: dbg_error_log("caldav", "Status: %d, Message: %s, User: %d, Path: %s", $status, $message, $session->principal->user_no(), $this->path);
1318: }
1319: if ( isset($c->dbg['statistics']) && $c->dbg['statistics'] ) {
1320: $memory = '';
1321: if ( function_exists('memory_get_usage') ) {
1322: $memory = sprintf( ', Memory: %dk, Peak: %dk', memory_get_usage()/1024, memory_get_peak_usage(true)/1024);
1323: }
1324: @dbg_error_log("statistics", "Method: %s, Status: %d, Script: %5.3lfs, Queries: %5.3lfs, URL: %s%s",
1325: $this->method, $status, $script_time, $c->total_query_time, $this->path, $memory);
1326: }
1327: try {
1328: @ob_flush();
1329: }
1330: catch( Exception $ignored ) {}
1331:
1332: if ( isset($c->metrics_style) && $c->metrics_style !== false ) {
1333: $flush_time = microtime(true) - $script_finish;
1334: $this->DoMetrics($status, $message_length, $script_time, $flush_time);
1335: }
1336:
1337: if ( isset($c->exit_after_memory_exceeds) && function_exists('memory_get_peak_usage') && memory_get_peak_usage(true) > $c->exit_after_memory_exceeds ) {
1338: @dbg_error_log("statistics", "Peak memory use exceeds %d bytes (%d) - killing process %d", $c->exit_after_memory_exceeds, memory_get_peak_usage(true), getmypid());
1339: register_shutdown_function( 'CalDAVRequest::kill_on_exit' );
1340: }
1341:
1342: exit(0);
1343: }
1344:
1345:
1346: 1347: 1348: 1349: 1350: 1351: 1352: 1353:
1354: function DoMetrics($status, $response_size, $script_time, $flush_time) {
1355: global $c;
1356: static $ns = 'metrics';
1357:
1358: $method = (empty($this->method) ? 'UNKNOWN' : $this->method);
1359:
1360:
1361:
1362: if ( $c->metrics_style != 'counters' ) {
1363: $cache = getCacheInstance();
1364: if ( $cache->isActive() ) {
1365:
1366: $base_key = $method.':';
1367: $count_like_this = $cache->increment( $ns, $base_key.$status );
1368: $cache->increment( $ns, $base_key.'size', $response_size );
1369: $cache->increment( $ns, $base_key.'script_time', intval($script_time * 1000000) );
1370: $cache->increment( $ns, $base_key.'flush_time', intval($flush_time * 1000000) );
1371: $cache->increment( $ns, $base_key.'query_time', intval($c->total_query_time * 1000000) );
1372:
1373: if ( $count_like_this == 1 ) {
1374:
1375:
1376:
1377: try {
1378: $index = unserialize($cache->get($ns, 'index'));
1379: } catch (Exception $e) {
1380: $index = array('methods' => array(), 'statuses' => array());
1381: }
1382: $index['methods'][$method] = 1;
1383: $index['statuses'][$status] = 1;
1384: $cache->set($ns, 'index', serialize($index), 0);
1385: }
1386: }
1387: else {
1388: error_log("Full statistics are only available with a working Memcache configuration");
1389: }
1390: }
1391:
1392:
1393: if ( $c->metrics_style != 'memcache' ) {
1394: $qstring = "SELECT nextval('%s')";
1395: switch( $method ) {
1396: case 'OPTIONS':
1397: case 'REPORT':
1398: case 'PROPFIND':
1399: case 'GET':
1400: case 'PUT':
1401: case 'HEAD':
1402: case 'PROPPATCH':
1403: case 'POST':
1404: case 'MKCALENDAR':
1405: case 'MKCOL':
1406: case 'DELETE':
1407: case 'MOVE':
1408: case 'ACL':
1409: case 'LOCK':
1410: case 'UNLOCK':
1411: case 'MKTICKET':
1412: case 'DELTICKET':
1413: case 'BIND':
1414: $counter = strtolower($this->method);
1415: break;
1416: default:
1417: $counter = 'unknown';
1418: break;
1419: }
1420: $qry = new AwlQuery( "SELECT nextval('metrics_count_" . $counter . "')" );
1421: $qry->Exec('always',__LINE__,__FILE__);
1422: }
1423: }
1424: }
1425:
1426: