1: <?php
2: 3: 4: 5: 6: 7:
8:
9: if ( preg_match('{/always.php$}', $_SERVER['SCRIPT_NAME'] ) ) header('Location: index.php');
10:
11:
12: $c = (object) array();
13: $c->script_start_time = microtime(true);
14:
15:
16: unset($session); unset($request); unset($dbconn); unset($_awl_dbconn); unset($include_properties);
17:
18:
19:
20: function early_exception_handler($e) {
21: if ( !headers_sent() ) {
22: header("Content-type: text/plain");
23: header( sprintf("HTTP/1.1 %d %s", 500, getStatusMessage(500)) );
24: }
25: else {
26: echo "<pre>\n";
27:
28: }
29: try {
30: @ob_flush();
31: }
32: catch( Exception $ignored ) {}
33: echo "Exception [".$e->getCode()."] ".$e->getmessage()."\n";
34: echo "At line ", $e->getLine(), " of ", $e->getFile(), "\n";
35: echo "================= Stack Trace ===================\n";
36:
37: $trace = array_reverse($e->getTrace());
38: foreach( $trace AS $k => $v ) {
39: printf( "%s[%d] %s%s%s()\n", $v['file'], $v['line'], (isset($v['class'])?$v['class']:''), (isset($v['type'])?$v['type']:''), (isset($v['function'])?$v['function']:'') );
40: }
41:
42: }
43: set_exception_handler('early_exception_handler');
44:
45: function early_catch_fatal_error() {
46: global $request;
47:
48: if ( !empty($request) ) return;
49:
50:
51: $e = error_get_last();
52:
53:
54: if (isset($e['type']) && $e['type'] == E_ERROR) {
55: if ( !headers_sent() ) {
56: header("Content-type: text/plain");
57: header( sprintf("HTTP/1.1 %d %s", 500, getStatusMessage(500)) );
58: }
59: echo "PHP Fatal error: ".$e['message']."\n";
60: echo "At line ", $e['line'], " of ", $e['file'], "\n";
61: error_log("PHP Fatal Error: '".$e['message']. "' at line ". $e['line']. " of ". $e['file']);
62: }
63: }
64: register_shutdown_function('early_catch_fatal_error');
65:
66: $c->default_timezone = ini_get ( 'date.timezone' );
67: if (empty ( $c->default_timezone )) {
68: $c->default_timezone = 'UTC';
69: if (isset ( $_SERVER ['HTTP_X_DAVICAL_TESTCASE'] )) {
70: $c->default_timezone = 'Pacific/Auckland';
71: }
72: }
73:
74:
75: $c->sysabbr = 'davical';
76: $c->admin_email = 'admin@davical.example.com';
77: $c->system_name = 'DAViCal CalDAV Server';
78: $c->domain_name = (isset($_SERVER['SERVER_NAME'])?$_SERVER['SERVER_NAME']:$_SERVER['SERVER_ADDR']);
79: $c->save_time_zone_defs = true;
80: $c->collections_always_exist = false;
81: $c->allow_get_email_visibility = false;
82: $c->permission_scan_depth = 2;
83: $c->expand_pdo_parameters = true;
84: $c->home_calendar_name = 'calendar';
85: $c->home_addressbook_name = 'addresses';
86: $c->enable_row_linking = true;
87: $c->enable_scheduling = false;
88: $c->http_auth_mode = 'Basic';
89:
90: $c->default_locale = 'en';
91: $c->locale_path = '../locale';
92: $c->base_url = preg_replace('#/[^/]+\.php.*$#', '', $_SERVER['SCRIPT_NAME']);
93: $c->base_directory = preg_replace('#/[^/]*$#', '', $_SERVER['DOCUMENT_ROOT']);
94: $c->default_privileges = array('read-free-busy', 'schedule-deliver');
95:
96: $c->enable_auto_schedule = true;
97:
98: $c->schema_major = $c->schema_minor = $c->schema_patch = 0;
99:
100: $c->stylesheets = array( $c->base_url.'/davical.css' );
101: $c->images = $c->base_url . '/images';
102:
103:
104: $c->template_usr = array( 'active' => true,
105: 'locale' => 'en_GB',
106: 'date_format_type' => 'E',
107: 'email_ok' => date('Y-m-d')
108: );
109:
110: $c->hide_TODO = true;
111: $c->readonly_webdav_collections = true;
112:
113:
114: $c->total_query_time = 0;
115:
116: $c->dbg = array();
117:
118:
119:
120: if ( isset($skip_errors) ) $skip_errors = true;
121: if ( ! @include_once('AWLUtilities.php') ) {
122: if ( isset($skip_errors) ) $skip_errors = false;
123: $try_paths = array(
124: '../../awl/inc'
125: , '/usr/share/awl/inc'
126: , '/usr/share/php/awl/inc'
127: , '/usr/local/share/awl/inc'
128: );
129: foreach( $try_paths AS $awl_include_path ) {
130: if ( @file_exists($awl_include_path.'/AWLUtilities.php') ) {
131: set_include_path( $awl_include_path. PATH_SEPARATOR. get_include_path());
132: break;
133: }
134: }
135: if ( ! @include_once('AWLUtilities.php') ) {
136: echo "Could not find the AWL libraries. Are they installed? Check your include_path in php.ini!\n";
137: @ob_flush(); exit(0);
138: }
139: }
140: if ( isset($skip_errors) ) $skip_errors = false;
141:
142:
143: set_include_path( '../inc'. PATH_SEPARATOR. get_include_path());
144:
145:
146:
147: if ( !isset($_SERVER['SERVER_NAME']) ) {
148: @dbg_error_log( 'WARN', "Your webserver is not setting the SERVER_NAME parameter. You may need to set \$c->domain_name in your configuration. Using IP address meanhwhile..." );
149: }
150:
151:
152: 153: 154: 155: 156:
157: ob_start( );
158: if ( @file_exists('/etc/davical/'.$_SERVER['SERVER_NAME'].'-conf.php') ) {
159: include('/etc/davical/'.$_SERVER['SERVER_NAME'].'-conf.php');
160: }
161: else if ( @file_exists('/etc/davical/config.php') ) {
162: include('/etc/davical/config.php');
163: }
164: else if ( @file_exists('/usr/local/etc/davical/'.$_SERVER['SERVER_NAME'].'-conf.php') ) {
165: include('/usr/local/etc/davical/'.$_SERVER['SERVER_NAME'].'-conf.php');
166: }
167: else if ( @file_exists('/usr/local/etc/davical/config.php') ) {
168: include('/usr/local/etc/davical/config.php');
169: }
170: else if ( @file_exists('../config/config.php') ) {
171: include('../config/config.php');
172: }
173: else if ( @file_exists('config/config.php') ) {
174: include('config/config.php');
175: }
176: else {
177: include('davical_configuration_missing.php');
178: @ob_flush(); exit(0);
179: }
180: $config_warnings = trim(ob_get_contents());
181: ob_end_clean();
182:
183: 184: 185:
186: if ( isset($c->trust_x_forwarded) && $c->trust_x_forwarded ) {
187: if ( isset($_SERVER['X-Real-IP']) ) {
188: $_SERVER['REMOTE_ADDR'] = $_SERVER['X-Real-IP'];
189: } elseif ( isset($_SERVER['X-Forwarded-For']) ) {
190: list($_SERVER['REMOTE_ADDR'], $rest) = explode( ',', $_SERVER['X-Forwarded-For']);
191: }
192: if ( isset($_SERVER['X-Forwarded-Proto']) ) {
193: if ($_SERVER['X-Forwarded-Proto'] == 'https') {
194: $_SERVER['HTTPS'] = 'on';
195: } else {
196: $_SERVER['HTTPS'] = 'off';
197: }
198: }
199: if ( isset($_SERVER['X-Forwarded-Port']) ) {
200: $_SERVER['SERVER_PORT'] = $_SERVER['X-Forwarded-Port'];
201: }
202: }
203:
204: 205: 206:
207: $c->protocol_server_port = sprintf( '%s://%s%s',
208: (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on'? 'https' : 'http'),
209: $_SERVER['SERVER_NAME'],
210: (
211: ( (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] != 'on')
212: && (!isset($_SERVER['SERVER_PORT']) || $_SERVER['SERVER_PORT'] == 80) )
213: || ( isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on'
214: && (!isset($_SERVER['SERVER_PORT']) || $_SERVER['SERVER_PORT'] == 443) )
215: ? ''
216: : ':'.$_SERVER['SERVER_PORT']
217: ) );
218: $c->protocol_server_port_script = $c->protocol_server_port . ($_SERVER['SCRIPT_NAME'] == '/index.php' ? '' : $_SERVER['SCRIPT_NAME']);
219:
220:
221: if ( !isset($c->page_title) ) $c->page_title = $c->system_name;
222:
223: if ( isset($_SERVER['HTTP_X_DAVICAL_TESTCASE']) ) {
224: @dbg_error_log( 'LOG', '==========> Test case =%s=', $_SERVER['HTTP_X_DAVICAL_TESTCASE'] );
225: }
226: else if ( isset($c->dbg['script_start']) && $c->dbg['script_start'] ) {
227:
228: @dbg_error_log( 'LOG', '==========> method =%s= =%s= =%s= =%s= =%s=',
229: $_SERVER['REQUEST_METHOD'], $c->protocol_server_port_script, (isset($_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO'] : '$_SERVER[PATH_INFO] undefined'), $c->base_url, $c->base_directory );
230: }
231:
232: 233: 234: 235:
236: putenv("LANG=". $c->default_locale);
237: if ( function_exists('awl_set_locale') ) {
238: awl_set_locale($c->default_locale);
239: init_gettext( 'davical', $c->locale_path );
240: }
241:
242: 243: 244: 245:
246: $c->code_version = 0;
247: $c->want_awl_version = '0.57';
248: $c->version_string = '1.1.5';
249: if ( isset($c->version_string) && preg_match( '/(\d+)\.(\d+)\.(\d+)(.*)/', $c->version_string, $matches) ) {
250: $c->code_major = $matches[1];
251: $c->code_minor = $matches[2];
252: $c->code_patch = $matches[3];
253: $c->code_version = (($c->code_major * 1000) + $c->code_minor).'.'.$c->code_patch;
254: dbg_error_log('caldav', 'Version (%d.%d.%d) == %s', $c->code_major, $c->code_minor, $c->code_patch, $c->code_version);
255: @header( sprintf('Server: %d.%d', $c->code_major, $c->code_minor) );
256: }
257:
258: 259: 260:
261: $_SERVER['SERVER_NAME'] = $c->domain_name;
262:
263: require_once('AwlQuery.php');
264:
265: $c->want_dbversion = array(1,3,2);
266: $c->schema_version = 0;
267: $qry = new AwlQuery( 'SELECT schema_major, schema_minor, schema_patch FROM awl_db_revision ORDER BY schema_id DESC LIMIT 1;' );
268: if ( $qry->Exec('always',__LINE__,__FILE__) && $row = $qry->Fetch() ) {
269: $c->schema_version = doubleval( sprintf( '%d%03d.%03d', $row->schema_major, $row->schema_minor, $row->schema_patch) );
270: $c->wanted_version = doubleval( sprintf( '%d%03d.%03d', $c->want_dbversion[0], $c->want_dbversion[1], $c->want_dbversion[2]) );
271: $c->schema_major = $row->schema_major;
272: $c->schema_minor = $row->schema_minor;
273: $c->schema_patch = $row->schema_patch;
274: if ( $c->schema_version < $c->wanted_version ) {
275: $c->messages[] = sprintf( 'Database schema needs upgrading. Current: %d.%d.%d, Desired: %d.%d.%d',
276: $row->schema_major, $row->schema_minor, $row->schema_patch, $c->want_dbversion[0], $c->want_dbversion[1], $c->want_dbversion[2]);
277: }
278: if ( isset($c->default_timezone) ) {
279: $qry->QDo('SET TIMEZONE TO ?', $c->default_timezone );
280: }
281: }
282: date_default_timezone_set ( $c->default_timezone );
283:
284: require_once('Principal.php');
285:
286: 287: 288: 289: 290:
291: function getStatusMessage($status) {
292: switch( $status ) {
293: case 100: $ans = 'Continue'; break;
294: case 101: $ans = 'Switching Protocols'; break;
295: case 200: $ans = 'OK'; break;
296: case 201: $ans = 'Created'; break;
297: case 202: $ans = 'Accepted'; break;
298: case 203: $ans = 'Non-Authoritative Information'; break;
299: case 204: $ans = 'No Content'; break;
300: case 205: $ans = 'Reset Content'; break;
301: case 206: $ans = 'Partial Content'; break;
302: case 207: $ans = 'Multi-Status'; break;
303: case 300: $ans = 'Multiple Choices'; break;
304: case 301: $ans = 'Moved Permanently'; break;
305: case 302: $ans = 'Found'; break;
306: case 303: $ans = 'See Other'; break;
307: case 304: $ans = 'Not Modified'; break;
308: case 305: $ans = 'Use Proxy'; break;
309: case 307: $ans = 'Temporary Redirect'; break;
310: case 400: $ans = 'Bad Request'; break;
311: case 401: $ans = 'Unauthorized'; break;
312: case 402: $ans = 'Payment Required'; break;
313: case 403: $ans = 'Forbidden'; break;
314: case 404: $ans = 'Not Found'; break;
315: case 405: $ans = 'Method Not Allowed'; break;
316: case 406: $ans = 'Not Acceptable'; break;
317: case 407: $ans = 'Proxy Authentication Required'; break;
318: case 408: $ans = 'Request Timeout'; break;
319: case 409: $ans = 'Conflict'; break;
320: case 410: $ans = 'Gone'; break;
321: case 411: $ans = 'Length Required'; break;
322: case 412: $ans = 'Precondition Failed'; break;
323: case 413: $ans = 'Request Entity Too Large'; break;
324: case 414: $ans = 'Request-URI Too Long'; break;
325: case 415: $ans = 'Unsupported Media Type'; break;
326: case 416: $ans = 'Requested Range Not Satisfiable'; break;
327: case 417: $ans = 'Expectation Failed'; break;
328: case 422: $ans = 'Unprocessable Entity'; break;
329: case 423: $ans = 'Locked'; break;
330: case 424: $ans = 'Failed Dependency'; break;
331: case 500: $ans = 'Internal Server Error'; break;
332: case 501: $ans = 'Not Implemented'; break;
333: case 502: $ans = 'Bad Gateway'; break;
334: case 503: $ans = 'Service Unavailable'; break;
335: case 504: $ans = 'Gateway Timeout'; break;
336: case 505: $ans = 'HTTP Version Not Supported'; break;
337: default: $ans = 'Unknown HTTP Status Code '.$status;
338: }
339: return $ans;
340: }
341:
342:
343: 344: 345: 346: 347:
348: function ConstructURL( $partial_path, $force_script = false ) {
349: global $c;
350:
351: if ( ! isset($c->_url_script_path) ) {
352: $c->protocol_server_port_script = str_replace( 'index.php', 'caldav.php', $c->protocol_server_port_script);
353: $c->_url_script_path = (preg_match('#/$#', $c->protocol_server_port_script) ? 'caldav.php' : '');
354: $c->_url_script_path = $c->protocol_server_port_script . $c->_url_script_path;
355: }
356:
357: $url = $c->_url_script_path;
358: if ( $force_script ) {
359: if ( ! preg_match( '#/caldav\.php$#', $url ) ) $url .= '/caldav.php';
360: }
361: $url .= str_replace( '%2F', '/', rawurlencode($partial_path));
362: $url = preg_replace( '#^(https?://.+)//#', '$1/', $url );
363: $url = preg_replace('#^https?://[^/]+#', '', $url );
364:
365: if ( strstr($url, 'caldav.php/caldav.php') !== false ) {
366:
367: $url = str_replace( 'caldav.php/caldav.php', 'caldav.php', $url );
368: }
369:
370: return $url;
371: }
372:
373:
374: 375: 376: 377: 378:
379: function DeconstructURL( $url, $force_script = false ) {
380: global $c;
381:
382: $dav_name = rawurldecode($url);
383: $dav_name = trim($dav_name);
384:
385:
386: if ( preg_match( '#^(/[^/]+/[^/]+).ics$#', $dav_name, $matches ) ) {
387: $dav_name = $matches[1]. '/';
388: }
389:
390:
391: if ( !isset($c->deconstruction_base_path) ) $c->deconstruction_base_path = ConstructURL('/');
392: if ( preg_match( '%^(.*?)'.str_replace('%', '\\%',$c->deconstruction_base_path).'(.*)$%', $dav_name, $matches ) ) {
393: if ( $matches[1] == '' || $matches[1] == $c->protocol_server_port ) {
394: $dav_name = $matches[2];
395: }
396: }
397:
398:
399: if ( strstr($dav_name,'//') ) $dav_name = preg_replace( '#//+#', '/', $dav_name);
400:
401: if ( substr($dav_name,0,1) != '/' ) $dav_name = '/'.$dav_name;
402:
403: return $dav_name;
404: }
405:
406:
407: 408: 409: 410:
411: function ISODateToHTTPDate( $isodate ) {
412:
413: $month = gmstrftime('%m', strtotime($isodate));
414: switch( intval($month) ) {
415: case 1: $month = 'Jan'; break;
416: case 2: $month = 'Feb'; break;
417: case 3: $month = 'Mar'; break;
418: case 4: $month = 'Apr'; break;
419: case 5: $month = 'May'; break;
420: case 6: $month = 'Jun'; break;
421: case 7: $month = 'Jul'; break;
422: case 8: $month = 'Aug'; break;
423: case 9: $month = 'Sep'; break;
424: case 10: $month = 'Oct'; break;
425: case 11: $month = 'Nov'; break;
426: case 12: $month = 'Dec'; break;
427: default:
428: throw new Exception('Invalid month '.$month);
429: }
430:
431: return( gmstrftime('%a, %d '.$month.' %Y %H:%M:%S GMT', strtotime($isodate)) );
432: }
433:
434: 435: 436: 437:
438: function DateToISODate( $indate, $in_utc=false ) {
439:
440: if ( $in_utc ) return( gmdate('Ymd\THis\Z', strtotime($indate)) );
441: return( date('c', strtotime($indate)) );
442: }
443:
444: 445: 446: 447: 448: 449:
450: define("DAVICAL_MAXPRIV", "65535");
451: define("DAVICAL_ADDRESSBOOK_MAXPRIV", "1023");
452: function privilege_to_bits( $raw_privs ) {
453: $out_priv = 0;
454:
455: if ( gettype($raw_privs) == 'string' ) $raw_privs = array( $raw_privs );
456:
457: if ( ! is_array($raw_privs) ) $raw_privs = array($raw_privs);
458:
459: foreach( $raw_privs AS $priv ) {
460: $trim_priv = trim(strtolower(preg_replace( '/^.*:/', '', $priv)));
461: switch( $trim_priv ) {
462: case 'read' : $out_priv |= 1; break;
463: case 'write-properties' : $out_priv |= 2; break;
464: case 'write-content' : $out_priv |= 4; break;
465: case 'unlock' : $out_priv |= 8; break;
466: case 'read-acl' : $out_priv |= 16; break;
467: case 'read-current-user-privilege-set' : $out_priv |= 32; break;
468: case 'bind' : $out_priv |= 64; break;
469: case 'unbind' : $out_priv |= 128; break;
470: case 'write-acl' : $out_priv |= 256; break;
471: case 'read-free-busy' : $out_priv |= 512; break;
472: case 'schedule-deliver-invite' : $out_priv |= 1024; break;
473: case 'schedule-deliver-reply' : $out_priv |= 2048; break;
474: case 'schedule-query-freebusy' : $out_priv |= 4096; break;
475: case 'schedule-send-invite' : $out_priv |= 8192; break;
476: case 'schedule-send-reply' : $out_priv |= 16384; break;
477: case 'schedule-send-freebusy' : $out_priv |= 32768; break;
478:
479:
480: case 'write' : $out_priv |= 198; break;
481: case 'schedule-deliver' : $out_priv |= 7168; break;
482: case 'schedule-send' : $out_priv |= 57344; break;
483: case 'all' : $out_priv = DAVICAL_MAXPRIV; break;
484: case 'fake_privilege_for_input' : break;
485: default:
486: dbg_error_log( 'ERROR', 'Cannot convert privilege of "%s" into bits.', $priv );
487:
488: }
489: }
490:
491:
492: if ( ($out_priv & DAVICAL_MAXPRIV) >= DAVICAL_MAXPRIV ) $out_priv = pow(2,24) - 1;
493: return $out_priv;
494: }
495:
496:
497: 498: 499: 500: 501: 502:
503: function bits_to_privilege( $raw_bits, $resourcetype = 'resource' ) {
504: $out_priv = array();
505:
506: if ( is_string($raw_bits) ) {
507: $raw_bits = bindec($raw_bits);
508: }
509:
510: if ( ($raw_bits & DAVICAL_MAXPRIV) == DAVICAL_MAXPRIV ) $out_priv[] = 'all';
511:
512: if ( ($raw_bits & 1) != 0 ) $out_priv[] = 'DAV::read';
513: if ( ($raw_bits & 8) != 0 ) $out_priv[] = 'DAV::unlock';
514: if ( ($raw_bits & 16) != 0 ) $out_priv[] = 'DAV::read-acl';
515: if ( ($raw_bits & 32) != 0 ) $out_priv[] = 'DAV::read-current-user-privilege-set';
516: if ( ($raw_bits & 256) != 0 ) $out_priv[] = 'DAV::write-acl';
517: if ( ($resourcetype == 'calendar' || $resourcetype == 'proxy' || $resourcetype == '*') && ($raw_bits & 512) != 0 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:read-free-busy';
518:
519: if ( ($raw_bits & 198) != 0 ) {
520: if ( ($raw_bits & 198) == 198 ) $out_priv[] = 'DAV::write';
521: if ( ($raw_bits & 2) != 0 ) $out_priv[] = 'DAV::write-properties';
522: if ( ($raw_bits & 4) != 0 ) $out_priv[] = 'DAV::write-content';
523: if ( ($raw_bits & 64) != 0 ) $out_priv[] = 'DAV::bind';
524: if ( ($raw_bits & 128) != 0 ) $out_priv[] = 'DAV::unbind';
525: }
526:
527: if ( ($resourcetype == 'schedule-inbox' || $resourcetype == '*') && ($raw_bits & 7168) != 0 ) {
528: if ( ($raw_bits & 7168) == 7168 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:schedule-deliver';
529: if ( ($raw_bits & 1024) != 0 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:schedule-deliver-invite';
530: if ( ($raw_bits & 2048) != 0 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:schedule-deliver-reply';
531: if ( ($raw_bits & 4096) != 0 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:schedule-query-freebusy';
532: }
533:
534: if ( ($resourcetype == 'schedule-outbox' || $resourcetype == '*') && ($raw_bits & 57344) != 0 ) {
535: if ( ($raw_bits & 57344) == 57344 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:schedule-send';
536: if ( ($raw_bits & 8192) != 0 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:schedule-send-invite';
537: if ( ($raw_bits & 16384) != 0 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:schedule-send-reply';
538: if ( ($raw_bits & 32768) != 0 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:schedule-send-freebusy';
539: }
540:
541:
542:
543: return $out_priv;
544: }
545:
546:
547: 548: 549:
550: function privileges_to_XML( $privilege_names, &$xmldoc=null ) {
551: if ( !isset($xmldoc) && isset($GLOBALS['reply']) ) $xmldoc = $GLOBALS['reply'];
552: $privileges = array();
553: foreach( $privilege_names AS $k ) {
554: $privilege = new XMLElement('privilege');
555: if ( isset($xmldoc) )
556: $xmldoc->NSElement($privilege,$k);
557: else
558: $privilege->NewElement($k);
559: $privileges[] = $privilege;
560: }
561: return $privileges;
562: }
563:
564: