3 * Functions that are needed for iScheduling requests
5 * - verifying Domain Key signatures
6 * - delivering remote scheduling requests to local users inboxes
7 * - Utility functions which we can use to decide whether this
8 * is a permitted activity for this user.
11 * @subpackage iSchedule
12 * @author Rob Ostensen <rob@boxacle.net>
13 * @copyright Rob Ostensen
14 * @license http://gnu.org/copyleft/gpl.html GNU GPL v3 or later
17 require_once("XMLDocument.php");
20 * A class for handling iScheduling requests.
23 * @subpackage iSchedule
32 private $try_anyway = false;
33 private $failed = false;
34 private $failOnError = true;
35 private $subdomainsOK = true;
36 private $remote_public_key ;
37 private $required_headers = Array ( 'host', // draft 01 section 7.1 required headers
41 private $disallowed_headers = Array ( 'connection', // draft 01 section 7.1 disallowed headers
45 'proxy-authorization',
51 function __construct ( )
54 $this->selector
= 'cal';
55 if ( is_object ( $c ) && isset ( $c->scheduling_dkim_selector
) )
57 $this->scheduling_dkim_domain
= $c->scheduling_dkim_domain
;
58 $this->scheduling_dkim_selector
= $c->scheduling_dkim_selector
;
59 $this->schedule_private_key
= $c->schedule_private_key
;
60 if ( ! preg_match ( '/BEGIN RSA PRIVATE KEY/', $this->schedule_private_key
) )
62 $key = file_get_contents ( $this->schedule_private_key
);
64 $this->schedule_private_key
= $key;
66 if ( isset ( $c->scheduling_dkim_algo
) )
67 $this->scheduling_dkim_algo
= $c->scheduling_dkim_algo
;
69 $this->scheduling_dkim_algo
= 'sha256';
70 if ( isset ( $c->scheduling_dkim_valid_time
) )
71 $this->valid_time
= $c->scheduling_dkim_valid_time
;
76 * gets the domainkey TXT record from DNS
81 // TODO handle parents of subdomains and procuration records
82 if ( $icfg [ $this->remote_selector
. '._domainkey.' . $this->remote_server
] )
84 $this->dk
= $icfg [ $this->remote_selector
. '._domainkey.' . $this->remote_server
];
88 $dkim = dns_get_record ( $this->remote_selector
. '._domainkey.' . $this->remote_server
, DNS_TXT
);
89 if ( count ( $dkim ) > 0 )
91 $this->dk
= $dkim [ 0 ] [ 'txt' ];
92 if ( $dkim [ 0 ] [ 'entries' ] )
95 foreach ( $dkim [ 0 ] [ 'entries' ] as $v )
97 $this->dk
.= trim ( $v );
100 dbg_error_log( 'ischedule', 'getTxt '. $this->dk
. ' XX');
104 dbg_error_log( 'ischedule', 'getTxt FAILED '. print_r ( $dkim ) );
105 $this->failed
= true;
112 * strictly for testing purposes
114 function setTxt ( $dk )
120 * parses DNS TXT record from domainkey lookup
122 function parseTxt ( )
124 if ( $this->failed
== true )
126 $clean = preg_replace ( '/\s?([;=])\s?/', '$1', $this->dk
);
127 $pairs = preg_split ( '/;/', $clean );
128 $this->parsed
= array();
129 foreach ( $pairs as $v )
131 list($key,$value) = preg_split ( '/=/', $v, 2 );
132 $value = trim ( $value, '\\' );
133 if ( preg_match ( '/(g|k|n|p|s|t|v)/', $key ) )
134 $this->parsed
[ $key ] = $value;
136 $this->parsed_ignored
[ $key ] = $value;
142 * validates that domainkey is acceptable for the current request
144 function validateKey ( )
146 $this->failed
= true;
147 if ( isset ( $this->parsed
[ 's' ] ) )
149 if ( ! preg_match ( '/(\*|calendar)/', $this->parsed
[ 's' ] ) ) {
150 dbg_error_log( 'ischedule', 'validateKey ERROR: bad selector' );
151 return false; // not a wildcard or calendar key
154 if ( isset ( $this->parsed
[ 'k' ] ) && $this->parsed
[ 'k' ] != 'rsa' ) {
155 dbg_error_log( 'ischedule', 'validateKey ERROR: bad key algorythm, algo was:' . $this->parsed
[ 'k' ] );
156 return false; // we only speak rsa for now
158 if ( isset ( $this->parsed
[ 't' ] ) && ! preg_match ( '/^[y:s]+$/', $this->parsed
[ 't' ] ) ) {
159 dbg_error_log( 'ischedule', 'validateKey ERROR: type mismatch' );
164 if ( preg_match ( '/y/', $this->parsed
[ 't' ] ) )
165 $this->failOnError
= false;
166 if ( preg_match ( '/s/', $this->parsed
[ 't' ] ) )
167 $this->subdomainsOK
= false;
169 if ( isset ( $this->parsed
[ 'g' ] ) )
170 $this->remote_user_rule
= $this->parsed
[ 'g' ];
172 $this->remote_user_rule
= '*';
173 if ( isset ( $this->parsed
[ 'p' ] ) )
175 if ( preg_match ( '/[^A-Za-z0-9_=+\/]/', $this->parsed
[ 'p' ] ) )
177 $data = "-----BEGIN PUBLIC KEY-----\n" . implode ("\n",str_split ( $this->parsed
[ 'p' ], 64 )) . "\n-----END PUBLIC KEY-----";
178 if ( $data === false )
180 $this->remote_public_key
= $data;
183 dbg_error_log( 'ischedule', 'validateKey ERROR: no key in dns record' . $this->parsed
[ 'p' ] );
186 $this->failed
= false;
191 * finds a remote calendar server via DNS SRV records
193 function getServer ( )
196 if ( $icfg [ $this->domain
] )
198 $this->remote_server
= $icfg [ $this->domain
] [ 'server' ];
199 $this->remote_port
= $icfg [ $this->domain
] [ 'port' ];
200 $this->remote_ssl
= $icfg [ $this->domain
] [ 'ssl' ];
203 $this->remote_ssl
= false;
204 $parts = explode ( '.', $this->domain
);
205 $tld = $parts [ count ( $parts ) - 1 ];
207 if ( strlen ( $tld ) == 2 && in_array ( $tld, Array ( 'uk', 'nz' ) ) )
208 $len = 3; // some country code tlds should have 3 components
209 if ( $this->domain
== 'mycaldav' ||
$this->domain
== 'altcaldav' )
211 while ( count ( $parts ) >= $len )
213 $r = dns_get_record ( '_ischedules._tcp.' . implode ( '.', $parts ) , DNS_SRV
);
214 if ( 0 < count ( $r ) )
216 $remote_server = $r [ 0 ] [ 'target' ];
217 $remote_port = $r [ 0 ] [ 'port' ];
218 $this->remote_ssl
= true;
221 if ( ! isset ( $remote_server ) )
223 $r = dns_get_record ( '_ischedule._tcp.' . implode ( '.', $parts ) , DNS_SRV
);
224 if ( 0 < count ( $r ) )
226 $remote_server = $r [ 0 ] [ 'target' ];
227 $remote_port = $r [ 0 ] [ 'port' ];
231 array_shift ( $parts );
233 if ( ! isset ( $remote_server ) )
235 if ( $this->try_anyway
== true )
237 if ( ! isset ( $remote_server ) )
238 $remote_server = $this->domain
;
239 if ( ! isset ( $remote_port ) )
243 dbg_error_log('ischedule', 'Domain %s did not have srv records for iSchedule', $this->domain
);
247 dbg_error_log('ischedule', $this->domain
. ' found srv records for ' . $remote_server . ':' . $remote_port );
248 $this->remote_server
= $remote_server;
249 $this->remote_port
= $remote_port;
254 * get capabilities from remote server
256 function getCapabilities ( $domain = null )
258 if ( $domain != null && $this->domain
!= $domain )
259 $this->domain
= $domain;
260 if ( ! isset ( $this->remote_server
) && isset ( $this->domain
) && ! $this->getServer ( ) )
262 $this->remote_url
= 'http'. ( $this->remote_ssl ?
's' : '' ) . '://' .
263 $this->remote_server
. ':' . $this->remote_port
. '/.well-known/ischedule';
264 $remote_capabilities = file_get_contents ( $this->remote_url
. '?query=capabilities' );
265 if ( $remote_capabilities === false )
267 $xml_parser = xml_parser_create_ns('UTF-8');
268 $this->xml_tags
= array();
269 xml_parser_set_option ( $xml_parser, XML_OPTION_SKIP_WHITE
, 1 );
270 xml_parser_set_option ( $xml_parser, XML_OPTION_CASE_FOLDING
, 0 );
271 $rc = xml_parse_into_struct( $xml_parser, $remote_capabilities, $this->xml_tags
);
272 if ( $rc == false ) {
273 dbg_error_log( 'ERROR', 'XML parsing error: %s at line %d, column %d',
274 xml_error_string(xml_get_error_code($xml_parser)),
275 xml_get_current_line_number($xml_parser), xml_get_current_column_number($xml_parser) );
276 dbg_error_log('ischedule', $this->domain
. ' iSchedule error parsing remote xml' );
279 xml_parser_free($xml_parser);
280 $xmltree = BuildXMLTree( $this->xml_tags
);
281 if ( !is_object($xmltree) ) {
282 dbg_error_log('ischedule', $this->domain
. ' iSchedule error in remote xml' );
283 $request->DoResponse( 406, translate("REPORT body is not valid XML data!") );
286 dbg_error_log('ischedule', $this->domain
. ' got capabilites' );
287 $this->capabilities_xml
= $xmltree;
292 * query capabilities retrieved from server
294 function queryCapabilities ( $capability, $domain = null )
296 if ( ! isset ( $this->capabilities_xml
) )
298 dbg_error_log('ischedule', $this->domain
. ' capabilities not set, quering for capability:' . $capability );
299 if ( $domain == null )
301 if ( $this->domain
!= $domain )
302 $this->domain
= $domain;
303 if ( ! $this->getCapabilities ( ) )
306 switch ( $capability )
311 $comp = $this->capabilities_xml
->GetPath ( 'urn:ietf:params:xml:ns:ischedule:supported-scheduling-message-set/urn:ietf:params:xml:ns:ischedule:comp' );
312 foreach ( $comp as $c )
314 if ( $c->GetAttribute ( 'name' ) == $capability )
318 case 'VFREEBUSY/REQUEST':
320 case 'VTODO/REQUEST':
324 case 'VEVENT/REQUEST':
326 case 'VEVENT/CANCEL':
327 case 'VEVENT/PUBLISH':
328 case 'VEVENT/COUNTER':
329 case 'VEVENT/DECLINECOUNTER':
330 dbg_error_log('ischedule', $this->domain
. ' xml query' );
331 $comp = $this->capabilities_xml
->GetPath ( 'urn:ietf:params:xml:ns:ischedule:supported-scheduling-message-set/urn:ietf:params:xml:ns:ischedule:comp' );
332 list ( $component, $method ) = explode ( '/', $capability );
333 dbg_error_log('ischedule', $this->domain
. ' quering for capability:' . count ( $comp ) . ' ' . $component );
334 foreach ( $comp as $c )
336 dbg_error_log('ischedule', $this->domain
. ' quering for capability:' . $c->GetAttribute ( 'name' ) . ' == ' . $component );
337 if ( $c->GetAttribute ( 'name' ) == $component )
339 $methods = $c->GetElements ( 'urn:ietf:params:xml:ns:ischedule:method' );
340 if ( count ( $methods ) == 0 )
341 return true; // seems like we should accept everything if there are no children
342 foreach ( $methods as $m )
344 if ( $m->GetAttribute ( 'name' ) == $method )
356 * signs a POST body and headers
358 * @param string $body the body of the POST
359 * @param array $headers the headers to sign as passed to header ();
361 function signDKIM ( $headers, $body )
363 if ( $this->scheduling_dkim_domain
== null )
366 if ( is_array ( $headers ) !== true )
368 foreach ( $headers as $key => $value )
370 $b .= $key . ': ' . $value . "\r\n";
373 $dk['a'] = 'rsa-' . $this->scheduling_dkim_algo
;
374 $dk['s'] = $this->selector
;
375 $dk['d'] = $this->scheduling_dkim_domain
;
376 $dk['c'] = 'simple-http'; // implied canonicalization of simple-http/simple from rfc4871 Section-3.5
377 if ( isset ( $_SERVER['SERVER_NAME'] ) && strstr ( $_SERVER['SERVER_NAME'], $this->domain
) !== false ) // don't use when testing
378 $dk['i'] = '@' . $_SERVER['SERVER_NAME']; //optional
379 $dk['q'] = 'dns/txt'; // optional, dns/txt is the default if missing
380 $dk['l'] = strlen ( $body ); //optional
381 $dk['t'] = time ( ); // timestamp of signature, optional
382 if ( isset ( $this->valid_time
) )
383 $dk['x'] = $this->valid_time
; // unix timestamp expiriation of signature, optional
384 $dk['h'] = implode ( ':', array_keys ( $headers ) );
385 $dk['bh'] = base64_encode ( hash ( 'sha256', $body , true ) );
387 foreach ( $dk as $key => $val )
388 $value .= "$key=$val; ";
390 $tosign = $b . 'DKIM-Signature: ' . $value;
391 openssl_sign ( $tosign, $sig, $this->schedule_private_key
, $this->scheduling_dkim_algo
);
392 $this->tosign
= $tosign;
393 $value .= base64_encode ( $sig );
398 * send request to remote server
399 * $address should be an email address or an array of email addresses all with the same domain
400 * $type should be in the format COMPONENT/METHOD eg (VFREEBUSY, VEVENT/REQUEST, VEVENT/REPLY, etc. )
401 * $data is the vcalendar data N.B. must already be rendered into text format
403 function sendRequest ( $address, $type, $data )
406 if ( empty($this->scheduling_dkim_domain
) )
408 if ( is_array ( $address ) )
409 list ( $user, $domain ) = explode ( '@', $address[0] );
411 list ( $user, $domain ) = explode ( '@', $address );
412 if ( ! $this->getCapabilities ( $domain ) )
414 dbg_error_log('ischedule', $domain . ' did not have iSchedule capabilities for ' . $type );
417 dbg_error_log('ischedule', $domain . ' trying with iSchedule capabilities for ' . $type );
418 if ( $this->queryCapabilities ( $type ) )
420 dbg_error_log('ischedule', $domain . ' trying with iSchedule capabilities for ' . $type . ' OK');
421 list ( $component, $method ) = explode ( '/', $type );
422 $headers = array ( );
423 $headers['iSchedule-Version'] = '1.0';
424 $headers['Originator'] = 'mailto:' . $session->email
;
425 if ( is_array ( $address ) )
426 $headers['Recipient'] = implode ( ', ' , $address );
428 $headers['Recipient'] = $address;
429 $headers['Content-Type'] = 'text/calendar; component=' . $component ;
431 $headers['Content-Type'] .= '; method=' . $method;
432 $headers['DKIM-Signature'] = $this->signDKIM ( $headers, $body );
433 if ( $headers['DKIM-Signature'] == false )
435 $request_headers = array ( );
436 foreach ( $headers as $k => $v )
437 $request_headers[] = $k . ': ' . $v;
438 $curl = curl_init ( $this->remote_url
);
439 curl_setopt ( $curl, CURLOPT_RETURNTRANSFER
, true );
440 curl_setopt ( $curl, CURLOPT_HTTPHEADER
, array() ); // start with no headers set
441 curl_setopt ( $curl, CURLOPT_HTTPHEADER
, $request_headers );
442 curl_setopt ( $curl, CURLOPT_SSL_VERIFYPEER
, false);
443 curl_setopt ( $curl, CURLOPT_SSL_VERIFYHOST
, false);
444 curl_setopt ( $curl, CURLOPT_POST
, 1);
445 curl_setopt ( $curl, CURLOPT_POSTFIELDS
, $data);
446 curl_setopt ( $curl, CURLOPT_CUSTOMREQUEST
, 'POST' );
447 $xmlresponse = curl_exec ( $curl );
448 $info = curl_getinfo ( $curl );
449 curl_close ( $curl );
450 if ( $info['http_code'] >= 400 )
452 dbg_error_log ( 'ischedule', 'remote server returned error (%s)', $info['http_code'] );
456 error_log ( 'remote response '. $xmlresponse . print_r ( $info, true ) );
457 $xml_parser = xml_parser_create_ns('UTF-8');
459 xml_parser_set_option ( $xml_parser, XML_OPTION_SKIP_WHITE
, 1 );
460 xml_parser_set_option ( $xml_parser, XML_OPTION_CASE_FOLDING
, 0 );
461 $rc = xml_parse_into_struct( $xml_parser, $xmlresponse, $xml_tags );
462 if ( $rc == false ) {
463 dbg_error_log( 'ERROR', 'XML parsing error: %s at line %d, column %d',
464 xml_error_string(xml_get_error_code($xml_parser)),
465 xml_get_current_line_number($xml_parser), xml_get_current_column_number($xml_parser) );
468 $xmltree = BuildXMLTree( $xml_tags );
469 xml_parser_free($xml_parser);
470 if ( !is_object($xmltree) ) {
471 dbg_error_log( 'ERROR', 'iSchedule RESPONSE body is not valid XML data!' );
474 $resp = $xmltree->GetPath ( '/*/urn:ietf:params:xml:ns:ischedule:response' );
476 foreach ( $resp as $r )
478 $recipient = $r->GetElements ( 'urn:ietf:params:xml:ns:ischedule:recipient' );
479 $status = $r->GetElements ( 'urn:ietf:params:xml:ns:ischedule:request-status' );
480 $calendardata = $r->GetElements ( 'urn:ietf:params:xml:ns:ischedule:calendar-data' );
481 if ( count ( $recipient ) < 1 )
482 continue; // this should be an error
483 if ( count ( $calendardata ) > 0 )
485 $result [ $recipient[0]->GetContent() ] = $calendardata[0]->GetContent();
489 $result [ $recipient[0]->GetContent() ] = $status[0]->GetContent();
492 if ( count ( $result ) < 1 )
502 * parses and validates DK header
504 * @param string $sig the value of the DKIM-Signature header
506 function parseDKIM ( $sig )
509 $this->failed
= true;
510 $tags = preg_split ( '/;[\s\t]/', $sig );
511 foreach ( $tags as $v )
513 list($key,$value) = preg_split ( '/=/', $v, 2 );
514 $dkim[$key] = $value;
516 // the canonicalization method is currently undefined as of draft-01 of the iSchedule spec
517 // but it does define the value, it should be simple-http. RFC4871 also defines two methods
518 // simple and relaxed, simple is probably the same as simple http
519 // relaxed allows for header case folding and whitespace folding, see section 3.4.4 of RFC4871
520 if ( ! preg_match ( '{(simple|simple-http|relaxed)(/(simple|simple-http|relaxed))?}', $dkim['c'], $matches ) ) // canonicalization method
521 return 'bad canonicalization:' . $dkim['c'] ;
522 if ( count ( $matches ) > 2 )
523 $this->body_cannon
= $matches[2];
525 $this->body_cannon
= $matches[1];
526 $this->header_cannon
= $matches[1];
527 // signing algorythm REQUIRED
528 if ( $dkim['a'] != 'rsa-sha1' && $dkim['a'] != 'rsa-sha256' ) // we only support the minimum required
529 return 'bad signing algorythm:' . $dkim['a'] ;
530 // query method to retrieve public key, could/should we add https to the spec? REQUIRED
531 if ( $dkim['q'] != 'dns/txt' )
532 return 'bad query method';
533 // domain of the signing entity REQUIRED
534 if ( ! isset ( $dkim['d'] ) )
535 return 'missing signing domain';
536 $this->remote_server
= $dkim['d'];
537 // identity of signing AGENT, OPTIONAL
538 if ( isset ( $dkim['i'] ) )
540 // if present, domain of the signing agent must be a match or a subdomain of the signing domain
541 if ( ! stristr ( $dkim['i'], $dkim['d'] ) ) // RFC4871 does not specify a case match requirement
542 return 'signing domain mismatch';
543 // grab the local part of the signing agent if it's an email address
544 if ( strstr ( $dkim [ 'i' ], '@' ) )
545 $this->remote_user
= substr ( $dkim [ 'i' ], 0, strpos ( $dkim [ 'i' ], '@' ) - 1 );
547 // selector used to retrieve public key REQUIRED
548 if ( ! isset ( $dkim['s'] ) )
549 return 'missing selector';
550 $this->remote_selector
= $dkim['s'];
551 // signed header fields, colon seperated REQUIRED
552 if ( ! isset ( $dkim['h'] ) )
553 return 'missing list of signed headers';
554 $this->signed_headers
= preg_split ( '/:/', $dkim['h'] );
557 foreach ( $this->signed_headers
as $h )
559 $sh[] = strtolower ( $h );
560 if ( in_array ( strtolower ( $h ), $this->disallowed_headers
) )
561 return "$h is NOT allowed in signed header fields per RFC4871 or iSchedule";
563 foreach ( $this->required_headers
as $h )
564 if ( ! in_array ( strtolower ( $h ), $sh ) )
565 return "$h is REQUIRED but missing in signed header fields per iSchedule";
566 // body hash REQUIRED
567 if ( ! isset ( $dkim['bh'] ) )
568 return 'missing body signature';
569 // signed header hash REQUIRED
570 if ( ! isset ( $dkim['b'] ) )
571 return 'missing signature in b field';
572 // length of body used for signing
573 if ( isset ( $dkim['l'] ) )
574 $this->signed_length
= $dkim['l'];
575 $this->failed
= false;
576 $this->DKSig
= $dkim;
581 * split up a mailto uri into domain and user components
582 * TODO handle other uri types (eg http)
584 function parseURI ( $uri )
586 if ( preg_match ( '/^mailto:([^@]+)@([^\s\t\n]+)/', $uri, $matches ) )
588 $this->remote_user
= $matches[1];
589 $this->domain
= $matches[2];
596 * verifies parsed DKIM header is valid for current message with a signature from the public key in DNS
597 * TODO handle multiple headers of the same name
599 function verifySignature ( )
602 $this->failed
= true;
604 foreach ( $this->signed_headers
as $h )
605 if ( isset ( $_SERVER['HTTP_' . strtoupper ( strtr ( $h, '-', '_' ) ) ] ) )
606 $signed .= "$h: " . $_SERVER['HTTP_' . strtoupper ( strtr ( $h, '-', '_' ) ) ] . "\r\n";
608 $signed .= "$h: " . $_SERVER[ strtoupper ( strtr ( $h, '-', '_' ) ) ] . "\r\n";
609 if ( ! isset ( $_SERVER['HTTP_ORIGINATOR'] ) ||
stripos ( $signed, 'Originator' ) === false ) //required header, must be signed
610 return "missing Originator";
611 if ( ! isset ( $_SERVER['HTTP_RECIPIENT'] ) ||
stripos ( $signed, 'Recipient' ) === false ) //required header, must be signed
612 return "missing Recipient";
613 if ( ! isset ( $_SERVER['HTTP_ISCHEDULE_VERSION'] ) ||
$_SERVER['HTTP_ISCHEDULE_VERSION'] != '1' ) //required header and we only speak version 1 for now
614 return "missing or mismatch ischedule-version header";
615 $body = $request->raw_post
;
616 if ( ! isset ( $this->signed_length
) ) // Should we use the Content-Length header if the signed length is missing?
617 $this->signed_length
= strlen ( $body );
619 $body = substr ( $body, 0, $this->signed_length
);
620 if ( isset ( $this->remote_user_rule
) )
621 if ( $this->remote_user_rule
!= '*' && ! stristr ( $this->remote_user
, $this->remote_user_rule
) )
622 return "remote user rule failure";
623 $hash_algo = preg_replace ( '/^.*(sha1|sha256).*/','$1', $this->DKSig
['a'] );
624 $body_hash = base64_encode ( hash ( $hash_algo, $body , true ) );
625 if ( $this->DKSig
['bh'] != $body_hash )
626 return "body hash mismatch";
627 $sig = $_SERVER['HTTP_DKIM_SIGNATURE'];
628 $sig = preg_replace ( '/ b=[^;\s\r\n\t]+/', ' b=', $sig );
629 $signed .= 'DKIM-Signature: ' . $sig;
630 $verify = openssl_verify ( $signed, base64_decode ( $this->DKSig
['b'] ), $this->remote_public_key
, $hash_algo );
633 openssl_sign ( $signed, $sigb, $this->schedule_private_key
, $hash_algo );
634 $sigc = base64_encode ( $sigb );
635 $verify1 = openssl_verify ( $signed, $sigc, $this->remote_public_key
, $hash_algo );
636 return "signature verification failed " . $this->remote_public_key
. " \n\n". $sig . " \n" . $hash_algo . "\n". print_r ($verify,1) . " XX " . $verify1 . "\n";
638 $this->failed
= false;
643 * checks that current request has a valid DKIM signature signed by a currently valid key from DNS
645 function validateRequest ( )
648 if ( isset ( $_SERVER['HTTP_DKIM_SIGNATURE'] ) )
649 $sig = $_SERVER['HTTP_DKIM_SIGNATURE'];
652 $request->DoResponse( 403, translate('DKIM signature missing') );
655 if ( isset ( $_SERVER['HTTP_ORGANIZER'] ) )
656 $request->DoResponse( 403, translate('Organizer Missing') );
658 dbg_error_log ('ischedule','beginning validation');
659 $err = $this->parseDKIM ( $sig );
660 if ( $err !== true ||
$this->failed
)
661 $request->DoResponse( 412, 'DKIM signature invalid ' . "\n" . $err . "\n" );
662 if ( ! $this->getTxt () ||
$this->failed
) // this could also be a 424 failed dependency response
663 $request->DoResponse( 400, translate('DKIM signature validation failed(DNS ERROR)') );
664 if ( ! $this->parseTxt () ||
$this->failed
)
665 $request->DoResponse( 400, translate('DKIM signature validation failed(KEY Parse ERROR)') );
666 if ( ! $this->validateKey () ||
$this->failed
)
667 $request->DoResponse( 400, translate('DKIM signature validation failed(KEY Validation ERROR)') );
668 $err = $this->verifySignature ();
669 if ( $err !== true ||
$this->failed
)
670 $request->DoResponse( 412, translate('DKIM signature validation failed(Signature verification ERROR)') . '\n' . $err );
671 dbg_error_log ('ischedule','signature ok');