Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.
   1  <?php
   2  
   3  /**
   4   * Licensed to Jasig under one or more contributor license
   5   * agreements. See the NOTICE file distributed with this work for
   6   * additional information regarding copyright ownership.
   7   *
   8   * Jasig licenses this file to you under the Apache License,
   9   * Version 2.0 (the "License"); you may not use this file except in
  10   * compliance with the License. You may obtain a copy of the License at:
  11   *
  12   * http://www.apache.org/licenses/LICENSE-2.0
  13   *
  14   * Unless required by applicable law or agreed to in writing, software
  15   * distributed under the License is distributed on an "AS IS" BASIS,
  16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17   * See the License for the specific language governing permissions and
  18   * limitations under the License.
  19   *
  20   *
  21   *
  22   * Interface class of the phpCAS library
  23   * PHP Version 5
  24   *
  25   * @file     CAS/CAS.php
  26   * @category Authentication
  27   * @package  PhpCAS
  28   * @author   Pascal Aubry <pascal.aubry@univ-rennes1.fr>
  29   * @author   Olivier Berger <olivier.berger@it-sudparis.eu>
  30   * @author   Brett Bieber <brett.bieber@gmail.com>
  31   * @author   Joachim Fritschi <jfritschi@freenet.de>
  32   * @author   Adam Franco <afranco@middlebury.edu>
  33   * @license  http://www.apache.org/licenses/LICENSE-2.0  Apache License 2.0
  34   * @link     https://wiki.jasig.org/display/CASC/phpCAS
  35   * @ingroup public
  36   */
  37  
  38  
  39  //
  40  // hack by Vangelis Haniotakis to handle the absence of $_SERVER['REQUEST_URI']
  41  // in IIS
  42  //
  43  if (!isset($_SERVER['REQUEST_URI']) && isset($_SERVER['SCRIPT_NAME']) && isset($_SERVER['QUERY_STRING'])) {
  44      $_SERVER['REQUEST_URI'] = $_SERVER['SCRIPT_NAME'] . '?' . $_SERVER['QUERY_STRING'];
  45  }
  46  
  47  // Add a E_USER_DEPRECATED for php versions <= 5.2
  48  if (!defined('E_USER_DEPRECATED')) {
  49      define('E_USER_DEPRECATED', E_USER_NOTICE);
  50  }
  51  
  52  
  53  // ########################################################################
  54  //  CONSTANTS
  55  // ########################################################################
  56  
  57  // ------------------------------------------------------------------------
  58  //  CAS VERSIONS
  59  // ------------------------------------------------------------------------
  60  
  61  /**
  62   * phpCAS version. accessible for the user by phpCAS::getVersion().
  63   */
  64  define('PHPCAS_VERSION', '1.3.8');
  65  
  66  /**
  67   * @addtogroup public
  68   * @{
  69   */
  70  
  71  /**
  72   * phpCAS supported protocols. accessible for the user by phpCAS::getSupportedProtocols().
  73   */
  74  
  75  /**
  76   * CAS version 1.0
  77   */
  78  define("CAS_VERSION_1_0", '1.0');
  79  /*!
  80   * CAS version 2.0
  81  */
  82  define("CAS_VERSION_2_0", '2.0');
  83  /**
  84   * CAS version 3.0
  85   */
  86  define("CAS_VERSION_3_0", '3.0');
  87  
  88  // ------------------------------------------------------------------------
  89  //  SAML defines
  90  // ------------------------------------------------------------------------
  91  
  92  /**
  93   * SAML protocol
  94   */
  95  define("SAML_VERSION_1_1", 'S1');
  96  
  97  /**
  98   * XML header for SAML POST
  99   */
 100  define("SAML_XML_HEADER", '<?xml version="1.0" encoding="UTF-8"?>');
 101  
 102  /**
 103   * SOAP envelope for SAML POST
 104   */
 105  define("SAML_SOAP_ENV", '<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Header/>');
 106  
 107  /**
 108   * SOAP body for SAML POST
 109   */
 110  define("SAML_SOAP_BODY", '<SOAP-ENV:Body>');
 111  
 112  /**
 113   * SAMLP request
 114   */
 115  define("SAMLP_REQUEST", '<samlp:Request xmlns:samlp="urn:oasis:names:tc:SAML:1.0:protocol"  MajorVersion="1" MinorVersion="1" RequestID="_192.168.16.51.1024506224022" IssueInstant="2002-06-19T17:03:44.022Z">');
 116  define("SAMLP_REQUEST_CLOSE", '</samlp:Request>');
 117  
 118  /**
 119   * SAMLP artifact tag (for the ticket)
 120   */
 121  define("SAML_ASSERTION_ARTIFACT", '<samlp:AssertionArtifact>');
 122  
 123  /**
 124   * SAMLP close
 125   */
 126  define("SAML_ASSERTION_ARTIFACT_CLOSE", '</samlp:AssertionArtifact>');
 127  
 128  /**
 129   * SOAP body close
 130   */
 131  define("SAML_SOAP_BODY_CLOSE", '</SOAP-ENV:Body>');
 132  
 133  /**
 134   * SOAP envelope close
 135   */
 136  define("SAML_SOAP_ENV_CLOSE", '</SOAP-ENV:Envelope>');
 137  
 138  /**
 139   * SAML Attributes
 140   */
 141  define("SAML_ATTRIBUTES", 'SAMLATTRIBS');
 142  
 143  /**
 144   * SAML Attributes
 145   */
 146  define("DEFAULT_ERROR", 'Internal script failure');
 147  
 148  /** @} */
 149  /**
 150   * @addtogroup publicPGTStorage
 151   * @{
 152   */
 153  // ------------------------------------------------------------------------
 154  //  FILE PGT STORAGE
 155  // ------------------------------------------------------------------------
 156  /**
 157   * Default path used when storing PGT's to file
 158   */
 159  define("CAS_PGT_STORAGE_FILE_DEFAULT_PATH", session_save_path());
 160  /** @} */
 161  // ------------------------------------------------------------------------
 162  // SERVICE ACCESS ERRORS
 163  // ------------------------------------------------------------------------
 164  /**
 165   * @addtogroup publicServices
 166   * @{
 167   */
 168  
 169  /**
 170   * phpCAS::service() error code on success
 171   */
 172  define("PHPCAS_SERVICE_OK", 0);
 173  /**
 174   * phpCAS::service() error code when the PT could not retrieve because
 175   * the CAS server did not respond.
 176   */
 177  define("PHPCAS_SERVICE_PT_NO_SERVER_RESPONSE", 1);
 178  /**
 179   * phpCAS::service() error code when the PT could not retrieve because
 180   * the response of the CAS server was ill-formed.
 181   */
 182  define("PHPCAS_SERVICE_PT_BAD_SERVER_RESPONSE", 2);
 183  /**
 184   * phpCAS::service() error code when the PT could not retrieve because
 185   * the CAS server did not want to.
 186   */
 187  define("PHPCAS_SERVICE_PT_FAILURE", 3);
 188  /**
 189   * phpCAS::service() error code when the service was not available.
 190   */
 191  define("PHPCAS_SERVICE_NOT_AVAILABLE", 4);
 192  
 193  // ------------------------------------------------------------------------
 194  // SERVICE TYPES
 195  // ------------------------------------------------------------------------
 196  /**
 197   * phpCAS::getProxiedService() type for HTTP GET
 198   */
 199  define("PHPCAS_PROXIED_SERVICE_HTTP_GET", 'CAS_ProxiedService_Http_Get');
 200  /**
 201   * phpCAS::getProxiedService() type for HTTP POST
 202   */
 203  define("PHPCAS_PROXIED_SERVICE_HTTP_POST", 'CAS_ProxiedService_Http_Post');
 204  /**
 205   * phpCAS::getProxiedService() type for IMAP
 206   */
 207  define("PHPCAS_PROXIED_SERVICE_IMAP", 'CAS_ProxiedService_Imap');
 208  
 209  
 210  /** @} */
 211  // ------------------------------------------------------------------------
 212  //  LANGUAGES
 213  // ------------------------------------------------------------------------
 214  /**
 215   * @addtogroup publicLang
 216   * @{
 217   */
 218  
 219  define("PHPCAS_LANG_ENGLISH", 'CAS_Languages_English');
 220  define("PHPCAS_LANG_FRENCH", 'CAS_Languages_French');
 221  define("PHPCAS_LANG_GREEK", 'CAS_Languages_Greek');
 222  define("PHPCAS_LANG_GERMAN", 'CAS_Languages_German');
 223  define("PHPCAS_LANG_JAPANESE", 'CAS_Languages_Japanese');
 224  define("PHPCAS_LANG_SPANISH", 'CAS_Languages_Spanish');
 225  define("PHPCAS_LANG_CATALAN", 'CAS_Languages_Catalan');
 226  define("PHPCAS_LANG_CHINESE_SIMPLIFIED", 'CAS_Languages_ChineseSimplified');
 227  
 228  /** @} */
 229  
 230  /**
 231   * @addtogroup internalLang
 232   * @{
 233   */
 234  
 235  /**
 236   * phpCAS default language (when phpCAS::setLang() is not used)
 237   */
 238  define("PHPCAS_LANG_DEFAULT", PHPCAS_LANG_ENGLISH);
 239  
 240  /** @} */
 241  // ------------------------------------------------------------------------
 242  //  DEBUG
 243  // ------------------------------------------------------------------------
 244  /**
 245   * @addtogroup publicDebug
 246   * @{
 247   */
 248  
 249  /**
 250   * The default directory for the debug file under Unix.
 251   * @return  string directory for the debug file
 252   */
 253  function gettmpdir() {
 254  if (!empty($_ENV['TMP'])) { return realpath($_ENV['TMP']); }
 255  if (!empty($_ENV['TMPDIR'])) { return realpath( $_ENV['TMPDIR']); }
 256  if (!empty($_ENV['TEMP'])) { return realpath( $_ENV['TEMP']); }
 257  return "/tmp";
 258  }
 259  define('DEFAULT_DEBUG_DIR', gettmpdir()."/");
 260  
 261  /** @} */
 262  
 263  // include the class autoloader
 264  require_once dirname(__FILE__) . '/CAS/Autoload.php';
 265  
 266  /**
 267   * The phpCAS class is a simple container for the phpCAS library. It provides CAS
 268   * authentication for web applications written in PHP.
 269   *
 270   * @ingroup public
 271   * @class phpCAS
 272   * @category Authentication
 273   * @package  PhpCAS
 274   * @author   Pascal Aubry <pascal.aubry@univ-rennes1.fr>
 275   * @author   Olivier Berger <olivier.berger@it-sudparis.eu>
 276   * @author   Brett Bieber <brett.bieber@gmail.com>
 277   * @author   Joachim Fritschi <jfritschi@freenet.de>
 278   * @author   Adam Franco <afranco@middlebury.edu>
 279   * @license  http://www.apache.org/licenses/LICENSE-2.0  Apache License 2.0
 280   * @link     https://wiki.jasig.org/display/CASC/phpCAS
 281   */
 282  
 283  class phpCAS
 284  {
 285  
 286      /**
 287       * This variable is used by the interface class phpCAS.
 288       *
 289       * @var CAS_Client
 290       * @hideinitializer
 291       */
 292      private static $_PHPCAS_CLIENT;
 293  
 294      /**
 295       * @var array
 296       * This variable is used to store where the initializer is called from
 297       * (to print a comprehensive error in case of multiple calls).
 298       *
 299       * @hideinitializer
 300       */
 301      private static $_PHPCAS_INIT_CALL;
 302  
 303      /**
 304       * @var array
 305       * This variable is used to store phpCAS debug mode.
 306       *
 307       * @hideinitializer
 308       */
 309      private static $_PHPCAS_DEBUG;
 310  
 311      /**
 312       * This variable is used to enable verbose mode
 313       * This pevents debug info to be show to the user. Since it's a security
 314       * feature the default is false
 315       *
 316       * @hideinitializer
 317       */
 318      private static $_PHPCAS_VERBOSE = false;
 319  
 320  
 321      // ########################################################################
 322      //  INITIALIZATION
 323      // ########################################################################
 324  
 325      /**
 326       * @addtogroup publicInit
 327       * @{
 328       */
 329  
 330      /**
 331       * phpCAS client initializer.
 332       *
 333       * @param string $server_version  the version of the CAS server
 334       * @param string $server_hostname the hostname of the CAS server
 335       * @param int    $server_port     the port the CAS server is running on
 336       * @param string $server_uri      the URI the CAS server is responding on
 337       * @param bool   $changeSessionID Allow phpCAS to change the session_id (Single
 338       * Sign Out/handleLogoutRequests is based on that change)
 339       *
 340       * @return void a newly created CAS_Client object
 341       * @note Only one of the phpCAS::client() and phpCAS::proxy functions should be
 342       * called, only once, and before all other methods (except phpCAS::getVersion()
 343       * and phpCAS::setDebug()).
 344       */
 345      public static function client($server_version, $server_hostname,
 346          $server_port, $server_uri, $changeSessionID = true
 347      ) {
 348          phpCAS :: traceBegin();
 349          if (is_object(self::$_PHPCAS_CLIENT)) {
 350              phpCAS :: error(self::$_PHPCAS_INIT_CALL['method'] . '() has already been called (at ' . self::$_PHPCAS_INIT_CALL['file'] . ':' . self::$_PHPCAS_INIT_CALL['line'] . ')');
 351          }
 352  
 353          // store where the initializer is called from
 354          $dbg = debug_backtrace();
 355          self::$_PHPCAS_INIT_CALL = array (
 356              'done' => true,
 357              'file' => $dbg[0]['file'],
 358              'line' => $dbg[0]['line'],
 359              'method' => __CLASS__ . '::' . __FUNCTION__
 360          );
 361  
 362          // initialize the object $_PHPCAS_CLIENT
 363          try {
 364              self::$_PHPCAS_CLIENT = new CAS_Client(
 365                  $server_version, false, $server_hostname, $server_port, $server_uri,
 366                  $changeSessionID
 367              );
 368          } catch (Exception $e) {
 369              phpCAS :: error(get_class($e) . ': ' . $e->getMessage());
 370          }
 371          phpCAS :: traceEnd();
 372      }
 373  
 374      /**
 375       * phpCAS proxy initializer.
 376       *
 377       * @param string $server_version  the version of the CAS server
 378       * @param string $server_hostname the hostname of the CAS server
 379       * @param int    $server_port     the port the CAS server is running on
 380       * @param string $server_uri      the URI the CAS server is responding on
 381       * @param bool   $changeSessionID Allow phpCAS to change the session_id (Single
 382       * Sign Out/handleLogoutRequests is based on that change)
 383       *
 384       * @return void a newly created CAS_Client object
 385       * @note Only one of the phpCAS::client() and phpCAS::proxy functions should be
 386       * called, only once, and before all other methods (except phpCAS::getVersion()
 387       * and phpCAS::setDebug()).
 388       */
 389      public static function proxy($server_version, $server_hostname,
 390          $server_port, $server_uri, $changeSessionID = true
 391      ) {
 392          phpCAS :: traceBegin();
 393          if (is_object(self::$_PHPCAS_CLIENT)) {
 394              phpCAS :: error(self::$_PHPCAS_INIT_CALL['method'] . '() has already been called (at ' . self::$_PHPCAS_INIT_CALL['file'] . ':' . self::$_PHPCAS_INIT_CALL['line'] . ')');
 395          }
 396  
 397          // store where the initialzer is called from
 398          $dbg = debug_backtrace();
 399          self::$_PHPCAS_INIT_CALL = array (
 400              'done' => true,
 401              'file' => $dbg[0]['file'],
 402              'line' => $dbg[0]['line'],
 403              'method' => __CLASS__ . '::' . __FUNCTION__
 404          );
 405  
 406          // initialize the object $_PHPCAS_CLIENT
 407          try {
 408              self::$_PHPCAS_CLIENT = new CAS_Client(
 409                  $server_version, true, $server_hostname, $server_port, $server_uri,
 410                  $changeSessionID
 411              );
 412          } catch (Exception $e) {
 413              phpCAS :: error(get_class($e) . ': ' . $e->getMessage());
 414          }
 415          phpCAS :: traceEnd();
 416      }
 417  
 418      /**
 419       * Answer whether or not the client or proxy has been initialized
 420       *
 421       * @return bool
 422       */
 423      public static function isInitialized ()
 424      {
 425          return (is_object(self::$_PHPCAS_CLIENT));
 426      }
 427  
 428      /** @} */
 429      // ########################################################################
 430      //  DEBUGGING
 431      // ########################################################################
 432  
 433      /**
 434       * @addtogroup publicDebug
 435       * @{
 436       */
 437  
 438      /**
 439       * Set/unset debug mode
 440       *
 441       * @param string $filename the name of the file used for logging, or false
 442       * to stop debugging.
 443       *
 444       * @return void
 445       */
 446      public static function setDebug($filename = '')
 447      {
 448          if ($filename != false && gettype($filename) != 'string') {
 449              phpCAS :: error('type mismatched for parameter $dbg (should be false or the name of the log file)');
 450          }
 451          if ($filename === false) {
 452              self::$_PHPCAS_DEBUG['filename'] = false;
 453  
 454          } else {
 455              if (empty ($filename)) {
 456                  if (preg_match('/^Win.*/', getenv('OS'))) {
 457                      if (isset ($_ENV['TMP'])) {
 458                          $debugDir = $_ENV['TMP'] . '/';
 459                      } else {
 460                          $debugDir = '';
 461                      }
 462                  } else {
 463                      $debugDir = DEFAULT_DEBUG_DIR;
 464                  }
 465                  $filename = $debugDir . 'phpCAS.log';
 466              }
 467  
 468              if (empty (self::$_PHPCAS_DEBUG['unique_id'])) {
 469                  self::$_PHPCAS_DEBUG['unique_id'] = substr(strtoupper(md5(uniqid(''))), 0, 4);
 470              }
 471  
 472              self::$_PHPCAS_DEBUG['filename'] = $filename;
 473              self::$_PHPCAS_DEBUG['indent'] = 0;
 474  
 475              phpCAS :: trace('START ('.date("Y-m-d H:i:s").') phpCAS-' . PHPCAS_VERSION . ' ******************');
 476          }
 477      }
 478  
 479      /**
 480       * Enable verbose errors messages in the website output
 481       * This is a security relevant since internal status info may leak an may
 482       * help an attacker. Default is therefore false
 483       *
 484       * @param bool $verbose enable verbose output
 485       *
 486       * @return void
 487       */
 488      public static function setVerbose($verbose)
 489      {
 490          if ($verbose === true) {
 491              self::$_PHPCAS_VERBOSE = true;
 492          } else {
 493              self::$_PHPCAS_VERBOSE = false;
 494          }
 495      }
 496  
 497  
 498      /**
 499       * Show is verbose mode is on
 500       *
 501       * @return bool verbose
 502       */
 503      public static function getVerbose()
 504      {
 505          return self::$_PHPCAS_VERBOSE;
 506      }
 507  
 508      /**
 509       * Logs a string in debug mode.
 510       *
 511       * @param string $str the string to write
 512       *
 513       * @return void
 514       * @private
 515       */
 516      public static function log($str)
 517      {
 518          $indent_str = ".";
 519  
 520  
 521          if (!empty(self::$_PHPCAS_DEBUG['filename'])) {
 522              // Check if file exists and modifiy file permissions to be only
 523              // readable by the webserver
 524              if (!file_exists(self::$_PHPCAS_DEBUG['filename'])) {
 525                  touch(self::$_PHPCAS_DEBUG['filename']);
 526                  // Chmod will fail on windows
 527                  @chmod(self::$_PHPCAS_DEBUG['filename'], 0600);
 528              }
 529              for ($i = 0; $i < self::$_PHPCAS_DEBUG['indent']; $i++) {
 530  
 531                  $indent_str .= '|    ';
 532              }
 533              // allow for multiline output with proper identing. Usefull for
 534              // dumping cas answers etc.
 535              $str2 = str_replace("\n", "\n" . self::$_PHPCAS_DEBUG['unique_id'] . ' ' . $indent_str, $str);
 536              error_log(self::$_PHPCAS_DEBUG['unique_id'] . ' ' . $indent_str . $str2 . "\n", 3, self::$_PHPCAS_DEBUG['filename']);
 537          }
 538  
 539      }
 540  
 541      /**
 542       * This method is used by interface methods to print an error and where the
 543       * function was originally called from.
 544       *
 545       * @param string $msg the message to print
 546       *
 547       * @return void
 548       * @private
 549       */
 550      public static function error($msg)
 551      {
 552          phpCAS :: traceBegin();
 553          $dbg = debug_backtrace();
 554          $function = '?';
 555          $file = '?';
 556          $line = '?';
 557          if (is_array($dbg)) {
 558              for ($i = 1; $i < sizeof($dbg); $i++) {
 559                  if (is_array($dbg[$i]) && isset($dbg[$i]['class']) ) {
 560                      if ($dbg[$i]['class'] == __CLASS__) {
 561                          $function = $dbg[$i]['function'];
 562                          $file = $dbg[$i]['file'];
 563                          $line = $dbg[$i]['line'];
 564                      }
 565                  }
 566              }
 567          }
 568          if (self::$_PHPCAS_VERBOSE) {
 569              echo "<br />\n<b>phpCAS error</b>: <font color=\"FF0000\"><b>" . __CLASS__ . "::" . $function . '(): ' . htmlentities($msg) . "</b></font> in <b>" . $file . "</b> on line <b>" . $line . "</b><br />\n";
 570          } else {
 571              echo "<br />\n<b>Error</b>: <font color=\"FF0000\"><b>". DEFAULT_ERROR ."</b><br />\n";
 572          }
 573          phpCAS :: trace($msg . ' in ' . $file . 'on line ' . $line );
 574          phpCAS :: traceEnd();
 575  
 576          throw new CAS_GracefullTerminationException(__CLASS__ . "::" . $function . '(): ' . $msg);
 577      }
 578  
 579      /**
 580       * This method is used to log something in debug mode.
 581       *
 582       * @param string $str string to log
 583       *
 584       * @return void
 585       */
 586      public static function trace($str)
 587      {
 588          $dbg = debug_backtrace();
 589          phpCAS :: log($str . ' [' . basename($dbg[0]['file']) . ':' . $dbg[0]['line'] . ']');
 590      }
 591  
 592      /**
 593       * This method is used to indicate the start of the execution of a function
 594       * in debug mode.
 595       *
 596       * @return void
 597       */
 598      public static function traceBegin()
 599      {
 600          $dbg = debug_backtrace();
 601          $str = '=> ';
 602          if (!empty ($dbg[1]['class'])) {
 603              $str .= $dbg[1]['class'] . '::';
 604          }
 605          $str .= $dbg[1]['function'] . '(';
 606          if (is_array($dbg[1]['args'])) {
 607              foreach ($dbg[1]['args'] as $index => $arg) {
 608                  if ($index != 0) {
 609                      $str .= ', ';
 610                  }
 611                  if (is_object($arg)) {
 612                      $str .= get_class($arg);
 613                  } else {
 614                      $str .= str_replace(array("\r\n", "\n", "\r"), "", var_export($arg, true));
 615                  }
 616              }
 617          }
 618          if (isset($dbg[1]['file'])) {
 619              $file = basename($dbg[1]['file']);
 620          } else {
 621              $file = 'unknown_file';
 622          }
 623          if (isset($dbg[1]['line'])) {
 624              $line = $dbg[1]['line'];
 625          } else {
 626              $line = 'unknown_line';
 627          }
 628          $str .= ') [' . $file . ':' . $line . ']';
 629          phpCAS :: log($str);
 630          if (!isset(self::$_PHPCAS_DEBUG['indent'])) {
 631              self::$_PHPCAS_DEBUG['indent'] = 0;
 632          } else {
 633              self::$_PHPCAS_DEBUG['indent']++;
 634          }
 635      }
 636  
 637      /**
 638       * This method is used to indicate the end of the execution of a function in
 639       * debug mode.
 640       *
 641       * @param mixed $res the result of the function
 642       *
 643       * @return void
 644       */
 645      public static function traceEnd($res = '')
 646      {
 647          if (empty(self::$_PHPCAS_DEBUG['indent'])) {
 648              self::$_PHPCAS_DEBUG['indent'] = 0;
 649          } else {
 650              self::$_PHPCAS_DEBUG['indent']--;
 651          }
 652          $str = '';
 653          if (is_object($res)) {
 654              $str .= '<= ' . get_class($res);
 655          } else {
 656              $str .= '<= ' . str_replace(array("\r\n", "\n", "\r"), "", var_export($res, true));
 657          }
 658  
 659          phpCAS :: log($str);
 660      }
 661  
 662      /**
 663       * This method is used to indicate the end of the execution of the program
 664       *
 665       * @return void
 666       */
 667      public static function traceExit()
 668      {
 669          phpCAS :: log('exit()');
 670          while (self::$_PHPCAS_DEBUG['indent'] > 0) {
 671              phpCAS :: log('-');
 672              self::$_PHPCAS_DEBUG['indent']--;
 673          }
 674      }
 675  
 676      /** @} */
 677      // ########################################################################
 678      //  INTERNATIONALIZATION
 679      // ########################################################################
 680      /**
 681      * @addtogroup publicLang
 682      * @{
 683      */
 684  
 685      /**
 686       * This method is used to set the language used by phpCAS.
 687       *
 688       * @param string $lang string representing the language.
 689       *
 690       * @return void
 691       *
 692       * @sa PHPCAS_LANG_FRENCH, PHPCAS_LANG_ENGLISH
 693       * @note Can be called only once.
 694       */
 695      public static function setLang($lang)
 696      {
 697          phpCAS::_validateClientExists();
 698  
 699          try {
 700              self::$_PHPCAS_CLIENT->setLang($lang);
 701          } catch (Exception $e) {
 702              phpCAS :: error(get_class($e) . ': ' . $e->getMessage());
 703          }
 704      }
 705  
 706      /** @} */
 707      // ########################################################################
 708      //  VERSION
 709      // ########################################################################
 710      /**
 711      * @addtogroup public
 712      * @{
 713      */
 714  
 715      /**
 716       * This method returns the phpCAS version.
 717       *
 718       * @return string the phpCAS version.
 719       */
 720      public static function getVersion()
 721      {
 722          return PHPCAS_VERSION;
 723      }
 724  
 725      /**
 726       * This method returns supported protocols.
 727       *
 728       * @return array an array of all supported protocols. Use internal protocol name as array key.
 729       */
 730      public static function getSupportedProtocols()
 731      {
 732          $supportedProtocols = array();
 733          $supportedProtocols[CAS_VERSION_1_0] = 'CAS 1.0';
 734          $supportedProtocols[CAS_VERSION_2_0] = 'CAS 2.0';
 735          $supportedProtocols[CAS_VERSION_3_0] = 'CAS 3.0';
 736          $supportedProtocols[SAML_VERSION_1_1] = 'SAML 1.1';
 737  
 738          return $supportedProtocols;
 739      }
 740  
 741      /** @} */
 742      // ########################################################################
 743      //  HTML OUTPUT
 744      // ########################################################################
 745      /**
 746      * @addtogroup publicOutput
 747      * @{
 748      */
 749  
 750      /**
 751       * This method sets the HTML header used for all outputs.
 752       *
 753       * @param string $header the HTML header.
 754       *
 755       * @return void
 756       */
 757      public static function setHTMLHeader($header)
 758      {
 759          phpCAS::_validateClientExists();
 760  
 761          try {
 762              self::$_PHPCAS_CLIENT->setHTMLHeader($header);
 763          } catch (Exception $e) {
 764              phpCAS :: error(get_class($e) . ': ' . $e->getMessage());
 765          }
 766      }
 767  
 768      /**
 769       * This method sets the HTML footer used for all outputs.
 770       *
 771       * @param string $footer the HTML footer.
 772       *
 773       * @return void
 774       */
 775      public static function setHTMLFooter($footer)
 776      {
 777          phpCAS::_validateClientExists();
 778  
 779          try {
 780              self::$_PHPCAS_CLIENT->setHTMLFooter($footer);
 781          } catch (Exception $e) {
 782              phpCAS :: error(get_class($e) . ': ' . $e->getMessage());
 783          }
 784      }
 785  
 786      /** @} */
 787      // ########################################################################
 788      //  PGT STORAGE
 789      // ########################################################################
 790      /**
 791      * @addtogroup publicPGTStorage
 792      * @{
 793      */
 794  
 795      /**
 796       * This method can be used to set a custom PGT storage object.
 797       *
 798       * @param CAS_PGTStorage_AbstractStorage $storage a PGT storage object that inherits from the
 799       * CAS_PGTStorage_AbstractStorage class
 800       *
 801       * @return void
 802       */
 803      public static function setPGTStorage($storage)
 804      {
 805          phpCAS :: traceBegin();
 806          phpCAS::_validateProxyExists();
 807  
 808          try {
 809              self::$_PHPCAS_CLIENT->setPGTStorage($storage);
 810          } catch (Exception $e) {
 811              phpCAS :: error(get_class($e) . ': ' . $e->getMessage());
 812          }
 813          phpCAS :: traceEnd();
 814      }
 815  
 816      /**
 817       * This method is used to tell phpCAS to store the response of the
 818       * CAS server to PGT requests in a database.
 819       *
 820       * @param string $dsn_or_pdo     a dsn string to use for creating a PDO
 821       * object or a PDO object
 822       * @param string $username       the username to use when connecting to the
 823       * database
 824       * @param string $password       the password to use when connecting to the
 825       * database
 826       * @param string $table          the table to use for storing and retrieving
 827       * PGT's
 828       * @param string $driver_options any driver options to use when connecting
 829       * to the database
 830       *
 831       * @return void
 832       */
 833      public static function setPGTStorageDb($dsn_or_pdo, $username='',
 834          $password='', $table='', $driver_options=null
 835      ) {
 836          phpCAS :: traceBegin();
 837          phpCAS::_validateProxyExists();
 838  
 839          try {
 840              self::$_PHPCAS_CLIENT->setPGTStorageDb($dsn_or_pdo, $username, $password, $table, $driver_options);
 841          } catch (Exception $e) {
 842              phpCAS :: error(get_class($e) . ': ' . $e->getMessage());
 843          }
 844          phpCAS :: traceEnd();
 845      }
 846  
 847      /**
 848       * This method is used to tell phpCAS to store the response of the
 849       * CAS server to PGT requests onto the filesystem.
 850       *
 851       * @param string $path the path where the PGT's should be stored
 852       *
 853       * @return void
 854       */
 855      public static function setPGTStorageFile($path = '')
 856      {
 857          phpCAS :: traceBegin();
 858          phpCAS::_validateProxyExists();
 859  
 860          try {
 861              self::$_PHPCAS_CLIENT->setPGTStorageFile($path);
 862          } catch (Exception $e) {
 863              phpCAS :: error(get_class($e) . ': ' . $e->getMessage());
 864          }
 865          phpCAS :: traceEnd();
 866      }
 867      /** @} */
 868      // ########################################################################
 869      // ACCESS TO EXTERNAL SERVICES
 870      // ########################################################################
 871      /**
 872      * @addtogroup publicServices
 873      * @{
 874      */
 875  
 876      /**
 877       * Answer a proxy-authenticated service handler.
 878       *
 879       * @param string $type The service type. One of
 880       * PHPCAS_PROXIED_SERVICE_HTTP_GET; PHPCAS_PROXIED_SERVICE_HTTP_POST;
 881       * PHPCAS_PROXIED_SERVICE_IMAP
 882       *
 883       * @return CAS_ProxiedService
 884       * @throws InvalidArgumentException If the service type is unknown.
 885       */
 886      public static function getProxiedService ($type)
 887      {
 888          phpCAS :: traceBegin();
 889          phpCAS::_validateProxyExists();
 890  
 891          try {
 892              $res = self::$_PHPCAS_CLIENT->getProxiedService($type);
 893          } catch (Exception $e) {
 894              phpCAS :: error(get_class($e) . ': ' . $e->getMessage());
 895          }
 896  
 897          phpCAS :: traceEnd();
 898          return $res;
 899      }
 900  
 901      /**
 902       * Initialize a proxied-service handler with the proxy-ticket it should use.
 903       *
 904       * @param CAS_ProxiedService $proxiedService Proxied Service Handler
 905       *
 906       * @return void
 907       * @throws CAS_ProxyTicketException If there is a proxy-ticket failure.
 908       *	 	 The code of the Exception will be one of:
 909       *	 	 	 PHPCAS_SERVICE_PT_NO_SERVER_RESPONSE
 910       *	 	 	 PHPCAS_SERVICE_PT_BAD_SERVER_RESPONSE
 911       *	 	 	 PHPCAS_SERVICE_PT_FAILURE
 912       */
 913      public static function initializeProxiedService (CAS_ProxiedService $proxiedService)
 914      {
 915          phpCAS::_validateProxyExists();
 916  
 917          try {
 918              self::$_PHPCAS_CLIENT->initializeProxiedService($proxiedService);
 919          } catch (Exception $e) {
 920              phpCAS :: error(get_class($e) . ': ' . $e->getMessage());
 921          }
 922      }
 923  
 924      /**
 925       * This method is used to access an HTTP[S] service.
 926       *
 927       * @param string $url       the service to access.
 928       * @param int &$err_code an error code Possible values are
 929       * PHPCAS_SERVICE_OK (on success), PHPCAS_SERVICE_PT_NO_SERVER_RESPONSE,
 930       * PHPCAS_SERVICE_PT_BAD_SERVER_RESPONSE, PHPCAS_SERVICE_PT_FAILURE,
 931       * PHPCAS_SERVICE_NOT_AVAILABLE.
 932       * @param string &$output   the output of the service (also used to give an
 933       * error message on failure).
 934       *
 935       * @return bool true on success, false otherwise (in this later case,
 936       * $err_code gives the reason why it failed and $output contains an error
 937       * message).
 938       */
 939      public static function serviceWeb($url, & $err_code, & $output)
 940      {
 941          phpCAS :: traceBegin();
 942          phpCAS::_validateProxyExists();
 943  
 944          try {
 945              $res = self::$_PHPCAS_CLIENT->serviceWeb($url, $err_code, $output);
 946          } catch (Exception $e) {
 947              phpCAS :: error(get_class($e) . ': ' . $e->getMessage());
 948          }
 949  
 950          phpCAS :: traceEnd($res);
 951          return $res;
 952      }
 953  
 954      /**
 955       * This method is used to access an IMAP/POP3/NNTP service.
 956       *
 957       * @param string $url       a string giving the URL of the service,
 958       * including the mailing box for IMAP URLs, as accepted by imap_open().
 959       * @param string $service   a string giving for CAS retrieve Proxy ticket
 960       * @param string $flags     options given to imap_open().
 961       * @param int &$err_code an error code Possible values are
 962       * PHPCAS_SERVICE_OK (on success), PHPCAS_SERVICE_PT_NO_SERVER_RESPONSE,
 963       * PHPCAS_SERVICE_PT_BAD_SERVER_RESPONSE, PHPCAS_SERVICE_PT_FAILURE,
 964       * PHPCAS_SERVICE_NOT_AVAILABLE.
 965       * @param string &$err_msg  an error message on failure
 966       * @param string &$pt       the Proxy Ticket (PT) retrieved from the CAS
 967       * server to access the URL on success, false on error).
 968       *
 969       * @return object|false IMAP stream on success, false otherwise (in this later
 970       * case, $err_code gives the reason why it failed and $err_msg contains an
 971       * error message).
 972       */
 973      public static function serviceMail($url, $service, $flags, & $err_code, & $err_msg, & $pt)
 974      {
 975          phpCAS :: traceBegin();
 976          phpCAS::_validateProxyExists();
 977  
 978          try {
 979              $res = self::$_PHPCAS_CLIENT->serviceMail($url, $service, $flags, $err_code, $err_msg, $pt);
 980          } catch (Exception $e) {
 981              phpCAS :: error(get_class($e) . ': ' . $e->getMessage());
 982          }
 983  
 984          phpCAS :: traceEnd($res);
 985          return $res;
 986      }
 987  
 988      /** @} */
 989      // ########################################################################
 990      //  AUTHENTICATION
 991      // ########################################################################
 992      /**
 993      * @addtogroup publicAuth
 994      * @{
 995      */
 996  
 997      /**
 998       * Set the times authentication will be cached before really accessing the
 999       * CAS server in gateway mode:
1000       * - -1: check only once, and then never again (until you pree login)
1001       * - 0: always check
1002       * - n: check every "n" time
1003       *
1004       * @param int $n an integer.
1005       *
1006       * @return void
1007       */
1008      public static function setCacheTimesForAuthRecheck($n)
1009      {
1010          phpCAS::_validateClientExists();
1011  
1012          try {
1013              self::$_PHPCAS_CLIENT->setCacheTimesForAuthRecheck($n);
1014          } catch (Exception $e) {
1015              phpCAS :: error(get_class($e) . ': ' . $e->getMessage());
1016          }
1017      }
1018  
1019  
1020      /**
1021       * Set a callback function to be run when receiving CAS attributes
1022       *
1023       * The callback function will be passed an $success_elements
1024       * payload of the response (\DOMElement) as its first parameter.
1025       *
1026       * @param string $function       Callback function
1027       * @param array  $additionalArgs optional array of arguments
1028       *
1029       * @return void
1030       */
1031      public static function setCasAttributeParserCallback($function, array $additionalArgs = array())
1032      {
1033          phpCAS::_validateClientExists();
1034  
1035          self::$_PHPCAS_CLIENT->setCasAttributeParserCallback($function, $additionalArgs);
1036      }
1037  
1038      /**
1039       * Set a callback function to be run when a user authenticates.
1040       *
1041       * The callback function will be passed a $logoutTicket as its first
1042       * parameter, followed by any $additionalArgs you pass. The $logoutTicket
1043       * parameter is an opaque string that can be used to map the session-id to
1044       * logout request in order to support single-signout in applications that
1045       * manage their own sessions (rather than letting phpCAS start the session).
1046       *
1047       * phpCAS::forceAuthentication() will always exit and forward client unless
1048       * they are already authenticated. To perform an action at the moment the user
1049       * logs in (such as registering an account, performing logging, etc), register
1050       * a callback function here.
1051       *
1052       * @param callable $function       Callback function
1053       * @param array  $additionalArgs optional array of arguments
1054       *
1055       * @return void
1056       */
1057      public static function setPostAuthenticateCallback ($function, array $additionalArgs = array())
1058      {
1059          phpCAS::_validateClientExists();
1060  
1061          self::$_PHPCAS_CLIENT->setPostAuthenticateCallback($function, $additionalArgs);
1062      }
1063  
1064      /**
1065       * Set a callback function to be run when a single-signout request is
1066       * received. The callback function will be passed a $logoutTicket as its
1067       * first parameter, followed by any $additionalArgs you pass. The
1068       * $logoutTicket parameter is an opaque string that can be used to map a
1069       * session-id to the logout request in order to support single-signout in
1070       * applications that manage their own sessions (rather than letting phpCAS
1071       * start and destroy the session).
1072       *
1073       * @param callable $function       Callback function
1074       * @param array  $additionalArgs optional array of arguments
1075       *
1076       * @return void
1077       */
1078      public static function setSingleSignoutCallback ($function, array $additionalArgs = array())
1079      {
1080          phpCAS::_validateClientExists();
1081  
1082          self::$_PHPCAS_CLIENT->setSingleSignoutCallback($function, $additionalArgs);
1083      }
1084  
1085      /**
1086       * This method is called to check if the user is already authenticated
1087       * locally or has a global cas session. A already existing cas session is
1088       * determined by a cas gateway call.(cas login call without any interactive
1089       * prompt)
1090       *
1091       * @return bool true when the user is authenticated, false when a previous
1092       * gateway login failed or the function will not return if the user is
1093       * redirected to the cas server for a gateway login attempt
1094       */
1095      public static function checkAuthentication()
1096      {
1097          phpCAS :: traceBegin();
1098          phpCAS::_validateClientExists();
1099  
1100          $auth = self::$_PHPCAS_CLIENT->checkAuthentication();
1101  
1102          // store where the authentication has been checked and the result
1103          self::$_PHPCAS_CLIENT->markAuthenticationCall($auth);
1104  
1105          phpCAS :: traceEnd($auth);
1106          return $auth;
1107      }
1108  
1109      /**
1110       * This method is called to force authentication if the user was not already
1111       * authenticated. If the user is not authenticated, halt by redirecting to
1112       * the CAS server.
1113       *
1114       * @return bool Authentication
1115       */
1116      public static function forceAuthentication()
1117      {
1118          phpCAS :: traceBegin();
1119          phpCAS::_validateClientExists();
1120          $auth = self::$_PHPCAS_CLIENT->forceAuthentication();
1121  
1122          // store where the authentication has been checked and the result
1123          self::$_PHPCAS_CLIENT->markAuthenticationCall($auth);
1124  
1125          /*      if (!$auth) {
1126           phpCAS :: trace('user is not authenticated, redirecting to the CAS server');
1127          self::$_PHPCAS_CLIENT->forceAuthentication();
1128          } else {
1129          phpCAS :: trace('no need to authenticate (user `' . phpCAS :: getUser() . '\' is already authenticated)');
1130          }*/
1131  
1132          phpCAS :: traceEnd();
1133          return $auth;
1134      }
1135  
1136      /**
1137       * This method is called to renew the authentication.
1138       *
1139       * @return void
1140       **/
1141      public static function renewAuthentication()
1142      {
1143          phpCAS :: traceBegin();
1144          phpCAS::_validateClientExists();
1145  
1146          $auth = self::$_PHPCAS_CLIENT->renewAuthentication();
1147  
1148          // store where the authentication has been checked and the result
1149          self::$_PHPCAS_CLIENT->markAuthenticationCall($auth);
1150  
1151          //self::$_PHPCAS_CLIENT->renewAuthentication();
1152          phpCAS :: traceEnd();
1153      }
1154  
1155      /**
1156       * This method is called to check if the user is authenticated (previously or by
1157       * tickets given in the URL).
1158       *
1159       * @return bool true when the user is authenticated.
1160       */
1161      public static function isAuthenticated()
1162      {
1163          phpCAS :: traceBegin();
1164          phpCAS::_validateClientExists();
1165  
1166          // call the isAuthenticated method of the $_PHPCAS_CLIENT object
1167          $auth = self::$_PHPCAS_CLIENT->isAuthenticated();
1168  
1169          // store where the authentication has been checked and the result
1170          self::$_PHPCAS_CLIENT->markAuthenticationCall($auth);
1171  
1172          phpCAS :: traceEnd($auth);
1173          return $auth;
1174      }
1175  
1176      /**
1177       * Checks whether authenticated based on $_SESSION. Useful to avoid
1178       * server calls.
1179       *
1180       * @return bool true if authenticated, false otherwise.
1181       * @since 0.4.22 by Brendan Arnold
1182       */
1183      public static function isSessionAuthenticated()
1184      {
1185          phpCAS::_validateClientExists();
1186  
1187          return (self::$_PHPCAS_CLIENT->isSessionAuthenticated());
1188      }
1189  
1190      /**
1191       * This method returns the CAS user's login name.
1192       *
1193       * @return string the login name of the authenticated user
1194       * @warning should only be called after phpCAS::forceAuthentication()
1195       * or phpCAS::checkAuthentication().
1196       * */
1197      public static function getUser()
1198      {
1199          phpCAS::_validateClientExists();
1200  
1201          try {
1202              return self::$_PHPCAS_CLIENT->getUser();
1203          } catch (Exception $e) {
1204              phpCAS :: error(get_class($e) . ': ' . $e->getMessage());
1205          }
1206      }
1207  
1208      /**
1209       * Answer attributes about the authenticated user.
1210       *
1211       * @warning should only be called after phpCAS::forceAuthentication()
1212       * or phpCAS::checkAuthentication().
1213       *
1214       * @return array
1215       */
1216      public static function getAttributes()
1217      {
1218          phpCAS::_validateClientExists();
1219  
1220          try {
1221              return self::$_PHPCAS_CLIENT->getAttributes();
1222          } catch (Exception $e) {
1223              phpCAS :: error(get_class($e) . ': ' . $e->getMessage());
1224          }
1225      }
1226  
1227      /**
1228       * Answer true if there are attributes for the authenticated user.
1229       *
1230       * @warning should only be called after phpCAS::forceAuthentication()
1231       * or phpCAS::checkAuthentication().
1232       *
1233       * @return bool
1234       */
1235      public static function hasAttributes()
1236      {
1237          phpCAS::_validateClientExists();
1238  
1239          try {
1240              return self::$_PHPCAS_CLIENT->hasAttributes();
1241          } catch (Exception $e) {
1242              phpCAS :: error(get_class($e) . ': ' . $e->getMessage());
1243          }
1244      }
1245  
1246      /**
1247       * Answer true if an attribute exists for the authenticated user.
1248       *
1249       * @param string $key attribute name
1250       *
1251       * @return bool
1252       * @warning should only be called after phpCAS::forceAuthentication()
1253       * or phpCAS::checkAuthentication().
1254       */
1255      public static function hasAttribute($key)
1256      {
1257          phpCAS::_validateClientExists();
1258  
1259          try {
1260              return self::$_PHPCAS_CLIENT->hasAttribute($key);
1261          } catch (Exception $e) {
1262              phpCAS :: error(get_class($e) . ': ' . $e->getMessage());
1263          }
1264      }
1265  
1266      /**
1267       * Answer an attribute for the authenticated user.
1268       *
1269       * @param string $key attribute name
1270       *
1271       * @return mixed string for a single value or an array if multiple values exist.
1272       * @warning should only be called after phpCAS::forceAuthentication()
1273       * or phpCAS::checkAuthentication().
1274       */
1275      public static function getAttribute($key)
1276      {
1277          phpCAS::_validateClientExists();
1278  
1279          try {
1280              return self::$_PHPCAS_CLIENT->getAttribute($key);
1281          } catch (Exception $e) {
1282              phpCAS :: error(get_class($e) . ': ' . $e->getMessage());
1283          }
1284      }
1285  
1286      /**
1287       * Handle logout requests.
1288       *
1289       * @param bool  $check_client    additional safety check
1290       * @param array $allowed_clients array of allowed clients
1291       *
1292       * @return void
1293       */
1294      public static function handleLogoutRequests($check_client = true, $allowed_clients = array())
1295      {
1296          phpCAS::_validateClientExists();
1297  
1298          return (self::$_PHPCAS_CLIENT->handleLogoutRequests($check_client, $allowed_clients));
1299      }
1300  
1301      /**
1302       * This method returns the URL to be used to login.
1303       *
1304       * @return string the login URL
1305       */
1306      public static function getServerLoginURL()
1307      {
1308          phpCAS::_validateClientExists();
1309  
1310          return self::$_PHPCAS_CLIENT->getServerLoginURL();
1311      }
1312  
1313      /**
1314       * Set the login URL of the CAS server.
1315       *
1316       * @param string $url the login URL
1317       *
1318       * @return void
1319       * @since 0.4.21 by Wyman Chan
1320       */
1321      public static function setServerLoginURL($url = '')
1322      {
1323          phpCAS :: traceBegin();
1324          phpCAS::_validateClientExists();
1325  
1326          try {
1327              self::$_PHPCAS_CLIENT->setServerLoginURL($url);
1328          } catch (Exception $e) {
1329              phpCAS :: error(get_class($e) . ': ' . $e->getMessage());
1330          }
1331  
1332          phpCAS :: traceEnd();
1333      }
1334  
1335      /**
1336       * Set the serviceValidate URL of the CAS server.
1337       * Used for all CAS versions of URL validations.
1338       * Examples:
1339       * CAS 1.0 http://www.exemple.com/validate
1340       * CAS 2.0 http://www.exemple.com/validateURL
1341       * CAS 3.0 http://www.exemple.com/p3/serviceValidate
1342       *
1343       * @param string $url the serviceValidate URL
1344       *
1345       * @return void
1346       */
1347      public static function setServerServiceValidateURL($url = '')
1348      {
1349          phpCAS :: traceBegin();
1350          phpCAS::_validateClientExists();
1351  
1352          try {
1353              self::$_PHPCAS_CLIENT->setServerServiceValidateURL($url);
1354          } catch (Exception $e) {
1355              phpCAS :: error(get_class($e) . ': ' . $e->getMessage());
1356          }
1357  
1358          phpCAS :: traceEnd();
1359      }
1360  
1361      /**
1362       * Set the proxyValidate URL of the CAS server.
1363       * Used for all CAS versions of proxy URL validations
1364       * Examples:
1365       * CAS 1.0 http://www.exemple.com/
1366       * CAS 2.0 http://www.exemple.com/proxyValidate
1367       * CAS 3.0 http://www.exemple.com/p3/proxyValidate
1368       *
1369       * @param string $url the proxyValidate URL
1370       *
1371       * @return void
1372       */
1373      public static function setServerProxyValidateURL($url = '')
1374      {
1375          phpCAS :: traceBegin();
1376          phpCAS::_validateClientExists();
1377  
1378          try {
1379              self::$_PHPCAS_CLIENT->setServerProxyValidateURL($url);
1380          } catch (Exception $e) {
1381              phpCAS :: error(get_class($e) . ': ' . $e->getMessage());
1382          }
1383  
1384          phpCAS :: traceEnd();
1385      }
1386  
1387      /**
1388       * Set the samlValidate URL of the CAS server.
1389       *
1390       * @param string $url the samlValidate URL
1391       *
1392       * @return void
1393       */
1394      public static function setServerSamlValidateURL($url = '')
1395      {
1396          phpCAS :: traceBegin();
1397          phpCAS::_validateClientExists();
1398  
1399          try {
1400              self::$_PHPCAS_CLIENT->setServerSamlValidateURL($url);
1401          } catch (Exception $e) {
1402              phpCAS :: error(get_class($e) . ': ' . $e->getMessage());
1403          }
1404  
1405          phpCAS :: traceEnd();
1406      }
1407  
1408      /**
1409       * This method returns the URL to be used to logout.
1410       *
1411       * @return string the URL to use to log out
1412       */
1413      public static function getServerLogoutURL()
1414      {
1415          phpCAS::_validateClientExists();
1416  
1417          return self::$_PHPCAS_CLIENT->getServerLogoutURL();
1418      }
1419  
1420      /**
1421       * Set the logout URL of the CAS server.
1422       *
1423       * @param string $url the logout URL
1424       *
1425       * @return void
1426       * @since 0.4.21 by Wyman Chan
1427       */
1428      public static function setServerLogoutURL($url = '')
1429      {
1430          phpCAS :: traceBegin();
1431          phpCAS::_validateClientExists();
1432  
1433          try {
1434              self::$_PHPCAS_CLIENT->setServerLogoutURL($url);
1435          } catch (Exception $e) {
1436              phpCAS :: error(get_class($e) . ': ' . $e->getMessage());
1437          }
1438  
1439          phpCAS :: traceEnd();
1440      }
1441  
1442      /**
1443       * This method is used to logout from CAS.
1444       *
1445       * @param string $params an array that contains the optional url and
1446       * service parameters that will be passed to the CAS server
1447       *
1448       * @return void
1449       */
1450      public static function logout($params = "")
1451      {
1452          phpCAS :: traceBegin();
1453          phpCAS::_validateClientExists();
1454  
1455          $parsedParams = array ();
1456          if ($params != "") {
1457              if (is_string($params)) {
1458                  phpCAS :: error('method `phpCAS::logout($url)\' is now deprecated, use `phpCAS::logoutWithUrl($url)\' instead');
1459              }
1460              if (!is_array($params)) {
1461                  phpCAS :: error('type mismatched for parameter $params (should be `array\')');
1462              }
1463              foreach ($params as $key => $value) {
1464                  if ($key != "service" && $key != "url") {
1465                      phpCAS :: error('only `url\' and `service\' parameters are allowed for method `phpCAS::logout($params)\'');
1466                  }
1467                  $parsedParams[$key] = $value;
1468              }
1469          }
1470          self::$_PHPCAS_CLIENT->logout($parsedParams);
1471          // never reached
1472          phpCAS :: traceEnd();
1473      }
1474  
1475      /**
1476       * This method is used to logout from CAS. Halts by redirecting to the CAS
1477       * server.
1478       *
1479       * @param string $service a URL that will be transmitted to the CAS server
1480       *
1481       * @return void
1482       */
1483      public static function logoutWithRedirectService($service)
1484      {
1485          phpCAS :: traceBegin();
1486          phpCAS::_validateClientExists();
1487  
1488          if (!is_string($service)) {
1489              phpCAS :: error('type mismatched for parameter $service (should be `string\')');
1490          }
1491          self::$_PHPCAS_CLIENT->logout(array ( "service" => $service ));
1492          // never reached
1493          phpCAS :: traceEnd();
1494      }
1495  
1496      /**
1497       * This method is used to logout from CAS. Halts by redirecting to the CAS
1498       * server.
1499       *
1500       * @param string $url a URL that will be transmitted to the CAS server
1501       *
1502       * @return void
1503       * @deprecated The url parameter has been removed from the CAS server as of
1504       * version 3.3.5.1
1505       */
1506      public static function logoutWithUrl($url)
1507      {
1508          trigger_error('Function deprecated for cas servers >= 3.3.5.1', E_USER_DEPRECATED);
1509          phpCAS :: traceBegin();
1510          if (!is_object(self::$_PHPCAS_CLIENT)) {
1511              phpCAS :: error('this method should only be called after ' . __CLASS__ . '::client() or' . __CLASS__ . '::proxy()');
1512          }
1513          if (!is_string($url)) {
1514              phpCAS :: error('type mismatched for parameter $url (should be `string\')');
1515          }
1516          self::$_PHPCAS_CLIENT->logout(array ( "url" => $url ));
1517          // never reached
1518          phpCAS :: traceEnd();
1519      }
1520  
1521      /**
1522       * This method is used to logout from CAS. Halts by redirecting to the CAS
1523       * server.
1524       *
1525       * @param string $service a URL that will be transmitted to the CAS server
1526       * @param string $url     a URL that will be transmitted to the CAS server
1527       *
1528       * @return void
1529       *
1530       * @deprecated The url parameter has been removed from the CAS server as of
1531       * version 3.3.5.1
1532       */
1533      public static function logoutWithRedirectServiceAndUrl($service, $url)
1534      {
1535          trigger_error('Function deprecated for cas servers >= 3.3.5.1', E_USER_DEPRECATED);
1536          phpCAS :: traceBegin();
1537          phpCAS::_validateClientExists();
1538  
1539          if (!is_string($service)) {
1540              phpCAS :: error('type mismatched for parameter $service (should be `string\')');
1541          }
1542          if (!is_string($url)) {
1543              phpCAS :: error('type mismatched for parameter $url (should be `string\')');
1544          }
1545          self::$_PHPCAS_CLIENT->logout(
1546              array (
1547                  "service" => $service,
1548                  "url" => $url
1549              )
1550          );
1551          // never reached
1552          phpCAS :: traceEnd();
1553      }
1554  
1555      /**
1556       * Set the fixed URL that will be used by the CAS server to transmit the
1557       * PGT. When this method is not called, a phpCAS script uses its own URL
1558       * for the callback.
1559       *
1560       * @param string $url the URL
1561       *
1562       * @return void
1563       */
1564      public static function setFixedCallbackURL($url = '')
1565      {
1566          phpCAS :: traceBegin();
1567          phpCAS::_validateProxyExists();
1568  
1569          try {
1570              self::$_PHPCAS_CLIENT->setCallbackURL($url);
1571          } catch (Exception $e) {
1572              phpCAS :: error(get_class($e) . ': ' . $e->getMessage());
1573          }
1574  
1575          phpCAS :: traceEnd();
1576      }
1577  
1578      /**
1579       * Set the fixed URL that will be set as the CAS service parameter. When this
1580       * method is not called, a phpCAS script uses its own URL.
1581       *
1582       * @param string $url the URL
1583       *
1584       * @return void
1585       */
1586      public static function setFixedServiceURL($url)
1587      {
1588          phpCAS :: traceBegin();
1589          phpCAS::_validateProxyExists();
1590  
1591          try {
1592              self::$_PHPCAS_CLIENT->setURL($url);
1593          } catch (Exception $e) {
1594              phpCAS :: error(get_class($e) . ': ' . $e->getMessage());
1595          }
1596  
1597          phpCAS :: traceEnd();
1598      }
1599  
1600      /**
1601       * Get the URL that is set as the CAS service parameter.
1602       *
1603       * @return string Service Url
1604       */
1605      public static function getServiceURL()
1606      {
1607          phpCAS::_validateProxyExists();
1608          return (self::$_PHPCAS_CLIENT->getURL());
1609      }
1610  
1611      /**
1612       * Retrieve a Proxy Ticket from the CAS server.
1613       *
1614       * @param string $target_service Url string of service to proxy
1615       * @param int &$err_code      error code
1616       * @param string &$err_msg       error message
1617       *
1618       * @return string Proxy Ticket
1619       */
1620      public static function retrievePT($target_service, & $err_code, & $err_msg)
1621      {
1622          phpCAS::_validateProxyExists();
1623  
1624          try {
1625              return (self::$_PHPCAS_CLIENT->retrievePT($target_service, $err_code, $err_msg));
1626          } catch (Exception $e) {
1627              phpCAS :: error(get_class($e) . ': ' . $e->getMessage());
1628          }
1629      }
1630  
1631      /**
1632       * Set the certificate of the CAS server CA and if the CN should be properly
1633       * verified.
1634       *
1635       * @param string $cert        CA certificate file name
1636       * @param bool   $validate_cn Validate CN in certificate (default true)
1637       *
1638       * @return void
1639       */
1640      public static function setCasServerCACert($cert, $validate_cn = true)
1641      {
1642          phpCAS :: traceBegin();
1643          phpCAS::_validateClientExists();
1644  
1645          try {
1646              self::$_PHPCAS_CLIENT->setCasServerCACert($cert, $validate_cn);
1647          } catch (Exception $e) {
1648              phpCAS :: error(get_class($e) . ': ' . $e->getMessage());
1649          }
1650  
1651          phpCAS :: traceEnd();
1652      }
1653  
1654      /**
1655       * Set no SSL validation for the CAS server.
1656       *
1657       * @return void
1658       */
1659      public static function setNoCasServerValidation()
1660      {
1661          phpCAS :: traceBegin();
1662          phpCAS::_validateClientExists();
1663  
1664          phpCAS :: trace('You have configured no validation of the legitimacy of the cas server. This is not recommended for production use.');
1665          self::$_PHPCAS_CLIENT->setNoCasServerValidation();
1666          phpCAS :: traceEnd();
1667      }
1668  
1669  
1670      /**
1671       * Disable the removal of a CAS-Ticket from the URL when authenticating
1672       * DISABLING POSES A SECURITY RISK:
1673       * We normally remove the ticket by an additional redirect as a security
1674       * precaution to prevent a ticket in the HTTP_REFERRER or be carried over in
1675       * the URL parameter
1676       *
1677       * @return void
1678       */
1679      public static function setNoClearTicketsFromUrl()
1680      {
1681          phpCAS :: traceBegin();
1682          phpCAS::_validateClientExists();
1683  
1684          self::$_PHPCAS_CLIENT->setNoClearTicketsFromUrl();
1685          phpCAS :: traceEnd();
1686      }
1687  
1688      /** @} */
1689  
1690      /**
1691       * Change CURL options.
1692       * CURL is used to connect through HTTPS to CAS server
1693       *
1694       * @param string $key   the option key
1695       * @param string $value the value to set
1696       *
1697       * @return void
1698       */
1699      public static function setExtraCurlOption($key, $value)
1700      {
1701          phpCAS :: traceBegin();
1702          phpCAS::_validateClientExists();
1703  
1704          self::$_PHPCAS_CLIENT->setExtraCurlOption($key, $value);
1705          phpCAS :: traceEnd();
1706      }
1707  
1708      /**
1709       * Set a salt/seed for the session-id hash to make it harder to guess.
1710       *
1711       * When $changeSessionID = true phpCAS will create a session-id that is derived
1712       * from the service ticket. Doing so allows phpCAS to look-up and destroy the
1713       * proper session on single-log-out requests. While the service tickets
1714       * provided by the CAS server may include enough data to generate a strong
1715       * hash, clients may provide an additional salt to ensure that session ids
1716       * are not guessable if the session tickets do not have enough entropy.
1717       *
1718       * @param string $salt The salt to combine with the session ticket.
1719       *
1720       * @return void
1721       */
1722       public static function setSessionIdSalt($salt) {
1723         phpCAS :: traceBegin();
1724         phpCAS::_validateClientExists();
1725         self::$_PHPCAS_CLIENT->setSessionIdSalt($salt);
1726         phpCAS :: traceEnd();
1727       }
1728  
1729      /**
1730       * If you want your service to be proxied you have to enable it (default
1731       * disabled) and define an accepable list of proxies that are allowed to
1732       * proxy your service.
1733       *
1734       * Add each allowed proxy definition object. For the normal CAS_ProxyChain
1735       * class, the constructor takes an array of proxies to match. The list is in
1736       * reverse just as seen from the service. Proxies have to be defined in reverse
1737       * from the service to the user. If a user hits service A and gets proxied via
1738       * B to service C the list of acceptable on C would be array(B,A). The definition
1739       * of an individual proxy can be either a string or a regexp (preg_match is used)
1740       * that will be matched against the proxy list supplied by the cas server
1741       * when validating the proxy tickets. The strings are compared starting from
1742       * the beginning and must fully match with the proxies in the list.
1743       * Example:
1744       * 	 	 phpCAS::allowProxyChain(new CAS_ProxyChain(array(
1745       *	 	 	 	 'https://app.example.com/'
1746       *	 	 	 )));
1747       * 	 	 phpCAS::allowProxyChain(new CAS_ProxyChain(array(
1748       *	 	 	 	 '/^https:\/\/app[0-9]\.example\.com\/rest\//',
1749       *	 	 	 	 'http://client.example.com/'
1750       *	 	 	 )));
1751       *
1752       * For quick testing or in certain production screnarios you might want to
1753       * allow allow any other valid service to proxy your service. To do so, add
1754       * the "Any" chain:
1755       *	 	 phpCAS::allowProxyChain(new CAS_ProxyChain_Any);
1756       * THIS SETTING IS HOWEVER NOT RECOMMENDED FOR PRODUCTION AND HAS SECURITY
1757       * IMPLICATIONS: YOU ARE ALLOWING ANY SERVICE TO ACT ON BEHALF OF A USER
1758       * ON THIS SERVICE.
1759       *
1760       * @param CAS_ProxyChain_Interface $proxy_chain A proxy-chain that will be
1761       * matched against the proxies requesting access
1762       *
1763       * @return void
1764       */
1765      public static function allowProxyChain(CAS_ProxyChain_Interface $proxy_chain)
1766      {
1767          phpCAS :: traceBegin();
1768          phpCAS::_validateClientExists();
1769  
1770          if (self::$_PHPCAS_CLIENT->getServerVersion() !== CAS_VERSION_2_0
1771              && self::$_PHPCAS_CLIENT->getServerVersion() !== CAS_VERSION_3_0
1772          ) {
1773              phpCAS :: error('this method can only be used with the cas 2.0/3.0 protocols');
1774          }
1775          self::$_PHPCAS_CLIENT->getAllowedProxyChains()->allowProxyChain($proxy_chain);
1776          phpCAS :: traceEnd();
1777      }
1778  
1779      /**
1780       * Answer an array of proxies that are sitting in front of this application.
1781       * This method will only return a non-empty array if we have received and
1782       * validated a Proxy Ticket.
1783       *
1784       * @return array
1785       * @access public
1786       * @since 6/25/09
1787       */
1788      public static function getProxies ()
1789      {
1790          phpCAS::_validateProxyExists();
1791  
1792          return(self::$_PHPCAS_CLIENT->getProxies());
1793      }
1794  
1795      // ########################################################################
1796      // PGTIOU/PGTID and logoutRequest rebroadcasting
1797      // ########################################################################
1798  
1799      /**
1800       * Add a pgtIou/pgtId and logoutRequest rebroadcast node.
1801       *
1802       * @param string $rebroadcastNodeUrl The rebroadcast node URL. Can be
1803       * hostname or IP.
1804       *
1805       * @return void
1806       */
1807      public static function addRebroadcastNode($rebroadcastNodeUrl)
1808      {
1809          phpCAS::traceBegin();
1810          phpCAS::log('rebroadcastNodeUrl:'.$rebroadcastNodeUrl);
1811          phpCAS::_validateClientExists();
1812  
1813          try {
1814              self::$_PHPCAS_CLIENT->addRebroadcastNode($rebroadcastNodeUrl);
1815          } catch (Exception $e) {
1816              phpCAS :: error(get_class($e) . ': ' . $e->getMessage());
1817          }
1818  
1819          phpCAS::traceEnd();
1820      }
1821  
1822      /**
1823       * This method is used to add header parameters when rebroadcasting
1824       * pgtIou/pgtId or logoutRequest.
1825       *
1826       * @param String $header Header to send when rebroadcasting.
1827       *
1828       * @return void
1829       */
1830      public static function addRebroadcastHeader($header)
1831      {
1832          phpCAS :: traceBegin();
1833          phpCAS::_validateClientExists();
1834  
1835          try {
1836              self::$_PHPCAS_CLIENT->addRebroadcastHeader($header);
1837          } catch (Exception $e) {
1838              phpCAS :: error(get_class($e) . ': ' . $e->getMessage());
1839          }
1840  
1841          phpCAS :: traceEnd();
1842      }
1843  
1844      /**
1845       * Checks if a client already exists
1846       *
1847       * @throws CAS_OutOfSequenceBeforeClientException
1848       *
1849       * @return void
1850       */
1851      private static function _validateClientExists()
1852      {
1853          if (!is_object(self::$_PHPCAS_CLIENT)) {
1854              throw new CAS_OutOfSequenceBeforeClientException();
1855          }
1856      }
1857  
1858      /**
1859       * Checks of a proxy client aready exists
1860       *
1861       * @throws CAS_OutOfSequenceBeforeProxyException
1862       *
1863       * @return void
1864       */
1865      private static function _validateProxyExists()
1866      {
1867          if (!is_object(self::$_PHPCAS_CLIENT)) {
1868              throw new CAS_OutOfSequenceBeforeProxyException();
1869          }
1870      }
1871  
1872      /**
1873       * For testing purposes, use this method to set the client to a test double
1874       *
1875       * @return void
1876       */
1877      public static function setCasClient(\CAS_Client $client)
1878      {
1879          self::$_PHPCAS_CLIENT = $client;
1880      }
1881  }
1882  // ########################################################################
1883  // DOCUMENTATION
1884  // ########################################################################
1885  
1886  // ########################################################################
1887  //  MAIN PAGE
1888  
1889  /**
1890   * @mainpage
1891   *
1892   * The following pages only show the source documentation.
1893   *
1894   */
1895  
1896  // ########################################################################
1897  //  MODULES DEFINITION
1898  
1899  /** @defgroup public User interface */
1900  
1901  /** @defgroup publicInit Initialization
1902   *  @ingroup public */
1903  
1904  /** @defgroup publicAuth Authentication
1905   *  @ingroup public */
1906  
1907  /** @defgroup publicServices Access to external services
1908   *  @ingroup public */
1909  
1910  /** @defgroup publicConfig Configuration
1911   *  @ingroup public */
1912  
1913  /** @defgroup publicLang Internationalization
1914   *  @ingroup publicConfig */
1915  
1916  /** @defgroup publicOutput HTML output
1917   *  @ingroup publicConfig */
1918  
1919  /** @defgroup publicPGTStorage PGT storage
1920   *  @ingroup publicConfig */
1921  
1922  /** @defgroup publicDebug Debugging
1923   *  @ingroup public */
1924  
1925  /** @defgroup internal Implementation */
1926  
1927  /** @defgroup internalAuthentication Authentication
1928   *  @ingroup internal */
1929  
1930  /** @defgroup internalBasic CAS Basic client features (CAS 1.0, Service Tickets)
1931   *  @ingroup internal */
1932  
1933  /** @defgroup internalProxy CAS Proxy features (CAS 2.0, Proxy Granting Tickets)
1934   *  @ingroup internal */
1935  
1936  /** @defgroup internalSAML CAS SAML features (SAML 1.1)
1937   *  @ingroup internal */
1938  
1939  /** @defgroup internalPGTStorage PGT storage
1940   *  @ingroup internalProxy */
1941  
1942  /** @defgroup internalPGTStorageDb PGT storage in a database
1943   *  @ingroup internalPGTStorage */
1944  
1945  /** @defgroup internalPGTStorageFile PGT storage on the filesystem
1946   *  @ingroup internalPGTStorage */
1947  
1948  /** @defgroup internalCallback Callback from the CAS server
1949   *  @ingroup internalProxy */
1950  
1951  /** @defgroup internalProxyServices Proxy other services
1952   *  @ingroup internalProxy */
1953  
1954  /** @defgroup internalService CAS client features (CAS 2.0, Proxied service)
1955   *  @ingroup internal */
1956  
1957  /** @defgroup internalConfig Configuration
1958   *  @ingroup internal */
1959  
1960  /** @defgroup internalBehave Internal behaviour of phpCAS
1961   *  @ingroup internalConfig */
1962  
1963  /** @defgroup internalOutput HTML output
1964   *  @ingroup internalConfig */
1965  
1966  /** @defgroup internalLang Internationalization
1967   *  @ingroup internalConfig
1968   *
1969   * To add a new language:
1970   * - 1. define a new constant PHPCAS_LANG_XXXXXX in CAS/CAS.php
1971   * - 2. copy any file from CAS/languages to CAS/languages/XXXXXX.php
1972   * - 3. Make the translations
1973   */
1974  
1975  /** @defgroup internalDebug Debugging
1976   *  @ingroup internal */
1977  
1978  /** @defgroup internalMisc Miscellaneous
1979   *  @ingroup internal */
1980  
1981  // ########################################################################
1982  //  EXAMPLES
1983  
1984  /**
1985   * @example example_simple.php
1986   */
1987  /**
1988   * @example example_service.php
1989   */
1990  /**
1991   * @example example_service_that_proxies.php
1992   */
1993  /**
1994   * @example example_service_POST.php
1995   */
1996  /**
1997   * @example example_proxy_serviceWeb.php
1998   */
1999  /**
2000   * @example example_proxy_serviceWeb_chaining.php
2001   */
2002  /**
2003   * @example example_proxy_POST.php
2004   */
2005  /**
2006   * @example example_proxy_GET.php
2007   */
2008  /**
2009   * @example example_lang.php
2010   */
2011  /**
2012   * @example example_html.php
2013   */
2014  /**
2015   * @example example_pgt_storage_file.php
2016   */
2017  /**
2018   * @example example_pgt_storage_db.php
2019   */
2020  /**
2021   * @example example_gateway.php
2022   */
2023  /**
2024   * @example example_logout.php
2025   */
2026  /**
2027   * @example example_rebroadcast.php
2028   */
2029  /**
2030   * @example example_custom_urls.php
2031   */
2032  /**
2033   * @example example_advanced_saml11.php
2034   */