Projet

Général

Profil

Paste
Statistiques
| Branche: | Révision:

ryxeo-glpi-git / lib / phpcas / client.php @ b67d8923

Historique | Voir | Annoter | Télécharger (63,5 ko)

1
<?php
2

    
3
/**
4
 * @file CAS/client.php
5
 * Main class of the phpCAS library
6
 */
7

    
8
// include internationalization stuff
9
include_once(dirname(__FILE__).'/languages/languages.php');
10

    
11
// include PGT storage classes
12
include_once(dirname(__FILE__).'/PGTStorage/pgt-main.php');
13

    
14
/**
15
 * @class CASClient
16
 * The CASClient class is a client interface that provides CAS authentication
17
 * to PHP applications.
18
 *
19
 * @author Pascal Aubry <pascal.aubry at univ-rennes1.fr>
20
 */
21

    
22
class CASClient
23
{
24

    
25
  // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
26
  // XX                                                                    XX
27
  // XX                          CONFIGURATION                             XX
28
  // XX                                                                    XX
29
  // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
30

    
31
  // ########################################################################
32
  //  HTML OUTPUT
33
  // ########################################################################
34
  /**
35
   * @addtogroup internalOutput
36
   * @{
37
   */  
38
  
39
  /**
40
   * This method filters a string by replacing special tokens by appropriate values
41
   * and prints it. The corresponding tokens are taken into account:
42
   * - __CAS_VERSION__
43
   * - __PHPCAS_VERSION__
44
   * - __SERVER_BASE_URL__
45
   *
46
   * Used by CASClient::PrintHTMLHeader() and CASClient::printHTMLFooter().
47
   *
48
   * @param $str the string to filter and output
49
   *
50
   * @private
51
   */
52
  function HTMLFilterOutput($str)
53
    {
54
      $str = str_replace('__CAS_VERSION__',$this->getServerVersion(),$str);
55
      $str = str_replace('__PHPCAS_VERSION__',phpCAS::getVersion(),$str);
56
      $str = str_replace('__SERVER_BASE_URL__',$this->getServerBaseURL(),$str);
57
      echo $str;
58
    }
59

    
60
  /**
61
   * A string used to print the header of HTML pages. Written by CASClient::setHTMLHeader(),
62
   * read by CASClient::printHTMLHeader().
63
   *
64
   * @hideinitializer
65
   * @private
66
   * @see CASClient::setHTMLHeader, CASClient::printHTMLHeader()
67
   */
68
  var $_output_header = '';
69
  
70
  /**
71
   * This method prints the header of the HTML output (after filtering). If
72
   * CASClient::setHTMLHeader() was not used, a default header is output.
73
   *
74
   * @param $title the title of the page
75
   *
76
   * @see HTMLFilterOutput()
77
   * @private
78
   */
79
  function printHTMLHeader($title)
80
    {
81
      $this->HTMLFilterOutput(str_replace('__TITLE__',
82
                                          $title,
83
                                          (empty($this->_output_header)
84
                                           ? '<html><head><title>__TITLE__</title></head><body><h1>__TITLE__</h1>'
85
                                           : $this->_output_header)
86
                                          )
87
                              );
88
    }
89

    
90
  /**
91
   * A string used to print the footer of HTML pages. Written by CASClient::setHTMLFooter(),
92
   * read by printHTMLFooter().
93
   *
94
   * @hideinitializer
95
   * @private
96
   * @see CASClient::setHTMLFooter, CASClient::printHTMLFooter()
97
   */
98
  var $_output_footer = '';
99
  
100
  /**
101
   * This method prints the footer of the HTML output (after filtering). If
102
   * CASClient::setHTMLFooter() was not used, a default footer is output.
103
   *
104
   * @see HTMLFilterOutput()
105
   * @private
106
   */
107
  function printHTMLFooter()
108
    {
109
      $this->HTMLFilterOutput(empty($this->_output_footer)
110
                              ?('<hr><address>phpCAS __PHPCAS_VERSION__ '.$this->getString(CAS_STR_USING_SERVER).' <a href="__SERVER_BASE_URL__">__SERVER_BASE_URL__</a> (CAS __CAS_VERSION__)</a></address></body></html>')
111
                              :$this->_output_footer);
112
    }
113

    
114
  /**
115
   * This method set the HTML header used for all outputs.
116
   *
117
   * @param $header the HTML header.
118
   *
119
   * @public
120
   */
121
  function setHTMLHeader($header)
122
    {
123
      $this->_output_header = $header;
124
    }
125

    
126
  /**
127
   * This method set the HTML footer used for all outputs.
128
   *
129
   * @param $footer the HTML footer.
130
   *
131
   * @public
132
   */
133
  function setHTMLFooter($footer)
134
    {
135
      $this->_output_footer = $footer;
136
    }
137

    
138
  /** @} */
139
  // ########################################################################
140
  //  INTERNATIONALIZATION
141
  // ########################################################################
142
  /**
143
   * @addtogroup internalLang
144
   * @{
145
   */  
146
  /**
147
   * A string corresponding to the language used by phpCAS. Written by 
148
   * CASClient::setLang(), read by CASClient::getLang().
149

150
   * @note debugging information is always in english (debug purposes only).
151
   *
152
   * @hideinitializer
153
   * @private
154
   * @sa CASClient::_strings, CASClient::getString()
155
   */
156
  var $_lang = '';
157
  
158
  /**
159
   * This method returns the language used by phpCAS.
160
   *
161
   * @return a string representing the language
162
   *
163
   * @private
164
   */
165
  function getLang()
166
    {
167
      if ( empty($this->_lang) )
168
        $this->setLang(PHPCAS_LANG_DEFAULT);
169
      return $this->_lang;
170
    }
171

    
172
  /**
173
   * array containing the strings used by phpCAS. Written by CASClient::setLang(), read by 
174
   * CASClient::getString() and used by CASClient::setLang().
175
   *
176
   * @note This array is filled by instructions in CAS/languages/<$this->_lang>.php
177
   *
178
   * @private
179
   * @see CASClient::_lang, CASClient::getString(), CASClient::setLang(), CASClient::getLang()
180
   */
181
  var $_strings;
182

    
183
  /**
184
   * This method returns a string depending on the language.
185
   *
186
   * @param $str the index of the string in $_string.
187
   *
188
   * @return the string corresponding to $index in $string.
189
   *
190
   * @private
191
   */
192
  function getString($str)
193
    {
194
      // call CASclient::getLang() to be sure the language is initialized
195
      $this->getLang();
196
      
197
      if ( !isset($this->_strings[$str]) ) {
198
        trigger_error('string `'.$str.'\' not defined for language `'.$this->getLang().'\'',E_USER_ERROR);
199
      }
200
      return $this->_strings[$str];
201
    }
202

    
203
  /**
204
   * This method is used to set the language used by phpCAS. 
205
   * @note Can be called only once.
206
   *
207
   * @param $lang a string representing the language.
208
   *
209
   * @public
210
   * @sa CAS_LANG_FRENCH, CAS_LANG_ENGLISH
211
   */
212
  function setLang($lang)
213
    {
214
      // include the corresponding language file
215
      include_once(dirname(__FILE__).'/languages/'.$lang.'.php');
216

    
217
      if ( !is_array($this->_strings) ) {
218
        trigger_error('language `'.$lang.'\' is not implemented',E_USER_ERROR);
219
      }
220
      $this->_lang = $lang;
221
    }
222

    
223
  /** @} */
224
  // ########################################################################
225
  //  CAS SERVER CONFIG
226
  // ########################################################################
227
  /**
228
   * @addtogroup internalConfig
229
   * @{
230
   */  
231
  
232
  /**
233
   * a record to store information about the CAS server.
234
   * - $_server["version"]: the version of the CAS server
235
   * - $_server["hostname"]: the hostname of the CAS server
236
   * - $_server["port"]: the port the CAS server is running on
237
   * - $_server["uri"]: the base URI the CAS server is responding on
238
   * - $_server["base_url"]: the base URL of the CAS server
239
   * - $_server["login_url"]: the login URL of the CAS server
240
   * - $_server["service_validate_url"]: the service validating URL of the CAS server
241
   * - $_server["proxy_url"]: the proxy URL of the CAS server
242
   * - $_server["proxy_validate_url"]: the proxy validating URL of the CAS server
243
   * - $_server["logout_url"]: the logout URL of the CAS server
244
   *
245
   * $_server["version"], $_server["hostname"], $_server["port"] and $_server["uri"]
246
   * are written by CASClient::CASClient(), read by CASClient::getServerVersion(), 
247
   * CASClient::getServerHostname(), CASClient::getServerPort() and CASClient::getServerURI().
248
   *
249
   * The other fields are written and read by CASClient::getServerBaseURL(), 
250
   * CASClient::getServerLoginURL(), CASClient::getServerServiceValidateURL(), 
251
   * CASClient::getServerProxyValidateURL() and CASClient::getServerLogoutURL().
252
   *
253
   * @hideinitializer
254
   * @private
255
   */
256
  var $_server = array(
257
                       'version' => -1,
258
                       'hostname' => 'none',
259
                       'port' => -1,
260
                       'uri' => 'none'
261
                       );
262
  
263
  /**
264
   * This method is used to retrieve the version of the CAS server.
265
   * @return the version of the CAS server.
266
   * @private
267
   */
268
  function getServerVersion()
269
    { 
270
      return $this->_server['version']; 
271
    }
272

    
273
  /**
274
   * This method is used to retrieve the hostname of the CAS server.
275
   * @return the hostname of the CAS server.
276
   * @private
277
   */
278
  function getServerHostname()
279
    { return $this->_server['hostname']; }
280

    
281
  /**
282
   * This method is used to retrieve the port of the CAS server.
283
   * @return the port of the CAS server.
284
   * @private
285
   */
286
  function getServerPort()
287
    { return $this->_server['port']; }
288

    
289
  /**
290
   * This method is used to retrieve the URI of the CAS server.
291
   * @return a URI.
292
   * @private
293
   */
294
  function getServerURI()
295
    { return $this->_server['uri']; }
296

    
297
  /**
298
   * This method is used to retrieve the base URL of the CAS server.
299
   * @return a URL.
300
   * @private
301
   */
302
  function getServerBaseURL()
303
    { 
304
      // the URL is build only when needed
305
      if ( empty($this->_server['base_url']) ) {
306
        $this->_server['base_url'] = 'https://'
307
          .$this->getServerHostname()
308
          .':'
309
          .$this->getServerPort()
310
          .$this->getServerURI();
311
      }
312
      return $this->_server['base_url']; 
313
    }
314

    
315
  /**
316
   * This method is used to retrieve the login URL of the CAS server.
317
   * @param $gateway true to check authentication, false to force it
318
   * @return a URL.
319
   * @private
320
   */
321
  function getServerLoginURL($gateway=false)
322
    { 
323
        $cas=new phpCAS();
324
      $cas->traceBegin();
325
      // the URL is build only when needed
326
      if ( empty($this->_server['login_url']) ) {
327
        $this->_server['login_url'] = $this->getServerBaseURL();
328
        $this->_server['login_url'] .= 'login?service=';
329
//        $this->_server['login_url'] .= preg_replace('/&/','%26',$this->getURL());
330
        $this->_server['login_url'] .= urlencode($this->getURL());
331
        if ($gateway) {
332
          $this->_server['login_url'] .= '&gateway=true';
333
        }
334
      }
335
      $cas->traceEnd($this->_server['login_url']);
336
      return $this->_server['login_url']; 
337
    }
338

    
339
  /**
340
   * This method sets the login URL of the CAS server.
341
   * @param $url the login URL
342
   * @private
343
   * @since 0.4.21 by Wyman Chan
344
   */
345
  function setServerLoginURL($url)
346
    {
347
      return $this->_server['login_url'] = $url;
348
    }
349

    
350
  /**
351
   * This method is used to retrieve the service validating URL of the CAS server.
352
   * @return a URL.
353
   * @private
354
   */
355
  function getServerServiceValidateURL()
356
    { 
357
      // the URL is build only when needed
358
      if ( empty($this->_server['service_validate_url']) ) {
359
        switch ($this->getServerVersion()) {
360
        case CAS_VERSION_1_0:
361
          $this->_server['service_validate_url'] = $this->getServerBaseURL().'validate';
362
          break;
363
        case CAS_VERSION_2_0:
364
          $this->_server['service_validate_url'] = $this->getServerBaseURL().'serviceValidate';
365
          break;
366
        }
367
      }
368
//      return $this->_server['service_validate_url'].'?service='.preg_replace('/&/','%26',$this->getURL()); 
369
      return $this->_server['service_validate_url'].'?service='.urlencode($this->getURL()); 
370
    }
371

    
372
  /**
373
   * This method is used to retrieve the proxy validating URL of the CAS server.
374
   * @return a URL.
375
   * @private
376
   */
377
  function getServerProxyValidateURL()
378
    { 
379
      // the URL is build only when needed
380
      if ( empty($this->_server['proxy_validate_url']) ) {
381
        switch ($this->getServerVersion()) {
382
        case CAS_VERSION_1_0:
383
          $this->_server['proxy_validate_url'] = '';
384
          break;
385
        case CAS_VERSION_2_0:
386
          $this->_server['proxy_validate_url'] = $this->getServerBaseURL().'proxyValidate';
387
          break;
388
        }
389
      }
390
//      return $this->_server['proxy_validate_url'].'?service='.preg_replace('/&/','%26',$this->getURL()); 
391
      return $this->_server['proxy_validate_url'].'?service='.urlencode($this->getURL()); 
392
    }
393

    
394
  /**
395
   * This method is used to retrieve the proxy URL of the CAS server.
396
   * @return a URL.
397
   * @private
398
   */
399
  function getServerProxyURL()
400
    { 
401
      // the URL is build only when needed
402
      if ( empty($this->_server['proxy_url']) ) {
403
        switch ($this->getServerVersion()) {
404
        case CAS_VERSION_1_0:
405
          $this->_server['proxy_url'] = '';
406
          break;
407
        case CAS_VERSION_2_0:
408
          $this->_server['proxy_url'] = $this->getServerBaseURL().'proxy';
409
          break;
410
        }
411
      }
412
      return $this->_server['proxy_url']; 
413
    }
414

    
415
  /**
416
   * This method is used to retrieve the logout URL of the CAS server.
417
   * @return a URL.
418
   * @private
419
   */
420
  function getServerLogoutURL()
421
    { 
422
      // the URL is build only when needed
423
      if ( empty($this->_server['logout_url']) ) {
424
        $this->_server['logout_url'] = $this->getServerBaseURL().'logout';
425
      }
426
      return $this->_server['logout_url']; 
427
    }
428

    
429
  /**
430
   * This method sets the logout URL of the CAS server.
431
   * @param $url the logout URL
432
   * @private
433
   * @since 0.4.21 by Wyman Chan
434
   */
435
  function setServerLogoutURL($url)
436
    {
437
      return $this->_server['logout_url'] = $url;
438
    }
439

    
440
  /**
441
   * This method checks to see if the request is secured via HTTPS
442
   * @return true if https, false otherwise
443
   * @private
444
   */
445
  function isHttps() {
446
    //if ( isset($_SERVER['HTTPS']) && !empty($_SERVER['HTTPS']) ) {
447
    //0.4.24 by Hinnack
448
    if ( isset($_SERVER['HTTPS']) && !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') {
449
      return true;
450
    } else {
451
      return false;
452
    }
453
  }
454

    
455
  // ########################################################################
456
  //  CONSTRUCTOR
457
  // ########################################################################
458
   /**
459
    * CASClient constructor.
460
    *
461
    * @param $server_version the version of the CAS server
462
    * @param $proxy TRUE if the CAS client is a CAS proxy, FALSE otherwise
463
    * @param $server_hostname the hostname of the CAS server
464
    * @param $server_port the port the CAS server is running on
465
    * @param $server_uri the URI the CAS server is responding on
466
    * @param $start_session Have phpCAS start PHP sessions (default true)
467
    *
468
    * @return a newly created CASClient object
469
    *
470
    * @public
471
    */
472
  function CASClient(
473
          $server_version,
474
        $proxy,
475
        $server_hostname,
476
        $server_port,
477
        $server_uri,
478
        $start_session = true) {
479

    
480
   $cas = new phpCAS;
481
    $cas->traceBegin();
482

    
483
    //activate session mechanism if desired
484
    if (!session_id()) {
485
      session_start();
486
    }
487

    
488
    $this->_proxy = $proxy;
489

    
490
    //check version
491
    switch ($server_version) {
492
      case CAS_VERSION_1_0:
493
        if ( $this->isProxy() )
494
          $cas->error('CAS proxies are not supported in CAS '
495
              .$server_version);
496
        break;
497
      case CAS_VERSION_2_0:
498
        break;
499
      default:
500
        phpCAS::error('this version of CAS (`'
501
            .$server_version
502
            .'\') is not supported by phpCAS '
503
            .$cas->getVersion());
504
    }
505
    $this->_server['version'] = $server_version;
506

    
507
    //check hostname
508
    if ( empty($server_hostname) 
509
        || !preg_match('/[\.\d\-abcdefghijklmnopqrstuvwxyz]*/',$server_hostname) ) {
510
      $cas->error('bad CAS server hostname (`'.$server_hostname.'\')');
511
    }
512
    $this->_server['hostname'] = $server_hostname;
513

    
514
    //check port
515
    if ( $server_port == 0 
516
        || !is_int($server_port) ) {
517
      $cas->error('bad CAS server port (`'.$server_hostname.'\')');
518
    }
519
    $this->_server['port'] = $server_port;
520

    
521
    //check URI
522
    if ( !preg_match('/[\.\d\-_abcdefghijklmnopqrstuvwxyz\/]*/',$server_uri) ) {
523
      $cas->error('bad CAS server URI (`'.$server_uri.'\')');
524
    }
525
    //add leading and trailing `/' and remove doubles      
526
    $server_uri = preg_replace('/\/\//','/','/'.$server_uri.'/');
527
    $this->_server['uri'] = $server_uri;
528

    
529
    //set to callback mode if PgtIou and PgtId CGI GET parameters are provided 
530
    if ( $this->isProxy() ) {
531
      $this->setCallbackMode(!empty($_GET['pgtIou'])&&!empty($_GET['pgtId']));
532
    }
533

    
534
    if ( $this->isCallbackMode() ) {
535
      //callback mode: check that phpCAS is secured
536
      if ( !$this->isHttps() ) {
537
       $cas->error('CAS proxies must be secured to use phpCAS; PGT\'s will not be received from the CAS server');
538
      }
539
    } else {
540
      //normal mode: get ticket and remove it from CGI parameters for developpers
541
      $ticket = (isset($_GET['ticket']) ? $_GET['ticket'] : null);
542
      switch ($this->getServerVersion()) {
543
        case CAS_VERSION_1_0: // check for a Service Ticket
544
          if( preg_match('/^ST-/',$ticket) ) {
545
            $cas->trace('ST \''.$ticket.'\' found');
546
            //ST present
547
            $this->setST($ticket);
548
            //ticket has been taken into account, unset it to hide it to applications
549
            unset($_GET['ticket']);
550
          } else if ( !empty($ticket) ) {
551
            //ill-formed ticket, halt
552
            $cas->error('ill-formed ticket found in the URL (ticket=`'.htmlentities($ticket).'\')');
553
          }
554
          break;
555
        case CAS_VERSION_2_0: // check for a Service or Proxy Ticket
556
          if( preg_match('/^[SP]T-/',$ticket) ) {
557
            $cas->trace('ST or PT \''.$ticket.'\' found');
558
            $this->setPT($ticket);
559
            unset($_GET['ticket']);
560
          } else if ( !empty($ticket) ) {
561
            //ill-formed ticket, halt
562
            $cas->error('ill-formed ticket found in the URL (ticket=`'.htmlentities($ticket).'\')');
563
          } 
564
          break;
565
        }
566
        }
567
    $cas->traceEnd();
568
  }
569

    
570
  /** @} */
571

    
572
  // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
573
  // XX                                                                    XX
574
  // XX                           AUTHENTICATION                           XX
575
  // XX                                                                    XX
576
  // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
577

    
578
  /**
579
   * @addtogroup internalAuthentication
580
   * @{
581
   */  
582
  
583
  /**
584
   * The Authenticated user. Written by CASClient::setUser(), read by CASClient::getUser().
585
   * @attention client applications should use phpCAS::getUser().
586
   *
587
   * @hideinitializer
588
   * @private
589
   */
590
  var $_user = '';
591
  
592
  /**
593
   * This method sets the CAS user's login name.
594
   *
595
   * @param $user the login name of the authenticated user.
596
   *
597
   * @private
598
   */
599
  function setUser($user)
600
    {
601
      $this->_user = $user;
602
    }
603

    
604
  /**
605
   * This method returns the CAS user's login name.
606
   * @warning should be called only after CASClient::forceAuthentication() or 
607
   * CASClient::isAuthenticated(), otherwise halt with an error.
608
   *
609
   * @return the login name of the authenticated user
610
   */
611
  function getUser()
612
    {
613
      if ( empty($this->_user) ) {
614
        phpCAS::error('this method should be used only after '.__CLASS__.'::forceAuthentication() or '.__CLASS__.'::isAuthenticated()');
615
      }
616
      return $this->_user;
617
    }
618

    
619
  /**
620
   * This method is called to be sure that the user is authenticated. When not 
621
   * authenticated, halt by redirecting to the CAS server; otherwise return TRUE.
622
   * @return TRUE when the user is authenticated; otherwise halt.
623
   * @public
624
   */
625
  function forceAuthentication()
626
    {
627
        $cas=new phpCas();
628
      $cas->traceBegin();
629

    
630
      if ( $this->isAuthenticated() ) {
631
        // the user is authenticated, nothing to be done.
632
            $cas->trace('no need to authenticate');
633
            $res = TRUE;
634
      } else {
635
            // the user is not authenticated, redirect to the CAS server
636
        unset($_SESSION['phpCAS']['auth_checked']);
637
            $this->redirectToCas(FALSE/* no gateway */);        
638
            // never reached
639
            $res = FALSE;
640
      }
641
      $cas->traceEnd($res);
642
      return $res;
643
    }
644

    
645
  /**
646
   * An integer that gives the number of times authentication will be cached before rechecked.
647
   *
648
   * @hideinitializer
649
   * @private
650
   */
651
  var $_cache_times_for_auth_recheck = 0;
652
  
653
  /**
654
   * Set the number of times authentication will be cached before rechecked.
655
   *
656
   * @param $n an integer.
657
   *
658
   * @public
659
   */
660
  function setCacheTimesForAuthRequest($n)
661
    {
662
      $this->_cache_times_for_auth_recheck = n;
663
    }
664

    
665
  /**
666
   * This method is called to check whether the user is authenticated or not.
667
   * @return TRUE when the user is authenticated, FALSE otherwise.
668
   * @public
669
   */
670
  function checkAuthentication()
671
    {
672
      phpCAS::traceBegin();
673

    
674
      if ( $this->isAuthenticated() ) {
675
            phpCAS::trace('user is authenticated');
676
            $res = TRUE;
677
      } else if (isset($_SESSION['phpCAS']['auth_checked'])) {
678
        // the previous request has redirected the client to the CAS server with gateway=true
679
        unset($_SESSION['phpCAS']['auth_checked']);
680
        $res = FALSE;
681
      } else {
682
//        $_SESSION['phpCAS']['auth_checked'] = true;
683
//            $this->redirectToCas(TRUE/* gateway */);        
684
//            // never reached
685
//            $res = FALSE;
686
        // avoid a check against CAS on every request
687
        if (! isset($_SESSION['phpCAS']['unauth_count']) )
688
           $_SESSION['phpCAS']['unauth_count'] = -2; // uninitialized
689
        
690
        if (($_SESSION['phpCAS']['unauth_count'] != -2 && $this->_cache_times_for_auth_recheck == -1) 
691
          || ($_SESSION['phpCAS']['unauth_count'] >= 0 && $_SESSION['phpCAS']['unauth_count'] < $this->_cache_times_for_auth_recheck))
692
        {
693
           $res = FALSE;
694
           
695
           if ($this->_cache_times_for_auth_recheck != -1)
696
           {
697
                             $_SESSION['phpCAS']['unauth_count']++;
698
                     phpCAS::trace('user is not authenticated (cached for '.$_SESSION['phpCAS']['unauth_count'].' times of '.$this->_cache_times_for_auth_recheck.')');
699
           }
700
           else
701
           {
702
                     phpCAS::trace('user is not authenticated (cached for until login pressed)');
703
           }
704
        }
705
        else
706
        {
707
                 $_SESSION['phpCAS']['unauth_count'] = 0;
708
            $_SESSION['phpCAS']['auth_checked'] = true;
709
            phpCAS::trace('user is not authenticated (cache reset)');
710
                $this->redirectToCas(TRUE/* gateway */);        
711
                // never reached
712
                $res = FALSE;
713
        }
714
      }
715
      phpCAS::traceEnd($res);
716
      return $res;
717
    }
718
  
719
  /**
720
   * This method is called to check if the user is authenticated (previously or by
721
   * tickets given in the URL).
722
   *
723
   * @return TRUE when the user is authenticated.
724
   *
725
   * @public
726
   */
727
  function isAuthenticated()
728
  {
729
        $cas=new phpCas();
730
      $cas->traceBegin();
731
      $res = FALSE;
732
      $validate_url = '';
733

    
734
      if ( $this->wasPreviouslyAuthenticated() ) {
735
                   // the user has already (previously during the session) been 
736
                 // authenticated, nothing to be done.
737
            $cas->trace('user was already authenticated, no need to look for tickets');
738
            $res = TRUE;
739
      } 
740
          elseif ( $this->hasST() ) {
741
            // if a Service Ticket was given, validate it
742
            $cas->trace('ST `'.$this->getST().'\' is present');
743
            $this->validateST($validate_url,$text_response,$tree_response); // if it fails, it halts
744
            $cas->trace('ST `'.$this->getST().'\' was validated');
745
            if ( $this->isProxy() ) {
746
                   $this->validatePGT($validate_url,$text_response,$tree_response); // idem
747
                   $cas->trace('PGT `'.$this->getPGT().'\' was validated');
748
                   $_SESSION['phpCAS']['pgt'] = $this->getPGT();
749
                }
750
                $_SESSION['phpCAS']['user'] = $this->getUser();
751
                $res = TRUE;
752
        }
753
        elseif ( $this->hasPT() ) {
754
                // if a Proxy Ticket was given, validate it
755
                $cas->trace('PT `'.$this->getPT().'\' is present');
756
                $this->validatePT($validate_url,$text_response,$tree_response); // note: if it fails, it halts
757
                $cas->trace('PT `'.$this->getPT().'\' was validated');
758
                if ( $this->isProxy() ) {
759
                   $this->validatePGT($validate_url,$text_response,$tree_response); // idem
760
                   $cas->trace('PGT `'.$this->getPGT().'\' was validated');
761
                   $_SESSION['phpCAS']['pgt'] = $this->getPGT();
762
                }
763
            $_SESSION['phpCAS']['user'] = $this->getUser();
764
                $res = TRUE;
765
        } 
766
        else {
767
            // no ticket given, not authenticated
768
            $cas->trace('no ticket found');
769
        }
770

    
771
        $cas->traceEnd($res);
772
        return $res;
773
  }
774
  
775
  /**
776
   * This method tells if the current session is authenticated.
777
   * @return true if authenticated based soley on $_SESSION variable
778
   * @since 0.4.22 by Brendan Arnold
779
   */
780
  function isSessionAuthenticated ()
781
    {
782
      return !empty($_SESSION['phpCAS']['user']);
783
    }
784

    
785
  /**
786
   * This method tells if the user has already been (previously) authenticated
787
   * by looking into the session variables.
788
   *
789
   * @note This function switches to callback mode when needed.
790
   *
791
   * @return TRUE when the user has already been authenticated; FALSE otherwise.
792
   *
793
   * @private
794
   */
795
  function wasPreviouslyAuthenticated()
796
    {
797
        $cas=new phpCas();
798
      $cas->traceBegin();
799

    
800
      if ( $this->isCallbackMode() ) {
801
        $this->callback();
802
      }
803

    
804
      $auth = FALSE;
805

    
806
      if ( $this->isProxy() ) {
807
        // CAS proxy: username and PGT must be present
808
        if ( $this->isSessionAuthenticated() && !empty($_SESSION['phpCAS']['pgt']) ) {
809
          // authentication already done
810
          $this->setUser($_SESSION['phpCAS']['user']);
811
          $this->setPGT($_SESSION['phpCAS']['pgt']);
812
          $cas->trace('user = `'.$_SESSION['phpCAS']['user'].'\', PGT = `'.$_SESSION['phpCAS']['pgt'].'\''); 
813
          $auth = TRUE;
814
        } elseif ( $this->isSessionAuthenticated() && empty($_SESSION['phpCAS']['pgt']) ) {
815
          // these two variables should be empty or not empty at the same time
816
          $cas->trace('username found (`'.$_SESSION['phpCAS']['user'].'\') but PGT is empty');
817
          // unset all tickets to enforce authentication
818
          unset($_SESSION['phpCAS']);
819
          $this->setST('');
820
          $this->setPT('');
821
        } elseif ( !$this->isSessionAuthenticated() && !empty($_SESSION['phpCAS']['pgt']) ) {
822
          // these two variables should be empty or not empty at the same time
823
          $cas->trace('PGT found (`'.$_SESSION['phpCAS']['pgt'].'\') but username is empty'); 
824
          // unset all tickets to enforce authentication
825
          unset($_SESSION['phpCAS']);
826
          $this->setST('');
827
          $this->setPT('');
828
        } else {
829
          $cas->trace('neither user not PGT found'); 
830
        }
831
      } else {
832
        // `simple' CAS client (not a proxy): username must be present
833
        if ( $this->isSessionAuthenticated() ) {
834
          // authentication already done
835
          $this->setUser($_SESSION['phpCAS']['user']);
836
          $cas->trace('user = `'.$_SESSION['phpCAS']['user'].'\''); 
837
          $auth = TRUE;
838
        } else {
839
          $cas->trace('no user found');
840
        }
841
      }
842
      
843
      $cas->traceEnd($auth);
844
      return $auth;
845
    }
846
  
847
  /**
848
   * This method is used to redirect the client to the CAS server.
849
   * It is used by CASClient::forceAuthentication() and CASClient::checkAuthentication().
850
   * @param $gateway true to check authentication, false to force it
851
   * @public
852
   */
853
  function redirectToCas($gateway=false)
854
    {
855
        $cas=new phpCas();
856
      $cas->traceBegin();
857
      $cas_url = $this->getServerLoginURL($gateway);
858
      header('Location: '.$cas_url);
859
      $this->printHTMLHeader($this->getString(CAS_STR_AUTHENTICATION_WANTED));
860
      printf('<p>'.$this->getString(CAS_STR_SHOULD_HAVE_BEEN_REDIRECTED).'</p>',$cas_url);
861
      $this->printHTMLFooter();
862
      $cas->traceExit();
863
      exit();
864
    }
865
  
866
  /**
867
   * This method is used to logout from CAS.
868
   * @param $url a URL that will be transmitted to the CAS server (to come back to when logged out)
869
   * @public
870
   */
871
  function logout($url = "")
872
    {
873
        $cas=new phpCAS();
874
      $cas->traceBegin();
875
      $cas_url = $this->getServerLogoutURL();
876
      // v0.4.14 sebastien.gougeon at univ-rennes1.fr
877
      // header('Location: '.$cas_url);
878
      if ( $url != "" ) {
879
        $url = '?service=' . $url;
880
      }
881
      header('Location: '.$cas_url . $url);
882
      session_unset();
883
      session_destroy();
884
      $this->printHTMLHeader($this->getString(CAS_STR_LOGOUT));
885
      printf('<p>'.$this->getString(CAS_STR_SHOULD_HAVE_BEEN_REDIRECTED).'</p>',$cas_url);
886
      $this->printHTMLFooter();
887
      $cas->traceExit();
888
      exit();
889
    }
890
  
891
  /** @} */
892

    
893
  // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
894
  // XX                                                                    XX
895
  // XX                  BASIC CLIENT FEATURES (CAS 1.0)                   XX
896
  // XX                                                                    XX
897
  // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
898

    
899
  // ########################################################################
900
  //  ST
901
  // ########################################################################
902
  /**
903
   * @addtogroup internalBasic
904
   * @{
905
   */  
906
  
907
  /**
908
   * the Service Ticket provided in the URL of the request if present
909
   * (empty otherwise). Written by CASClient::CASClient(), read by 
910
   * CASClient::getST() and CASClient::hasPGT().
911
   *
912
   * @hideinitializer
913
   * @private
914
   */
915
  var $_st = '';
916
  
917
  /**
918
   * This method returns the Service Ticket provided in the URL of the request.
919
   * @return The service ticket.
920
   * @private
921
   */
922
  function getST()
923
    { return $this->_st; }
924

    
925
  /**
926
   * This method stores the Service Ticket.
927
   * @param $st The Service Ticket.
928
   * @private
929
   */
930
  function setST($st)
931
    { $this->_st = $st; }
932

    
933
  /**
934
   * This method tells if a Service Ticket was stored.
935
   * @return TRUE if a Service Ticket has been stored.
936
   * @private
937
   */
938
  function hasST()
939
    { return !empty($this->_st); }
940

    
941
  /** @} */
942

    
943
  // ########################################################################
944
  //  ST VALIDATION
945
  // ########################################################################
946
  /**
947
   * @addtogroup internalBasic
948
   * @{
949
   */  
950

    
951
  /**
952
   * This method is used to validate a ST; halt on failure, and sets $validate_url,
953
   * $text_reponse and $tree_response on success. These parameters are used later
954
   * by CASClient::validatePGT() for CAS proxies.
955
   * 
956
   * @param $validate_url the URL of the request to the CAS server.
957
   * @param $text_response the response of the CAS server, as is (XML text).
958
   * @param $tree_response the response of the CAS server, as a DOM XML tree.
959
   *
960
   * @return bool TRUE when successfull, halt otherwise by calling CASClient::authError().
961
   *
962
   * @private
963
   */
964
  function validateST($validate_url,&$text_response,&$tree_response)
965
    {
966
      phpCAS::traceBegin();
967
      // build the URL to validate the ticket
968
      $validate_url = $this->getServerServiceValidateURL().'&ticket='.$this->getST();
969
      if ( $this->isProxy() ) {
970
                   // pass the callback url for CAS proxies
971
                 $validate_url .= '&pgtUrl='.$this->getCallbackURL();
972
      }
973

    
974
      // open and read the URL
975
      if ( !$this->readURL($validate_url,''/*cookies*/,$headers,$text_response,$err_msg) ) {
976
        phpCAS::trace('could not open URL \''.$validate_url.'\' to validate ('.$err_msg.')');
977
        $this->authError('ST not validated',
978
                         $validate_url,
979
                         TRUE/*$no_response*/);
980
      }
981

    
982
      // analyze the result depending on the version
983
      switch ($this->getServerVersion()) {
984
      case CAS_VERSION_1_0:
985
        if (preg_match('/^no\n/',$text_response)) {
986
          phpCAS::trace('ST has not been validated');
987
          $this->authError('ST not validated',
988
                       $validate_url,
989
                       FALSE/*$no_response*/,
990
                       FALSE/*$bad_response*/,
991
                       $text_response);
992
        }
993
        if (!preg_match('/^yes\n/',$text_response)) {
994
          phpCAS::trace('ill-formed response');
995
          $this->authError('ST not validated',
996
                       $validate_url,
997
                       FALSE/*$no_response*/,
998
                       TRUE/*$bad_response*/,
999
                       $text_response);
1000
        }
1001
        // ST has been validated, extract the user name
1002
        $arr = preg_split('/\n/',$text_response);
1003
        $this->setUser(trim($arr[1]));
1004
        break;
1005
      case CAS_VERSION_2_0:
1006
        // read the response of the CAS server into a DOM object
1007
        if ( !($dom = domxml_open_mem($text_response))) {
1008
          phpCAS::trace('domxml_open_mem() failed');
1009
          $this->authError('ST not validated',
1010
                       $validate_url,
1011
                       FALSE/*$no_response*/,
1012
                       TRUE/*$bad_response*/,
1013
                       $text_response);
1014
        }
1015
        // read the root node of the XML tree
1016
        if ( !($tree_response = $dom->document_element()) ) {
1017
          phpCAS::trace('document_element() failed');
1018
          $this->authError('ST not validated',
1019
                       $validate_url,
1020
                       FALSE/*$no_response*/,
1021
                       TRUE/*$bad_response*/,
1022
                       $text_response);
1023
        }
1024
        // insure that tag name is 'serviceResponse'
1025
        if ( $tree_response->node_name() != 'serviceResponse' ) {
1026
          phpCAS::trace('bad XML root node (should be `serviceResponse\' instead of `'.$tree_response->node_name().'\'');
1027
          $this->authError('ST not validated',
1028
                       $validate_url,
1029
                       FALSE/*$no_response*/,
1030
                       TRUE/*$bad_response*/,
1031
                       $text_response);
1032
        }
1033
        if ( sizeof($success_elements = $tree_response->get_elements_by_tagname("authenticationSuccess")) != 0) {
1034
          // authentication succeded, extract the user name
1035
          if ( sizeof($user_elements = $success_elements[0]->get_elements_by_tagname("user")) == 0) {
1036
            phpCAS::trace('<authenticationSuccess> found, but no <user>');
1037
            $this->authError('ST not validated',
1038
                         $validate_url,
1039
                         FALSE/*$no_response*/,
1040
                         TRUE/*$bad_response*/,
1041
                         $text_response);
1042
          }
1043
          $user = trim($user_elements[0]->get_content());
1044
          phpCAS::trace('user = `'.$user);
1045
          $this->setUser($user);
1046
          
1047
        } else if ( sizeof($failure_elements = $tree_response->get_elements_by_tagname("authenticationFailure")) != 0) {
1048
          phpCAS::trace('<authenticationFailure> found');
1049
          // authentication failed, extract the error code and message
1050
          $this->authError('ST not validated',
1051
                       $validate_url,
1052
                       FALSE/*$no_response*/,
1053
                       FALSE/*$bad_response*/,
1054
                       $text_response,
1055
                       $failure_elements[0]->get_attribute('code')/*$err_code*/,
1056
                       trim($failure_elements[0]->get_content())/*$err_msg*/);
1057
        } else {
1058
          phpCAS::trace('neither <authenticationSuccess> nor <authenticationFailure> found');
1059
          $this->authError('ST not validated',
1060
                       $validate_url,
1061
                       FALSE/*$no_response*/,
1062
                       TRUE/*$bad_response*/,
1063
                       $text_response);
1064
        }
1065
        break;
1066
      }
1067
      
1068
      // at this step, ST has been validated and $this->_user has been set,
1069
      phpCAS::traceEnd(TRUE);
1070
      return TRUE;
1071
    }
1072

    
1073
  /** @} */
1074

    
1075
  // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
1076
  // XX                                                                    XX
1077
  // XX                     PROXY FEATURES (CAS 2.0)                       XX
1078
  // XX                                                                    XX
1079
  // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
1080

    
1081
  // ########################################################################
1082
  //  PROXYING
1083
  // ########################################################################
1084
  /**
1085
   * @addtogroup internalProxy
1086
   * @{
1087
   */
1088

    
1089
  /**
1090
   * A boolean telling if the client is a CAS proxy or not. Written by CASClient::CASClient(), 
1091
   * read by CASClient::isProxy().
1092
   *
1093
   * @private
1094
   */
1095
  var $_proxy;
1096
  
1097
  /**
1098
   * Tells if a CAS client is a CAS proxy or not
1099
   *
1100
   * @return TRUE when the CAS client is a CAs proxy, FALSE otherwise
1101
   *
1102
   * @private
1103
   */
1104
  function isProxy()
1105
    {
1106
      return $this->_proxy;
1107
    }
1108

    
1109
  /** @} */
1110
  // ########################################################################
1111
  //  PGT
1112
  // ########################################################################
1113
  /**
1114
   * @addtogroup internalProxy
1115
   * @{
1116
   */  
1117
  
1118
  /**
1119
   * the Proxy Grnting Ticket given by the CAS server (empty otherwise). 
1120
   * Written by CASClient::setPGT(), read by CASClient::getPGT() and CASClient::hasPGT().
1121
   *
1122
   * @hideinitializer
1123
   * @private
1124
   */
1125
  var $_pgt = '';
1126
  
1127
  /**
1128
   * This method returns the Proxy Granting Ticket given by the CAS server.
1129
   * @return The Proxy Granting Ticket.
1130
   * @private
1131
   */
1132
  function getPGT()
1133
    { return $this->_pgt; }
1134

    
1135
  /**
1136
   * This method stores the Proxy Granting Ticket.
1137
   * @param $pgt The Proxy Granting Ticket.
1138
   * @private
1139
   */
1140
  function setPGT($pgt)
1141
    { $this->_pgt = $pgt; }
1142

    
1143
  /**
1144
   * This method tells if a Proxy Granting Ticket was stored.
1145
   * @return TRUE if a Proxy Granting Ticket has been stored.
1146
   * @private
1147
   */
1148
  function hasPGT()
1149
    { return !empty($this->_pgt); }
1150

    
1151
  /** @} */
1152

    
1153
  // ########################################################################
1154
  //  CALLBACK MODE
1155
  // ########################################################################
1156
  /**
1157
   * @addtogroup internalCallback
1158
   * @{
1159
   */  
1160
  /**
1161
   * each PHP script using phpCAS in proxy mode is its own callback to get the
1162
   * PGT back from the CAS server. callback_mode is detected by the constructor
1163
   * thanks to the GET parameters.
1164
   */
1165

    
1166
  /**
1167
   * a boolean to know if the CAS client is running in callback mode. Written by
1168
   * CASClient::setCallBackMode(), read by CASClient::isCallbackMode().
1169
   *
1170
   * @hideinitializer
1171
   * @private
1172
   */
1173
  var $_callback_mode = FALSE;
1174
  
1175
  /**
1176
   * This method sets/unsets callback mode.
1177
   *
1178
   * @param $callback_mode TRUE to set callback mode, FALSE otherwise.
1179
   *
1180
   * @private
1181
   */
1182
  function setCallbackMode($callback_mode)
1183
    {
1184
      $this->_callback_mode = $callback_mode;
1185
    }
1186

    
1187
  /**
1188
   * This method returns TRUE when the CAs client is running i callback mode, 
1189
   * FALSE otherwise.
1190
   *
1191
   * @return A boolean.
1192
   *
1193
   * @private
1194
   */
1195
  function isCallbackMode()
1196
    {
1197
      return $this->_callback_mode;
1198
    }
1199

    
1200
  /**
1201
   * the URL that should be used for the PGT callback (in fact the URL of the 
1202
   * current request without any CGI parameter). Written and read by 
1203
   * CASClient::getCallbackURL().
1204
   *
1205
   * @hideinitializer
1206
   * @private
1207
   */
1208
  var $_callback_url = '';
1209

    
1210
  /**
1211
   * This method returns the URL that should be used for the PGT callback (in
1212
   * fact the URL of the current request without any CGI parameter, except if
1213
   * phpCAS::setFixedCallbackURL() was used).
1214
   *
1215
   * @return The callback URL
1216
   *
1217
   * @private
1218
   */
1219
  function getCallbackURL()
1220
    {
1221
      // the URL is built when needed only
1222
      if ( empty($this->_callback_url) ) {
1223
        $final_uri = '';
1224
            // remove the ticket if present in the URL
1225
            $final_uri = 'https://';
1226
            /* replaced by Julien Marchal - v0.4.6
1227
             * $this->uri .= $_SERVER['SERVER_NAME'];
1228
             */
1229
        if(empty($_SERVER['HTTP_X_FORWARDED_SERVER'])){
1230
          /* replaced by teedog - v0.4.12
1231
           * $final_uri .= $_SERVER['SERVER_NAME'];
1232
           */
1233
          if (empty($_SERVER['SERVER_NAME'])) {
1234
            $final_uri .= $_SERVER['HTTP_HOST'];
1235
          } else {
1236
            $final_uri .= $_SERVER['SERVER_NAME'];
1237
          }
1238
        } else {
1239
          $final_uri .= $_SERVER['HTTP_X_FORWARDED_SERVER'];
1240
        }
1241
            if ( ($this->isHttps() && $_SERVER['SERVER_PORT']!=443)
1242
               || (!$this->isHttps() && $_SERVER['SERVER_PORT']!=80) ) {
1243
              $final_uri .= ':';
1244
              $final_uri .= $_SERVER['SERVER_PORT'];
1245
            }
1246
            $request_uri = $_SERVER['REQUEST_URI'];
1247
            $request_uri = preg_replace('/\?.*$/','',$request_uri);
1248
            $final_uri .= $request_uri;
1249
            $this->setCallbackURL($final_uri);
1250
      }
1251
      return $this->_callback_url;
1252
    }
1253

    
1254
  /**
1255
   * This method sets the callback url.
1256
   *
1257
   * @param $callback_url url to set callback 
1258
   *
1259
   * @private
1260
   */
1261
  function setCallbackURL($url)
1262
    {
1263
      return $this->_callback_url = $url;
1264
    }
1265

    
1266
  /**
1267
   * This method is called by CASClient::CASClient() when running in callback
1268
   * mode. It stores the PGT and its PGT Iou, prints its output and halts.
1269
   *
1270
   * @private
1271
   */
1272
  function callback()
1273
    {
1274
      phpCAS::traceBegin();
1275
      $this->printHTMLHeader('phpCAS callback');
1276
      $pgt_iou = $_GET['pgtIou'];
1277
      $pgt = $_GET['pgtId'];
1278
      phpCAS::trace('Storing PGT `'.$pgt.'\' (id=`'.$pgt_iou.'\')');
1279
      echo '<p>Storing PGT `'.$pgt.'\' (id=`'.$pgt_iou.'\').</p>';
1280
      $this->storePGT($pgt,$pgt_iou);
1281
      $this->printHTMLFooter();
1282
      phpCAS::traceExit();
1283
    }
1284

    
1285
  /** @} */
1286

    
1287
  // ########################################################################
1288
  //  PGT STORAGE
1289
  // ########################################################################
1290
  /**
1291
   * @addtogroup internalPGTStorage
1292
   * @{
1293
   */  
1294
    
1295
  /**
1296
   * an instance of a class inheriting of PGTStorage, used to deal with PGT
1297
   * storage. Created by CASClient::setPGTStorageFile() or CASClient::setPGTStorageDB(), used 
1298
   * by CASClient::setPGTStorageFile(), CASClient::setPGTStorageDB() and CASClient::initPGTStorage().
1299
   *
1300
   * @hideinitializer
1301
   * @private
1302
   */
1303
  var $_pgt_storage = null;
1304

    
1305
  /**
1306
   * This method is used to initialize the storage of PGT's.
1307
   * Halts on error.
1308
   *
1309
   * @private
1310
   */
1311
  function initPGTStorage()
1312
    {
1313
      // if no SetPGTStorageXxx() has been used, default to file
1314
      if ( !is_object($this->_pgt_storage) ) {
1315
        $this->setPGTStorageFile();
1316
      }
1317

    
1318
      // initializes the storage
1319
      $this->_pgt_storage->init();
1320
    }
1321
  
1322
  /**
1323
   * This method stores a PGT. Halts on error.
1324
   *
1325
   * @param $pgt the PGT to store
1326
   * @param $pgt_iou its corresponding Iou
1327
   *
1328
   * @private
1329
   */
1330
  function storePGT($pgt,$pgt_iou)
1331
    {
1332
      // ensure that storage is initialized
1333
      $this->initPGTStorage();
1334
      // writes the PGT
1335
      $this->_pgt_storage->write($pgt,$pgt_iou);
1336
    }
1337
  
1338
  /**
1339
   * This method reads a PGT from its Iou and deletes the corresponding storage entry.
1340
   *
1341
   * @param $pgt_iou the PGT Iou
1342
   *
1343
   * @return The PGT corresponding to the Iou, FALSE when not found.
1344
   *
1345
   * @private
1346
   */
1347
  function loadPGT($pgt_iou)
1348
    {
1349
      // ensure that storage is initialized
1350
      $this->initPGTStorage();
1351
      // read the PGT
1352
      return $this->_pgt_storage->read($pgt_iou);
1353
    }
1354
  
1355
  /**
1356
   * This method is used to tell phpCAS to store the response of the
1357
   * CAS server to PGT requests onto the filesystem. 
1358
   *
1359
   * @param $format the format used to store the PGT's (`plain' and `xml' allowed)
1360
   * @param $path the path where the PGT's should be stored
1361
   *
1362
   * @public
1363
   */
1364
  function setPGTStorageFile($format='',
1365
                             $path='')
1366
    {
1367
      // check that the storage has not already been set
1368
      if ( is_object($this->_pgt_storage) ) {
1369
        phpCAS::error('PGT storage already defined');
1370
      }
1371

    
1372
      // create the storage object
1373
      $this->_pgt_storage = new PGTStorageFile($this,$format,$path);
1374
    }
1375
  
1376
  /**
1377
   * This method is used to tell phpCAS to store the response of the
1378
   * CAS server to PGT requests into a database. 
1379
   * @note The connection to the database is done only when needed. 
1380
   * As a consequence, bad parameters are detected only when 
1381
   * initializing PGT storage.
1382
   *
1383
   * @param $user the user to access the data with
1384
   * @param $password the user's password
1385
   * @param $database_type the type of the database hosting the data
1386
   * @param $hostname the server hosting the database
1387
   * @param $port the port the server is listening on
1388
   * @param $database the name of the database
1389
   * @param $table the name of the table storing the data
1390
   *
1391
   * @public
1392
   */
1393
  function setPGTStorageDB($user,
1394
                           $password,
1395
                           $database_type,
1396
                           $hostname,
1397
                           $port,
1398
                           $database,
1399
                           $table)
1400
    {
1401
      // check that the storage has not already been set
1402
      if ( is_object($this->_pgt_storage) ) {
1403
        phpCAS::error('PGT storage already defined');
1404
      }
1405

    
1406
      // warn the user that he should use file storage...
1407
      trigger_error('PGT storage into database is an experimental feature, use at your own risk',E_USER_WARNING);
1408

    
1409
      // create the storage object
1410
      $this->_pgt_storage = new PGTStorageDB($this,$user,$password,$database_type,$hostname,$port,$database,$table);
1411
    }
1412
  
1413
  // ########################################################################
1414
  //  PGT VALIDATION
1415
  // ########################################################################
1416
  /**
1417
   * This method is used to validate a PGT; halt on failure.
1418
   * 
1419
   * @param $validate_url the URL of the request to the CAS server.
1420
   * @param $text_response the response of the CAS server, as is (XML text); result
1421
   * of CASClient::validateST() or CASClient::validatePT().
1422
   * @param $tree_response the response of the CAS server, as a DOM XML tree; result
1423
   * of CASClient::validateST() or CASClient::validatePT().
1424
   *
1425
   * @return bool TRUE when successfull, halt otherwise by calling CASClient::authError().
1426
   *
1427
   * @private
1428
   */
1429
  function validatePGT(&$validate_url,$text_response,$tree_response)
1430
    {
1431
      phpCAS::traceBegin();
1432
      if ( sizeof($arr = $tree_response->get_elements_by_tagname("proxyGrantingTicket")) == 0) {
1433
        phpCAS::trace('<proxyGrantingTicket> not found');
1434
        // authentication succeded, but no PGT Iou was transmitted
1435
        $this->authError('Ticket validated but no PGT Iou transmitted',
1436
                     $validate_url,
1437
                     FALSE/*$no_response*/,
1438
                     FALSE/*$bad_response*/,
1439
                     $text_response);
1440
      } else {
1441
        // PGT Iou transmitted, extract it
1442
        $pgt_iou = trim($arr[0]->get_content());
1443
        $pgt = $this->loadPGT($pgt_iou);
1444
        if ( $pgt == FALSE ) {
1445
          phpCAS::trace('could not load PGT');
1446
          $this->authError('PGT Iou was transmitted but PGT could not be retrieved',
1447
                       $validate_url,
1448
                       FALSE/*$no_response*/,
1449
                       FALSE/*$bad_response*/,
1450
                       $text_response);
1451
        }
1452
        $this->setPGT($pgt);
1453
      }
1454
      phpCAS::traceEnd(TRUE);
1455
      return TRUE;
1456
    }
1457

    
1458
  // ########################################################################
1459
  //  PGT VALIDATION
1460
  // ########################################################################
1461

    
1462
  /**
1463
   * This method is used to retrieve PT's from the CAS server thanks to a PGT.
1464
   * 
1465
   * @param $target_service the service to ask for with the PT.
1466
   * @param $err_code an error code (PHPCAS_SERVICE_OK on success).
1467
   * @param $err_msg an error message (empty on success).
1468
   *
1469
   * @return a Proxy Ticket, or FALSE on error.
1470
   *
1471
   * @private
1472
   */
1473
  function retrievePT($target_service,&$err_code,&$err_msg)
1474
    {
1475
      phpCAS::traceBegin();
1476

    
1477
      // by default, $err_msg is set empty and $pt to TRUE. On error, $pt is
1478
      // set to false and $err_msg to an error message. At the end, if $pt is FALSE 
1479
      // and $error_msg is still empty, it is set to 'invalid response' (the most
1480
      // commonly encountered error).
1481
      $err_msg = '';
1482

    
1483
      // build the URL to retrieve the PT
1484
//      $cas_url = $this->getServerProxyURL().'?targetService='.preg_replace('/&/','%26',$target_service).'&pgt='.$this->getPGT();
1485
      $cas_url = $this->getServerProxyURL().'?targetService='.urlencode($target_service).'&pgt='.$this->getPGT();
1486

    
1487
      // open and read the URL
1488
      if ( !$this->readURL($cas_url,''/*cookies*/,$headers,$cas_response,$err_msg) ) {
1489
        phpCAS::trace('could not open URL \''.$cas_url.'\' to validate ('.$err_msg.')');
1490
        $err_code = PHPCAS_SERVICE_PT_NO_SERVER_RESPONSE;
1491
        $err_msg = 'could not retrieve PT (no response from the CAS server)';
1492
        phpCAS::traceEnd(FALSE);
1493
        return FALSE;
1494
      }
1495

    
1496
      $bad_response = FALSE;
1497

    
1498
      if ( !$bad_response ) {
1499
        // read the response of the CAS server into a DOM object
1500
        if ( !($dom = @domxml_open_mem($cas_response))) {
1501
          phpCAS::trace('domxml_open_mem() failed');
1502
          // read failed
1503
          $bad_response = TRUE;
1504
        } 
1505
      }
1506

    
1507
      if ( !$bad_response ) {
1508
        // read the root node of the XML tree
1509
        if ( !($root = $dom->document_element()) ) {
1510
          phpCAS::trace('document_element() failed');
1511
          // read failed
1512
          $bad_response = TRUE;
1513
        } 
1514
      }
1515

    
1516
      if ( !$bad_response ) {
1517
        // insure that tag name is 'serviceResponse'
1518
        if ( $root->node_name() != 'serviceResponse' ) {
1519
          phpCAS::trace('node_name() failed');
1520
          // bad root node
1521
          $bad_response = TRUE;
1522
        } 
1523
      }
1524

    
1525
      if ( !$bad_response ) {
1526
        // look for a proxySuccess tag
1527
        if ( sizeof($arr = $root->get_elements_by_tagname("proxySuccess")) != 0) {
1528
          // authentication succeded, look for a proxyTicket tag
1529
          if ( sizeof($arr = $root->get_elements_by_tagname("proxyTicket")) != 0) {
1530
            $err_code = PHPCAS_SERVICE_OK;
1531
            $err_msg = '';
1532
        phpCAS::trace('original PT: '.trim($arr[0]->get_content()));
1533
        $pt = trim($arr[0]->get_content());
1534
            phpCAS::traceEnd($pt);
1535
            return $pt;
1536
          } else {
1537
            phpCAS::trace('<proxySuccess> was found, but not <proxyTicket>');
1538
          }
1539
        } 
1540
        // look for a proxyFailure tag
1541
        else if ( sizeof($arr = $root->get_elements_by_tagname("proxyFailure")) != 0) {
1542
          // authentication failed, extract the error
1543
          $err_code = PHPCAS_SERVICE_PT_FAILURE;
1544
          $err_msg = 'PT retrieving failed (code=`'
1545
            .$arr[0]->get_attribute('code')
1546
            .'\', message=`'
1547
            .trim($arr[0]->get_content())
1548
            .'\')';
1549
          phpCAS::traceEnd(FALSE);
1550
          return FALSE;
1551
        } else {
1552
          phpCAS::trace('neither <proxySuccess> nor <proxyFailure> found');
1553
        }
1554
      }
1555

    
1556
      // at this step, we are sure that the response of the CAS server was ill-formed
1557
      $err_code = PHPCAS_SERVICE_PT_BAD_SERVER_RESPONSE;
1558
      $err_msg = 'Invalid response from the CAS server (response=`'.$cas_response.'\')';
1559

    
1560
      phpCAS::traceEnd(FALSE);
1561
      return FALSE;
1562
    }
1563

    
1564
  // ########################################################################
1565
  // ACCESS TO EXTERNAL SERVICES
1566
  // ########################################################################
1567

    
1568
  /**
1569
   * This method is used to acces a remote URL.
1570
   *
1571
   * @param $url the URL to access.
1572
   * @param $cookies an array containing cookies strings such as 'name=val'
1573
   * @param $headers an array containing the HTTP header lines of the response
1574
   * (an empty array on failure).
1575
   * @param $body the body of the response, as a string (empty on failure).
1576
   * @param $err_msg an error message, filled on failure.
1577
   *
1578
   * @return TRUE on success, FALSE otherwise (in this later case, $err_msg
1579
   * contains an error message).
1580
   *
1581
   * @private
1582
   */
1583
  function readURL($url,$cookies,&$headers,&$body,&$err_msg)
1584
    {
1585
        $cas=new phpCAS();
1586
      $cas->traceBegin();
1587
      $headers = '';
1588
      $body = '';
1589
      $err_msg = '';
1590

    
1591
      $res = TRUE;
1592

    
1593
      // initialize the CURL session
1594
      $ch = curl_init($url);
1595
        
1596
          // verify the the server's certificate corresponds to its name
1597
          curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 1);
1598
          // but do not verify the certificate itself
1599
          curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
1600

    
1601
      // return the CURL output into a variable
1602
      curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
1603
      // include the HTTP header with the body
1604
      curl_setopt($ch, CURLOPT_HEADER, 1);
1605
      // add cookies headers
1606
      if ( is_array($cookies) ) {
1607
        curl_setopt($ch,CURLOPT_COOKIE,implode(';',$cookies));
1608
      }
1609
      // perform the query
1610
      $buf = curl_exec ($ch);
1611
      if ( $buf === FALSE ) {
1612
        $cas->trace('cur_exec() failed');
1613
        $err_msg = 'CURL error #'.curl_errno($ch).': '.curl_error($ch);
1614
        // close the CURL session
1615
        curl_close ($ch);
1616
        $res = FALSE;
1617
      } else {
1618
        // close the CURL session
1619
        curl_close ($ch);
1620
        
1621
        // find the end of the headers
1622
        // note: strpos($str,"\n\r\n\r") does not work (?)
1623
        $pos = FALSE;
1624
        for ($i=0; $i<strlen($buf); $i++) {
1625
          if ( $buf[$i] == chr(13) ) 
1626
            if ( $buf[$i+1] == chr(10) ) 
1627
              if ( $buf[$i+2] == chr(13) ) 
1628
                if ( $buf[$i+3] == chr(10) ) {
1629
                  // header found
1630
                  $pos = $i;
1631
                  break;
1632
                }
1633
        }
1634
        
1635
        if ( $pos === FALSE ) {
1636
          // end of header not found
1637
          $err_msg = 'no header found';
1638
          $cas->trace($err_msg);
1639
          $res = FALSE;
1640
        } else { 
1641
          // extract headers into an array
1642
          $headers = preg_split ("/[\n\r]+/",substr($buf,0,$pos));          
1643
          // extract body into a string
1644
          $body = substr($buf,$pos+4);
1645
        }
1646
      }
1647

    
1648
      $cas->traceEnd($res);
1649
      return $res;
1650
    }
1651

    
1652
  /**
1653
   * This method is used to access an HTTP[S] service.
1654
   * 
1655
   * @param $url the service to access.
1656
   * @param $err_code an error code Possible values are PHPCAS_SERVICE_OK (on
1657
   * success), PHPCAS_SERVICE_PT_NO_SERVER_RESPONSE, PHPCAS_SERVICE_PT_BAD_SERVER_RESPONSE,
1658
   * PHPCAS_SERVICE_PT_FAILURE, PHPCAS_SERVICE_NOT AVAILABLE.
1659
   * @param $output the output of the service (also used to give an error
1660
   * message on failure).
1661
   *
1662
   * @return TRUE on success, FALSE otherwise (in this later case, $err_code
1663
   * gives the reason why it failed and $output contains an error message).
1664
   *
1665
   * @public
1666
   */
1667
  function serviceWeb($url,&$err_code,&$output)
1668
    {
1669
      phpCAS::traceBegin();
1670
      // at first retrieve a PT
1671
      $pt = $this->retrievePT($url,$err_code,$output);
1672

    
1673
      $res = TRUE;
1674
      
1675
      // test if PT was retrieved correctly
1676
      if ( !$pt ) {
1677
        // note: $err_code and $err_msg are filled by CASClient::retrievePT()
1678
        phpCAS::trace('PT was not retrieved correctly');
1679
        $res = FALSE;
1680
      } else {
1681
        // add cookies if necessary
1682
        if ( is_array($_SESSION['phpCAS']['services'][$url]['cookies']) ) {
1683
          foreach ( $_SESSION['phpCAS']['services'][$url]['cookies'] as $name => $val ) { 
1684
            $cookies[] = $name.'='.$val;
1685
          }
1686
        }
1687
        
1688
        // build the URL including the PT
1689
        if ( strstr($url,'?') === FALSE ) {
1690
          $service_url = $url.'?ticket='.$pt;
1691
        } else {
1692
          $service_url = $url.'&ticket='.$pt;
1693
        }
1694
        
1695
        phpCAS::trace('reading URL`'.$service_url.'\'');
1696
        if ( !$this->readURL($service_url,$cookies,$headers,$output,$err_msg) ) {
1697
          phpCAS::trace('could not read URL`'.$service_url.'\'');
1698
          $err_code = PHPCAS_SERVICE_NOT_AVAILABLE;
1699
          // give an error message
1700
          $output = sprintf($this->getString(CAS_STR_SERVICE_UNAVAILABLE),
1701
                            $service_url,
1702
                            $err_msg);
1703
          $res = FALSE;
1704
        } else {
1705
          // URL has been fetched, extract the cookies
1706
          phpCAS::trace('URL`'.$service_url.'\' has been read, storing cookies:');
1707
          foreach ( $headers as $header ) {
1708
            // test if the header is a cookie
1709
            if ( preg_match('/^Set-Cookie:/',$header) ) {
1710
              // the header is a cookie, remove the beginning
1711
              $header_val = preg_replace('/^Set-Cookie: */','',$header);
1712
              // extract interesting information
1713
              $name_val = strtok($header_val,'; ');
1714
              // extract the name and the value of the cookie
1715
              $cookie_name = strtok($name_val,'=');
1716
              $cookie_val = strtok('=');
1717
              // store the cookie 
1718
              $_SESSION['phpCAS']['services'][$url]['cookies'][$cookie_name] = $cookie_val;
1719
              phpCAS::trace($cookie_name.' -> '.$cookie_val);
1720
            }
1721
          }
1722
        }
1723
      }
1724

    
1725
      phpCAS::traceEnd($res);
1726
      return $res;
1727
  }
1728

    
1729
  /**
1730
   * This method is used to access an IMAP/POP3/NNTP service.
1731
   * 
1732
   * @param $url a string giving the URL of the service, including the mailing box
1733
   * for IMAP URLs, as accepted by imap_open().
1734
   * @param $flags options given to imap_open().
1735
   * @param $err_code an error code Possible values are PHPCAS_SERVICE_OK (on
1736
   * success), PHPCAS_SERVICE_PT_NO_SERVER_RESPONSE, PHPCAS_SERVICE_PT_BAD_SERVER_RESPONSE,
1737
   * PHPCAS_SERVICE_PT_FAILURE, PHPCAS_SERVICE_NOT AVAILABLE.
1738
   * @param $err_msg an error message on failure
1739
   * @param $pt the Proxy Ticket (PT) retrieved from the CAS server to access the URL
1740
   * on success, FALSE on error).
1741
   *
1742
   * @return an IMAP stream on success, FALSE otherwise (in this later case, $err_code
1743
   * gives the reason why it failed and $err_msg contains an error message).
1744
   *
1745
   * @public
1746
   */
1747
  function serviceMail($url,$flags,&$err_code,&$err_msg,&$pt)
1748
    {
1749
      phpCAS::traceBegin();
1750
      // at first retrieve a PT
1751
      $pt = $this->retrievePT($target_service,$err_code,$output);
1752

    
1753
      $stream = FALSE;
1754
      
1755
      // test if PT was retrieved correctly
1756
      if ( !$pt ) {
1757
        // note: $err_code and $err_msg are filled by CASClient::retrievePT()
1758
        phpCAS::trace('PT was not retrieved correctly');
1759
      } else {
1760
        phpCAS::trace('opening IMAP URL `'.$url.'\'...');
1761
        $stream = @imap_open($url,$this->getUser(),$pt,$flags);
1762
        if ( !$stream ) {
1763
          phpCAS::trace('could not open URL');
1764
          $err_code = PHPCAS_SERVICE_NOT_AVAILABLE;
1765
          // give an error message
1766
          $err_msg = sprintf($this->getString(CAS_STR_SERVICE_UNAVAILABLE),
1767
                             $service_url,
1768
                             var_export(imap_errors(),TRUE));
1769
          $pt = FALSE;
1770
          $stream = FALSE;
1771
        } else {
1772
          phpCAS::trace('ok');
1773
        }
1774
      }
1775

    
1776
      phpCAS::traceEnd($stream);
1777
      return $stream;
1778
  }
1779

    
1780
  /** @} */
1781

    
1782
  // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
1783
  // XX                                                                    XX
1784
  // XX                  PROXIED CLIENT FEATURES (CAS 2.0)                 XX
1785
  // XX                                                                    XX
1786
  // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
1787

    
1788
  // ########################################################################
1789
  //  PT
1790
  // ########################################################################
1791
  /**
1792
   * @addtogroup internalProxied
1793
   * @{
1794
   */  
1795
  
1796
  /**
1797
   * the Proxy Ticket provided in the URL of the request if present
1798
   * (empty otherwise). Written by CASClient::CASClient(), read by 
1799
   * CASClient::getPT() and CASClient::hasPGT().
1800
   *
1801
   * @hideinitializer
1802
   * @private
1803
   */
1804
  var $_pt = '';
1805
  
1806
  /**
1807
   * This method returns the Proxy Ticket provided in the URL of the request.
1808
   * @return The proxy ticket.
1809
   * @private
1810
   */
1811
  function getPT()
1812
    {
1813
      return 'ST'.substr($this->_pt, 2);
1814
    }
1815

    
1816
  /**
1817
   * This method stores the Proxy Ticket.
1818
   * @param $pt The Proxy Ticket.
1819
   * @private
1820
   */
1821
  function setPT($pt)
1822
    { $this->_pt = $pt; }
1823

    
1824
  /**
1825
   * This method tells if a Proxy Ticket was stored.
1826
   * @return TRUE if a Proxy Ticket has been stored.
1827
   * @private
1828
   */
1829
  function hasPT()
1830
    { return !empty($this->_pt); }
1831

    
1832
  /** @} */
1833
  // ########################################################################
1834
  //  PT VALIDATION
1835
  // ########################################################################
1836
  /**
1837
   * @addtogroup internalProxied
1838
   * @{
1839
   */  
1840

    
1841
  /**
1842
   * This method is used to validate a PT; halt on failure
1843
   * 
1844
   * @return bool TRUE when successfull, halt otherwise by calling CASClient::authError().
1845
   *
1846
   * @private
1847
   */
1848
  function validatePT(&$validate_url,&$text_response,&$tree_response)
1849
    {
1850
        $cas=new phpCAS();
1851
      $cas->traceBegin();
1852
      // build the URL to validate the ticket
1853
      $validate_url = $this->getServerProxyValidateURL().'&ticket='.$this->getPT();
1854

    
1855
      if ( $this->isProxy() ) {
1856
      // pass the callback url for CAS proxies
1857
        $validate_url .= '&pgtUrl='.$this->getCallbackURL();
1858
      }
1859

    
1860
      // open and read the URL
1861
      if ( !$this->readURL($validate_url,''/*cookies*/,$headers,$text_response,$err_msg) ) {
1862
        $cas->trace('could not open URL \''.$validate_url.'\' to validate ('.$err_msg.')');
1863
        $this->authError('PT not validated',
1864
                         $validate_url,
1865
                         TRUE/*$no_response*/);
1866
      }
1867

    
1868
      // read the response of the CAS server into a DOM object
1869
      if ( !($dom = domxml_open_mem($text_response))) {
1870
        // read failed
1871
        $this->authError('PT not validated',
1872
                     $validate_url,
1873
                     FALSE/*$no_response*/,
1874
                     TRUE/*$bad_response*/,
1875
                     $text_response);
1876
      }
1877
      // read the root node of the XML tree
1878
      if ( !($tree_response = $dom->document_element()) ) {
1879
        // read failed
1880
        $this->authError('PT not validated',
1881
                     $validate_url,
1882
                     FALSE/*$no_response*/,
1883
                     TRUE/*$bad_response*/,
1884
                     $text_response);
1885
      }
1886
      // insure that tag name is 'serviceResponse'
1887
      if ( $tree_response->node_name() != 'serviceResponse' ) {
1888
        // bad root node
1889
        $this->authError('PT not validated',
1890
                     $validate_url,
1891
                     FALSE/*$no_response*/,
1892
                     TRUE/*$bad_response*/,
1893
                     $text_response);
1894
      }
1895
      if ( sizeof($arr = $tree_response->get_elements_by_tagname("authenticationSuccess")) != 0) {
1896
        // authentication succeded, extract the user name
1897
        if ( sizeof($arr = $tree_response->get_elements_by_tagname("user")) == 0) {
1898
          // no user specified => error
1899
          $this->authError('PT not validated',
1900
                       $validate_url,
1901
                       FALSE/*$no_response*/,
1902
                       TRUE/*$bad_response*/,
1903
                       $text_response);
1904
        }
1905
        $this->setUser(trim($arr[0]->get_content()));
1906
        
1907
      } else if ( sizeof($arr = $tree_response->get_elements_by_tagname("authenticationFailure")) != 0) {
1908
        // authentication succeded, extract the error code and message
1909
        $this->authError('PT not validated',
1910
                     $validate_url,
1911
                     FALSE/*$no_response*/,
1912
                     FALSE/*$bad_response*/,
1913
                     $text_response,
1914
                     $arr[0]->get_attribute('code')/*$err_code*/,
1915
                     trim($arr[0]->get_content())/*$err_msg*/);
1916
      } else {
1917
        $this->authError('PT not validated',
1918
                     $validate_url,        
1919
                     FALSE/*$no_response*/,
1920
                     TRUE/*$bad_response*/,
1921
                     $text_response);
1922
      }
1923
      
1924
      // at this step, PT has been validated and $this->_user has been set,
1925

    
1926
      $cas->traceEnd(TRUE);
1927
      return TRUE;
1928
    }
1929

    
1930
  /** @} */
1931

    
1932
  // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
1933
  // XX                                                                    XX
1934
  // XX                               MISC                                 XX
1935
  // XX                                                                    XX
1936
  // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
1937

    
1938
  /**
1939
   * @addtogroup internalMisc
1940
   * @{
1941
   */  
1942
  
1943
  // ########################################################################
1944
  //  URL
1945
  // ########################################################################
1946
  /**
1947
   * the URL of the current request (without any ticket CGI parameter). Written 
1948
   * and read by CASClient::getURL().
1949
   *
1950
   * @hideinitializer
1951
   * @private
1952
   */
1953
  var $_url = '';
1954

    
1955
  /**
1956
   * This method returns the URL of the current request (without any ticket
1957
   * CGI parameter).
1958
   *
1959
   * @return The URL
1960
   *
1961
   * @private
1962
   */
1963
  function getURL()
1964
    {
1965
        $cas=new phpCAS();
1966
     $cas->traceBegin();
1967
      // the URL is built when needed only
1968
      if ( empty($this->_url) ) {
1969
            $final_uri = '';
1970
            // remove the ticket if present in the URL
1971
            $final_uri = ($this->isHttps()) ? 'https' : 'http';
1972
            $final_uri .= '://';
1973
            /* replaced by Julien Marchal - v0.4.6
1974
             * $this->_url .= $_SERVER['SERVER_NAME'];
1975
             */
1976
        if(empty($_SERVER['HTTP_X_FORWARDED_SERVER'])){
1977
          /* replaced by teedog - v0.4.12
1978
           * $this->_url .= $_SERVER['SERVER_NAME'];
1979
           */
1980
          if (empty($_SERVER['SERVER_NAME'])) {
1981
            $server_name = $_SERVER['HTTP_HOST'];
1982
          } else {
1983
            $server_name = $_SERVER['SERVER_NAME'];
1984
          }
1985
        } else {
1986
          $server_name = $_SERVER['HTTP_X_FORWARDED_SERVER'];
1987
        }
1988
      $final_uri .= $server_name;
1989
      if (!strpos($server_name, ':')) {
1990
              if ( ($this->isHttps() && $_SERVER['SERVER_PORT']!=443)
1991
               || (!$this->isHttps() && $_SERVER['SERVER_PORT']!=80) ) {
1992
              $final_uri .= ':';
1993
              $final_uri .= $_SERVER['SERVER_PORT'];
1994
            }
1995
      }
1996

    
1997
          $final_uri .= strtok($_SERVER['REQUEST_URI'],"?");
1998
          $cgi_params = '?'.strtok("?");
1999
          // remove the ticket if present in the CGI parameters
2000
          $cgi_params = preg_replace('/&ticket=[^&]*/','',$cgi_params);
2001
          $cgi_params = preg_replace('/\?ticket=[^&;]*/','?',$cgi_params);
2002
          $cgi_params = preg_replace('/\?%26/','?',$cgi_params);
2003
          $cgi_params = preg_replace('/\?&/','?',$cgi_params);
2004
          $cgi_params = preg_replace('/\?$/','',$cgi_params);
2005
          $final_uri .= $cgi_params;
2006
          $this->setURL($final_uri);
2007
    }
2008
    $cas->traceEnd($this->_url);
2009
    return $this->_url;
2010
  }
2011

    
2012
  /**
2013
   * This method sets the URL of the current request 
2014
   *
2015
   * @param $url url to set for service
2016
   *
2017
   * @private
2018
   */
2019
  function setURL($url)
2020
    {
2021
      $this->_url = $url;
2022
    }
2023
  
2024
  // ########################################################################
2025
  //  AUTHENTICATION ERROR HANDLING
2026
  // ########################################################################
2027
  /**
2028
   * This method is used to print the HTML output when the user was not authenticated.
2029
   *
2030
   * @param $failure the failure that occured
2031
   * @param $cas_url the URL the CAS server was asked for
2032
   * @param $no_response the response from the CAS server (other 
2033
   * parameters are ignored if TRUE)
2034
   * @param $bad_response bad response from the CAS server ($err_code
2035
   * and $err_msg ignored if TRUE)
2036
   * @param $cas_response the response of the CAS server
2037
   * @param $err_code the error code given by the CAS server
2038
   * @param $err_msg the error message given by the CAS server
2039
   *
2040
   * @private
2041
   */
2042
  function authError($failure,$cas_url,$no_response,$bad_response='',$cas_response='',$err_code='',$err_msg='')
2043
    {
2044
      phpCAS::traceBegin();
2045

    
2046
      $this->printHTMLHeader($this->getString(CAS_STR_AUTHENTICATION_FAILED));
2047
      printf($this->getString(CAS_STR_YOU_WERE_NOT_AUTHENTICATED),$this->getURL(),$_SERVER['SERVER_ADMIN']);
2048
      phpCAS::trace('CAS URL: '.$cas_url);
2049
      phpCAS::trace('Authentication failure: '.$failure);
2050
      if ( $no_response ) {
2051
        phpCAS::trace('Reason: no response from the CAS server');
2052
      } else {
2053
        if ( $bad_response ) {
2054
            phpCAS::trace('Reason: bad response from the CAS server');
2055
        } else {
2056
          switch ($this->getServerVersion()) {
2057
          case CAS_VERSION_1_0:
2058
            phpCAS::trace('Reason: CAS error');
2059
            break;
2060
          case CAS_VERSION_2_0:
2061
            if ( empty($err_code) )
2062
              phpCAS::trace('Reason: no CAS error');
2063
            else
2064
              phpCAS::trace('Reason: ['.$err_code.'] CAS error: '.$err_msg);
2065
            break;
2066
          }
2067
        }
2068
        phpCAS::trace('CAS response: '.$cas_response);
2069
      }
2070
      $this->printHTMLFooter();
2071
      phpCAS::traceExit();
2072
      exit();
2073
    }
2074

    
2075
  /** @} */
2076
}
2077

    
2078
?>
Redmine Appliance - Powered by TurnKey Linux