]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - plugins/CasAuthentication/extlib/CAS/client.php
Merge commit 'origin/testing' into 0.9.x
[quix0rs-gnu-social.git] / plugins / CasAuthentication / extlib / CAS / client.php
1 <?php
2
3 /**
4  * @file CAS/client.php
5  * Main class of the phpCAS library
6  */
7
8 // include internationalization stuff
9 include_once(dirname(__FILE__).'/languages/languages.php');
10
11 // include PGT storage classes
12 include_once(dirname(__FILE__).'/PGTStorage/pgt-main.php');
13
14 /**
15  * @class CASClient
16  * The CASClient class is a client interface that provides CAS authentication
17  * to PHP applications.
18  *
19  * @author Pascal Aubry <pascal.aubry at univ-rennes1.fr>
20  */
21
22 class CASClient
23 {
24         
25         // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
26         // XX                                                                    XX
27         // XX                          CONFIGURATION                             XX
28         // XX                                                                    XX
29         // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
30         
31         // ########################################################################
32         //  HTML OUTPUT
33         // ########################################################################
34         /**
35          * @addtogroup internalOutput
36          * @{
37          */  
38         
39         /**
40          * This method filters a string by replacing special tokens by appropriate values
41          * and prints it. The corresponding tokens are taken into account:
42          * - __CAS_VERSION__
43          * - __PHPCAS_VERSION__
44          * - __SERVER_BASE_URL__
45          *
46          * Used by CASClient::PrintHTMLHeader() and CASClient::printHTMLFooter().
47          *
48          * @param $str the string to filter and output
49          *
50          * @private
51          */
52         function HTMLFilterOutput($str)
53                 {
54                 $str = str_replace('__CAS_VERSION__',$this->getServerVersion(),$str);
55                 $str = str_replace('__PHPCAS_VERSION__',phpCAS::getVersion(),$str);
56                 $str = str_replace('__SERVER_BASE_URL__',$this->getServerBaseURL(),$str);
57                 echo $str;
58                 }
59         
60         /**
61          * A string used to print the header of HTML pages. Written by CASClient::setHTMLHeader(),
62          * read by CASClient::printHTMLHeader().
63          *
64          * @hideinitializer
65          * @private
66          * @see CASClient::setHTMLHeader, CASClient::printHTMLHeader()
67          */
68         var $_output_header = '';
69         
70         /**
71          * This method prints the header of the HTML output (after filtering). If
72          * CASClient::setHTMLHeader() was not used, a default header is output.
73          *
74          * @param $title the title of the page
75          *
76          * @see HTMLFilterOutput()
77          * @private
78          */
79         function printHTMLHeader($title)
80                 {
81                 $this->HTMLFilterOutput(str_replace('__TITLE__',
82                         $title,
83                         (empty($this->_output_header)
84                                         ? '<html><head><title>__TITLE__</title></head><body><h1>__TITLE__</h1>'
85                                                         : $this->_output_header)
86                 )
87                 );
88                 }
89         
90         /**
91          * A string used to print the footer of HTML pages. Written by CASClient::setHTMLFooter(),
92          * read by printHTMLFooter().
93          *
94          * @hideinitializer
95          * @private
96          * @see CASClient::setHTMLFooter, CASClient::printHTMLFooter()
97          */
98         var $_output_footer = '';
99         
100         /**
101          * This method prints the footer of the HTML output (after filtering). If
102          * CASClient::setHTMLFooter() was not used, a default footer is output.
103          *
104          * @see HTMLFilterOutput()
105          * @private
106          */
107         function printHTMLFooter()
108                 {
109                 $this->HTMLFilterOutput(empty($this->_output_footer)
110                         ?('<hr><address>phpCAS __PHPCAS_VERSION__ '.$this->getString(CAS_STR_USING_SERVER).' <a href="__SERVER_BASE_URL__">__SERVER_BASE_URL__</a> (CAS __CAS_VERSION__)</a></address></body></html>')
111                                         :$this->_output_footer);
112                 }
113         
114         /**
115          * This method set the HTML header used for all outputs.
116          *
117          * @param $header the HTML header.
118          *
119          * @public
120          */
121         function setHTMLHeader($header)
122                 {
123                 $this->_output_header = $header;
124                 }
125         
126         /**
127          * This method set the HTML footer used for all outputs.
128          *
129          * @param $footer the HTML footer.
130          *
131          * @public
132          */
133         function setHTMLFooter($footer)
134                 {
135                 $this->_output_footer = $footer;
136                 }
137         
138         /** @} */
139         // ########################################################################
140         //  INTERNATIONALIZATION
141         // ########################################################################
142         /**
143          * @addtogroup internalLang
144          * @{
145          */  
146         /**
147          * A string corresponding to the language used by phpCAS. Written by 
148          * CASClient::setLang(), read by CASClient::getLang().
149          
150          * @note debugging information is always in english (debug purposes only).
151          *
152          * @hideinitializer
153          * @private
154          * @sa CASClient::_strings, CASClient::getString()
155          */
156         var $_lang = '';
157         
158         /**
159          * This method returns the language used by phpCAS.
160          *
161          * @return a string representing the language
162          *
163          * @private
164          */
165         function getLang()
166                 {
167                 if ( empty($this->_lang) )
168                         $this->setLang(PHPCAS_LANG_DEFAULT);
169                 return $this->_lang;
170                 }
171         
172         /**
173          * array containing the strings used by phpCAS. Written by CASClient::setLang(), read by 
174          * CASClient::getString() and used by CASClient::setLang().
175          *
176          * @note This array is filled by instructions in CAS/languages/<$this->_lang>.php
177          *
178          * @private
179          * @see CASClient::_lang, CASClient::getString(), CASClient::setLang(), CASClient::getLang()
180          */
181         var $_strings;
182         
183         /**
184          * This method returns a string depending on the language.
185          *
186          * @param $str the index of the string in $_string.
187          *
188          * @return the string corresponding to $index in $string.
189          *
190          * @private
191          */
192         function getString($str)
193                 {
194                 // call CASclient::getLang() to be sure the language is initialized
195                 $this->getLang();
196                 
197                 if ( !isset($this->_strings[$str]) ) {
198                         trigger_error('string `'.$str.'\' not defined for language `'.$this->getLang().'\'',E_USER_ERROR);
199                 }
200                 return $this->_strings[$str];
201                 }
202         
203         /**
204          * This method is used to set the language used by phpCAS. 
205          * @note Can be called only once.
206          *
207          * @param $lang a string representing the language.
208          *
209          * @public
210          * @sa CAS_LANG_FRENCH, CAS_LANG_ENGLISH
211          */
212         function setLang($lang)
213                 {
214                 // include the corresponding language file
215                 include_once(dirname(__FILE__).'/languages/'.$lang.'.php');
216                 
217                 if ( !is_array($this->_strings) ) {
218                         trigger_error('language `'.$lang.'\' is not implemented',E_USER_ERROR);
219                 }
220                 $this->_lang = $lang;
221                 }
222         
223         /** @} */
224         // ########################################################################
225         //  CAS SERVER CONFIG
226         // ########################################################################
227         /**
228          * @addtogroup internalConfig
229          * @{
230          */  
231         
232         /**
233          * a record to store information about the CAS server.
234          * - $_server["version"]: the version of the CAS server
235          * - $_server["hostname"]: the hostname of the CAS server
236          * - $_server["port"]: the port the CAS server is running on
237          * - $_server["uri"]: the base URI the CAS server is responding on
238          * - $_server["base_url"]: the base URL of the CAS server
239          * - $_server["login_url"]: the login URL of the CAS server
240          * - $_server["service_validate_url"]: the service validating URL of the CAS server
241          * - $_server["proxy_url"]: the proxy URL of the CAS server
242          * - $_server["proxy_validate_url"]: the proxy validating URL of the CAS server
243          * - $_server["logout_url"]: the logout URL of the CAS server
244          *
245          * $_server["version"], $_server["hostname"], $_server["port"] and $_server["uri"]
246          * are written by CASClient::CASClient(), read by CASClient::getServerVersion(), 
247          * CASClient::getServerHostname(), CASClient::getServerPort() and CASClient::getServerURI().
248          *
249          * The other fields are written and read by CASClient::getServerBaseURL(), 
250          * CASClient::getServerLoginURL(), CASClient::getServerServiceValidateURL(), 
251          * CASClient::getServerProxyValidateURL() and CASClient::getServerLogoutURL().
252          *
253          * @hideinitializer
254          * @private
255          */
256         var $_server = array(
257                 'version' => -1,
258                 'hostname' => 'none',
259                 'port' => -1,
260                 'uri' => 'none'
261         );
262         
263         /**
264          * This method is used to retrieve the version of the CAS server.
265          * @return the version of the CAS server.
266          * @private
267          */
268         function getServerVersion()
269                 { 
270                 return $this->_server['version']; 
271                 }
272         
273         /**
274          * This method is used to retrieve the hostname of the CAS server.
275          * @return the hostname of the CAS server.
276          * @private
277          */
278         function getServerHostname()
279                 { return $this->_server['hostname']; }
280         
281         /**
282          * This method is used to retrieve the port of the CAS server.
283          * @return the port of the CAS server.
284          * @private
285          */
286         function getServerPort()
287                 { return $this->_server['port']; }
288         
289         /**
290          * This method is used to retrieve the URI of the CAS server.
291          * @return a URI.
292          * @private
293          */
294         function getServerURI()
295                 { return $this->_server['uri']; }
296         
297         /**
298          * This method is used to retrieve the base URL of the CAS server.
299          * @return a URL.
300          * @private
301          */
302         function getServerBaseURL()
303                 { 
304                 // the URL is build only when needed
305                 if ( empty($this->_server['base_url']) ) {
306                         $this->_server['base_url'] = 'https://'
307                                 .$this->getServerHostname()
308                                 .':'
309                                 .$this->getServerPort()
310                                 .$this->getServerURI();
311                 }
312                 return $this->_server['base_url']; 
313                 }
314         
315         /**
316          * This method is used to retrieve the login URL of the CAS server.
317          * @param $gateway true to check authentication, false to force it
318          * @param $renew true to force the authentication with the CAS server
319          * NOTE : It is recommended that CAS implementations ignore the
320          "gateway" parameter if "renew" is set
321          * @return a URL.
322          * @private
323          */
324         function getServerLoginURL($gateway=false,$renew=false) {
325                 phpCAS::traceBegin();
326                 // the URL is build only when needed
327                 if ( empty($this->_server['login_url']) ) {
328                         $this->_server['login_url'] = $this->getServerBaseURL();
329                         $this->_server['login_url'] .= 'login?service=';
330                         // $this->_server['login_url'] .= preg_replace('/&/','%26',$this->getURL());
331                         $this->_server['login_url'] .= urlencode($this->getURL());
332                         if($renew) {
333                                 // It is recommended that when the "renew" parameter is set, its value be "true"
334                                 $this->_server['login_url'] .= '&renew=true';
335                         } elseif ($gateway) {
336                                 // It is recommended that when the "gateway" parameter is set, its value be "true"
337                                 $this->_server['login_url'] .= '&gateway=true';
338                         }
339                 }
340                 phpCAS::traceEnd($this->_server['login_url']);
341                 return $this->_server['login_url'];
342         } 
343         
344         /**
345          * This method sets the login URL of the CAS server.
346          * @param $url the login URL
347          * @private
348          * @since 0.4.21 by Wyman Chan
349          */
350         function setServerLoginURL($url)
351                 {
352                 return $this->_server['login_url'] = $url;
353                 }
354                 
355                 
356         /**
357          * This method sets the serviceValidate URL of the CAS server.
358          * @param $url the serviceValidate URL
359          * @private
360          * @since 1.1.0 by Joachim Fritschi
361          */
362         function setServerServiceValidateURL($url)
363                 {
364                 return $this->_server['service_validate_url'] = $url;
365                 }
366                 
367                 
368         /**
369          * This method sets the proxyValidate URL of the CAS server.
370          * @param $url the proxyValidate URL
371          * @private
372          * @since 1.1.0 by Joachim Fritschi
373          */
374         function setServerProxyValidateURL($url)
375                 {
376                 return $this->_server['proxy_validate_url'] = $url;
377                 }
378                 
379                 
380         /**
381          * This method sets the samlValidate URL of the CAS server.
382          * @param $url the samlValidate URL
383          * @private
384          * @since 1.1.0 by Joachim Fritschi
385          */
386         function setServerSamlValidateURL($url)
387                 {
388                 return $this->_server['saml_validate_url'] = $url;
389                 }
390                         
391         
392         /**
393          * This method is used to retrieve the service validating URL of the CAS server.
394          * @return a URL.
395          * @private
396          */
397         function getServerServiceValidateURL()
398                 { 
399                 // the URL is build only when needed
400                 if ( empty($this->_server['service_validate_url']) ) {
401                         switch ($this->getServerVersion()) {
402                                 case CAS_VERSION_1_0:
403                                         $this->_server['service_validate_url'] = $this->getServerBaseURL().'validate';
404                                         break;
405                                 case CAS_VERSION_2_0:
406                                         $this->_server['service_validate_url'] = $this->getServerBaseURL().'serviceValidate';
407                                         break;
408                         }
409                 }
410                 //      return $this->_server['service_validate_url'].'?service='.preg_replace('/&/','%26',$this->getURL()); 
411                 return $this->_server['service_validate_url'].'?service='.urlencode($this->getURL()); 
412                 }
413         /**
414         * This method is used to retrieve the SAML validating URL of the CAS server.
415         * @return a URL.
416         * @private
417         */
418         function getServerSamlValidateURL()
419         {
420         phpCAS::traceBegin();
421         // the URL is build only when needed
422         if ( empty($this->_server['saml_validate_url']) ) {
423                 switch ($this->getServerVersion()) {
424                 case SAML_VERSION_1_1:
425                         $this->_server['saml_validate_url'] = $this->getServerBaseURL().'samlValidate';
426                         break;
427                         }
428         }
429         phpCAS::traceEnd($this->_server['saml_validate_url'].'?TARGET='.urlencode($this->getURL()));
430         return $this->_server['saml_validate_url'].'?TARGET='.urlencode($this->getURL());
431         }
432         /**
433          * This method is used to retrieve the proxy validating URL of the CAS server.
434          * @return a URL.
435          * @private
436          */
437         function getServerProxyValidateURL()
438                 { 
439                 // the URL is build only when needed
440                 if ( empty($this->_server['proxy_validate_url']) ) {
441                         switch ($this->getServerVersion()) {
442                                 case CAS_VERSION_1_0:
443                                         $this->_server['proxy_validate_url'] = '';
444                                         break;
445                                 case CAS_VERSION_2_0:
446                                         $this->_server['proxy_validate_url'] = $this->getServerBaseURL().'proxyValidate';
447                                         break;
448                         }
449                 }
450                 //      return $this->_server['proxy_validate_url'].'?service='.preg_replace('/&/','%26',$this->getURL()); 
451                 return $this->_server['proxy_validate_url'].'?service='.urlencode($this->getURL()); 
452                 }
453         
454         /**
455          * This method is used to retrieve the proxy URL of the CAS server.
456          * @return a URL.
457          * @private
458          */
459         function getServerProxyURL()
460                 { 
461                 // the URL is build only when needed
462                 if ( empty($this->_server['proxy_url']) ) {
463                         switch ($this->getServerVersion()) {
464                                 case CAS_VERSION_1_0:
465                                         $this->_server['proxy_url'] = '';
466                                         break;
467                                 case CAS_VERSION_2_0:
468                                         $this->_server['proxy_url'] = $this->getServerBaseURL().'proxy';
469                                         break;
470                         }
471                 }
472                 return $this->_server['proxy_url']; 
473                 }
474         
475         /**
476          * This method is used to retrieve the logout URL of the CAS server.
477          * @return a URL.
478          * @private
479          */
480         function getServerLogoutURL()
481                 { 
482                 // the URL is build only when needed
483                 if ( empty($this->_server['logout_url']) ) {
484                         $this->_server['logout_url'] = $this->getServerBaseURL().'logout';
485                 }
486                 return $this->_server['logout_url']; 
487                 }
488         
489         /**
490          * This method sets the logout URL of the CAS server.
491          * @param $url the logout URL
492          * @private
493          * @since 0.4.21 by Wyman Chan
494          */
495         function setServerLogoutURL($url)
496                 {
497                 return $this->_server['logout_url'] = $url;
498                 }
499
500         /**
501          * An array to store extra curl options.
502          */     
503         var $_curl_options = array();
504
505         /**
506          * This method is used to set additional user curl options.
507          */
508         function setExtraCurlOption($key, $value)
509         {
510                 $this->_curl_options[$key] = $value;
511         }
512  
513         /**
514          * This method checks to see if the request is secured via HTTPS
515          * @return true if https, false otherwise
516          * @private
517          */
518         function isHttps() {
519                 //if ( isset($_SERVER['HTTPS']) && !empty($_SERVER['HTTPS']) ) {
520                 //0.4.24 by Hinnack
521                 if ( isset($_SERVER['HTTPS']) && !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') {
522                         return true;
523                 } else {
524                         return false;
525                 }
526         }
527         
528         // ########################################################################
529         //  CONSTRUCTOR
530         // ########################################################################
531         /**
532          * CASClient constructor.
533          *
534          * @param $server_version the version of the CAS server
535          * @param $proxy TRUE if the CAS client is a CAS proxy, FALSE otherwise
536          * @param $server_hostname the hostname of the CAS server
537          * @param $server_port the port the CAS server is running on
538          * @param $server_uri the URI the CAS server is responding on
539          * @param $start_session Have phpCAS start PHP sessions (default true)
540          *
541          * @return a newly created CASClient object
542          *
543          * @public
544          */
545         function CASClient(
546                                            $server_version,
547                                            $proxy,
548                                            $server_hostname,
549                                            $server_port,
550                                            $server_uri,
551                                            $start_session = true) {
552                 
553                 phpCAS::traceBegin();
554                 
555                 // the redirect header() call and DOM parsing code from domxml-php4-php5.php won't work in PHP4 compatibility mode
556                 if (version_compare(PHP_VERSION,'5','>=') && ini_get('zend.ze1_compatibility_mode')) {
557                         phpCAS::error('phpCAS cannot support zend.ze1_compatibility_mode. Sorry.');
558                 }
559                 // skip Session Handling for logout requests and if don't want it'
560                 if ($start_session && !$this->isLogoutRequest()) {
561                         phpCAS::trace("Starting session handling");
562                         // Check for Tickets from the CAS server
563                         if (empty($_GET['ticket'])){
564                                 phpCAS::trace("No ticket found");
565                                 // only create a session if necessary
566                                 if (!isset($_SESSION)) {
567                                         phpCAS::trace("No session found, creating new session");
568                                         session_start();
569                                 }
570                         }else{
571                                 phpCAS::trace("Ticket found");
572                                 // We have to copy any old data before renaming the session
573                                 if (isset($_SESSION)) {
574                                         phpCAS::trace("Old active session found, saving old data and destroying session");
575                                         $old_session = $_SESSION;
576                                         session_destroy();      
577                                 }else{
578                                         session_start();
579                                         phpCAS::trace("Starting possible old session to copy variables");
580                                         $old_session = $_SESSION;
581                                         session_destroy();      
582                                 }
583                                 // set up a new session, of name based on the ticket
584                                 $session_id = preg_replace('/[^\w]/','',$_GET['ticket']);
585                                 phpCAS::LOG("Session ID: " . $session_id);
586                                 session_id($session_id);
587                                 session_start();
588                                 // restore old session vars
589                                 if(isset($old_session)){
590                                         phpCAS::trace("Restoring old session vars");
591                                         $_SESSION = $old_session;
592                                 }
593                         }
594                 }else{
595                         phpCAS::trace("Skipping session creation");
596                 }
597
598                 
599                 // are we in proxy mode ?
600                 $this->_proxy = $proxy;
601                 
602                 //check version
603                 switch ($server_version) {
604                         case CAS_VERSION_1_0:
605                                 if ( $this->isProxy() )
606                                         phpCAS::error('CAS proxies are not supported in CAS '
607                                                 .$server_version);
608                                 break;
609                         case CAS_VERSION_2_0:
610                                 break;
611                         case SAML_VERSION_1_1:
612                                 break;
613                         default:
614                                 phpCAS::error('this version of CAS (`'
615                                         .$server_version
616                                         .'\') is not supported by phpCAS '
617                                         .phpCAS::getVersion());
618                 }
619                 $this->_server['version'] = $server_version;
620                 
621                 // check hostname
622                 if ( empty($server_hostname) 
623                                 || !preg_match('/[\.\d\-abcdefghijklmnopqrstuvwxyz]*/',$server_hostname) ) {
624                         phpCAS::error('bad CAS server hostname (`'.$server_hostname.'\')');
625                 }
626                 $this->_server['hostname'] = $server_hostname;
627                 
628                 // check port
629                 if ( $server_port == 0 
630                         || !is_int($server_port) ) {
631                         phpCAS::error('bad CAS server port (`'.$server_hostname.'\')');
632                 }
633                 $this->_server['port'] = $server_port;
634                 
635                 // check URI
636                 if ( !preg_match('/[\.\d\-_abcdefghijklmnopqrstuvwxyz\/]*/',$server_uri) ) {
637                         phpCAS::error('bad CAS server URI (`'.$server_uri.'\')');
638                 }
639                 // add leading and trailing `/' and remove doubles      
640                 $server_uri = preg_replace('/\/\//','/','/'.$server_uri.'/');
641                 $this->_server['uri'] = $server_uri;
642                 
643                 // set to callback mode if PgtIou and PgtId CGI GET parameters are provided 
644                 if ( $this->isProxy() ) {
645                         $this->setCallbackMode(!empty($_GET['pgtIou'])&&!empty($_GET['pgtId']));
646                 }
647                 
648                 if ( $this->isCallbackMode() ) {
649                         //callback mode: check that phpCAS is secured
650                         if ( !$this->isHttps() ) {
651                                 phpCAS::error('CAS proxies must be secured to use phpCAS; PGT\'s will not be received from the CAS server');
652                         }
653                 } else {
654                         //normal mode: get ticket and remove it from CGI parameters for developpers
655                         $ticket = (isset($_GET['ticket']) ? $_GET['ticket'] : null);
656                         switch ($this->getServerVersion()) {
657                                 case CAS_VERSION_1_0: // check for a Service Ticket
658                                         if( preg_match('/^ST-/',$ticket) ) {
659                                                 phpCAS::trace('ST \''.$ticket.'\' found');
660                                                 //ST present
661                                                 $this->setST($ticket);
662                                                 //ticket has been taken into account, unset it to hide it to applications
663                                                 unset($_GET['ticket']);
664                                         } else if ( !empty($ticket) ) {
665                                                 //ill-formed ticket, halt
666                                                 phpCAS::error('ill-formed ticket found in the URL (ticket=`'.htmlentities($ticket).'\')');
667                                         }
668                                         break;
669                                 case CAS_VERSION_2_0: // check for a Service or Proxy Ticket
670                                         if (preg_match('/^ST-/', $ticket)) {
671                                                 phpCAS::trace('ST \'' . $ticket . '\' found');
672                                                 $this->setST($ticket);
673                                                 unset ($_GET['ticket']);
674                                         } else if (preg_match('/^PT-/', $ticket)) {
675                                                 phpCAS::trace('PT \'' . $ticket . '\' found');
676                                                 $this->setPT($ticket);
677                                                 unset($_GET['ticket']);
678                                         } else if ( !empty($ticket) ) {
679                                                 //ill-formed ticket, halt
680                                                 phpCAS::error('ill-formed ticket found in the URL (ticket=`'.htmlentities($ticket).'\')');
681                                         } 
682                                         break;
683                                 case SAML_VERSION_1_1: // SAML just does Service Tickets
684                                         if( preg_match('/^[SP]T-/',$ticket) ) {
685                                         phpCAS::trace('SA \''.$ticket.'\' found');
686                                         $this->setSA($ticket);
687                                         unset($_GET['ticket']);
688                                         } else if ( !empty($ticket) ) {
689                                                 //ill-formed ticket, halt
690                                                 phpCAS::error('ill-formed ticket found in the URL (ticket=`'.htmlentities($ticket).'\')');
691                                         }
692                                         break;
693                         }
694                 }
695                 phpCAS::traceEnd();
696         }
697         
698         /** @} */
699         
700         // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
701         // XX                                                                    XX
702         // XX                           AUTHENTICATION                           XX
703         // XX                                                                    XX
704         // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
705         
706         /**
707          * @addtogroup internalAuthentication
708          * @{
709          */  
710         
711         /**
712          * The Authenticated user. Written by CASClient::setUser(), read by CASClient::getUser().
713          * @attention client applications should use phpCAS::getUser().
714          *
715          * @hideinitializer
716          * @private
717          */
718         var $_user = '';
719         
720         /**
721          * This method sets the CAS user's login name.
722          *
723          * @param $user the login name of the authenticated user.
724          *
725          * @private
726          */
727         function setUser($user)
728                 {
729                 $this->_user = $user;
730                 }
731         
732         /**
733          * This method returns the CAS user's login name.
734          * @warning should be called only after CASClient::forceAuthentication() or 
735          * CASClient::isAuthenticated(), otherwise halt with an error.
736          *
737          * @return the login name of the authenticated user
738          */
739         function getUser()
740                 {
741                 if ( empty($this->_user) ) {
742                         phpCAS::error('this method should be used only after '.__CLASS__.'::forceAuthentication() or '.__CLASS__.'::isAuthenticated()');
743                 }
744                 return $this->_user;
745                 }
746
747
748         
749         /***********************************************************************************************************************
750          * Atrributes section
751          * 
752          * @author Matthias Crauwels <matthias.crauwels@ugent.be>, Ghent University, Belgium
753          * 
754          ***********************************************************************************************************************/
755         /**
756          * The Authenticated users attributes. Written by CASClient::setAttributes(), read by CASClient::getAttributes().
757          * @attention client applications should use phpCAS::getAttributes().
758          *
759          * @hideinitializer
760          * @private
761          */     
762         var $_attributes = array();
763
764         function setAttributes($attributes)     
765                 { $this->_attributes = $attributes; }
766                 
767         function getAttributes() {
768                 if ( empty($this->_user) ) { // if no user is set, there shouldn't be any attributes also...
769                         phpCAS::error('this method should be used only after '.__CLASS__.'::forceAuthentication() or '.__CLASS__.'::isAuthenticated()');
770                 }
771                 return $this->_attributes;
772         }
773                 
774         function hasAttributes()
775                 { return !empty($this->_attributes); }
776                 
777         function hasAttribute($key)
778                 { return (is_array($this->_attributes) && array_key_exists($key, $this->_attributes)); }
779                 
780         function getAttribute($key)     {
781                 if($this->hasAttribute($key)) {
782                         return $this->_attributes[$key];
783                 }
784         }
785         
786         /**
787          * This method is called to renew the authentication of the user
788          * If the user is authenticated, renew the connection
789          * If not, redirect to CAS
790          * @public
791          */
792         function renewAuthentication(){
793                 phpCAS::traceBegin();
794                 // Either way, the user is authenticated by CAS
795                 if( isset( $_SESSION['phpCAS']['auth_checked'] ) )
796                         unset($_SESSION['phpCAS']['auth_checked']);
797                 if ( $this->isAuthenticated() ) {
798                         phpCAS::trace('user already authenticated; renew');
799                         $this->redirectToCas(false,true);
800                 } else {
801                         $this->redirectToCas();
802                 }
803                 phpCAS::traceEnd();
804         }
805
806         /**
807          * This method is called to be sure that the user is authenticated. When not 
808          * authenticated, halt by redirecting to the CAS server; otherwise return TRUE.
809          * @return TRUE when the user is authenticated; otherwise halt.
810          * @public
811          */
812         function forceAuthentication()
813                 {
814                 phpCAS::traceBegin();
815                 
816                 if ( $this->isAuthenticated() ) {
817                         // the user is authenticated, nothing to be done.
818                         phpCAS::trace('no need to authenticate');
819                         $res = TRUE;
820                 } else {
821                         // the user is not authenticated, redirect to the CAS server
822                         if (isset($_SESSION['phpCAS']['auth_checked'])) {
823                                 unset($_SESSION['phpCAS']['auth_checked']);
824                         }
825                         $this->redirectToCas(FALSE/* no gateway */);    
826                         // never reached
827                         $res = FALSE;
828                 }
829                 phpCAS::traceEnd($res);
830                 return $res;
831                 }
832         
833         /**
834          * An integer that gives the number of times authentication will be cached before rechecked.
835          *
836          * @hideinitializer
837          * @private
838          */
839         var $_cache_times_for_auth_recheck = 0;
840         
841         /**
842          * Set the number of times authentication will be cached before rechecked.
843          *
844          * @param $n an integer.
845          *
846          * @public
847          */
848         function setCacheTimesForAuthRecheck($n)
849                 {
850                 $this->_cache_times_for_auth_recheck = $n;
851                 }
852         
853         /**
854          * This method is called to check whether the user is authenticated or not.
855          * @return TRUE when the user is authenticated, FALSE otherwise.
856          * @public
857          */
858         function checkAuthentication()
859                 {
860                 phpCAS::traceBegin();
861                 
862                 if ( $this->isAuthenticated() ) {
863                         phpCAS::trace('user is authenticated');
864                         $res = TRUE;
865                 } else if (isset($_SESSION['phpCAS']['auth_checked'])) {
866                         // the previous request has redirected the client to the CAS server with gateway=true
867                         unset($_SESSION['phpCAS']['auth_checked']);
868                         $res = FALSE;
869                 } else {
870                         //        $_SESSION['phpCAS']['auth_checked'] = true;
871                         //          $this->redirectToCas(TRUE/* gateway */);    
872                         //          // never reached
873                         //          $res = FALSE;
874                         // avoid a check against CAS on every request
875                         if (! isset($_SESSION['phpCAS']['unauth_count']) )
876                                 $_SESSION['phpCAS']['unauth_count'] = -2; // uninitialized
877                         
878                         if (($_SESSION['phpCAS']['unauth_count'] != -2 && $this->_cache_times_for_auth_recheck == -1) 
879                                         || ($_SESSION['phpCAS']['unauth_count'] >= 0 && $_SESSION['phpCAS']['unauth_count'] < $this->_cache_times_for_auth_recheck))
880                         {
881                                 $res = FALSE;
882                                 
883                                 if ($this->_cache_times_for_auth_recheck != -1)
884                                 {
885                                         $_SESSION['phpCAS']['unauth_count']++;
886                                         phpCAS::trace('user is not authenticated (cached for '.$_SESSION['phpCAS']['unauth_count'].' times of '.$this->_cache_times_for_auth_recheck.')');
887                                 }
888                                 else
889                                 {
890                                         phpCAS::trace('user is not authenticated (cached for until login pressed)');
891                                 }
892                         }
893                         else
894                         {
895                                 $_SESSION['phpCAS']['unauth_count'] = 0;
896                                 $_SESSION['phpCAS']['auth_checked'] = true;
897                                 phpCAS::trace('user is not authenticated (cache reset)');
898                                 $this->redirectToCas(TRUE/* gateway */);        
899                                 // never reached
900                                 $res = FALSE;
901                         }
902                 }
903                 phpCAS::traceEnd($res);
904                 return $res;
905                 }
906         
907         /**
908          * This method is called to check if the user is authenticated (previously or by
909          * tickets given in the URL).
910          *
911          * @return TRUE when the user is authenticated. Also may redirect to the same URL without the ticket.
912          *
913          * @public
914          */
915         function isAuthenticated()
916                 {
917                         phpCAS::traceBegin();
918                         $res = FALSE;
919                         $validate_url = '';
920
921                         if ( $this->wasPreviouslyAuthenticated() ) {
922                                 // the user has already (previously during the session) been
923                                 // authenticated, nothing to be done.
924                                 phpCAS::trace('user was already authenticated, no need to look for tickets');
925                                 $res = TRUE;
926                         }
927                         else {
928                                 if ( $this->hasST() ) {
929                                         // if a Service Ticket was given, validate it
930                                         phpCAS::trace('ST `'.$this->getST().'\' is present');
931                                         $this->validateST($validate_url,$text_response,$tree_response); // if it fails, it halts
932                                         phpCAS::trace('ST `'.$this->getST().'\' was validated');
933                                         if ( $this->isProxy() ) {
934                                                 $this->validatePGT($validate_url,$text_response,$tree_response); // idem
935                                                 phpCAS::trace('PGT `'.$this->getPGT().'\' was validated');
936                                                 $_SESSION['phpCAS']['pgt'] = $this->getPGT();
937                                         }
938                                         $_SESSION['phpCAS']['user'] = $this->getUser();
939                                         $res = TRUE;
940                                 }
941                                 elseif ( $this->hasPT() ) {
942                                         // if a Proxy Ticket was given, validate it
943                                         phpCAS::trace('PT `'.$this->getPT().'\' is present');
944                                         $this->validatePT($validate_url,$text_response,$tree_response); // note: if it fails, it halts
945                                         phpCAS::trace('PT `'.$this->getPT().'\' was validated');
946                                         if ( $this->isProxy() ) {
947                                                 $this->validatePGT($validate_url,$text_response,$tree_response); // idem
948                                                 phpCAS::trace('PGT `'.$this->getPGT().'\' was validated');
949                                                 $_SESSION['phpCAS']['pgt'] = $this->getPGT();
950                                         }
951                                         $_SESSION['phpCAS']['user'] = $this->getUser();
952                                         $res = TRUE;
953                                 }
954                                 elseif ( $this->hasSA() ) {
955                                         // if we have a SAML ticket, validate it.
956                                         phpCAS::trace('SA `'.$this->getSA().'\' is present');
957                                         $this->validateSA($validate_url,$text_response,$tree_response); // if it fails, it halts
958                                         phpCAS::trace('SA `'.$this->getSA().'\' was validated');
959                                         $_SESSION['phpCAS']['user'] = $this->getUser();
960                                         $_SESSION['phpCAS']['attributes'] = $this->getAttributes();
961                                         $res = TRUE;
962                                 }
963                                 else {
964                                         // no ticket given, not authenticated
965                                         phpCAS::trace('no ticket found');
966                                 }
967                                 if ($res) {
968                                         // if called with a ticket parameter, we need to redirect to the app without the ticket so that CAS-ification is transparent to the browser (for later POSTS)
969                                         // most of the checks and errors should have been made now, so we're safe for redirect without masking error messages.
970                                         header('Location: '.$this->getURL());
971                                         phpCAS::log( "Prepare redirect to : ".$this->getURL() );
972                                 }
973                         }
974
975                         phpCAS::traceEnd($res);
976                         return $res;
977                 }
978         
979         /**
980          * This method tells if the current session is authenticated.
981          * @return true if authenticated based soley on $_SESSION variable
982          * @since 0.4.22 by Brendan Arnold
983          */
984         function isSessionAuthenticated ()
985                 {
986                 return !empty($_SESSION['phpCAS']['user']);
987                 }
988         
989         /**
990          * This method tells if the user has already been (previously) authenticated
991          * by looking into the session variables.
992          *
993          * @note This function switches to callback mode when needed.
994          *
995          * @return TRUE when the user has already been authenticated; FALSE otherwise.
996          *
997          * @private
998          */
999         function wasPreviouslyAuthenticated()
1000                 {
1001                 phpCAS::traceBegin();
1002                 
1003                 if ( $this->isCallbackMode() ) {
1004                         $this->callback();
1005                 }
1006                 
1007                 $auth = FALSE;
1008                 
1009                 if ( $this->isProxy() ) {
1010                         // CAS proxy: username and PGT must be present
1011                         if ( $this->isSessionAuthenticated() && !empty($_SESSION['phpCAS']['pgt']) ) {
1012                                 // authentication already done
1013                                 $this->setUser($_SESSION['phpCAS']['user']);
1014                                 $this->setPGT($_SESSION['phpCAS']['pgt']);
1015                                 phpCAS::trace('user = `'.$_SESSION['phpCAS']['user'].'\', PGT = `'.$_SESSION['phpCAS']['pgt'].'\''); 
1016                                 $auth = TRUE;
1017                         } elseif ( $this->isSessionAuthenticated() && empty($_SESSION['phpCAS']['pgt']) ) {
1018                                 // these two variables should be empty or not empty at the same time
1019                                 phpCAS::trace('username found (`'.$_SESSION['phpCAS']['user'].'\') but PGT is empty');
1020                                 // unset all tickets to enforce authentication
1021                                 unset($_SESSION['phpCAS']);
1022                                 $this->setST('');
1023                                 $this->setPT('');
1024                         } elseif ( !$this->isSessionAuthenticated() && !empty($_SESSION['phpCAS']['pgt']) ) {
1025                                 // these two variables should be empty or not empty at the same time
1026                                 phpCAS::trace('PGT found (`'.$_SESSION['phpCAS']['pgt'].'\') but username is empty'); 
1027                                 // unset all tickets to enforce authentication
1028                                 unset($_SESSION['phpCAS']);
1029                                 $this->setST('');
1030                                 $this->setPT('');
1031                         } else {
1032                                 phpCAS::trace('neither user not PGT found'); 
1033                         }
1034                 } else {
1035                         // `simple' CAS client (not a proxy): username must be present
1036                         if ( $this->isSessionAuthenticated() ) {
1037                                 // authentication already done
1038                                 $this->setUser($_SESSION['phpCAS']['user']);
1039                                 if(isset($_SESSION['phpCAS']['attributes'])){
1040                                         $this->setAttributes($_SESSION['phpCAS']['attributes']);
1041                                 }
1042                                 phpCAS::trace('user = `'.$_SESSION['phpCAS']['user'].'\''); 
1043                                 $auth = TRUE;
1044                         } else {
1045                                 phpCAS::trace('no user found');
1046                         }
1047                 }
1048                 
1049                 phpCAS::traceEnd($auth);
1050                 return $auth;
1051                 }
1052         
1053         /**
1054          * This method is used to redirect the client to the CAS server.
1055          * It is used by CASClient::forceAuthentication() and CASClient::checkAuthentication().
1056          * @param $gateway true to check authentication, false to force it
1057          * @param $renew true to force the authentication with the CAS server
1058          * @public
1059          */
1060         function redirectToCas($gateway=false,$renew=false){
1061                 phpCAS::traceBegin();
1062                 $cas_url = $this->getServerLoginURL($gateway,$renew);
1063                 header('Location: '.$cas_url);
1064                 phpCAS::log( "Redirect to : ".$cas_url );
1065                 
1066                 $this->printHTMLHeader($this->getString(CAS_STR_AUTHENTICATION_WANTED));
1067                 
1068                 printf('<p>'.$this->getString(CAS_STR_SHOULD_HAVE_BEEN_REDIRECTED).'</p>',$cas_url);
1069                 $this->printHTMLFooter();
1070                 
1071                 phpCAS::traceExit();
1072                 exit();
1073         }
1074
1075 //      /**
1076 //       * This method is used to logout from CAS.
1077 //       * @param $url a URL that will be transmitted to the CAS server (to come back to when logged out)
1078 //       * @public
1079 //       */
1080 //      function logout($url = "") {
1081 //              phpCAS::traceBegin();
1082 //              $cas_url = $this->getServerLogoutURL();
1083 //              // v0.4.14 sebastien.gougeon at univ-rennes1.fr
1084 //              // header('Location: '.$cas_url);
1085 //              if ( $url != "" ) {
1086 //                      // Adam Moore 1.0.0RC2
1087 //                      $url = '?service=' . $url . '&url=' . $url;
1088 //              }
1089 //              header('Location: '.$cas_url . $url);
1090 //              session_unset();
1091 //              session_destroy();
1092 //              $this->printHTMLHeader($this->getString(CAS_STR_LOGOUT));
1093 //              printf('<p>'.$this->getString(CAS_STR_SHOULD_HAVE_BEEN_REDIRECTED).'</p>',$cas_url);
1094 //              $this->printHTMLFooter();
1095 //              phpCAS::traceExit();
1096 //              exit();
1097 //      }
1098         
1099         /**
1100          * This method is used to logout from CAS.
1101          * @params $params an array that contains the optional url and service parameters that will be passed to the CAS server
1102          * @public
1103          */
1104         function logout($params) {
1105                 phpCAS::traceBegin();
1106                 $cas_url = $this->getServerLogoutURL();
1107                 $paramSeparator = '?';
1108                 if (isset($params['url'])) {
1109                         $cas_url = $cas_url . $paramSeparator . "url=" . urlencode($params['url']); 
1110                         $paramSeparator = '&';
1111                 }
1112                 if (isset($params['service'])) {
1113                         $cas_url = $cas_url . $paramSeparator . "service=" . urlencode($params['service']); 
1114                 }
1115                 header('Location: '.$cas_url);
1116                 phpCAS::log( "Prepare redirect to : ".$cas_url );
1117  
1118                 session_unset();
1119                 session_destroy();
1120                 
1121                 $this->printHTMLHeader($this->getString(CAS_STR_LOGOUT));
1122                 printf('<p>'.$this->getString(CAS_STR_SHOULD_HAVE_BEEN_REDIRECTED).'</p>',$cas_url);
1123                 $this->printHTMLFooter();
1124                 
1125                 phpCAS::traceExit();
1126                 exit();
1127         }
1128         
1129         /**
1130          * @return true if the current request is a logout request.
1131          * @private
1132          */
1133         function isLogoutRequest() {
1134                 return !empty($_POST['logoutRequest']);
1135         }
1136         
1137         /**
1138          * @return true if a logout request is allowed.
1139          * @private
1140          */
1141         function isLogoutRequestAllowed() {
1142         }
1143         
1144         /**
1145          * This method handles logout requests.
1146          * @param $check_client true to check the client bofore handling the request, 
1147          * false not to perform any access control. True by default.
1148          * @param $allowed_clients an array of host names allowed to send logout requests. 
1149          * By default, only the CAs server (declared in the constructor) will be allowed.
1150          * @public
1151          */
1152         function handleLogoutRequests($check_client=true, $allowed_clients=false) {
1153                 phpCAS::traceBegin();
1154                 if (!$this->isLogoutRequest()) {
1155                         phpCAS::log("Not a logout request");
1156                         phpCAS::traceEnd();
1157                         return;
1158                 }
1159                 phpCAS::log("Logout requested");
1160                 phpCAS::log("SAML REQUEST: ".$_POST['logoutRequest']);
1161                 if ($check_client) {
1162                         if (!$allowed_clients) {
1163                                 $allowed_clients = array( $this->getServerHostname() ); 
1164                         }
1165                         $client_ip = $_SERVER['REMOTE_ADDR'];
1166                         $client = gethostbyaddr($client_ip);
1167                         phpCAS::log("Client: ".$client."/".$client_ip); 
1168                         $allowed = false;
1169                         foreach ($allowed_clients as $allowed_client) {
1170                                 if (($client == $allowed_client) or ($client_ip == $allowed_client)) { 
1171                                         phpCAS::log("Allowed client '".$allowed_client."' matches, logout request is allowed");
1172                                         $allowed = true;
1173                                         break;
1174                                 } else {
1175                                         phpCAS::log("Allowed client '".$allowed_client."' does not match");
1176                                 }
1177                         }
1178                         if (!$allowed) {
1179                                 phpCAS::error("Unauthorized logout request from client '".$client."'");
1180                             printf("Unauthorized!");
1181                                 phpCAS::traceExit();
1182                                 exit();
1183                         }
1184                 } else {
1185                         phpCAS::log("No access control set");
1186                 }
1187                 // Extract the ticket from the SAML Request
1188                 preg_match("|<samlp:SessionIndex>(.*)</samlp:SessionIndex>|", $_POST['logoutRequest'], $tick, PREG_OFFSET_CAPTURE, 3);
1189                 $wrappedSamlSessionIndex = preg_replace('|<samlp:SessionIndex>|','',$tick[0][0]);
1190                 $ticket2logout = preg_replace('|</samlp:SessionIndex>|','',$wrappedSamlSessionIndex);
1191                 phpCAS::log("Ticket to logout: ".$ticket2logout);
1192                 $session_id = preg_replace('/[^\w]/','',$ticket2logout);
1193                 phpCAS::log("Session id: ".$session_id);
1194
1195                 // fix New session ID
1196                 session_id($session_id);
1197                 $_COOKIE[session_name()]=$session_id;
1198                 $_GET[session_name()]=$session_id;
1199                 
1200                 // Overwrite session
1201                 session_start();        
1202                 session_unset();
1203             session_destroy();
1204             printf("Disconnected!");
1205                 phpCAS::traceExit();
1206                 exit();
1207         }
1208         
1209         /** @} */
1210         
1211         // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
1212         // XX                                                                    XX
1213         // XX                  BASIC CLIENT FEATURES (CAS 1.0)                   XX
1214         // XX                                                                    XX
1215         // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
1216         
1217         // ########################################################################
1218         //  ST
1219         // ########################################################################
1220         /**
1221          * @addtogroup internalBasic
1222          * @{
1223          */  
1224         
1225         /**
1226          * the Service Ticket provided in the URL of the request if present
1227          * (empty otherwise). Written by CASClient::CASClient(), read by 
1228          * CASClient::getST() and CASClient::hasPGT().
1229          *
1230          * @hideinitializer
1231          * @private
1232          */
1233         var $_st = '';
1234         
1235         /**
1236          * This method returns the Service Ticket provided in the URL of the request.
1237          * @return The service ticket.
1238          * @private
1239          */
1240         function getST()
1241                 { return $this->_st; }
1242         
1243         /**
1244          * This method stores the Service Ticket.
1245          * @param $st The Service Ticket.
1246          * @private
1247          */
1248         function setST($st)
1249                 { $this->_st = $st; }
1250         
1251         /**
1252          * This method tells if a Service Ticket was stored.
1253          * @return TRUE if a Service Ticket has been stored.
1254          * @private
1255          */
1256         function hasST()
1257                 { return !empty($this->_st); }
1258         
1259         /** @} */
1260         
1261         // ########################################################################
1262         //  ST VALIDATION
1263         // ########################################################################
1264         /**
1265          * @addtogroup internalBasic
1266          * @{
1267          */  
1268         
1269         /**
1270          * the certificate of the CAS server.
1271          *
1272          * @hideinitializer
1273          * @private
1274          */
1275         var $_cas_server_cert = '';
1276         
1277         /**
1278          * the certificate of the CAS server CA.
1279          *
1280          * @hideinitializer
1281          * @private
1282          */
1283         var $_cas_server_ca_cert = '';
1284         
1285         /**
1286          * Set to true not to validate the CAS server.
1287          *
1288          * @hideinitializer
1289          * @private
1290          */
1291         var $_no_cas_server_validation = false;
1292         
1293         /**
1294          * Set the certificate of the CAS server.
1295          *
1296          * @param $cert the PEM certificate
1297          */
1298         function setCasServerCert($cert)
1299                 {
1300                 $this->_cas_server_cert = $cert;
1301                 }
1302         
1303         /**
1304          * Set the CA certificate of the CAS server.
1305          *
1306          * @param $cert the PEM certificate of the CA that emited the cert of the server
1307          */
1308         function setCasServerCACert($cert)
1309                 {
1310                 $this->_cas_server_ca_cert = $cert;
1311                 }
1312         
1313         /**
1314          * Set no SSL validation for the CAS server.
1315          */
1316         function setNoCasServerValidation()
1317                 {
1318                 $this->_no_cas_server_validation = true;
1319                 }
1320         
1321         /**
1322          * This method is used to validate a ST; halt on failure, and sets $validate_url,
1323          * $text_reponse and $tree_response on success. These parameters are used later
1324          * by CASClient::validatePGT() for CAS proxies.
1325          * 
1326          * @param $validate_url the URL of the request to the CAS server.
1327          * @param $text_response the response of the CAS server, as is (XML text).
1328          * @param $tree_response the response of the CAS server, as a DOM XML tree.
1329          *
1330          * @return bool TRUE when successfull, halt otherwise by calling CASClient::authError().
1331          *
1332          * @private
1333          */
1334         function validateST($validate_url,&$text_response,&$tree_response)
1335                 {
1336                 phpCAS::traceBegin();
1337                 // build the URL to validate the ticket
1338                 $validate_url = $this->getServerServiceValidateURL().'&ticket='.$this->getST();
1339                 if ( $this->isProxy() ) {
1340                         // pass the callback url for CAS proxies
1341                         $validate_url .= '&pgtUrl='.$this->getCallbackURL();
1342                 }
1343                 
1344                 // open and read the URL
1345                 if ( !$this->readURL($validate_url,''/*cookies*/,$headers,$text_response,$err_msg) ) {
1346                         phpCAS::trace('could not open URL \''.$validate_url.'\' to validate ('.$err_msg.')');
1347                         $this->authError('ST not validated',
1348                                 $validate_url,
1349                                 TRUE/*$no_response*/);
1350                 }
1351                 
1352                 // analyze the result depending on the version
1353                 switch ($this->getServerVersion()) {
1354                         case CAS_VERSION_1_0:
1355                                 if (preg_match('/^no\n/',$text_response)) {
1356                                         phpCAS::trace('ST has not been validated');
1357                                         $this->authError('ST not validated',
1358                                                 $validate_url,
1359                                                 FALSE/*$no_response*/,
1360                                                 FALSE/*$bad_response*/,
1361                                                 $text_response);
1362                                 }
1363                                 if (!preg_match('/^yes\n/',$text_response)) {
1364                                         phpCAS::trace('ill-formed response');
1365                                         $this->authError('ST not validated',
1366                                                 $validate_url,
1367                                                 FALSE/*$no_response*/,
1368                                                 TRUE/*$bad_response*/,
1369                                                 $text_response);
1370                                 }
1371                                 // ST has been validated, extract the user name
1372                                 $arr = preg_split('/\n/',$text_response);
1373                                 $this->setUser(trim($arr[1]));
1374                                 break;
1375                         case CAS_VERSION_2_0:
1376                                 // read the response of the CAS server into a DOM object
1377                                 if ( !($dom = domxml_open_mem($text_response))) {
1378                                         phpCAS::trace('domxml_open_mem() failed');
1379                                         $this->authError('ST not validated',
1380                                                 $validate_url,
1381                                                 FALSE/*$no_response*/,
1382                                                 TRUE/*$bad_response*/,
1383                                                 $text_response);
1384                                 }
1385                                 // read the root node of the XML tree
1386                                 if ( !($tree_response = $dom->document_element()) ) {
1387                                         phpCAS::trace('document_element() failed');
1388                                         $this->authError('ST not validated',
1389                                                 $validate_url,
1390                                                 FALSE/*$no_response*/,
1391                                                 TRUE/*$bad_response*/,
1392                                                 $text_response);
1393                                 }
1394                                 // insure that tag name is 'serviceResponse'
1395                                 if ( $tree_response->node_name() != 'serviceResponse' ) {
1396                                         phpCAS::trace('bad XML root node (should be `serviceResponse\' instead of `'.$tree_response->node_name().'\'');
1397                                         $this->authError('ST not validated',
1398                                                 $validate_url,
1399                                                 FALSE/*$no_response*/,
1400                                                 TRUE/*$bad_response*/,
1401                                                 $text_response);
1402                                 }
1403                                 if ( sizeof($success_elements = $tree_response->get_elements_by_tagname("authenticationSuccess")) != 0) {
1404                                         // authentication succeded, extract the user name
1405                                         if ( sizeof($user_elements = $success_elements[0]->get_elements_by_tagname("user")) == 0) {
1406                                                 phpCAS::trace('<authenticationSuccess> found, but no <user>');
1407                                                 $this->authError('ST not validated',
1408                                                         $validate_url,
1409                                                         FALSE/*$no_response*/,
1410                                                         TRUE/*$bad_response*/,
1411                                                         $text_response);
1412                                         }
1413                                         $user = trim($user_elements[0]->get_content());
1414                                         phpCAS::trace('user = `'.$user);
1415                                         $this->setUser($user);
1416                                         
1417                                 } else if ( sizeof($failure_elements = $tree_response->get_elements_by_tagname("authenticationFailure")) != 0) {
1418                                         phpCAS::trace('<authenticationFailure> found');
1419                                         // authentication failed, extract the error code and message
1420                                         $this->authError('ST not validated',
1421                                                 $validate_url,
1422                                                 FALSE/*$no_response*/,
1423                                                 FALSE/*$bad_response*/,
1424                                                 $text_response,
1425                                                 $failure_elements[0]->get_attribute('code')/*$err_code*/,
1426                                                 trim($failure_elements[0]->get_content())/*$err_msg*/);
1427                                 } else {
1428                                         phpCAS::trace('neither <authenticationSuccess> nor <authenticationFailure> found');
1429                                         $this->authError('ST not validated',
1430                                                 $validate_url,
1431                                                 FALSE/*$no_response*/,
1432                                                 TRUE/*$bad_response*/,
1433                                                 $text_response);
1434                                 }
1435                                 break;
1436                 }
1437                 
1438                 // at this step, ST has been validated and $this->_user has been set,
1439                 phpCAS::traceEnd(TRUE);
1440                 return TRUE;
1441                 }
1442
1443  // ########################################################################
1444  //  SAML VALIDATION
1445  // ########################################################################
1446    /**
1447     * @addtogroup internalBasic
1448     * @{
1449     */
1450
1451    /**
1452     * This method is used to validate a SAML TICKET; halt on failure, and sets $validate_url,
1453     * $text_reponse and $tree_response on success. These parameters are used later
1454     * by CASClient::validatePGT() for CAS proxies.
1455     *
1456     * @param $validate_url the URL of the request to the CAS server.
1457     * @param $text_response the response of the CAS server, as is (XML text).
1458     * @param $tree_response the response of the CAS server, as a DOM XML tree.
1459     *
1460     * @return bool TRUE when successfull, halt otherwise by calling CASClient::authError().
1461     *
1462     * @private
1463     */
1464    function validateSA($validate_url,&$text_response,&$tree_response)
1465      {
1466        phpCAS::traceBegin();
1467
1468        // build the URL to validate the ticket
1469        $validate_url = $this->getServerSamlValidateURL();
1470
1471        // open and read the URL
1472        if ( !$this->readURL($validate_url,''/*cookies*/,$headers,$text_response,$err_msg) ) {
1473            phpCAS::trace('could not open URL \''.$validate_url.'\' to validate ('.$err_msg.')');
1474            $this->authError('SA not validated', $validate_url, TRUE/*$no_response*/);
1475        }
1476
1477        phpCAS::trace('server version: '.$this->getServerVersion());
1478
1479        // analyze the result depending on the version
1480        switch ($this->getServerVersion()) {
1481        case SAML_VERSION_1_1:
1482
1483      // read the response of the CAS server into a DOM object
1484        if ( !($dom = domxml_open_mem($text_response))) {
1485          phpCAS::trace('domxml_open_mem() failed');
1486          $this->authError('SA not validated',
1487                       $validate_url,
1488                       FALSE/*$no_response*/,
1489                       TRUE/*$bad_response*/,
1490                       $text_response);
1491        }
1492        // read the root node of the XML tree
1493        if ( !($tree_response = $dom->document_element()) ) {
1494          phpCAS::trace('document_element() failed');
1495          $this->authError('SA not validated',
1496                       $validate_url,
1497                       FALSE/*$no_response*/,
1498                       TRUE/*$bad_response*/,
1499                       $text_response);
1500        }
1501        // insure that tag name is 'Envelope'
1502        if ( $tree_response->node_name() != 'Envelope' ) {
1503          phpCAS::trace('bad XML root node (should be `Envelope\' instead of `'.$tree_response->node_name().'\'');
1504          $this->authError('SA not validated',
1505                       $validate_url,
1506                       FALSE/*$no_response*/,
1507                       TRUE/*$bad_response*/,
1508                       $text_response);
1509        }
1510      // check for the NameIdentifier tag in the SAML response
1511        if ( sizeof($success_elements = $tree_response->get_elements_by_tagname("NameIdentifier")) != 0) {
1512        phpCAS::trace('NameIdentifier found');
1513          $user = trim($success_elements[0]->get_content());
1514          phpCAS::trace('user = `'.$user.'`');
1515          $this->setUser($user);
1516        $this->setSessionAttributes($text_response);
1517        } else {
1518          phpCAS::trace('no <NameIdentifier> tag found in SAML payload');
1519          $this->authError('SA not validated',
1520                       $validate_url,
1521                       FALSE/*$no_response*/,
1522                       TRUE/*$bad_response*/,
1523                       $text_response);
1524        }
1525        break;
1526        }
1527
1528        // at this step, ST has been validated and $this->_user has been set,
1529        phpCAS::traceEnd(TRUE);
1530        return TRUE;
1531      }
1532
1533    /**
1534     * This method will parse the DOM and pull out the attributes from the SAML
1535     * payload and put them into an array, then put the array into the session.
1536     *
1537     * @param $text_response the SAML payload.
1538     * @return bool TRUE when successfull, halt otherwise by calling CASClient::authError().
1539     *
1540     * @private
1541     */
1542  function setSessionAttributes($text_response)
1543  {
1544            phpCAS::traceBegin();
1545
1546            $result = FALSE;
1547
1548            if (isset($_SESSION[SAML_ATTRIBUTES])) {
1549              phpCAS::trace("session attrs already set.");  //testbml - do we care?
1550            }
1551
1552            $attr_array = array();
1553
1554                 if (($dom = domxml_open_mem($text_response))) {
1555                    $xPath = $dom->xpath_new_context();
1556                    $xPath->xpath_register_ns('samlp', 'urn:oasis:names:tc:SAML:1.0:protocol');
1557                    $xPath->xpath_register_ns('saml', 'urn:oasis:names:tc:SAML:1.0:assertion');
1558                    $nodelist = $xPath->xpath_eval("//saml:Attribute");
1559                    $attrs = $nodelist->nodeset;
1560                    phpCAS::trace($text_response);
1561                   foreach($attrs as $attr){
1562                       $xres = $xPath->xpath_eval("saml:AttributeValue", $attr);
1563                       $name = $attr->get_attribute("AttributeName");
1564                       $value_array = array();
1565                       foreach($xres->nodeset as $node){
1566                           $value_array[] = $node->get_content();
1567                          
1568                       }
1569                       phpCAS::trace("* " . $name . "=" . $value_array);
1570                       $attr_array[$name] = $value_array;
1571                    }
1572                    $_SESSION[SAML_ATTRIBUTES] = $attr_array;
1573                    // UGent addition...
1574                    foreach($attr_array as $attr_key => $attr_value) {
1575                       if(count($attr_value) > 1) {
1576                         $this->_attributes[$attr_key] = $attr_value;
1577                       }
1578                       else {
1579                         $this->_attributes[$attr_key] = $attr_value[0];
1580                       }
1581                    }
1582                    $result = TRUE;
1583                 }
1584        phpCAS::traceEnd($result);
1585        return $result;
1586  }
1587         
1588         /** @} */
1589         
1590         // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
1591         // XX                                                                    XX
1592         // XX                     PROXY FEATURES (CAS 2.0)                       XX
1593         // XX                                                                    XX
1594         // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
1595         
1596         // ########################################################################
1597         //  PROXYING
1598         // ########################################################################
1599         /**
1600          * @addtogroup internalProxy
1601          * @{
1602          */
1603         
1604         /**
1605          * A boolean telling if the client is a CAS proxy or not. Written by CASClient::CASClient(), 
1606          * read by CASClient::isProxy().
1607          *
1608          * @private
1609          */
1610         var $_proxy;
1611         
1612         /**
1613          * Tells if a CAS client is a CAS proxy or not
1614          *
1615          * @return TRUE when the CAS client is a CAs proxy, FALSE otherwise
1616          *
1617          * @private
1618          */
1619         function isProxy()
1620                 {
1621                 return $this->_proxy;
1622                 }
1623         
1624         /** @} */
1625         // ########################################################################
1626         //  PGT
1627         // ########################################################################
1628         /**
1629          * @addtogroup internalProxy
1630          * @{
1631          */  
1632         
1633         /**
1634          * the Proxy Grnting Ticket given by the CAS server (empty otherwise). 
1635          * Written by CASClient::setPGT(), read by CASClient::getPGT() and CASClient::hasPGT().
1636          *
1637          * @hideinitializer
1638          * @private
1639          */
1640         var $_pgt = '';
1641         
1642         /**
1643          * This method returns the Proxy Granting Ticket given by the CAS server.
1644          * @return The Proxy Granting Ticket.
1645          * @private
1646          */
1647         function getPGT()
1648                 { return $this->_pgt; }
1649         
1650         /**
1651          * This method stores the Proxy Granting Ticket.
1652          * @param $pgt The Proxy Granting Ticket.
1653          * @private
1654          */
1655         function setPGT($pgt)
1656                 { $this->_pgt = $pgt; }
1657         
1658         /**
1659          * This method tells if a Proxy Granting Ticket was stored.
1660          * @return TRUE if a Proxy Granting Ticket has been stored.
1661          * @private
1662          */
1663         function hasPGT()
1664                 { return !empty($this->_pgt); }
1665         
1666         /** @} */
1667         
1668         // ########################################################################
1669         //  CALLBACK MODE
1670         // ########################################################################
1671         /**
1672          * @addtogroup internalCallback
1673          * @{
1674          */  
1675         /**
1676          * each PHP script using phpCAS in proxy mode is its own callback to get the
1677          * PGT back from the CAS server. callback_mode is detected by the constructor
1678          * thanks to the GET parameters.
1679          */
1680         
1681         /**
1682          * a boolean to know if the CAS client is running in callback mode. Written by
1683          * CASClient::setCallBackMode(), read by CASClient::isCallbackMode().
1684          *
1685          * @hideinitializer
1686          * @private
1687          */
1688         var $_callback_mode = FALSE;
1689         
1690         /**
1691          * This method sets/unsets callback mode.
1692          *
1693          * @param $callback_mode TRUE to set callback mode, FALSE otherwise.
1694          *
1695          * @private
1696          */
1697         function setCallbackMode($callback_mode)
1698                 {
1699                 $this->_callback_mode = $callback_mode;
1700                 }
1701         
1702         /**
1703          * This method returns TRUE when the CAs client is running i callback mode, 
1704          * FALSE otherwise.
1705          *
1706          * @return A boolean.
1707          *
1708          * @private
1709          */
1710         function isCallbackMode()
1711                 {
1712                 return $this->_callback_mode;
1713                 }
1714         
1715         /**
1716          * the URL that should be used for the PGT callback (in fact the URL of the 
1717          * current request without any CGI parameter). Written and read by 
1718          * CASClient::getCallbackURL().
1719          *
1720          * @hideinitializer
1721          * @private
1722          */
1723         var $_callback_url = '';
1724         
1725         /**
1726          * This method returns the URL that should be used for the PGT callback (in
1727          * fact the URL of the current request without any CGI parameter, except if
1728          * phpCAS::setFixedCallbackURL() was used).
1729          *
1730          * @return The callback URL
1731          *
1732          * @private
1733          */
1734         function getCallbackURL()
1735                 {
1736                 // the URL is built when needed only
1737                 if ( empty($this->_callback_url) ) {
1738                         $final_uri = '';
1739                         // remove the ticket if present in the URL
1740                         $final_uri = 'https://';
1741                         /* replaced by Julien Marchal - v0.4.6
1742                          * $this->uri .= $_SERVER['SERVER_NAME'];
1743                          */
1744                         if(empty($_SERVER['HTTP_X_FORWARDED_SERVER'])){
1745                                 /* replaced by teedog - v0.4.12
1746                                  * $final_uri .= $_SERVER['SERVER_NAME'];
1747                                  */
1748                                 if (empty($_SERVER['SERVER_NAME'])) {
1749                                         $final_uri .= $_SERVER['HTTP_HOST'];
1750                                 } else {
1751                                         $final_uri .= $_SERVER['SERVER_NAME'];
1752                                 }
1753                         } else {
1754                                 $final_uri .= $_SERVER['HTTP_X_FORWARDED_SERVER'];
1755                         }
1756                         if ( ($this->isHttps() && $_SERVER['SERVER_PORT']!=443)
1757                                         || (!$this->isHttps() && $_SERVER['SERVER_PORT']!=80) ) {
1758                                 $final_uri .= ':';
1759                                 $final_uri .= $_SERVER['SERVER_PORT'];
1760                         }
1761                         $request_uri = $_SERVER['REQUEST_URI'];
1762                         $request_uri = preg_replace('/\?.*$/','',$request_uri);
1763                         $final_uri .= $request_uri;
1764                         $this->setCallbackURL($final_uri);
1765                 }
1766                 return $this->_callback_url;
1767                 }
1768         
1769         /**
1770          * This method sets the callback url.
1771          *
1772          * @param $callback_url url to set callback 
1773          *
1774          * @private
1775          */
1776         function setCallbackURL($url)
1777                 {
1778                 return $this->_callback_url = $url;
1779                 }
1780         
1781         /**
1782          * This method is called by CASClient::CASClient() when running in callback
1783          * mode. It stores the PGT and its PGT Iou, prints its output and halts.
1784          *
1785          * @private
1786          */
1787         function callback()
1788                 {
1789                 phpCAS::traceBegin();
1790                 $this->printHTMLHeader('phpCAS callback');
1791                 $pgt_iou = $_GET['pgtIou'];
1792                 $pgt = $_GET['pgtId'];
1793                 phpCAS::trace('Storing PGT `'.$pgt.'\' (id=`'.$pgt_iou.'\')');
1794                 echo '<p>Storing PGT `'.$pgt.'\' (id=`'.$pgt_iou.'\').</p>';
1795                 $this->storePGT($pgt,$pgt_iou);
1796                 $this->printHTMLFooter();
1797                 phpCAS::traceExit();
1798                 exit();
1799                 }
1800         
1801         /** @} */
1802         
1803         // ########################################################################
1804         //  PGT STORAGE
1805         // ########################################################################
1806         /**
1807          * @addtogroup internalPGTStorage
1808          * @{
1809          */  
1810         
1811         /**
1812          * an instance of a class inheriting of PGTStorage, used to deal with PGT
1813          * storage. Created by CASClient::setPGTStorageFile() or CASClient::setPGTStorageDB(), used 
1814          * by CASClient::setPGTStorageFile(), CASClient::setPGTStorageDB() and CASClient::initPGTStorage().
1815          *
1816          * @hideinitializer
1817          * @private
1818          */
1819         var $_pgt_storage = null;
1820         
1821         /**
1822          * This method is used to initialize the storage of PGT's.
1823          * Halts on error.
1824          *
1825          * @private
1826          */
1827         function initPGTStorage()
1828                 {
1829                 // if no SetPGTStorageXxx() has been used, default to file
1830                 if ( !is_object($this->_pgt_storage) ) {
1831                         $this->setPGTStorageFile();
1832                 }
1833                 
1834                 // initializes the storage
1835                 $this->_pgt_storage->init();
1836                 }
1837         
1838         /**
1839          * This method stores a PGT. Halts on error.
1840          *
1841          * @param $pgt the PGT to store
1842          * @param $pgt_iou its corresponding Iou
1843          *
1844          * @private
1845          */
1846         function storePGT($pgt,$pgt_iou)
1847                 {
1848                 // ensure that storage is initialized
1849                 $this->initPGTStorage();
1850                 // writes the PGT
1851                 $this->_pgt_storage->write($pgt,$pgt_iou);
1852                 }
1853         
1854         /**
1855          * This method reads a PGT from its Iou and deletes the corresponding storage entry.
1856          *
1857          * @param $pgt_iou the PGT Iou
1858          *
1859          * @return The PGT corresponding to the Iou, FALSE when not found.
1860          *
1861          * @private
1862          */
1863         function loadPGT($pgt_iou)
1864                 {
1865                 // ensure that storage is initialized
1866                 $this->initPGTStorage();
1867                 // read the PGT
1868                 return $this->_pgt_storage->read($pgt_iou);
1869                 }
1870         
1871         /**
1872          * This method is used to tell phpCAS to store the response of the
1873          * CAS server to PGT requests onto the filesystem. 
1874          *
1875          * @param $format the format used to store the PGT's (`plain' and `xml' allowed)
1876          * @param $path the path where the PGT's should be stored
1877          *
1878          * @public
1879          */
1880         function setPGTStorageFile($format='',
1881                 $path='')
1882                 {
1883                 // check that the storage has not already been set
1884                 if ( is_object($this->_pgt_storage) ) {
1885                         phpCAS::error('PGT storage already defined');
1886                 }
1887                 
1888                 // create the storage object
1889                 $this->_pgt_storage = new PGTStorageFile($this,$format,$path);
1890                 }
1891         
1892         /**
1893          * This method is used to tell phpCAS to store the response of the
1894          * CAS server to PGT requests into a database. 
1895          * @note The connection to the database is done only when needed. 
1896          * As a consequence, bad parameters are detected only when 
1897          * initializing PGT storage.
1898          *
1899          * @param $user the user to access the data with
1900          * @param $password the user's password
1901          * @param $database_type the type of the database hosting the data
1902          * @param $hostname the server hosting the database
1903          * @param $port the port the server is listening on
1904          * @param $database the name of the database
1905          * @param $table the name of the table storing the data
1906          *
1907          * @public
1908          */
1909         function setPGTStorageDB($user,
1910                                                          $password,
1911                                                          $database_type,
1912                                                          $hostname,
1913                                                          $port,
1914                                                          $database,
1915                                                          $table)
1916                 {
1917                 // check that the storage has not already been set
1918                 if ( is_object($this->_pgt_storage) ) {
1919                         phpCAS::error('PGT storage already defined');
1920                 }
1921                 
1922                 // warn the user that he should use file storage...
1923                 trigger_error('PGT storage into database is an experimental feature, use at your own risk',E_USER_WARNING);
1924                 
1925                 // create the storage object
1926                 $this->_pgt_storage = new PGTStorageDB($this,$user,$password,$database_type,$hostname,$port,$database,$table);
1927                 }
1928         
1929         // ########################################################################
1930         //  PGT VALIDATION
1931         // ########################################################################
1932         /**
1933          * This method is used to validate a PGT; halt on failure.
1934          * 
1935          * @param $validate_url the URL of the request to the CAS server.
1936          * @param $text_response the response of the CAS server, as is (XML text); result
1937          * of CASClient::validateST() or CASClient::validatePT().
1938          * @param $tree_response the response of the CAS server, as a DOM XML tree; result
1939          * of CASClient::validateST() or CASClient::validatePT().
1940          *
1941          * @return bool TRUE when successfull, halt otherwise by calling CASClient::authError().
1942          *
1943          * @private
1944          */
1945         function validatePGT(&$validate_url,$text_response,$tree_response)
1946                 {
1947                 // here cannot use phpCAS::traceBegin(); alongside domxml-php4-to-php5.php
1948                 phpCAS::log('start validatePGT()');
1949                 if ( sizeof($arr = $tree_response->get_elements_by_tagname("proxyGrantingTicket")) == 0) {
1950                         phpCAS::trace('<proxyGrantingTicket> not found');
1951                         // authentication succeded, but no PGT Iou was transmitted
1952                         $this->authError('Ticket validated but no PGT Iou transmitted',
1953                                 $validate_url,
1954                                 FALSE/*$no_response*/,
1955                                 FALSE/*$bad_response*/,
1956                                 $text_response);
1957                 } else {
1958                         // PGT Iou transmitted, extract it
1959                         $pgt_iou = trim($arr[0]->get_content());
1960                         $pgt = $this->loadPGT($pgt_iou);
1961                         if ( $pgt == FALSE ) {
1962                                 phpCAS::trace('could not load PGT');
1963                                 $this->authError('PGT Iou was transmitted but PGT could not be retrieved',
1964                                         $validate_url,
1965                                         FALSE/*$no_response*/,
1966                                         FALSE/*$bad_response*/,
1967                                         $text_response);
1968                         }
1969                         $this->setPGT($pgt);
1970                 }
1971                 // here, cannot use     phpCAS::traceEnd(TRUE); alongside domxml-php4-to-php5.php
1972                 phpCAS::log('end validatePGT()');
1973                 return TRUE;
1974                 }
1975         
1976         // ########################################################################
1977         //  PGT VALIDATION
1978         // ########################################################################
1979         
1980         /**
1981          * This method is used to retrieve PT's from the CAS server thanks to a PGT.
1982          * 
1983          * @param $target_service the service to ask for with the PT.
1984          * @param $err_code an error code (PHPCAS_SERVICE_OK on success).
1985          * @param $err_msg an error message (empty on success).
1986          *
1987          * @return a Proxy Ticket, or FALSE on error.
1988          *
1989          * @private
1990          */
1991         function retrievePT($target_service,&$err_code,&$err_msg)
1992                 {
1993                 phpCAS::traceBegin();
1994                 
1995                 // by default, $err_msg is set empty and $pt to TRUE. On error, $pt is
1996                 // set to false and $err_msg to an error message. At the end, if $pt is FALSE 
1997                 // and $error_msg is still empty, it is set to 'invalid response' (the most
1998                 // commonly encountered error).
1999                 $err_msg = '';
2000                 
2001                 // build the URL to retrieve the PT
2002                 //      $cas_url = $this->getServerProxyURL().'?targetService='.preg_replace('/&/','%26',$target_service).'&pgt='.$this->getPGT();
2003                 $cas_url = $this->getServerProxyURL().'?targetService='.urlencode($target_service).'&pgt='.$this->getPGT();
2004                 
2005                 // open and read the URL
2006                 if ( !$this->readURL($cas_url,''/*cookies*/,$headers,$cas_response,$err_msg) ) {
2007                         phpCAS::trace('could not open URL \''.$cas_url.'\' to validate ('.$err_msg.')');
2008                         $err_code = PHPCAS_SERVICE_PT_NO_SERVER_RESPONSE;
2009                         $err_msg = 'could not retrieve PT (no response from the CAS server)';
2010                         phpCAS::traceEnd(FALSE);
2011                         return FALSE;
2012                 }
2013                 
2014                 $bad_response = FALSE;
2015                 
2016                 if ( !$bad_response ) {
2017                         // read the response of the CAS server into a DOM object
2018                         if ( !($dom = @domxml_open_mem($cas_response))) {
2019                                 phpCAS::trace('domxml_open_mem() failed');
2020                                 // read failed
2021                                 $bad_response = TRUE;
2022                         } 
2023                 }
2024                 
2025                 if ( !$bad_response ) {
2026                         // read the root node of the XML tree
2027                         if ( !($root = $dom->document_element()) ) {
2028                                 phpCAS::trace('document_element() failed');
2029                                 // read failed
2030                                 $bad_response = TRUE;
2031                         } 
2032                 }
2033                 
2034                 if ( !$bad_response ) {
2035                         // insure that tag name is 'serviceResponse'
2036                         if ( $root->node_name() != 'serviceResponse' ) {
2037                                 phpCAS::trace('node_name() failed');
2038                                 // bad root node
2039                                 $bad_response = TRUE;
2040                         } 
2041                 }
2042                 
2043                 if ( !$bad_response ) {
2044                         // look for a proxySuccess tag
2045                         if ( sizeof($arr = $root->get_elements_by_tagname("proxySuccess")) != 0) {
2046                                 // authentication succeded, look for a proxyTicket tag
2047                                 if ( sizeof($arr = $root->get_elements_by_tagname("proxyTicket")) != 0) {
2048                                         $err_code = PHPCAS_SERVICE_OK;
2049                                         $err_msg = '';
2050                                         phpCAS::trace('original PT: '.trim($arr[0]->get_content()));
2051                                         $pt = trim($arr[0]->get_content());
2052                                         phpCAS::traceEnd($pt);
2053                                         return $pt;
2054                                 } else {
2055                                         phpCAS::trace('<proxySuccess> was found, but not <proxyTicket>');
2056                                 }
2057                         } 
2058                         // look for a proxyFailure tag
2059                         else if ( sizeof($arr = $root->get_elements_by_tagname("proxyFailure")) != 0) {
2060                                 // authentication failed, extract the error
2061                                 $err_code = PHPCAS_SERVICE_PT_FAILURE;
2062                                 $err_msg = 'PT retrieving failed (code=`'
2063                                         .$arr[0]->get_attribute('code')
2064                                         .'\', message=`'
2065                                         .trim($arr[0]->get_content())
2066                                         .'\')';
2067                                 phpCAS::traceEnd(FALSE);
2068                                 return FALSE;
2069                         } else {
2070                                 phpCAS::trace('neither <proxySuccess> nor <proxyFailure> found');
2071                         }
2072                 }
2073                 
2074                 // at this step, we are sure that the response of the CAS server was ill-formed
2075                 $err_code = PHPCAS_SERVICE_PT_BAD_SERVER_RESPONSE;
2076                 $err_msg = 'Invalid response from the CAS server (response=`'.$cas_response.'\')';
2077                 
2078                 phpCAS::traceEnd(FALSE);
2079                 return FALSE;
2080                 }
2081         
2082         // ########################################################################
2083         // ACCESS TO EXTERNAL SERVICES
2084         // ########################################################################
2085         
2086         /**
2087          * This method is used to acces a remote URL.
2088          *
2089          * @param $url the URL to access.
2090          * @param $cookies an array containing cookies strings such as 'name=val'
2091          * @param $headers an array containing the HTTP header lines of the response
2092          * (an empty array on failure).
2093          * @param $body the body of the response, as a string (empty on failure).
2094          * @param $err_msg an error message, filled on failure.
2095          *
2096          * @return TRUE on success, FALSE otherwise (in this later case, $err_msg
2097          * contains an error message).
2098          *
2099          * @private
2100          */
2101         function readURL($url,$cookies,&$headers,&$body,&$err_msg)
2102                 {
2103                 phpCAS::traceBegin();
2104                 $headers = '';
2105                 $body = '';
2106                 $err_msg = '';
2107                 
2108                 $res = TRUE;
2109                 
2110                 // initialize the CURL session
2111                 $ch = curl_init($url);
2112                 
2113                 if (version_compare(PHP_VERSION,'5.1.3','>=')) {
2114                         //only avaible in php5
2115                         curl_setopt_array($ch, $this->_curl_options);
2116                 } else {
2117                         foreach ($this->_curl_options as $key => $value) {
2118                                 curl_setopt($ch, $key, $value);
2119                         }
2120                 }
2121
2122                 if ($this->_cas_server_cert == '' && $this->_cas_server_ca_cert == '' && !$this->_no_cas_server_validation) {
2123                         phpCAS::error('one of the methods phpCAS::setCasServerCert(), phpCAS::setCasServerCACert() or phpCAS::setNoCasServerValidation() must be called.');
2124                 }
2125                 if ($this->_cas_server_cert != '' && $this->_cas_server_ca_cert != '') {
2126                         // This branch added by IDMS. Seems phpCAS implementor got a bit confused about the curl options CURLOPT_SSLCERT and CURLOPT_CAINFO
2127                         curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
2128                         curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 1);
2129                         curl_setopt($ch, CURLOPT_SSLCERT, $this->_cas_server_cert);
2130                         curl_setopt($ch, CURLOPT_CAINFO, $this->_cas_server_ca_cert);
2131                         curl_setopt($ch, CURLOPT_VERBOSE, '1');
2132                         phpCAS::trace('CURL: Set all required opts for mutual authentication ------');
2133                 } else if ($this->_cas_server_cert != '' ) {
2134                         curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
2135                         curl_setopt($ch, CURLOPT_SSLCERT, $this->_cas_server_cert);
2136                 } else if ($this->_cas_server_ca_cert != '') {
2137                         curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
2138                         curl_setopt($ch, CURLOPT_CAINFO, $this->_cas_server_ca_cert);
2139                 } else {
2140                         curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 1);
2141                         curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
2142                 }
2143                 
2144                 // return the CURL output into a variable
2145                 curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
2146                 // get the HTTP header with a callback
2147                 $this->_curl_headers = array(); // empty the headers array
2148                 curl_setopt($ch, CURLOPT_HEADERFUNCTION, array($this, '_curl_read_headers'));
2149                 // add cookies headers
2150                 if ( is_array($cookies) ) {
2151                         curl_setopt($ch,CURLOPT_COOKIE,implode(';',$cookies));
2152                 }
2153                 // add extra stuff if SAML
2154                 if ($this->hasSA()) {
2155                         $more_headers = array ("soapaction: http://www.oasis-open.org/committees/security",
2156                                                "cache-control: no-cache",
2157                                                "pragma: no-cache",
2158                                                "accept: text/xml",
2159                                                "connection: keep-alive",
2160                                                "content-type: text/xml");
2161
2162                        curl_setopt($ch, CURLOPT_HTTPHEADER, $more_headers);
2163                        curl_setopt($ch, CURLOPT_POST, 1);
2164                        $data = $this->buildSAMLPayload();
2165                        //phpCAS::trace('SAML Payload: '.print_r($data, TRUE));
2166                        curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
2167                 }
2168                 // perform the query
2169                 $buf = curl_exec ($ch);
2170                 //phpCAS::trace('CURL: Call completed. Response body is: \''.$buf.'\'');
2171                 if ( $buf === FALSE ) {
2172                         phpCAS::trace('curl_exec() failed');
2173                         $err_msg = 'CURL error #'.curl_errno($ch).': '.curl_error($ch);
2174                         //phpCAS::trace('curl error: '.$err_msg);
2175                         // close the CURL session
2176                         curl_close ($ch);
2177                         $res = FALSE;
2178                 } else {
2179                         // close the CURL session
2180                         curl_close ($ch);
2181                         
2182                         $headers = $this->_curl_headers;
2183                         $body = $buf;
2184                 }
2185                 
2186                 phpCAS::traceEnd($res);
2187                 return $res;
2188         }
2189
2190         /**
2191         * This method is used to build the SAML POST body sent to /samlValidate URL.
2192         *
2193         * @return the SOAP-encased SAMLP artifact (the ticket).
2194         *
2195         * @private
2196         */
2197         function buildSAMLPayload()
2198         {
2199         phpCAS::traceBegin();
2200
2201         //get the ticket
2202         $sa = $this->getSA();
2203         //phpCAS::trace("SA: ".$sa);
2204
2205         $body=SAML_SOAP_ENV.SAML_SOAP_BODY.SAMLP_REQUEST.SAML_ASSERTION_ARTIFACT.$sa.SAML_ASSERTION_ARTIFACT_CLOSE.SAMLP_REQUEST_CLOSE.SAML_SOAP_BODY_CLOSE.SAML_SOAP_ENV_CLOSE;
2206
2207         phpCAS::traceEnd($body);
2208         return ($body);
2209         }
2210
2211         /**
2212          * This method is the callback used by readURL method to request HTTP headers.
2213          */
2214         var $_curl_headers = array();
2215         function _curl_read_headers($ch, $header)
2216         {
2217                 $this->_curl_headers[] = $header;
2218                 return strlen($header);
2219         }
2220
2221         /**
2222          * This method is used to access an HTTP[S] service.
2223          * 
2224          * @param $url the service to access.
2225          * @param $err_code an error code Possible values are PHPCAS_SERVICE_OK (on
2226          * success), PHPCAS_SERVICE_PT_NO_SERVER_RESPONSE, PHPCAS_SERVICE_PT_BAD_SERVER_RESPONSE,
2227          * PHPCAS_SERVICE_PT_FAILURE, PHPCAS_SERVICE_NOT AVAILABLE.
2228          * @param $output the output of the service (also used to give an error
2229          * message on failure).
2230          *
2231          * @return TRUE on success, FALSE otherwise (in this later case, $err_code
2232          * gives the reason why it failed and $output contains an error message).
2233          *
2234          * @public
2235          */
2236         function serviceWeb($url,&$err_code,&$output)
2237                 {
2238                 phpCAS::traceBegin();
2239                 // at first retrieve a PT
2240                 $pt = $this->retrievePT($url,$err_code,$output);
2241                 
2242                 $res = TRUE;
2243                 
2244                 // test if PT was retrieved correctly
2245                 if ( !$pt ) {
2246                         // note: $err_code and $err_msg are filled by CASClient::retrievePT()
2247                         phpCAS::trace('PT was not retrieved correctly');
2248                         $res = FALSE;
2249                 } else {
2250                         // add cookies if necessary
2251                         if ( is_array($_SESSION['phpCAS']['services'][$url]['cookies']) ) {
2252                                 foreach ( $_SESSION['phpCAS']['services'][$url]['cookies'] as $name => $val ) { 
2253                                         $cookies[] = $name.'='.$val;
2254                                 }
2255                         }
2256                         
2257                         // build the URL including the PT
2258                         if ( strstr($url,'?') === FALSE ) {
2259                                 $service_url = $url.'?ticket='.$pt;
2260                         } else {
2261                                 $service_url = $url.'&ticket='.$pt;
2262                         }
2263                         
2264                         phpCAS::trace('reading URL`'.$service_url.'\'');
2265                         if ( !$this->readURL($service_url,$cookies,$headers,$output,$err_msg) ) {
2266                                 phpCAS::trace('could not read URL`'.$service_url.'\'');
2267                                 $err_code = PHPCAS_SERVICE_NOT_AVAILABLE;
2268                                 // give an error message
2269                                 $output = sprintf($this->getString(CAS_STR_SERVICE_UNAVAILABLE),
2270                                         $service_url,
2271                                         $err_msg);
2272                                 $res = FALSE;
2273                         } else {
2274                                 // URL has been fetched, extract the cookies
2275                                 phpCAS::trace('URL`'.$service_url.'\' has been read, storing cookies:');
2276                                 foreach ( $headers as $header ) {
2277                                         // test if the header is a cookie
2278                                         if ( preg_match('/^Set-Cookie:/',$header) ) {
2279                                                 // the header is a cookie, remove the beginning
2280                                                 $header_val = preg_replace('/^Set-Cookie: */','',$header);
2281                                                 // extract interesting information
2282                                                 $name_val = strtok($header_val,'; ');
2283                                                 // extract the name and the value of the cookie
2284                                                 $cookie_name = strtok($name_val,'=');
2285                                                 $cookie_val = strtok('=');
2286                                                 // store the cookie 
2287                                                 $_SESSION['phpCAS']['services'][$url]['cookies'][$cookie_name] = $cookie_val;
2288                                                 phpCAS::trace($cookie_name.' -> '.$cookie_val);
2289                                         }
2290                                 }
2291                         }
2292                 }
2293                 
2294                 phpCAS::traceEnd($res);
2295                 return $res;
2296                 }
2297         
2298         /**
2299          * This method is used to access an IMAP/POP3/NNTP service.
2300          * 
2301          * @param $url a string giving the URL of the service, including the mailing box
2302          * for IMAP URLs, as accepted by imap_open().
2303          * @param $service a string giving for CAS retrieve Proxy ticket
2304          * @param $flags options given to imap_open().
2305          * @param $err_code an error code Possible values are PHPCAS_SERVICE_OK (on
2306          * success), PHPCAS_SERVICE_PT_NO_SERVER_RESPONSE, PHPCAS_SERVICE_PT_BAD_SERVER_RESPONSE,
2307          * PHPCAS_SERVICE_PT_FAILURE, PHPCAS_SERVICE_NOT AVAILABLE.
2308          * @param $err_msg an error message on failure
2309          * @param $pt the Proxy Ticket (PT) retrieved from the CAS server to access the URL
2310          * on success, FALSE on error).
2311          *
2312          * @return an IMAP stream on success, FALSE otherwise (in this later case, $err_code
2313          * gives the reason why it failed and $err_msg contains an error message).
2314          *
2315          * @public
2316          */
2317         function serviceMail($url,$service,$flags,&$err_code,&$err_msg,&$pt)
2318                 {
2319                 phpCAS::traceBegin();
2320                 // at first retrieve a PT
2321                 $pt = $this->retrievePT($service,$err_code,$output);
2322                 
2323                 $stream = FALSE;
2324                 
2325                 // test if PT was retrieved correctly
2326                 if ( !$pt ) {
2327                         // note: $err_code and $err_msg are filled by CASClient::retrievePT()
2328                         phpCAS::trace('PT was not retrieved correctly');
2329                 } else {
2330                         phpCAS::trace('opening IMAP URL `'.$url.'\'...');
2331                         $stream = @imap_open($url,$this->getUser(),$pt,$flags);
2332                         if ( !$stream ) {
2333                                 phpCAS::trace('could not open URL');
2334                                 $err_code = PHPCAS_SERVICE_NOT_AVAILABLE;
2335                                 // give an error message
2336                                 $err_msg = sprintf($this->getString(CAS_STR_SERVICE_UNAVAILABLE),
2337                                         $service_url,
2338                                         var_export(imap_errors(),TRUE));
2339                                 $pt = FALSE;
2340                                 $stream = FALSE;
2341                         } else {
2342                                 phpCAS::trace('ok');
2343                         }
2344                 }
2345                 
2346                 phpCAS::traceEnd($stream);
2347                 return $stream;
2348                 }
2349         
2350         /** @} */
2351         
2352         // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
2353         // XX                                                                    XX
2354         // XX                  PROXIED CLIENT FEATURES (CAS 2.0)                 XX
2355         // XX                                                                    XX
2356         // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
2357         
2358         // ########################################################################
2359         //  PT
2360         // ########################################################################
2361         /**
2362          * @addtogroup internalProxied
2363          * @{
2364          */  
2365         
2366         /**
2367          * the Proxy Ticket provided in the URL of the request if present
2368          * (empty otherwise). Written by CASClient::CASClient(), read by 
2369          * CASClient::getPT() and CASClient::hasPGT().
2370          *
2371          * @hideinitializer
2372          * @private
2373          */
2374         var $_pt = '';
2375         
2376         /**
2377          * This method returns the Proxy Ticket provided in the URL of the request.
2378          * @return The proxy ticket.
2379          * @private
2380          */
2381         function getPT()
2382                 {
2383                 //      return 'ST'.substr($this->_pt, 2);
2384                 return $this->_pt;
2385                 }
2386         
2387         /**
2388          * This method stores the Proxy Ticket.
2389          * @param $pt The Proxy Ticket.
2390          * @private
2391          */
2392         function setPT($pt)
2393                 { $this->_pt = $pt; }
2394         
2395         /**
2396          * This method tells if a Proxy Ticket was stored.
2397          * @return TRUE if a Proxy Ticket has been stored.
2398          * @private
2399          */
2400         function hasPT()
2401                 { return !empty($this->_pt); }
2402         /**
2403        * This method returns the SAML Ticket provided in the URL of the request.
2404        * @return The SAML ticket.
2405        * @private
2406        */
2407        function getSA()
2408        { return 'ST'.substr($this->_sa, 2); }
2409
2410        /**
2411        * This method stores the SAML Ticket.
2412        * @param $sa The SAML Ticket.
2413        * @private
2414        */
2415        function setSA($sa)
2416        { $this->_sa = $sa; }
2417
2418        /**
2419        * This method tells if a SAML Ticket was stored.
2420        * @return TRUE if a SAML Ticket has been stored.
2421        * @private
2422        */
2423        function hasSA()
2424        { return !empty($this->_sa); }
2425
2426         /** @} */
2427         // ########################################################################
2428         //  PT VALIDATION
2429         // ########################################################################
2430         /**
2431          * @addtogroup internalProxied
2432          * @{
2433          */  
2434         
2435         /**
2436          * This method is used to validate a PT; halt on failure
2437          * 
2438          * @return bool TRUE when successfull, halt otherwise by calling CASClient::authError().
2439          *
2440          * @private
2441          */
2442         function validatePT(&$validate_url,&$text_response,&$tree_response)
2443                 {
2444                 phpCAS::traceBegin();
2445                 // build the URL to validate the ticket
2446                 $validate_url = $this->getServerProxyValidateURL().'&ticket='.$this->getPT();
2447                 
2448                 if ( $this->isProxy() ) {
2449                         // pass the callback url for CAS proxies
2450                         $validate_url .= '&pgtUrl='.$this->getCallbackURL();
2451                 }
2452                 
2453                 // open and read the URL
2454                 if ( !$this->readURL($validate_url,''/*cookies*/,$headers,$text_response,$err_msg) ) {
2455                         phpCAS::trace('could not open URL \''.$validate_url.'\' to validate ('.$err_msg.')');
2456                         $this->authError('PT not validated',
2457                                 $validate_url,
2458                                 TRUE/*$no_response*/);
2459                 }
2460                 
2461                 // read the response of the CAS server into a DOM object
2462                 if ( !($dom = domxml_open_mem($text_response))) {
2463                         // read failed
2464                         $this->authError('PT not validated',
2465                                 $validate_url,
2466                                 FALSE/*$no_response*/,
2467                                 TRUE/*$bad_response*/,
2468                                 $text_response);
2469                 }
2470                 // read the root node of the XML tree
2471                 if ( !($tree_response = $dom->document_element()) ) {
2472                         // read failed
2473                         $this->authError('PT not validated',
2474                                 $validate_url,
2475                                 FALSE/*$no_response*/,
2476                                 TRUE/*$bad_response*/,
2477                                 $text_response);
2478                 }
2479                 // insure that tag name is 'serviceResponse'
2480                 if ( $tree_response->node_name() != 'serviceResponse' ) {
2481                         // bad root node
2482                         $this->authError('PT not validated',
2483                                 $validate_url,
2484                                 FALSE/*$no_response*/,
2485                                 TRUE/*$bad_response*/,
2486                                 $text_response);
2487                 }
2488                 if ( sizeof($arr = $tree_response->get_elements_by_tagname("authenticationSuccess")) != 0) {
2489                         // authentication succeded, extract the user name
2490                         if ( sizeof($arr = $tree_response->get_elements_by_tagname("user")) == 0) {
2491                                 // no user specified => error
2492                                 $this->authError('PT not validated',
2493                                         $validate_url,
2494                                         FALSE/*$no_response*/,
2495                                         TRUE/*$bad_response*/,
2496                                         $text_response);
2497                         }
2498                         $this->setUser(trim($arr[0]->get_content()));
2499                         
2500                 } else if ( sizeof($arr = $tree_response->get_elements_by_tagname("authenticationFailure")) != 0) {
2501                         // authentication succeded, extract the error code and message
2502                         $this->authError('PT not validated',
2503                                 $validate_url,
2504                                 FALSE/*$no_response*/,
2505                                 FALSE/*$bad_response*/,
2506                                 $text_response,
2507                                 $arr[0]->get_attribute('code')/*$err_code*/,
2508                                 trim($arr[0]->get_content())/*$err_msg*/);
2509                 } else {
2510                         $this->authError('PT not validated',
2511                                 $validate_url,  
2512                                 FALSE/*$no_response*/,
2513                                 TRUE/*$bad_response*/,
2514                                 $text_response);
2515                 }
2516                 
2517                 // at this step, PT has been validated and $this->_user has been set,
2518                 
2519                 phpCAS::traceEnd(TRUE);
2520                 return TRUE;
2521                 }
2522         
2523         /** @} */
2524         
2525         // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
2526         // XX                                                                    XX
2527         // XX                               MISC                                 XX
2528         // XX                                                                    XX
2529         // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
2530         
2531         /**
2532          * @addtogroup internalMisc
2533          * @{
2534          */  
2535         
2536         // ########################################################################
2537         //  URL
2538         // ########################################################################
2539         /**
2540          * the URL of the current request (without any ticket CGI parameter). Written 
2541          * and read by CASClient::getURL().
2542          *
2543          * @hideinitializer
2544          * @private
2545          */
2546         var $_url = '';
2547         
2548         /**
2549          * This method returns the URL of the current request (without any ticket
2550          * CGI parameter).
2551          *
2552          * @return The URL
2553          *
2554          * @private
2555          */
2556         function getURL()
2557                 {
2558                 phpCAS::traceBegin();
2559                 // the URL is built when needed only
2560                 if ( empty($this->_url) ) {
2561                         $final_uri = '';
2562                         // remove the ticket if present in the URL
2563                         $final_uri = ($this->isHttps()) ? 'https' : 'http';
2564                         $final_uri .= '://';
2565                         /* replaced by Julien Marchal - v0.4.6
2566                          * $this->_url .= $_SERVER['SERVER_NAME'];
2567                          */
2568                         if(empty($_SERVER['HTTP_X_FORWARDED_SERVER'])){
2569                                 /* replaced by teedog - v0.4.12
2570                                  * $this->_url .= $_SERVER['SERVER_NAME'];
2571                                  */
2572                                 if (empty($_SERVER['SERVER_NAME'])) {
2573                                         $server_name = $_SERVER['HTTP_HOST'];
2574                                 } else {
2575                                         $server_name = $_SERVER['SERVER_NAME'];
2576                                 }
2577                         } else {
2578                                 $server_name = $_SERVER['HTTP_X_FORWARDED_SERVER'];
2579                         }
2580                         $final_uri .= $server_name;
2581                         if (!strpos($server_name, ':')) {
2582                                 if ( ($this->isHttps() && $_SERVER['SERVER_PORT']!=443)
2583                                                 || (!$this->isHttps() && $_SERVER['SERVER_PORT']!=80) ) {
2584                                         $final_uri .= ':';
2585                                         $final_uri .= $_SERVER['SERVER_PORT'];
2586                                 }
2587                         }
2588                         
2589                         $php_is_for_sissies = split("\?", $_SERVER['REQUEST_URI'], 2);
2590                         $final_uri .= $php_is_for_sissies[0];
2591                         if(sizeof($php_is_for_sissies) > 1){
2592                                 $cgi_params = '?' . $php_is_for_sissies[1];
2593                         } else {
2594                                 $cgi_params = '?';
2595                         }
2596                         // remove the ticket if present in the CGI parameters
2597                         $cgi_params = preg_replace('/&ticket=[^&]*/','',$cgi_params);
2598                         $cgi_params = preg_replace('/\?ticket=[^&;]*/','?',$cgi_params);
2599                         $cgi_params = preg_replace('/\?%26/','?',$cgi_params);
2600                         $cgi_params = preg_replace('/\?&/','?',$cgi_params);
2601                         $cgi_params = preg_replace('/\?$/','',$cgi_params);
2602                         $final_uri .= $cgi_params;
2603                         $this->setURL($final_uri);
2604                 }
2605                 phpCAS::traceEnd($this->_url);
2606                 return $this->_url;
2607                 }
2608         
2609         /**
2610          * This method sets the URL of the current request 
2611          *
2612          * @param $url url to set for service
2613          *
2614          * @private
2615          */
2616         function setURL($url)
2617                 {
2618                 $this->_url = $url;
2619                 }
2620         
2621         // ########################################################################
2622         //  AUTHENTICATION ERROR HANDLING
2623         // ########################################################################
2624         /**
2625          * This method is used to print the HTML output when the user was not authenticated.
2626          *
2627          * @param $failure the failure that occured
2628          * @param $cas_url the URL the CAS server was asked for
2629          * @param $no_response the response from the CAS server (other 
2630          * parameters are ignored if TRUE)
2631          * @param $bad_response bad response from the CAS server ($err_code
2632          * and $err_msg ignored if TRUE)
2633          * @param $cas_response the response of the CAS server
2634          * @param $err_code the error code given by the CAS server
2635          * @param $err_msg the error message given by the CAS server
2636          *
2637          * @private
2638          */
2639         function authError($failure,$cas_url,$no_response,$bad_response='',$cas_response='',$err_code='',$err_msg='')
2640                 {
2641                 phpCAS::traceBegin();
2642                 
2643                 $this->printHTMLHeader($this->getString(CAS_STR_AUTHENTICATION_FAILED));
2644                 printf($this->getString(CAS_STR_YOU_WERE_NOT_AUTHENTICATED),$this->getURL(),$_SERVER['SERVER_ADMIN']);
2645                 phpCAS::trace('CAS URL: '.$cas_url);
2646                 phpCAS::trace('Authentication failure: '.$failure);
2647                 if ( $no_response ) {
2648                         phpCAS::trace('Reason: no response from the CAS server');
2649                 } else {
2650                         if ( $bad_response ) {
2651                                 phpCAS::trace('Reason: bad response from the CAS server');
2652                         } else {
2653                                 switch ($this->getServerVersion()) {
2654                                         case CAS_VERSION_1_0:
2655                                                 phpCAS::trace('Reason: CAS error');
2656                                                 break;
2657                                         case CAS_VERSION_2_0:
2658                                                 if ( empty($err_code) )
2659                                                         phpCAS::trace('Reason: no CAS error');
2660                                                 else
2661                                                         phpCAS::trace('Reason: ['.$err_code.'] CAS error: '.$err_msg);
2662                                                 break;
2663                                 }
2664                         }
2665                         phpCAS::trace('CAS response: '.$cas_response);
2666                 }
2667                 $this->printHTMLFooter();
2668                 phpCAS::traceExit();
2669                 exit();
2670                 }
2671         
2672         /** @} */
2673 }
2674
2675 ?>