Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

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