1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164: 165: 166: 167: 168: 169: 170: 171: 172: 173: 174: 175: 176: 177: 178: 179: 180: 181: 182: 183: 184: 185: 186: 187: 188: 189: 190: 191: 192: 193: 194: 195: 196: 197: 198: 199: 200: 201: 202: 203: 204: 205: 206: 207: 208: 209: 210: 211: 212: 213: 214: 215: 216: 217: 218: 219: 220: 221: 222: 223: 224: 225: 226: 227: 228: 229: 230: 231: 232: 233: 234: 235: 236: 237: 238: 239: 240: 241: 242: 243: 244: 245: 246: 247: 248: 249: 250: 251: 252: 253: 254: 255: 256: 257: 258: 259: 260: 261: 262: 263: 264: 265: 266: 267: 268: 269: 270: 271: 272: 273: 274: 275: 276: 277: 278: 279: 280: 281: 282: 283: 284: 285: 286: 287: 288: 289: 290: 291: 292: 293: 294: 295: 296: 297: 298: 299: 300: 301: 302: 303: 304: 305: 306: 307: 308: 309: 310: 311: 312: 313: 314: 315: 316: 317: 318: 319: 320: 321: 322: 323: 324: 325: 326: 327: 328: 329: 330: 331: 332: 333: 334: 335: 336: 337: 338: 339: 340: 341: 342: 343: 344: 345: 346: 347: 348: 349: 350: 351: 352: 353: 354: 355: 356: 357: 358: 359: 360: 361: 362: 363: 364: 365: 366: 367: 368: 369: 370: 371: 372: 373: 374: 375: 376: 377: 378: 379: 380: 381: 382: 383: 384: 385: 386: 387: 388: 389: 390: 391: 392: 393: 394: 395: 396: 397: 398: 399: 400: 401: 402: 403: 404: 405: 406: 407: 408: 409: 410: 411: 412: 413: 414: 415: 416: 417: 418: 419: 420: 421: 422: 423: 424: 425: 426: 427: 428: 429: 430: 431: 432: 433: 434: 435: 436: 437: 438: 439: 440: 441: 442: 443: 444: 445: 446: 447: 448: 449: 450: 451: 452: 453: 454: 455: 456: 457: 458: 459: 460: 461: 462: 463: 464: 465: 466: 467: 468: 469: 470: 471: 472: 473: 474: 475: 476: 477: 478: 479: 480: 481: 482: 483: 484: 485: 486: 487: 488: 489: 490: 491: 492: 493: 494: 495: 496: 497: 498: 499: 500: 501: 502: 503: 504: 505: 506: 507: 508: 509: 510: 511: 512: 513: 514: 515: 516: 517: 518: 519: 520: 521: 522: 523: 524: 525: 526: 527: 528: 529: 530: 531: 532: 533: 534: 535: 536: 537: 538: 539: 540: 541: 542: 543: 544: 545: 546: 547: 548: 549: 550: 551: 552: 553: 554: 555: 556: 557: 558: 559: 560: 561: 562: 563: 564: 565: 566: 567: 568: 569: 570: 571: 572: 573: 574: 575: 576: 577: 578: 579: 580: 581: 582: 583: 584: 585: 586: 587: 588: 589: 590: 591: 592: 593: 594: 595: 596: 597: 598: 599: 600: 601: 602: 603: 604: 605: 606: 607: 608: 609: 610: 611: 612: 613: 614: 615: 616: 617: 618: 619: 620: 621: 622: 623: 624: 625: 626: 627: 628: 629: 630: 631: 632: 633: 634: 635: 636: 637: 638: 639: 640: 641: 642: 643: 644: 645: 646: 647: 648: 649: 650: 651: 652: 653: 654: 655: 656: 657: 658: 659: 660: 661: 662: 663: 664: 665: 666: 667: 668: 669: 670: 671: 672: 673: 674: 675: 676:
<?php
require_once("XMLDocument.php");
class iSchedule
{
public $parsed;
public $selector;
public $domain;
private $dk;
private $DKSig;
private $try_anyway = false;
private $failed = false;
private $failOnError = true;
private $subdomainsOK = true;
private $remote_public_key ;
private $required_headers = Array ( 'host',
'originator',
'recipient',
'content-type' );
private $disallowed_headers = Array ( 'connection',
'keep-alive',
'dkim-signature',
'proxy-authenticate',
'proxy-authorization',
'te',
'trailers',
'transfer-encoding',
'upgrade' );
function __construct ( )
{
global $c;
$this->selector = 'cal';
if ( is_object ( $c ) && isset ( $c->scheduling_dkim_selector ) )
{
$this->scheduling_dkim_domain = $c->scheduling_dkim_domain ;
$this->scheduling_dkim_selector = $c->scheduling_dkim_selector ;
$this->schedule_private_key = $c->schedule_private_key ;
if ( ! preg_match ( '/BEGIN RSA PRIVATE KEY/', $this->schedule_private_key ) )
{
$key = file_get_contents ( $this->schedule_private_key );
if ( $key !== false )
$this->schedule_private_key = $key;
}
if ( isset ( $c->scheduling_dkim_algo ) )
$this->scheduling_dkim_algo = $c->scheduling_dkim_algo;
else
$this->scheduling_dkim_algo = 'sha256';
if ( isset ( $c->scheduling_dkim_valid_time ) )
$this->valid_time = $c->scheduling_dkim_valid_time;
}
}
function getTxt ()
{
global $icfg;
if ( $icfg [ $this->remote_selector . '._domainkey.' . $this->remote_server ] )
{
$this->dk = $icfg [ $this->remote_selector . '._domainkey.' . $this->remote_server ];
return true;
}
$dkim = dns_get_record ( $this->remote_selector . '._domainkey.' . $this->remote_server , DNS_TXT );
if ( count ( $dkim ) > 0 )
{
$this->dk = $dkim [ 0 ] [ 'txt' ];
if ( $dkim [ 0 ] [ 'entries' ] )
{
$this->dk = '';
foreach ( $dkim [ 0 ] [ 'entries' ] as $v )
{
$this->dk .= trim ( $v );
}
}
dbg_error_log( 'ischedule', 'getTxt '. $this->dk . ' XX');
}
else
{
dbg_error_log( 'ischedule', 'getTxt FAILED '. print_r ( $dkim ) );
$this->failed = true;
return false;
}
return true;
}
function setTxt ( $dk )
{
$this->dk = $dk;
}
function parseTxt ( )
{
if ( $this->failed == true )
return false;
$clean = preg_replace ( '/\s?([;=])\s?/', '$1', $this->dk );
$pairs = preg_split ( '/;/', $clean );
$this->parsed = array();
foreach ( $pairs as $v )
{
list($key,$value) = preg_split ( '/=/', $v, 2 );
$value = trim ( $value, '\\' );
if ( preg_match ( '/(g|k|n|p|s|t|v)/', $key ) )
$this->parsed [ $key ] = $value;
else
$this->parsed_ignored [ $key ] = $value;
}
return true;
}
function validateKey ( )
{
$this->failed = true;
if ( isset ( $this->parsed [ 's' ] ) )
{
if ( ! preg_match ( '/(\*|calendar)/', $this->parsed [ 's' ] ) ) {
dbg_error_log( 'ischedule', 'validateKey ERROR: bad selector' );
return false;
}
}
if ( isset ( $this->parsed [ 'k' ] ) && $this->parsed [ 'k' ] != 'rsa' ) {
dbg_error_log( 'ischedule', 'validateKey ERROR: bad key algorythm, algo was:' . $this->parsed [ 'k' ] );
return false;
}
if ( isset ( $this->parsed [ 't' ] ) && ! preg_match ( '/^[y:s]+$/', $this->parsed [ 't' ] ) ) {
dbg_error_log( 'ischedule', 'validateKey ERROR: type mismatch' );
return false;
}
else
{
if ( preg_match ( '/y/', $this->parsed [ 't' ] ) )
$this->failOnError = false;
if ( preg_match ( '/s/', $this->parsed [ 't' ] ) )
$this->subdomainsOK = false;
}
if ( isset ( $this->parsed [ 'g' ] ) )
$this->remote_user_rule = $this->parsed [ 'g' ];
else
$this->remote_user_rule = '*';
if ( isset ( $this->parsed [ 'p' ] ) )
{
if ( preg_match ( '/[^A-Za-z0-9_=+\/]/', $this->parsed [ 'p' ] ) )
return false;
$data = "-----BEGIN PUBLIC KEY-----\n" . implode ("\n",str_split ( $this->parsed [ 'p' ], 64 )) . "\n-----END PUBLIC KEY-----";
if ( $data === false )
return false;
$this->remote_public_key = $data;
}
else {
dbg_error_log( 'ischedule', 'validateKey ERROR: no key in dns record' . $this->parsed [ 'p' ] );
return false;
}
$this->failed = false;
return true;
}
function getServer ( )
{
global $icfg;
if ( $icfg [ $this->domain ] )
{
$this->remote_server = $icfg [ $this->domain ] [ 'server' ];
$this->remote_port = $icfg [ $this->domain ] [ 'port' ];
$this->remote_ssl = $icfg [ $this->domain ] [ 'ssl' ];
return true;
}
$this->remote_ssl = false;
$parts = explode ( '.', $this->domain );
$tld = $parts [ count ( $parts ) - 1 ];
$len = 2;
if ( strlen ( $tld ) == 2 && in_array ( $tld, Array ( 'uk', 'nz' ) ) )
$len = 3;
if ( $this->domain == 'mycaldav' || $this->domain == 'altcaldav' )
$len = 1;
while ( count ( $parts ) >= $len )
{
$r = dns_get_record ( '_ischedules._tcp.' . implode ( '.', $parts ) , DNS_SRV );
if ( 0 < count ( $r ) )
{
$remote_server = $r [ 0 ] [ 'target' ];
$remote_port = $r [ 0 ] [ 'port' ];
$this->remote_ssl = true;
break;
}
if ( ! isset ( $remote_server ) )
{
$r = dns_get_record ( '_ischedule._tcp.' . implode ( '.', $parts ) , DNS_SRV );
if ( 0 < count ( $r ) )
{
$remote_server = $r [ 0 ] [ 'target' ];
$remote_port = $r [ 0 ] [ 'port' ];
break;
}
}
array_shift ( $parts );
}
if ( ! isset ( $remote_server ) )
{
if ( $this->try_anyway == true )
{
if ( ! isset ( $remote_server ) )
$remote_server = $this->domain;
if ( ! isset ( $remote_port ) )
$remote_port = 80;
}
else {
dbg_error_log('ischedule', 'Domain %s did not have srv records for iSchedule', $this->domain );
return false;
}
}
dbg_error_log('ischedule', $this->domain . ' found srv records for ' . $remote_server . ':' . $remote_port );
$this->remote_server = $remote_server;
$this->remote_port = $remote_port;
return true;
}
function getCapabilities ( $domain = null )
{
if ( $domain != null && $this->domain != $domain )
$this->domain = $domain;
if ( ! isset ( $this->remote_server ) && isset ( $this->domain ) && ! $this->getServer ( ) )
return false;
$this->remote_url = 'http'. ( $this->remote_ssl ? 's' : '' ) . '://' .
$this->remote_server . ':' . $this->remote_port . '/.well-known/ischedule';
$remote_capabilities = file_get_contents ( $this->remote_url . '?query=capabilities' );
if ( $remote_capabilities === false )
return false;
$xml_parser = xml_parser_create_ns('UTF-8');
$this->xml_tags = array();
xml_parser_set_option ( $xml_parser, XML_OPTION_SKIP_WHITE, 1 );
xml_parser_set_option ( $xml_parser, XML_OPTION_CASE_FOLDING, 0 );
$rc = xml_parse_into_struct( $xml_parser, $remote_capabilities, $this->xml_tags );
if ( $rc == false ) {
dbg_error_log( 'ERROR', 'XML parsing error: %s at line %d, column %d',
xml_error_string(xml_get_error_code($xml_parser)),
xml_get_current_line_number($xml_parser), xml_get_current_column_number($xml_parser) );
dbg_error_log('ischedule', $this->domain . ' iSchedule error parsing remote xml' );
return false;
}
xml_parser_free($xml_parser);
$xmltree = BuildXMLTree( $this->xml_tags );
if ( !is_object($xmltree) ) {
dbg_error_log('ischedule', $this->domain . ' iSchedule error in remote xml' );
$request->DoResponse( 406, translate("REPORT body is not valid XML data!") );
return false;
}
dbg_error_log('ischedule', $this->domain . ' got capabilites' );
$this->capabilities_xml = $xmltree;
return true;
}
function queryCapabilities ( $capability, $domain = null )
{
if ( ! isset ( $this->capabilities_xml ) )
{
dbg_error_log('ischedule', $this->domain . ' capabilities not set, quering for capability:' . $capability );
if ( $domain == null )
return false;
if ( $this->domain != $domain )
$this->domain = $domain;
if ( ! $this->getCapabilities ( ) )
return false;
}
switch ( $capability )
{
case 'VEVENT':
case 'VFREEBUSY':
case 'VTODO':
$comp = $this->capabilities_xml->GetPath ( 'urn:ietf:params:xml:ns:ischedule:supported-scheduling-message-set/urn:ietf:params:xml:ns:ischedule:comp' );
foreach ( $comp as $c )
{
if ( $c->GetAttribute ( 'name' ) == $capability )
return true;
}
return false;
case 'VFREEBUSY/REQUEST':
case 'VTODO/ADD':
case 'VTODO/REQUEST':
case 'VTODO/REPLY':
case 'VTODO/CANCEL':
case 'VEVENT/ADD':
case 'VEVENT/REQUEST':
case 'VEVENT/REPLY':
case 'VEVENT/CANCEL':
case 'VEVENT/PUBLISH':
case 'VEVENT/COUNTER':
case 'VEVENT/DECLINECOUNTER':
dbg_error_log('ischedule', $this->domain . ' xml query' );
$comp = $this->capabilities_xml->GetPath ( 'urn:ietf:params:xml:ns:ischedule:supported-scheduling-message-set/urn:ietf:params:xml:ns:ischedule:comp' );
list ( $component, $method ) = explode ( '/', $capability );
dbg_error_log('ischedule', $this->domain . ' quering for capability:' . count ( $comp ) . ' ' . $component );
foreach ( $comp as $c )
{
dbg_error_log('ischedule', $this->domain . ' quering for capability:' . $c->GetAttribute ( 'name' ) . ' == ' . $component );
if ( $c->GetAttribute ( 'name' ) == $component )
{
$methods = $c->GetElements ( 'urn:ietf:params:xml:ns:ischedule:method' );
if ( count ( $methods ) == 0 )
return true;
foreach ( $methods as $m )
{
if ( $m->GetAttribute ( 'name' ) == $method )
return true;
}
}
}
return false;
default:
return false;
}
}
function signDKIM ( $headers, $body )
{
if ( $this->scheduling_dkim_domain == null )
return false;
$b = '';
if ( is_array ( $headers ) !== true )
return false;
foreach ( $headers as $key => $value )
{
$b .= $key . ': ' . $value . "\r\n";
}
$dk['v'] = '1';
$dk['a'] = 'rsa-' . $this->scheduling_dkim_algo;
$dk['s'] = $this->selector;
$dk['d'] = $this->scheduling_dkim_domain;
$dk['c'] = 'simple-http';
if ( isset ( $_SERVER['SERVER_NAME'] ) && strstr ( $_SERVER['SERVER_NAME'], $this->domain ) !== false )
$dk['i'] = '@' . $_SERVER['SERVER_NAME'];
$dk['q'] = 'dns/txt';
$dk['l'] = strlen ( $body );
$dk['t'] = time ( );
if ( isset ( $this->valid_time ) )
$dk['x'] = $this->valid_time;
$dk['h'] = implode ( ':', array_keys ( $headers ) );
$dk['bh'] = base64_encode ( hash ( 'sha256', $body , true ) );
$value = '';
foreach ( $dk as $key => $val )
$value .= "$key=$val; ";
$value .= 'b=';
$tosign = $b . 'DKIM-Signature: ' . $value;
openssl_sign ( $tosign, $sig, $this->schedule_private_key, $this->scheduling_dkim_algo );
$this->tosign = $tosign;
$value .= base64_encode ( $sig );
return $value;
}
function sendRequest ( $address, $type, $data )
{
global $session;
if ( empty($this->scheduling_dkim_domain) )
return false;
if ( is_array ( $address ) )
list ( $user, $domain ) = explode ( '@', $address[0] );
else
list ( $user, $domain ) = explode ( '@', $address );
if ( ! $this->getCapabilities ( $domain ) )
{
dbg_error_log('ischedule', $domain . ' did not have iSchedule capabilities for ' . $type );
return false;
}
dbg_error_log('ischedule', $domain . ' trying with iSchedule capabilities for ' . $type );
if ( $this->queryCapabilities ( $type ) )
{
dbg_error_log('ischedule', $domain . ' trying with iSchedule capabilities for ' . $type . ' OK');
list ( $component, $method ) = explode ( '/', $type );
$headers = array ( );
$headers['iSchedule-Version'] = '1.0';
$headers['Originator'] = 'mailto:' . $session->email;
if ( is_array ( $address ) )
$headers['Recipient'] = implode ( ', ' , $address );
else
$headers['Recipient'] = $address;
$headers['Content-Type'] = 'text/calendar; component=' . $component ;
if ( $method )
$headers['Content-Type'] .= '; method=' . $method;
$headers['DKIM-Signature'] = $this->signDKIM ( $headers, $body );
if ( $headers['DKIM-Signature'] == false )
return false;
$request_headers = array ( );
foreach ( $headers as $k => $v )
$request_headers[] = $k . ': ' . $v;
$curl = curl_init ( $this->remote_url );
curl_setopt ( $curl, CURLOPT_RETURNTRANSFER, true );
curl_setopt ( $curl, CURLOPT_HTTPHEADER, array() );
curl_setopt ( $curl, CURLOPT_HTTPHEADER, $request_headers );
curl_setopt ( $curl, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt ( $curl, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt ( $curl, CURLOPT_POST, 1);
curl_setopt ( $curl, CURLOPT_POSTFIELDS, $data);
curl_setopt ( $curl, CURLOPT_CUSTOMREQUEST, 'POST' );
$xmlresponse = curl_exec ( $curl );
$info = curl_getinfo ( $curl );
curl_close ( $curl );
if ( $info['http_code'] >= 400 )
{
dbg_error_log ( 'ischedule', 'remote server returned error (%s)', $info['http_code'] );
return false;
}
error_log ( 'remote response '. $xmlresponse . print_r ( $info, true ) );
$xml_parser = xml_parser_create_ns('UTF-8');
$xml_tags = array();
xml_parser_set_option ( $xml_parser, XML_OPTION_SKIP_WHITE, 1 );
xml_parser_set_option ( $xml_parser, XML_OPTION_CASE_FOLDING, 0 );
$rc = xml_parse_into_struct( $xml_parser, $xmlresponse, $xml_tags );
if ( $rc == false ) {
dbg_error_log( 'ERROR', 'XML parsing error: %s at line %d, column %d',
xml_error_string(xml_get_error_code($xml_parser)),
xml_get_current_line_number($xml_parser), xml_get_current_column_number($xml_parser) );
return false;
}
$xmltree = BuildXMLTree( $xml_tags );
xml_parser_free($xml_parser);
if ( !is_object($xmltree) ) {
dbg_error_log( 'ERROR', 'iSchedule RESPONSE body is not valid XML data!' );
return false;
}
$resp = $xmltree->GetPath ( '/*/urn:ietf:params:xml:ns:ischedule:response' );
$result = array();
foreach ( $resp as $r )
{
$recipient = $r->GetElements ( 'urn:ietf:params:xml:ns:ischedule:recipient' );
$status = $r->GetElements ( 'urn:ietf:params:xml:ns:ischedule:request-status' );
$calendardata = $r->GetElements ( 'urn:ietf:params:xml:ns:ischedule:calendar-data' );
if ( count ( $recipient ) < 1 )
continue;
if ( count ( $calendardata ) > 0 )
{
$result [ $recipient[0]->GetContent() ] = $calendardata[0]->GetContent();
}
else
{
$result [ $recipient[0]->GetContent() ] = $status[0]->GetContent();
}
}
if ( count ( $result ) < 1 )
return false;
else
return $result;
}
else
return false;
}
function parseDKIM ( $sig )
{
$this->failed = true;
$tags = preg_split ( '/;[\s\t]/', $sig );
foreach ( $tags as $v )
{
list($key,$value) = preg_split ( '/=/', $v, 2 );
$dkim[$key] = $value;
}
if ( ! preg_match ( '{(simple|simple-http|relaxed)(/(simple|simple-http|relaxed))?}', $dkim['c'], $matches ) )
return 'bad canonicalization:' . $dkim['c'] ;
if ( count ( $matches ) > 2 )
$this->body_cannon = $matches[2];
else
$this->body_cannon = $matches[1];
$this->header_cannon = $matches[1];
if ( $dkim['a'] != 'rsa-sha1' && $dkim['a'] != 'rsa-sha256' )
return 'bad signing algorythm:' . $dkim['a'] ;
if ( $dkim['q'] != 'dns/txt' )
return 'bad query method';
if ( ! isset ( $dkim['d'] ) )
return 'missing signing domain';
$this->remote_server = $dkim['d'];
if ( isset ( $dkim['i'] ) )
{
if ( ! stristr ( $dkim['i'], $dkim['d'] ) )
return 'signing domain mismatch';
if ( strstr ( $dkim [ 'i' ], '@' ) )
$this->remote_user = substr ( $dkim [ 'i' ], 0, strpos ( $dkim [ 'i' ], '@' ) - 1 );
}
if ( ! isset ( $dkim['s'] ) )
return 'missing selector';
$this->remote_selector = $dkim['s'];
if ( ! isset ( $dkim['h'] ) )
return 'missing list of signed headers';
$this->signed_headers = preg_split ( '/:/', $dkim['h'] );
$sh = Array ();
foreach ( $this->signed_headers as $h )
{
$sh[] = strtolower ( $h );
if ( in_array ( strtolower ( $h ), $this->disallowed_headers ) )
return "$h is NOT allowed in signed header fields per RFC4871 or iSchedule";
}
foreach ( $this->required_headers as $h )
if ( ! in_array ( strtolower ( $h ), $sh ) )
return "$h is REQUIRED but missing in signed header fields per iSchedule";
if ( ! isset ( $dkim['bh'] ) )
return 'missing body signature';
if ( ! isset ( $dkim['b'] ) )
return 'missing signature in b field';
if ( isset ( $dkim['l'] ) )
$this->signed_length = $dkim['l'];
$this->failed = false;
$this->DKSig = $dkim;
return true;
}
function parseURI ( $uri )
{
if ( preg_match ( '/^mailto:([^@]+)@([^\s\t\n]+)/', $uri, $matches ) )
{
$this->remote_user = $matches[1];
$this->domain = $matches[2];
}
else
return false;
}
function verifySignature ( )
{
global $request,$c;
$this->failed = true;
$signed = '';
foreach ( $this->signed_headers as $h )
if ( isset ( $_SERVER['HTTP_' . strtoupper ( strtr ( $h, '-', '_' ) ) ] ) )
$signed .= "$h: " . $_SERVER['HTTP_' . strtoupper ( strtr ( $h, '-', '_' ) ) ] . "\r\n";
else
$signed .= "$h: " . $_SERVER[ strtoupper ( strtr ( $h, '-', '_' ) ) ] . "\r\n";
if ( ! isset ( $_SERVER['HTTP_ORIGINATOR'] ) || stripos ( $signed, 'Originator' ) === false )
return "missing Originator";
if ( ! isset ( $_SERVER['HTTP_RECIPIENT'] ) || stripos ( $signed, 'Recipient' ) === false )
return "missing Recipient";
if ( ! isset ( $_SERVER['HTTP_ISCHEDULE_VERSION'] ) || $_SERVER['HTTP_ISCHEDULE_VERSION'] != '1' )
return "missing or mismatch ischedule-version header";
$body = $request->raw_post;
if ( ! isset ( $this->signed_length ) )
$this->signed_length = strlen ( $body );
else
$body = substr ( $body, 0, $this->signed_length );
if ( isset ( $this->remote_user_rule ) )
if ( $this->remote_user_rule != '*' && ! stristr ( $this->remote_user, $this->remote_user_rule ) )
return "remote user rule failure";
$hash_algo = preg_replace ( '/^.*(sha1|sha256).*/','$1', $this->DKSig['a'] );
$body_hash = base64_encode ( hash ( $hash_algo, $body , true ) );
if ( $this->DKSig['bh'] != $body_hash )
return "body hash mismatch";
$sig = $_SERVER['HTTP_DKIM_SIGNATURE'];
$sig = preg_replace ( '/ b=[^;\s\r\n\t]+/', ' b=', $sig );
$signed .= 'DKIM-Signature: ' . $sig;
$verify = openssl_verify ( $signed, base64_decode ( $this->DKSig['b'] ), $this->remote_public_key, $hash_algo );
if ( $verify != 1 )
{
openssl_sign ( $signed, $sigb, $this->schedule_private_key, $hash_algo );
$sigc = base64_encode ( $sigb );
$verify1 = openssl_verify ( $signed, $sigc, $this->remote_public_key, $hash_algo );
return "signature verification failed " . $this->remote_public_key . " \n\n". $sig . " \n" . $hash_algo . "\n". print_r ($verify,1) . " XX " . $verify1 . "\n";
}
$this->failed = false;
return true;
}
function validateRequest ( )
{
global $request;
if ( isset ( $_SERVER['HTTP_DKIM_SIGNATURE'] ) )
$sig = $_SERVER['HTTP_DKIM_SIGNATURE'];
else
{
$request->DoResponse( 403, translate('DKIM signature missing') );
return false;
}
if ( isset ( $_SERVER['HTTP_ORGANIZER'] ) )
$request->DoResponse( 403, translate('Organizer Missing') );
dbg_error_log ('ischedule','beginning validation');
$err = $this->parseDKIM ( $sig );
if ( $err !== true || $this->failed )
$request->DoResponse( 412, 'DKIM signature invalid ' . "\n" . $err . "\n" );
if ( ! $this->getTxt () || $this->failed )
$request->DoResponse( 400, translate('DKIM signature validation failed(DNS ERROR)') );
if ( ! $this->parseTxt () || $this->failed )
$request->DoResponse( 400, translate('DKIM signature validation failed(KEY Parse ERROR)') );
if ( ! $this->validateKey () || $this->failed )
$request->DoResponse( 400, translate('DKIM signature validation failed(KEY Validation ERROR)') );
$err = $this->verifySignature ();
if ( $err !== true || $this->failed )
$request->DoResponse( 412, translate('DKIM signature validation failed(Signature verification ERROR)') . '\n' . $err );
dbg_error_log ('ischedule','signature ok');
return true;
}
}