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
38use Psr\Log\LoggerInterface;
39
40//
41// hack by Vangelis Haniotakis to handle the absence of $_SERVER['REQUEST_URI']
42// in IIS
43//
44if (!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 */
60define('PHPCAS_VERSION', '1.6.1');
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 */
74define("CAS_VERSION_1_0", '1.0');
75/*!
76 * CAS version 2.0
77*/
78define("CAS_VERSION_2_0", '2.0');
79/**
80 * CAS version 3.0
81 */
82define("CAS_VERSION_3_0", '3.0');
83
84// ------------------------------------------------------------------------
85//  SAML defines
86// ------------------------------------------------------------------------
87
88/**
89 * SAML protocol
90 */
91define("SAML_VERSION_1_1", 'S1');
92
93/**
94 * XML header for SAML POST
95 */
96define("SAML_XML_HEADER", '<?xml version="1.0" encoding="UTF-8"?>');
97
98/**
99 * SOAP envelope for SAML POST
100 */
101define("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 */
106define("SAML_SOAP_BODY", '<SOAP-ENV:Body>');
107
108/**
109 * SAMLP request
110 */
111define("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">');
112define("SAMLP_REQUEST_CLOSE", '</samlp:Request>');
113
114/**
115 * SAMLP artifact tag (for the ticket)
116 */
117define("SAML_ASSERTION_ARTIFACT", '<samlp:AssertionArtifact>');
118
119/**
120 * SAMLP close
121 */
122define("SAML_ASSERTION_ARTIFACT_CLOSE", '</samlp:AssertionArtifact>');
123
124/**
125 * SOAP body close
126 */
127define("SAML_SOAP_BODY_CLOSE", '</SOAP-ENV:Body>');
128
129/**
130 * SOAP envelope close
131 */
132define("SAML_SOAP_ENV_CLOSE", '</SOAP-ENV:Envelope>');
133
134/**
135 * SAML Attributes
136 */
137define("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 */
150define("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 */
163define("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 */
168define("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 */
173define("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 */
178define("PHPCAS_SERVICE_PT_FAILURE", 3);
179/**
180 * phpCAS::service() error code when the service was not available.
181 */
182define("PHPCAS_SERVICE_NOT_AVAILABLE", 4);
183
184// ------------------------------------------------------------------------
185// SERVICE TYPES
186// ------------------------------------------------------------------------
187/**
188 * phpCAS::getProxiedService() type for HTTP GET
189 */
190define("PHPCAS_PROXIED_SERVICE_HTTP_GET", 'CAS_ProxiedService_Http_Get');
191/**
192 * phpCAS::getProxiedService() type for HTTP POST
193 */
194define("PHPCAS_PROXIED_SERVICE_HTTP_POST", 'CAS_ProxiedService_Http_Post');
195/**
196 * phpCAS::getProxiedService() type for IMAP
197 */
198define("PHPCAS_PROXIED_SERVICE_IMAP", 'CAS_ProxiedService_Imap');
199
200
201/** @} */
202// ------------------------------------------------------------------------
203//  LANGUAGES
204// ------------------------------------------------------------------------
205/**
206 * @addtogroup publicLang
207 * @{
208 */
209
210define("PHPCAS_LANG_ENGLISH", 'CAS_Languages_English');
211define("PHPCAS_LANG_FRENCH", 'CAS_Languages_French');
212define("PHPCAS_LANG_GREEK", 'CAS_Languages_Greek');
213define("PHPCAS_LANG_GERMAN", 'CAS_Languages_German');
214define("PHPCAS_LANG_JAPANESE", 'CAS_Languages_Japanese');
215define("PHPCAS_LANG_SPANISH", 'CAS_Languages_Spanish');
216define("PHPCAS_LANG_CATALAN", 'CAS_Languages_Catalan');
217define("PHPCAS_LANG_CHINESE_SIMPLIFIED", 'CAS_Languages_ChineseSimplified');
218define("PHPCAS_LANG_GALEGO", 'CAS_Languages_Galego');
219define("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 */
231define("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 */
246function gettmpdir() {
247if (!empty($_ENV['TMP'])) { return realpath($_ENV['TMP']); }
248if (!empty($_ENV['TMPDIR'])) { return realpath( $_ENV['TMPDIR']); }
249if (!empty($_ENV['TEMP'])) { return realpath( $_ENV['TEMP']); }
250return "/tmp";
251}
252define('DEFAULT_DEBUG_DIR', gettmpdir()."/");
253
254/** @} */
255
256// include the class autoloader
257require_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
276class 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 */
2084