~ubuntu-branches/ubuntu/breezy/moodle/breezy

« back to all changes in this revision

Viewing changes to lib/cas/client.php

  • Committer: Bazaar Package Importer
  • Author(s): Andrew Mitchell
  • Date: 2005-10-13 02:00:59 UTC
  • mfrom: (1.1.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20051013020059-y2qcyo41t7nqppcg
Tags: 1.5.2-1ubuntu1
* Resync with debian (security update)
* changed dependencys to php5
* changed apache dependency to apache2 
* References
  CAN-2005-2247

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
   * @return a URL.
 
318
   * @private
 
319
   */
 
320
  function getServerLoginURL()
 
321
    { 
 
322
      // the URL is build only when needed
 
323
      if ( empty($this->_server['login_url']) ) {
 
324
        $this->_server['login_url'] = $this->getServerBaseURL().'login?service='.preg_replace('/&/','%26',$this->getURL());
 
325
      }
 
326
      return $this->_server['login_url']; 
 
327
    }
 
328
 
 
329
  /**
 
330
   * This method is used to retrieve the service validating URL of the CAS server.
 
331
   * @return a URL.
 
332
   * @private
 
333
   */
 
334
  function getServerServiceValidateURL()
 
335
    { 
 
336
      // the URL is build only when needed
 
337
      if ( empty($this->_server['service_validate_url']) ) {
 
338
        switch ($this->getServerVersion()) {
 
339
        case CAS_VERSION_1_0:
 
340
          $this->_server['service_validate_url'] = $this->getServerBaseURL().'validate';
 
341
          break;
 
342
        case CAS_VERSION_2_0:
 
343
          $this->_server['service_validate_url'] = $this->getServerBaseURL().'serviceValidate';
 
344
          break;
 
345
        }
 
346
      }
 
347
      return $this->_server['service_validate_url'].'?service='.preg_replace('/&/','%26',$this->getURL()); 
 
348
    }
 
349
 
 
350
  /**
 
351
   * This method is used to retrieve the proxy validating URL of the CAS server.
 
352
   * @return a URL.
 
353
   * @private
 
354
   */
 
355
  function getServerProxyValidateURL()
 
356
    { 
 
357
      // the URL is build only when needed
 
358
      if ( empty($this->_server['proxy_validate_url']) ) {
 
359
        switch ($this->getServerVersion()) {
 
360
        case CAS_VERSION_1_0:
 
361
          $this->_server['proxy_validate_url'] = '';
 
362
          break;
 
363
        case CAS_VERSION_2_0:
 
364
          $this->_server['proxy_validate_url'] = $this->getServerBaseURL().'proxyValidate';
 
365
          break;
 
366
        }
 
367
      }
 
368
      return $this->_server['proxy_validate_url'].'?service='.preg_replace('/&/','%26',$this->getURL()); 
 
369
    }
 
370
 
 
371
  /**
 
372
   * This method is used to retrieve the proxy URL of the CAS server.
 
373
   * @return a URL.
 
374
   * @private
 
375
   */
 
376
  function getServerProxyURL()
 
377
    { 
 
378
      // the URL is build only when needed
 
379
      if ( empty($this->_server['proxy_url']) ) {
 
380
        switch ($this->getServerVersion()) {
 
381
        case CAS_VERSION_1_0:
 
382
          $this->_server['proxy_url'] = '';
 
383
          break;
 
384
        case CAS_VERSION_2_0:
 
385
          $this->_server['proxy_url'] = $this->getServerBaseURL().'proxy';
 
386
          break;
 
387
        }
 
388
      }
 
389
      return $this->_server['proxy_url']; 
 
390
    }
 
391
 
 
392
  /**
 
393
   * This method is used to retrieve the logout URL of the CAS server.
 
394
   * @return a URL.
 
395
   * @private
 
396
   */
 
397
  function getServerLogoutURL()
 
398
    { 
 
399
      // the URL is build only when needed
 
400
      if ( empty($this->_server['logout_url']) ) {
 
401
        $this->_server['logout_url'] = $this->getServerBaseURL().'logout';
 
402
      }
 
403
      return $this->_server['logout_url']; 
 
404
    }
 
405
 
 
406
  // ########################################################################
 
407
  //  CONSTRUCTOR
 
408
  // ########################################################################
 
409
  /**
 
410
   * CASClient constructor.
 
411
   *
 
412
   * @param $server_version the version of the CAS server
 
413
   * @param $proxy TRUE if the CAS client is a CAS proxy, FALSE otherwise
 
414
   * @param $server_hostname the hostname of the CAS server
 
415
   * @param $server_port the port the CAS server is running on
 
416
   * @param $server_uri the URI the CAS server is responding on
 
417
   *
 
418
   * @return a newly created CASClient object
 
419
   *
 
420
   * @public
 
421
   */
 
422
  function CASClient($server_version,
 
423
                     $proxy,
 
424
                     $server_hostname,
 
425
                     $server_port,
 
426
                     $server_uri)
 
427
    {
 
428
      phpCAS::traceBegin();
 
429
 
 
430
      // activate session mechanism
 
431
      session_start();
 
432
 
 
433
      $this->_proxy = $proxy;
 
434
 
 
435
      // check version
 
436
      switch ($server_version) {
 
437
      case CAS_VERSION_1_0:
 
438
        if ( $this->isProxy() )
 
439
          phpCAS::error('CAS proxies are not supported in CAS '
 
440
                        .$server_version);
 
441
        break;
 
442
      case CAS_VERSION_2_0:
 
443
        break;
 
444
      default:
 
445
        phpCAS::error('this version of CAS (`'
 
446
                      .$server_version
 
447
                      .'\') is not supported by phpCAS '
 
448
                        .phpCAS::getVersion());
 
449
      }
 
450
      $this->_server['version'] = $server_version;
 
451
 
 
452
      // check hostname
 
453
      if ( empty($server_hostname) 
 
454
           || !preg_match('/[\.\d\-abcdefghijklmnopqrstuvwxyz]*/',$server_hostname) ) {
 
455
        phpCAS::error('bad CAS server hostname (`'.$server_hostname.'\')');
 
456
      }
 
457
      $this->_server['hostname'] = $server_hostname;
 
458
 
 
459
      // check port
 
460
      if ( $server_port == 0 
 
461
           || !is_int($server_port) ) {
 
462
        phpCAS::error('bad CAS server port (`'.$server_hostname.'\')');
 
463
      }
 
464
      $this->_server['port'] = $server_port;
 
465
 
 
466
      // check URI
 
467
      if ( !preg_match('/[\.\d\-_abcdefghijklmnopqrstuvwxyz\/]*/',$server_uri) ) {
 
468
        phpCAS::error('bad CAS server URI (`'.$server_uri.'\')');
 
469
      }
 
470
      // add leading and trailing `/' and remove doubles      
 
471
      $server_uri = preg_replace('/\/\//','/','/'.$server_uri.'/');
 
472
      $this->_server['uri'] = $server_uri;
 
473
 
 
474
      // set to callback mode if PgtIou and PgtId CGI GET parameters are provided 
 
475
      if ( $this->isProxy() ) {
 
476
        $this->setCallbackMode(!empty($_GET['pgtIou'])&&!empty($_GET['pgtId']));
 
477
      }
 
478
 
 
479
      if ( $this->isCallbackMode() ) {
 
480
        // callback mode: check that phpCAS is secured
 
481
        if ( $_SERVER['HTTPS'] != 'on' ) {
 
482
          phpCAS::error('CAS proxies must be secured to use phpCAS; PGT\'s will not be received from the CAS server');
 
483
        }
 
484
      } else {
 
485
        // normal mode: get ticket and remove it from CGI parameters for developpers
 
486
        $ticket = $_GET['ticket'];
 
487
        // at first check for a Service Ticket
 
488
        if( preg_match('/^ST-/',$ticket)) {
 
489
          phpCAS::trace('ST \''.$ticket.'\' found');
 
490
          // ST present
 
491
          $this->setST($ticket);
 
492
        } 
 
493
        // in a second time check for a Proxy Ticket (CAS >= 2.0)
 
494
        else if( ($this->getServerVersion()!=CAS_VERSION_1_0) && preg_match('/^PT-/',$ticket) ) {
 
495
          phpCAS::trace('PT \''.$ticket.'\' found');
 
496
          $this->setPT($ticket);
 
497
        } 
 
498
        // ill-formed ticket, halt
 
499
        else if ( !empty($ticket) ) {
 
500
          phpCAS::error('ill-formed ticket found in the URL (ticket=`'.htmlentities($ticket).'\')');
 
501
        }
 
502
        // ticket has been taken into account, unset it to hide it to applications
 
503
        unset($_GET['ticket']);
 
504
      }
 
505
      phpCAS::traceEnd();
 
506
    }
 
507
 
 
508
  /** @} */
 
509
 
 
510
  // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 
511
  // XX                                                                    XX
 
512
  // XX                           AUTHENTICATION                           XX
 
513
  // XX                                                                    XX
 
514
  // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 
515
 
 
516
  /**
 
517
   * @addtogroup internalAuthentication
 
518
   * @{
 
519
   */  
 
520
  
 
521
  /**
 
522
   * The Authenticated user. Written by CASClient::setUser(), read by CASClient::getUser().
 
523
   * @attention client applications should use phpCAS::getUser().
 
524
   *
 
525
   * @hideinitializer
 
526
   * @private
 
527
   */
 
528
  var $_user = '';
 
529
  
 
530
  /**
 
531
   * This method sets the CAS user's login name.
 
532
   *
 
533
   * @param $user the login name of the authenticated user.
 
534
   *
 
535
   * @private
 
536
   */
 
537
  function setUser($user)
 
538
    {
 
539
      $this->_user = $user;
 
540
    }
 
541
 
 
542
  /**
 
543
   * This method returns the CAS user's login name.
 
544
   * @warning should be called only after CASClient::authenticateIfNeeded() or 
 
545
   * CASClient::isAuthenticated(), otherwise halt with an error.
 
546
   *
 
547
   * @return the login name of the authenticated user
 
548
   */
 
549
  function getUser()
 
550
    {
 
551
      if ( empty($this->_user) ) {
 
552
        phpCAS::error('this method should be used only after '.__CLASS__.'::authenticateIfNeeded() or '.__CLASS__.'::isAuthenticated()');
 
553
      }
 
554
      return $this->_user;
 
555
    }
 
556
 
 
557
  /**
 
558
   * This method is called to check if the user is authenticated. When not 
 
559
   * authenticated, halt by redirecting to the CAS server; otherwise return TRUE.
 
560
   *
 
561
   * @return TRUE when the user is authenticated; otherwise halt.
 
562
   *
 
563
   * @public
 
564
   */
 
565
  function authenticateIfNeeded()
 
566
    {
 
567
      phpCAS::traceBegin();
 
568
      $res = TRUE;
 
569
 
 
570
      if ( $this->isAuthenticated() ) {
 
571
        // the user is authenticated, nothing to be done.
 
572
        phpCAS::trace('no need to authenticate');
 
573
        $res = TRUE;
 
574
      } else {
 
575
        // the user is not authenticated, redirect to the CAS server
 
576
        $this->forceAuthentication();   
 
577
        // never reached
 
578
        $res = FALSE;
 
579
      }
 
580
      phpCAS::traceEnd($res);
 
581
      return $res;
 
582
    }
 
583
  
 
584
  /**
 
585
   * This method is called to check if the user is authenticated (previously or by
 
586
   * tickets given in the URL
 
587
   *
 
588
   * @return TRUE when the user is authenticated; otherwise halt.
 
589
   *
 
590
   * @public
 
591
   */
 
592
  function isAuthenticated()
 
593
    {
 
594
      phpCAS::traceBegin();
 
595
      $res = FALSE;
 
596
      $validate_url = '';
 
597
 
 
598
      if ( $this->wasPreviouslyAuthenticated() ) {
 
599
        // the user has already (previously during the session) been 
 
600
        // authenticated, nothing to be done.
 
601
        phpCAS::trace('user was already authenticated, no need to look for tickets');
 
602
        $res = TRUE;
 
603
      } elseif ( $this->hasST() ) {
 
604
        // if a Service Ticket was given, validate it
 
605
        phpCAS::trace('ST `'.$this->getST().'\' is present');
 
606
        $this->validateST($validate_url,$text_response,$tree_response); // if it fails, it halts
 
607
        phpCAS::trace('ST `'.$this->getST().'\' was validated');
 
608
        if ( $this->isProxy() ) {
 
609
          $this->validatePGT($validate_url,$text_response,$tree_response); // idem
 
610
          phpCAS::trace('PGT `'.$this->getPGT().'\' was validated');
 
611
          $_SESSION['phpCAS']['pgt'] = $this->getPGT();
 
612
        }
 
613
        $_SESSION['phpCAS']['user'] = $this->getUser();
 
614
        $res = TRUE;
 
615
      } elseif ( $this->hasPT() ) {
 
616
        // if a Proxy Ticket was given, validate it
 
617
        phpCAS::trace('PT `'.$this->getPT().'\' is present');
 
618
        $this->validatePT($validate_url,$text_response,$tree_response); // note: if it fails, it halts
 
619
        phpCAS::trace('PT `'.$this->getPT().'\' was validated');
 
620
        if ( $this->isProxy() ) {
 
621
          $this->validatePGT($validate_url,$text_response,$tree_response); // idem
 
622
          phpCAS::trace('PGT `'.$this->getPGT().'\' was validated');
 
623
          $_SESSION['phpCAS']['pgt'] = $this->getPGT();
 
624
        }
 
625
        $_SESSION['phpCAS']['user'] = $this->getUser();
 
626
        $res = TRUE;
 
627
      } else {
 
628
        // no ticket given, not authenticated
 
629
        phpCAS::trace('no ticket found');
 
630
      }
 
631
 
 
632
      phpCAS::traceEnd($res);
 
633
      return $res;
 
634
    }
 
635
  
 
636
  /**
 
637
   * This method tells if the user has already been (previously) authenticated
 
638
   * by looking into the session variables.
 
639
   *
 
640
   * @note This function switches to callback mode when needed.
 
641
   *
 
642
   * @return TRUE when the user has already been authenticated; FALSE otherwise.
 
643
   *
 
644
   * @private
 
645
   */
 
646
  function wasPreviouslyAuthenticated()
 
647
    {
 
648
      phpCAS::traceBegin();
 
649
 
 
650
      if ( $this->isCallbackMode() ) {
 
651
        $this->callback();
 
652
      }
 
653
 
 
654
      $auth = FALSE;
 
655
 
 
656
      if ( $this->isProxy() ) {
 
657
        // CAS proxy: username and PGT must be present
 
658
        if ( !empty($_SESSION['phpCAS']['user']) && !empty($_SESSION['phpCAS']['pgt']) ) {
 
659
          // authentication already done
 
660
          $this->setUser($_SESSION['phpCAS']['user']);
 
661
          $this->setPGT($_SESSION['phpCAS']['pgt']);
 
662
          phpCAS::trace('user = `'.$_SESSION['phpCAS']['user'].'\', PGT = `'.$_SESSION['phpCAS']['pgt'].'\''); 
 
663
          $auth = TRUE;
 
664
        } elseif ( !empty($_SESSION['phpCAS']['user']) && empty($_SESSION['phpCAS']['pgt']) ) {
 
665
          // these two variables should be empty or not empty at the same time
 
666
          phpCAS::trace('username found (`'.$_SESSION['phpCAS']['user'].'\') but PGT is empty');
 
667
          // unset all tickets to enforce authentication
 
668
          unset($_SESSION['phpCAS']);
 
669
          $this->setST('');
 
670
          $this->setPT('');
 
671
        } elseif ( empty($_SESSION['phpCAS']['user']) && !empty($_SESSION['phpCAS']['pgt']) ) {
 
672
          // these two variables should be empty or not empty at the same time
 
673
          phpCAS::trace('PGT found (`'.$_SESSION['phpCAS']['pgt'].'\') but username is empty'); 
 
674
          // unset all tickets to enforce authentication
 
675
          unset($_SESSION['phpCAS']);
 
676
          $this->setST('');
 
677
          $this->setPT('');
 
678
        } else {
 
679
          phpCAS::trace('neither user not PGT found'); 
 
680
        }
 
681
      } else {
 
682
        // `simple' CAS client (not a proxy): username must be present
 
683
        if ( !empty($_SESSION['phpCAS']['user']) ) {
 
684
          // authentication already done
 
685
          $this->setUser($_SESSION['phpCAS']['user']);
 
686
          phpCAS::trace('user = `'.$_SESSION['phpCAS']['user'].'\''); 
 
687
          $auth = TRUE;
 
688
        } else {
 
689
          phpCAS::trace('no user found');
 
690
        }
 
691
      }
 
692
      
 
693
      phpCAS::traceEnd($auth);
 
694
      return $auth;
 
695
    }
 
696
  
 
697
  /**
 
698
   * This method is used to force authentication by redirecting to the CAS server.
 
699
   * It is used by CASClient::authenticateIfNeeded() when the user is not authenticated,
 
700
   * and can also be called by phpCAS::forceAuthentication().
 
701
   *
 
702
   * @public
 
703
   */
 
704
  function forceAuthentication()
 
705
    {
 
706
      phpCAS::traceBegin();
 
707
      $cas_url = $this->getServerLoginURL();
 
708
      header('Location: '.$cas_url);
 
709
      $this->printHTMLHeader($this->getString(CAS_STR_AUTHENTICATION_WANTED));
 
710
      printf('<p>'.$this->getString(CAS_STR_SHOULD_HAVE_BEEN_REDIRECTED).'</p>',$cas_url);
 
711
      $this->printHTMLFooter();
 
712
      phpCAS::traceExit();
 
713
      exit();
 
714
    }
 
715
  
 
716
  /**
 
717
   * This method is used to logout from CAS.
 
718
   * @param $url a URL that will be transmitted to the CAS server (to come back to when logged out)
 
719
   * @public
 
720
   */
 
721
  function logout($url = "")
 
722
    {
 
723
      phpCAS::traceBegin();
 
724
      $cas_url = $this->getServerLogoutURL();
 
725
      // v0.4.14 sebastien.gougeon at univ-rennes1.fr
 
726
      // header('Location: '.$cas_url);
 
727
      if ( $url != "" ) {
 
728
        $url = '?service=' . $url;
 
729
      }
 
730
      header('Location: '.$cas_url . $url);
 
731
      session_unset();
 
732
      session_destroy();
 
733
      $this->printHTMLHeader($this->getString(CAS_STR_LOGOUT));
 
734
      printf('<p>'.$this->getString(CAS_STR_SHOULD_HAVE_BEEN_REDIRECTED).'</p>',$cas_url);
 
735
      $this->printHTMLFooter();
 
736
      phpCAS::traceExit();
 
737
      exit();
 
738
    }
 
739
  
 
740
  /** @} */
 
741
 
 
742
  // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 
743
  // XX                                                                    XX
 
744
  // XX                  BASIC CLIENT FEATURES (CAS 1.0)                   XX
 
745
  // XX                                                                    XX
 
746
  // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 
747
 
 
748
  // ########################################################################
 
749
  //  ST
 
750
  // ########################################################################
 
751
  /**
 
752
   * @addtogroup internalBasic
 
753
   * @{
 
754
   */  
 
755
  
 
756
  /**
 
757
   * the Service Ticket provided in the URL of the request if present
 
758
   * (empty otherwise). Written by CASClient::CASClient(), read by 
 
759
   * CASClient::getST() and CASClient::hasPGT().
 
760
   *
 
761
   * @hideinitializer
 
762
   * @private
 
763
   */
 
764
  var $_st = '';
 
765
  
 
766
  /**
 
767
   * This method returns the Service Ticket provided in the URL of the request.
 
768
   * @return The service ticket.
 
769
   * @private
 
770
   */
 
771
  function getST()
 
772
    { return $this->_st; }
 
773
 
 
774
  /**
 
775
   * This method stores the Service Ticket.
 
776
   * @param $st The Service Ticket.
 
777
   * @private
 
778
   */
 
779
  function setST($st)
 
780
    { $this->_st = $st; }
 
781
 
 
782
  /**
 
783
   * This method tells if a Service Ticket was stored.
 
784
   * @return TRUE if a Service Ticket has been stored.
 
785
   * @private
 
786
   */
 
787
  function hasST()
 
788
    { return !empty($this->_st); }
 
789
 
 
790
  /** @} */
 
791
 
 
792
  // ########################################################################
 
793
  //  ST VALIDATION
 
794
  // ########################################################################
 
795
  /**
 
796
   * @addtogroup internalBasic
 
797
   * @{
 
798
   */  
 
799
 
 
800
  /**
 
801
   * This method is used to validate a ST; halt on failure, and sets $validate_url,
 
802
   * $text_reponse and $tree_response on success. These parameters are used later
 
803
   * by CASClient::validatePGT() for CAS proxies.
 
804
   * 
 
805
   * @param $validate_url the URL of the request to the CAS server.
 
806
   * @param $text_response the response of the CAS server, as is (XML text).
 
807
   * @param $tree_response the response of the CAS server, as a DOM XML tree.
 
808
   *
 
809
   * @return bool TRUE when successfull, halt otherwise by calling CASClient::authError().
 
810
   *
 
811
   * @private
 
812
   */
 
813
  function validateST($validate_url,&$text_response,&$tree_response)
 
814
    {
 
815
      phpCAS::traceBegin();
 
816
      // build the URL to validate the ticket
 
817
      $validate_url = $this->getServerServiceValidateURL().'&ticket='.$this->getST();
 
818
      if ( $this->isProxy() ) {
 
819
        // pass the callback url for CAS proxies
 
820
        $validate_url .= '&pgtUrl='.$this->getCallbackURL();
 
821
      }
 
822
 
 
823
      // open and read the URL
 
824
      if ( !$this->readURL($validate_url,''/*cookies*/,$headers,$text_response,$err_msg) ) {
 
825
        phpCAS::trace('could not open URL \''.$validate_url.'\' to validate ('.$err_msg.')');
 
826
        $this->authError('ST not validated',
 
827
                         $validate_url,
 
828
                         TRUE/*$no_response*/);
 
829
      }
 
830
 
 
831
      // analyze the result depending on the version
 
832
      switch ($this->getServerVersion()) {
 
833
      case CAS_VERSION_1_0:
 
834
        if (preg_match('/^no\n/',$text_response)) {
 
835
          phpCAS::trace('ST has not been validated');
 
836
          $this->authError('ST not validated',
 
837
                       $validate_url,
 
838
                       FALSE/*$no_response*/,
 
839
                       FALSE/*$bad_response*/,
 
840
                       $text_response);
 
841
        }
 
842
        if (!preg_match('/^yes\n/',$text_response)) {
 
843
          phpCAS::trace('ill-formed response');
 
844
          $this->authError('ST not validated',
 
845
                       $validate_url,
 
846
                       FALSE/*$no_response*/,
 
847
                       TRUE/*$bad_response*/,
 
848
                       $text_response);
 
849
        }
 
850
        // ST has been validated, extract the user name
 
851
        $arr = preg_split('/\n/',$text_response);
 
852
        $this->setUser(trim($arr[1]));
 
853
        break;
 
854
      case CAS_VERSION_2_0:
 
855
        // read the response of the CAS server into a DOM object
 
856
        if ( !($dom = domxml_open_mem($text_response))) {
 
857
          phpCAS::trace('domxml_open_mem() failed');
 
858
          $this->authError('ST not validated',
 
859
                       $validate_url,
 
860
                       FALSE/*$no_response*/,
 
861
                       TRUE/*$bad_response*/,
 
862
                       $text_response);
 
863
        }
 
864
        // read the root node of the XML tree
 
865
        if ( !($tree_response = $dom->document_element()) ) {
 
866
          phpCAS::trace('document_element() failed');
 
867
          $this->authError('ST not validated',
 
868
                       $validate_url,
 
869
                       FALSE/*$no_response*/,
 
870
                       TRUE/*$bad_response*/,
 
871
                       $text_response);
 
872
        }
 
873
        // insure that tag name is 'serviceResponse'
 
874
        if ( $tree_response->node_name() != 'serviceResponse' ) {
 
875
          phpCAS::trace('bad XML root node (should be `serviceResponse\' instead of `'.$tree_response->node_name().'\'');
 
876
          $this->authError('ST not validated',
 
877
                       $validate_url,
 
878
                       FALSE/*$no_response*/,
 
879
                       TRUE/*$bad_response*/,
 
880
                       $text_response);
 
881
        }
 
882
        if ( sizeof($success_elements = $tree_response->get_elements_by_tagname("authenticationSuccess")) != 0) {
 
883
          // authentication succeded, extract the user name
 
884
          if ( sizeof($user_elements = $success_elements[0]->get_elements_by_tagname("user")) == 0) {
 
885
            phpCAS::trace('<authenticationSuccess> found, but no <user>');
 
886
            $this->authError('ST not validated',
 
887
                         $validate_url,
 
888
                         FALSE/*$no_response*/,
 
889
                         TRUE/*$bad_response*/,
 
890
                         $text_response);
 
891
          }
 
892
          $user = trim($user_elements[0]->get_content());
 
893
          phpCAS::trace('user = `'.$user);
 
894
          $this->setUser($user);
 
895
          
 
896
        } else if ( sizeof($failure_elements = $tree_response->get_elements_by_tagname("authenticationFailure")) != 0) {
 
897
          phpCAS::trace('<authenticationFailure> found');
 
898
          // authentication failed, extract the error code and message
 
899
          $this->authError('ST not validated',
 
900
                       $validate_url,
 
901
                       FALSE/*$no_response*/,
 
902
                       FALSE/*$bad_response*/,
 
903
                       $text_response,
 
904
                       $failure_elements[0]->get_attribute('code')/*$err_code*/,
 
905
                       trim($failure_elements[0]->get_content())/*$err_msg*/);
 
906
        } else {
 
907
          phpCAS::trace('neither <authenticationSuccess> nor <authenticationFailure> found');
 
908
          $this->authError('ST not validated',
 
909
                       $validate_url,
 
910
                       FALSE/*$no_response*/,
 
911
                       TRUE/*$bad_response*/,
 
912
                       $text_response);
 
913
        }
 
914
        break;
 
915
      }
 
916
      
 
917
      // at this step, ST has been validated and $this->_user has been set,
 
918
      phpCAS::traceEnd(TRUE);
 
919
      return TRUE;
 
920
    }
 
921
 
 
922
  /** @} */
 
923
 
 
924
  // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 
925
  // XX                                                                    XX
 
926
  // XX                     PROXY FEATURES (CAS 2.0)                       XX
 
927
  // XX                                                                    XX
 
928
  // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 
929
 
 
930
  // ########################################################################
 
931
  //  PROXYING
 
932
  // ########################################################################
 
933
  /**
 
934
   * @addtogroup internalProxy
 
935
   * @{
 
936
   */
 
937
 
 
938
  /**
 
939
   * A boolean telling if the client is a CAS proxy or not. Written by CASClient::CASClient(), 
 
940
   * read by CASClient::isProxy().
 
941
   *
 
942
   * @private
 
943
   */
 
944
  var $_proxy;
 
945
  
 
946
  /**
 
947
   * Tells if a CAS client is a CAS proxy or not
 
948
   *
 
949
   * @return TRUE when the CAS client is a CAs proxy, FALSE otherwise
 
950
   *
 
951
   * @private
 
952
   */
 
953
  function isProxy()
 
954
    {
 
955
      return $this->_proxy;
 
956
    }
 
957
 
 
958
  /** @} */
 
959
  // ########################################################################
 
960
  //  PGT
 
961
  // ########################################################################
 
962
  /**
 
963
   * @addtogroup internalProxy
 
964
   * @{
 
965
   */  
 
966
  
 
967
  /**
 
968
   * the Proxy Grnting Ticket given by the CAS server (empty otherwise). 
 
969
   * Written by CASClient::setPGT(), read by CASClient::getPGT() and CASClient::hasPGT().
 
970
   *
 
971
   * @hideinitializer
 
972
   * @private
 
973
   */
 
974
  var $_pgt = '';
 
975
  
 
976
  /**
 
977
   * This method returns the Proxy Granting Ticket given by the CAS server.
 
978
   * @return The Proxy Granting Ticket.
 
979
   * @private
 
980
   */
 
981
  function getPGT()
 
982
    { return $this->_pgt; }
 
983
 
 
984
  /**
 
985
   * This method stores the Proxy Granting Ticket.
 
986
   * @param $pgt The Proxy Granting Ticket.
 
987
   * @private
 
988
   */
 
989
  function setPGT($pgt)
 
990
    { $this->_pgt = $pgt; }
 
991
 
 
992
  /**
 
993
   * This method tells if a Proxy Granting Ticket was stored.
 
994
   * @return TRUE if a Proxy Granting Ticket has been stored.
 
995
   * @private
 
996
   */
 
997
  function hasPGT()
 
998
    { return !empty($this->_pgt); }
 
999
 
 
1000
  /** @} */
 
1001
 
 
1002
  // ########################################################################
 
1003
  //  CALLBACK MODE
 
1004
  // ########################################################################
 
1005
  /**
 
1006
   * @addtogroup internalCallback
 
1007
   * @{
 
1008
   */  
 
1009
  /**
 
1010
   * each PHP script using phpCAS in proxy mode is its own callback to get the
 
1011
   * PGT back from the CAS server. callback_mode is detected by the constructor
 
1012
   * thanks to the GET parameters.
 
1013
   */
 
1014
 
 
1015
  /**
 
1016
   * a boolean to know if the CAS client is running in callback mode. Written by
 
1017
   * CASClient::setCallBackMode(), read by CASClient::isCallbackMode().
 
1018
   *
 
1019
   * @hideinitializer
 
1020
   * @private
 
1021
   */
 
1022
  var $_callback_mode = FALSE;
 
1023
  
 
1024
  /**
 
1025
   * This method sets/unsets callback mode.
 
1026
   *
 
1027
   * @param $callback_mode TRUE to set callback mode, FALSE otherwise.
 
1028
   *
 
1029
   * @private
 
1030
   */
 
1031
  function setCallbackMode($callback_mode)
 
1032
    {
 
1033
      $this->_callback_mode = $callback_mode;
 
1034
    }
 
1035
 
 
1036
  /**
 
1037
   * This method returns TRUE when the CAs client is running i callback mode, 
 
1038
   * FALSE otherwise.
 
1039
   *
 
1040
   * @return A boolean.
 
1041
   *
 
1042
   * @private
 
1043
   */
 
1044
  function isCallbackMode()
 
1045
    {
 
1046
      return $this->_callback_mode;
 
1047
    }
 
1048
 
 
1049
  /**
 
1050
   * the URL that should be used for the PGT callback (in fact the URL of the 
 
1051
   * current request without any CGI parameter). Written and read by 
 
1052
   * CASClient::getCallbackURL().
 
1053
   *
 
1054
   * @hideinitializer
 
1055
   * @private
 
1056
   */
 
1057
  var $_callback_url = '';
 
1058
 
 
1059
  /**
 
1060
   * This method returns the URL that should be used for the PGT callback (in
 
1061
   * fact the URL of the current request without any CGI parameter).
 
1062
   *
 
1063
   * @return The callback URL
 
1064
   *
 
1065
   * @private
 
1066
   */
 
1067
  function getCallbackURL()
 
1068
    {
 
1069
      // the URL is built when needed only
 
1070
      if ( empty($this->_callback_url) ) {
 
1071
        // remove the ticket if present in the URL
 
1072
        $this->_callback_url = 'https://';
 
1073
        /* replaced by Julien Marchal - v0.4.6
 
1074
         * $this->_callback_url .= $_SERVER['SERVER_NAME'];
 
1075
         */
 
1076
        if(empty($_SERVER['HTTP_X_FORWARDED_SERVER'])){
 
1077
          /* replaced by teedog - v0.4.12
 
1078
           * $this->_callback_url .= $_SERVER['SERVER_NAME'];
 
1079
           */
 
1080
          if (empty($_SERVER['SERVER_NAME'])) {
 
1081
            $this->_callback_url .= $_SERVER['HTTP_HOST'];
 
1082
          } else {
 
1083
            $this->_callback_url .= $_SERVER['SERVER_NAME'];
 
1084
          }
 
1085
        } else {
 
1086
          $this->_callback_url .= $_SERVER['HTTP_X_FORWARDED_SERVER'];
 
1087
        }
 
1088
        if ( ($_SERVER['HTTPS']=='on' && $_SERVER['SERVER_PORT']!=443)
 
1089
             || ($_SERVER['HTTPS']!='on' && $_SERVER['SERVER_PORT']!=80) ) {
 
1090
          $this->_callback_url .= ':';
 
1091
          $this->_callback_url .= $_SERVER['SERVER_PORT'];
 
1092
        }
 
1093
        $request_uri = $_SERVER['REQUEST_URI'];
 
1094
        $request_uri = preg_replace('/\?.*$/','',$request_uri);
 
1095
        $this->_callback_url .= $request_uri;
 
1096
      }
 
1097
      return $this->_callback_url;
 
1098
    }
 
1099
 
 
1100
  /**
 
1101
   * This method is called by CASClient::CASClient() when running in callback
 
1102
   * mode. It stores the PGT and its PGT Iou, prints its output and halts.
 
1103
   *
 
1104
   * @private
 
1105
   */
 
1106
  function callback()
 
1107
    {
 
1108
      phpCAS::traceBegin();
 
1109
      $this->printHTMLHeader('phpCAS callback');
 
1110
      $pgt_iou = $_GET['pgtIou'];
 
1111
      $pgt = $_GET['pgtId'];
 
1112
      phpCAS::trace('Storing PGT `'.$pgt.'\' (id=`'.$pgt_iou.'\')');
 
1113
      echo '<p>Storing PGT `'.$pgt.'\' (id=`'.$pgt_iou.'\').</p>';
 
1114
      $this->storePGT($pgt,$pgt_iou);
 
1115
      $this->printHTMLFooter();
 
1116
      phpCAS::traceExit();
 
1117
    }
 
1118
 
 
1119
  /** @} */
 
1120
 
 
1121
  // ########################################################################
 
1122
  //  PGT STORAGE
 
1123
  // ########################################################################
 
1124
  /**
 
1125
   * @addtogroup internalPGTStorage
 
1126
   * @{
 
1127
   */  
 
1128
    
 
1129
  /**
 
1130
   * an instance of a class inheriting of PGTStorage, used to deal with PGT
 
1131
   * storage. Created by CASClient::setPGTStorageFile() or CASClient::setPGTStorageDB(), used 
 
1132
   * by CASClient::setPGTStorageFile(), CASClient::setPGTStorageDB() and CASClient::initPGTStorage().
 
1133
   *
 
1134
   * @hideinitializer
 
1135
   * @private
 
1136
   */
 
1137
  var $_pgt_storage = null;
 
1138
 
 
1139
  /**
 
1140
   * This method is used to initialize the storage of PGT's.
 
1141
   * Halts on error.
 
1142
   *
 
1143
   * @private
 
1144
   */
 
1145
  function initPGTStorage()
 
1146
    {
 
1147
      // if no SetPGTStorageXxx() has been used, default to file
 
1148
      if ( !is_object($this->_pgt_storage) ) {
 
1149
        $this->setPGTStorageFile();
 
1150
      }
 
1151
 
 
1152
      // initializes the storage
 
1153
      $this->_pgt_storage->init();
 
1154
    }
 
1155
  
 
1156
  /**
 
1157
   * This method stores a PGT. Halts on error.
 
1158
   *
 
1159
   * @param $pgt the PGT to store
 
1160
   * @param $pgt_iou its corresponding Iou
 
1161
   *
 
1162
   * @private
 
1163
   */
 
1164
  function storePGT($pgt,$pgt_iou)
 
1165
    {
 
1166
      // ensure that storage is initialized
 
1167
      $this->initPGTStorage();
 
1168
      // writes the PGT
 
1169
      $this->_pgt_storage->write($pgt,$pgt_iou);
 
1170
    }
 
1171
  
 
1172
  /**
 
1173
   * This method reads a PGT from its Iou and deletes the corresponding storage entry.
 
1174
   *
 
1175
   * @param $pgt_iou the PGT Iou
 
1176
   *
 
1177
   * @return The PGT corresponding to the Iou, FALSE when not found.
 
1178
   *
 
1179
   * @private
 
1180
   */
 
1181
  function loadPGT($pgt_iou)
 
1182
    {
 
1183
      // ensure that storage is initialized
 
1184
      $this->initPGTStorage();
 
1185
      // read the PGT
 
1186
      return $this->_pgt_storage->read($pgt_iou);
 
1187
    }
 
1188
  
 
1189
  /**
 
1190
   * This method is used to tell phpCAS to store the response of the
 
1191
   * CAS server to PGT requests onto the filesystem. 
 
1192
   *
 
1193
   * @param $format the format used to store the PGT's (`plain' and `xml' allowed)
 
1194
   * @param $path the path where the PGT's should be stored
 
1195
   *
 
1196
   * @public
 
1197
   */
 
1198
  function setPGTStorageFile($format='',
 
1199
                             $path='')
 
1200
    {
 
1201
      // check that the storage has not already been set
 
1202
      if ( is_object($this->_pgt_storage) ) {
 
1203
        phpCAS::error('PGT storage already defined');
 
1204
      }
 
1205
 
 
1206
      // create the storage object
 
1207
      $this->_pgt_storage = &new PGTStorageFile($this,$format,$path);
 
1208
    }
 
1209
  
 
1210
  /**
 
1211
   * This method is used to tell phpCAS to store the response of the
 
1212
   * CAS server to PGT requests into a database. 
 
1213
   * @note The connection to the database is done only when needed. 
 
1214
   * As a consequence, bad parameters are detected only when 
 
1215
   * initializing PGT storage.
 
1216
   *
 
1217
   * @param $user the user to access the data with
 
1218
   * @param $password the user's password
 
1219
   * @param $database_type the type of the database hosting the data
 
1220
   * @param $hostname the server hosting the database
 
1221
   * @param $port the port the server is listening on
 
1222
   * @param $database the name of the database
 
1223
   * @param $table the name of the table storing the data
 
1224
   *
 
1225
   * @public
 
1226
   */
 
1227
  function setPGTStorageDB($user,
 
1228
                           $password,
 
1229
                           $database_type,
 
1230
                           $hostname,
 
1231
                           $port,
 
1232
                           $database,
 
1233
                           $table)
 
1234
    {
 
1235
      // check that the storage has not already been set
 
1236
      if ( is_object($this->_pgt_storage) ) {
 
1237
        phpCAS::error('PGT storage already defined');
 
1238
      }
 
1239
 
 
1240
      // warn the user that he should use file storage...
 
1241
      trigger_error('PGT storage into database is an experimental feature, use at your own risk',E_USER_WARNING);
 
1242
 
 
1243
      // create the storage object
 
1244
      $this->_pgt_storage = & new PGTStorageDB($this,$user,$password,$database_type,$hostname,$port,$database,$table);
 
1245
    }
 
1246
  
 
1247
  // ########################################################################
 
1248
  //  PGT VALIDATION
 
1249
  // ########################################################################
 
1250
  /**
 
1251
   * This method is used to validate a PGT; halt on failure.
 
1252
   * 
 
1253
   * @param $validate_url the URL of the request to the CAS server.
 
1254
   * @param $text_response the response of the CAS server, as is (XML text); result
 
1255
   * of CASClient::validateST() or CASClient::validatePT().
 
1256
   * @param $tree_response the response of the CAS server, as a DOM XML tree; result
 
1257
   * of CASClient::validateST() or CASClient::validatePT().
 
1258
   *
 
1259
   * @return bool TRUE when successfull, halt otherwise by calling CASClient::authError().
 
1260
   *
 
1261
   * @private
 
1262
   */
 
1263
  function validatePGT(&$validate_url,$text_response,$tree_response)
 
1264
    {
 
1265
      phpCAS::traceBegin();
 
1266
      if ( sizeof($arr = $tree_response->get_elements_by_tagname("proxyGrantingTicket")) == 0) {
 
1267
        phpCAS::trace('<proxyGrantingTicket> not found');
 
1268
        // authentication succeded, but no PGT Iou was transmitted
 
1269
        $this->authError('Ticket validated but no PGT Iou transmitted',
 
1270
                     $validate_url,
 
1271
                     FALSE/*$no_response*/,
 
1272
                     FALSE/*$bad_response*/,
 
1273
                     $text_response);
 
1274
      } else {
 
1275
        // PGT Iou transmitted, extract it
 
1276
        $pgt_iou = trim($arr[0]->get_content());
 
1277
        $pgt = $this->loadPGT($pgt_iou);
 
1278
        if ( $pgt == FALSE ) {
 
1279
          phpCAS::trace('could not load PGT');
 
1280
          $this->authError('PGT Iou was transmitted but PGT could not be retrieved',
 
1281
                       $validate_url,
 
1282
                       FALSE/*$no_response*/,
 
1283
                       FALSE/*$bad_response*/,
 
1284
                       $text_response);
 
1285
        }
 
1286
        $this->setPGT($pgt);
 
1287
      }
 
1288
      phpCAS::traceEnd(TRUE);
 
1289
      return TRUE;
 
1290
    }
 
1291
 
 
1292
  // ########################################################################
 
1293
  //  PGT VALIDATION
 
1294
  // ########################################################################
 
1295
 
 
1296
  /**
 
1297
   * This method is used to retrieve PT's from the CAS server thanks to a PGT.
 
1298
   * 
 
1299
   * @param $target_service the service to ask for with the PT.
 
1300
   * @param $err_code an error code (PHPCAS_SERVICE_OK on success).
 
1301
   * @param $err_msg an error message (empty on success).
 
1302
   *
 
1303
   * @return a Proxy Ticket, or FALSE on error.
 
1304
   *
 
1305
   * @private
 
1306
   */
 
1307
  function retrievePT($target_service,&$err_code,&$err_msg)
 
1308
    {
 
1309
      phpCAS::traceBegin();
 
1310
 
 
1311
      // by default, $err_msg is set empty and $pt to TRUE. On error, $pt is
 
1312
      // set to false and $err_msg to an error message. At the end, if $pt is FALSE 
 
1313
      // and $error_msg is still empty, it is set to 'invalid response' (the most
 
1314
      // commonly encountered error).
 
1315
      $err_msg = '';
 
1316
 
 
1317
      // build the URL to retrieve the PT
 
1318
      $cas_url = $this->getServerProxyURL().'?targetService='.preg_replace('/&/','%26',$target_service).'&pgt='.$this->getPGT();
 
1319
 
 
1320
      // open and read the URL
 
1321
      if ( !$this->readURL($cas_url,''/*cookies*/,$headers,$cas_response,$err_msg) ) {
 
1322
        phpCAS::trace('could not open URL \''.$cas_url.'\' to validate ('.$err_msg.')');
 
1323
        $err_code = PHPCAS_SERVICE_PT_NO_SERVER_RESPONSE;
 
1324
        $err_msg = 'could not retrieve PT (no response from the CAS server)';
 
1325
        phpCAS::traceEnd(FALSE);
 
1326
        return FALSE;
 
1327
      }
 
1328
 
 
1329
      $bas_response = FALSE;
 
1330
 
 
1331
      if ( !$bad_response ) {
 
1332
        // read the response of the CAS server into a DOM object
 
1333
        if ( !($dom = @domxml_open_mem($cas_response))) {
 
1334
          phpCAS::trace('domxml_open_mem() failed');
 
1335
          // read failed
 
1336
          $bad_response = TRUE;
 
1337
        } 
 
1338
      }
 
1339
 
 
1340
      if ( !$bad_response ) {
 
1341
        // read the root node of the XML tree
 
1342
        if ( !($root = $dom->document_element()) ) {
 
1343
          phpCAS::trace('document_element() failed');
 
1344
          // read failed
 
1345
          $bad_response = TRUE;
 
1346
        } 
 
1347
      }
 
1348
 
 
1349
      if ( !$bad_response ) {
 
1350
        // insure that tag name is 'serviceResponse'
 
1351
        if ( $root->node_name() != 'serviceResponse' ) {
 
1352
          phpCAS::trace('node_name() failed');
 
1353
          // bad root node
 
1354
          $bad_response = TRUE;
 
1355
        } 
 
1356
      }
 
1357
 
 
1358
      if ( !$bad_response ) {
 
1359
        // look for a proxySuccess tag
 
1360
        if ( sizeof($arr = $root->get_elements_by_tagname("proxySuccess")) != 0) {
 
1361
          // authentication succeded, look for a proxyTicket tag
 
1362
          if ( sizeof($arr = $root->get_elements_by_tagname("proxyTicket")) != 0) {
 
1363
            $err_code = PHPCAS_SERVICE_OK;
 
1364
            $err_msg = '';
 
1365
            $pt = trim($arr[0]->get_content());
 
1366
            phpCAS::traceEnd($pt);
 
1367
            return $pt;
 
1368
          } else {
 
1369
            phpCAS::trace('<proxySuccess> was found, but not <proxyTicket>');
 
1370
          }
 
1371
        } 
 
1372
        // look for a proxyFailure tag
 
1373
        else if ( sizeof($arr = $root->get_elements_by_tagname("proxyFailure")) != 0) {
 
1374
          // authentication failed, extract the error
 
1375
          $err_code = PHPCAS_SERVICE_PT_FAILURE;
 
1376
          $err_msg = 'PT retrieving failed (code=`'
 
1377
            .$arr[0]->get_attribute('code')
 
1378
            .'\', message=`'
 
1379
            .trim($arr[0]->get_content())
 
1380
            .'\')';
 
1381
          phpCAS::traceEnd(FALSE);
 
1382
          return FALSE;
 
1383
        } else {
 
1384
          phpCAS::trace('neither <proxySuccess> nor <proxyFailure> found');
 
1385
        }
 
1386
      }
 
1387
 
 
1388
      // at this step, we are sure that the response of the CAS server was ill-formed
 
1389
      $err_code = PHPCAS_SERVICE_PT_BAD_SERVER_RESPONSE;
 
1390
      $err_msg = 'Invalid response from the CAS server (response=`'.$cas_response.'\')';
 
1391
 
 
1392
      phpCAS::traceEnd(FALSE);
 
1393
      return FALSE;
 
1394
    }
 
1395
 
 
1396
  // ########################################################################
 
1397
  // ACCESS TO EXTERNAL SERVICES
 
1398
  // ########################################################################
 
1399
 
 
1400
  /**
 
1401
   * This method is used to acces a remote URL.
 
1402
   *
 
1403
   * @param $url the URL to access.
 
1404
   * @param $cookies an array containing cookies strings such as 'name=val'
 
1405
   * @param $headers an array containing the HTTP header lines of the response
 
1406
   * (an empty array on failure).
 
1407
   * @param $body the body of the response, as a string (empty on failure).
 
1408
   * @param $err_msg an error message, filled on failure.
 
1409
   *
 
1410
   * @return TRUE on success, FALSE otherwise (in this later case, $err_msg
 
1411
   * contains an error message).
 
1412
   *
 
1413
   * @private
 
1414
   */
 
1415
  function readURL($url,$cookies,&$headers,&$body,&$err_msg)
 
1416
    {
 
1417
      phpCAS::traceBegin();
 
1418
      $headers = '';
 
1419
      $body = '';
 
1420
      $err_msg = '';
 
1421
 
 
1422
      $res = TRUE;
 
1423
 
 
1424
      // initialize the CURL session
 
1425
      $ch = curl_init($url);
 
1426
        
 
1427
          // verify the the server's certificate corresponds to its name
 
1428
          curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 1);
 
1429
          // but do not verify the certificate itself
 
1430
          curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
 
1431
 
 
1432
      // return the CURL output into a variable
 
1433
      curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
 
1434
      // include the HTTP header with the body
 
1435
      curl_setopt($ch, CURLOPT_HEADER, 1);
 
1436
      // add cookies headers
 
1437
      if ( is_array($cookies) ) {
 
1438
        curl_setopt($ch,CURLOPT_COOKIE,implode(';',$cookies));
 
1439
      }
 
1440
      // perform the query
 
1441
      $buf = curl_exec ($ch);
 
1442
      if ( $buf === FALSE ) {
 
1443
        phpCAS::trace('cur_exec() failed');
 
1444
        $err_msg = 'CURL error #'.curl_errno($ch).': '.curl_error($ch);
 
1445
        // close the CURL session
 
1446
        curl_close ($ch);
 
1447
        $res = FALSE;
 
1448
      } else {
 
1449
        // close the CURL session
 
1450
        curl_close ($ch);
 
1451
        
 
1452
        // find the end of the headers
 
1453
        // note: strpos($str,"\n\r\n\r") does not work (?)
 
1454
        $pos = FALSE;
 
1455
        for ($i=0; $i<strlen($buf); $i++) {
 
1456
          if ( $buf[$i] == chr(13) ) 
 
1457
            if ( $buf[$i+1] == chr(10) ) 
 
1458
              if ( $buf[$i+2] == chr(13) ) 
 
1459
                if ( $buf[$i+3] == chr(10) ) {
 
1460
                  // header found
 
1461
                  $pos = $i;
 
1462
                  break;
 
1463
                }
 
1464
        }
 
1465
        
 
1466
        if ( $pos === FALSE ) {
 
1467
          // end of header not found
 
1468
          $err_msg = 'no header found';
 
1469
          phpCAS::trace($err_msg);
 
1470
          $res = FALSE;
 
1471
        } else { 
 
1472
          // extract headers into an array
 
1473
          $headers = preg_split ("/[\n\r]+/",substr($buf,0,$pos));        
 
1474
          // extract body into a string
 
1475
          $body = substr($buf,$pos+4);
 
1476
        }
 
1477
      }
 
1478
 
 
1479
      phpCAS::traceEnd($res);
 
1480
      return $res;
 
1481
    }
 
1482
 
 
1483
  /**
 
1484
   * This method is used to access an HTTP[S] service.
 
1485
   * 
 
1486
   * @param $url the service to access.
 
1487
   * @param $err_code an error code Possible values are PHPCAS_SERVICE_OK (on
 
1488
   * success), PHPCAS_SERVICE_PT_NO_SERVER_RESPONSE, PHPCAS_SERVICE_PT_BAD_SERVER_RESPONSE,
 
1489
   * PHPCAS_SERVICE_PT_FAILURE, PHPCAS_SERVICE_NOT AVAILABLE.
 
1490
   * @param $output the output of the service (also used to give an error
 
1491
   * message on failure).
 
1492
   *
 
1493
   * @return TRUE on success, FALSE otherwise (in this later case, $err_code
 
1494
   * gives the reason why it failed and $output contains an error message).
 
1495
   *
 
1496
   * @public
 
1497
   */
 
1498
  function serviceWeb($url,&$err_code,&$output)
 
1499
    {
 
1500
      phpCAS::traceBegin();
 
1501
      // at first retrieve a PT
 
1502
      $pt = $this->retrievePT($url,$err_code,$output);
 
1503
 
 
1504
      $res = TRUE;
 
1505
      
 
1506
      // test if PT was retrieved correctly
 
1507
      if ( !$pt ) {
 
1508
        // note: $err_code and $err_msg are filled by CASClient::retrievePT()
 
1509
        phpCAS::trace('PT was not retrieved correctly');
 
1510
        $res = FALSE;
 
1511
      } else {
 
1512
        // add cookies if necessary
 
1513
        if ( is_array($_SESSION['phpCAS']['services'][$url]['cookies']) ) {
 
1514
          foreach ( $_SESSION['phpCAS']['services'][$url]['cookies'] as $name => $val ) { 
 
1515
            $cookies[] = $name.'='.$val;
 
1516
          }
 
1517
        }
 
1518
        
 
1519
        // build the URL including the PT
 
1520
        if ( strstr($url,'?') === FALSE ) {
 
1521
          $service_url = $url.'?ticket='.$pt;
 
1522
        } else {
 
1523
          $service_url = $url.'&ticket='.$pt;
 
1524
        }
 
1525
        
 
1526
        phpCAS::trace('reading URL`'.$service_url.'\'');
 
1527
        if ( !$this->readURL($service_url,$cookies,$headers,$output,$err_msg) ) {
 
1528
          phpCAS::trace('could not read URL`'.$service_url.'\'');
 
1529
          $err_code = PHPCAS_SERVICE_NOT_AVAILABLE;
 
1530
          // give an error message
 
1531
          $output = sprintf($this->getString(CAS_STR_SERVICE_UNAVAILABLE),
 
1532
                            $service_url,
 
1533
                            $err_msg);
 
1534
          $res = FALSE;
 
1535
        } else {
 
1536
          // URL has been fetched, extract the cookies
 
1537
          phpCAS::trace('URL`'.$service_url.'\' has been read, storing cookies:');
 
1538
          foreach ( $headers as $header ) {
 
1539
            // test if the header is a cookie
 
1540
            if ( preg_match('/^Set-Cookie:/',$header) ) {
 
1541
              // the header is a cookie, remove the beginning
 
1542
              $header_val = preg_replace('/^Set-Cookie: */','',$header);
 
1543
              // extract interesting information
 
1544
              $name_val = strtok($header_val,'; ');
 
1545
              // extract the name and the value of the cookie
 
1546
              $cookie_name = strtok($name_val,'=');
 
1547
              $cookie_val = strtok('=');
 
1548
              // store the cookie 
 
1549
              $_SESSION['phpCAS']['services'][$url]['cookies'][$cookie_name] = $cookie_val;
 
1550
              phpCAS::trace($cookie_name.' -> '.$cookie_val);
 
1551
            }
 
1552
          }
 
1553
        }
 
1554
      }
 
1555
 
 
1556
      phpCAS::traceEnd($res);
 
1557
      return $res;
 
1558
  }
 
1559
 
 
1560
  /**
 
1561
   * This method is used to access an IMAP/POP3/NNTP service.
 
1562
   * 
 
1563
   * @param $url a string giving the URL of the service, including the mailing box
 
1564
   * for IMAP URLs, as accepted by imap_open().
 
1565
   * @param $flags options given to imap_open().
 
1566
   * @param $err_code an error code Possible values are PHPCAS_SERVICE_OK (on
 
1567
   * success), PHPCAS_SERVICE_PT_NO_SERVER_RESPONSE, PHPCAS_SERVICE_PT_BAD_SERVER_RESPONSE,
 
1568
   * PHPCAS_SERVICE_PT_FAILURE, PHPCAS_SERVICE_NOT AVAILABLE.
 
1569
   * @param $err_msg an error message on failure
 
1570
   * @param $pt the Proxy Ticket (PT) retrieved from the CAS server to access the URL
 
1571
   * on success, FALSE on error).
 
1572
   *
 
1573
   * @return an IMAP stream on success, FALSE otherwise (in this later case, $err_code
 
1574
   * gives the reason why it failed and $err_msg contains an error message).
 
1575
   *
 
1576
   * @public
 
1577
   */
 
1578
  function serviceMail($url,$flags,&$err_code,&$err_msg,&$pt)
 
1579
    {
 
1580
      phpCAS::traceBegin();
 
1581
      // at first retrieve a PT
 
1582
      $pt = $this->retrievePT($target_service,$err_code,$output);
 
1583
 
 
1584
      $stream = FALSE;
 
1585
      
 
1586
      // test if PT was retrieved correctly
 
1587
      if ( !$pt ) {
 
1588
        // note: $err_code and $err_msg are filled by CASClient::retrievePT()
 
1589
        phpCAS::trace('PT was not retrieved correctly');
 
1590
      } else {
 
1591
        phpCAS::trace('opening IMAP URL `'.$url.'\'...');
 
1592
        $stream = @imap_open($url,$this->getUser(),$pt,$flags);
 
1593
        if ( !$stream ) {
 
1594
          phpCAS::trace('could not open URL');
 
1595
          $err_code = PHPCAS_SERVICE_NOT_AVAILABLE;
 
1596
          // give an error message
 
1597
          $err_msg = sprintf($this->getString(CAS_STR_SERVICE_UNAVAILABLE),
 
1598
                             $service_url,
 
1599
                             var_export(imap_errors(),TRUE));
 
1600
          $pt = FALSE;
 
1601
          $stream = FALSE;
 
1602
        } else {
 
1603
          phpCAS::trace('ok');
 
1604
        }
 
1605
      }
 
1606
 
 
1607
      phpCAS::traceEnd($stream);
 
1608
      return $stream;
 
1609
  }
 
1610
 
 
1611
  /** @} */
 
1612
 
 
1613
  // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 
1614
  // XX                                                                    XX
 
1615
  // XX                  PROXIED CLIENT FEATURES (CAS 2.0)                 XX
 
1616
  // XX                                                                    XX
 
1617
  // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 
1618
 
 
1619
  // ########################################################################
 
1620
  //  PT
 
1621
  // ########################################################################
 
1622
  /**
 
1623
   * @addtogroup internalProxied
 
1624
   * @{
 
1625
   */  
 
1626
  
 
1627
  /**
 
1628
   * the Proxy Ticket provided in the URL of the request if present
 
1629
   * (empty otherwise). Written by CASClient::CASClient(), read by 
 
1630
   * CASClient::getPT() and CASClient::hasPGT().
 
1631
   *
 
1632
   * @hideinitializer
 
1633
   * @private
 
1634
   */
 
1635
  var $_pt = '';
 
1636
  
 
1637
  /**
 
1638
   * This method returns the Proxy Ticket provided in the URL of the request.
 
1639
   * @return The proxy ticket.
 
1640
   * @private
 
1641
   */
 
1642
  function getPT()
 
1643
    { return $this->_pt; }
 
1644
 
 
1645
  /**
 
1646
   * This method stores the Proxy Ticket.
 
1647
   * @param $pt The Proxy Ticket.
 
1648
   * @private
 
1649
   */
 
1650
  function setPT($pt)
 
1651
    { $this->_pt = $pt; }
 
1652
 
 
1653
  /**
 
1654
   * This method tells if a Proxy Ticket was stored.
 
1655
   * @return TRUE if a Proxy Ticket has been stored.
 
1656
   * @private
 
1657
   */
 
1658
  function hasPT()
 
1659
    { return !empty($this->_pt); }
 
1660
 
 
1661
  /** @} */
 
1662
  // ########################################################################
 
1663
  //  PT VALIDATION
 
1664
  // ########################################################################
 
1665
  /**
 
1666
   * @addtogroup internalProxied
 
1667
   * @{
 
1668
   */  
 
1669
 
 
1670
  /**
 
1671
   * This method is used to validate a PT; halt on failure
 
1672
   * 
 
1673
   * @return bool TRUE when successfull, halt otherwise by calling CASClient::authError().
 
1674
   *
 
1675
   * @private
 
1676
   */
 
1677
  function validatePT(&$validate_url,&$text_response,&$tree_response)
 
1678
    {
 
1679
      phpCAS::traceBegin();
 
1680
      // build the URL to validate the ticket
 
1681
      $validate_url = $this->getServerProxyValidateURL().'&ticket='.$this->getPT();
 
1682
 
 
1683
      if ( $this->isProxy() ) {
 
1684
      // pass the callback url for CAS proxies
 
1685
        $validate_url .= '&pgtUrl='.$this->getCallbackURL();
 
1686
      }
 
1687
 
 
1688
      // open and read the URL
 
1689
      if ( !$this->readURL($validate_url,''/*cookies*/,$headers,$text_response,$err_msg) ) {
 
1690
        phpCAS::trace('could not open URL \''.$validate_url.'\' to validate ('.$err_msg.')');
 
1691
        $this->authError('PT not validated',
 
1692
                         $validate_url,
 
1693
                         TRUE/*$no_response*/);
 
1694
      }
 
1695
 
 
1696
      // read the response of the CAS server into a DOM object
 
1697
      if ( !($dom = domxml_open_mem($text_response))) {
 
1698
        // read failed
 
1699
        $this->authError('PT not validated',
 
1700
                     $alidate_url,
 
1701
                     FALSE/*$no_response*/,
 
1702
                     TRUE/*$bad_response*/,
 
1703
                     $text_response);
 
1704
      }
 
1705
      // read the root node of the XML tree
 
1706
      if ( !($tree_response = $dom->document_element()) ) {
 
1707
        // read failed
 
1708
        $this->authError('PT not validated',
 
1709
                     $validate_url,
 
1710
                     FALSE/*$no_response*/,
 
1711
                     TRUE/*$bad_response*/,
 
1712
                     $text_response);
 
1713
      }
 
1714
      // insure that tag name is 'serviceResponse'
 
1715
      if ( $tree_response->node_name() != 'serviceResponse' ) {
 
1716
        // bad root node
 
1717
        $this->authError('PT not validated',
 
1718
                     $validate_url,
 
1719
                     FALSE/*$no_response*/,
 
1720
                     TRUE/*$bad_response*/,
 
1721
                     $text_response);
 
1722
      }
 
1723
      if ( sizeof($arr = $tree_response->get_elements_by_tagname("authenticationSuccess")) != 0) {
 
1724
        // authentication succeded, extract the user name
 
1725
        if ( sizeof($arr = $tree_response->get_elements_by_tagname("user")) == 0) {
 
1726
          // no user specified => error
 
1727
          $this->authError('PT not validated',
 
1728
                       $validate_url,
 
1729
                       FALSE/*$no_response*/,
 
1730
                       TRUE/*$bad_response*/,
 
1731
                       $text_response);
 
1732
        }
 
1733
        $this->setUser(trim($arr[0]->get_content()));
 
1734
        
 
1735
      } else if ( sizeof($arr = $tree_response->get_elements_by_tagname("authenticationFailure")) != 0) {
 
1736
        // authentication succeded, extract the error code and message
 
1737
        $this->authError('PT not validated',
 
1738
                     $validate_url,
 
1739
                     FALSE/*$no_response*/,
 
1740
                     FALSE/*$bad_response*/,
 
1741
                     $text_response,
 
1742
                     $arr[0]->get_attribute('code')/*$err_code*/,
 
1743
                     trim($arr[0]->get_content())/*$err_msg*/);
 
1744
      } else {
 
1745
        $this->authError('PT not validated',
 
1746
                     $validate_url,     
 
1747
                     FALSE/*$no_response*/,
 
1748
                     TRUE/*$bad_response*/,
 
1749
                     $text_response);
 
1750
      }
 
1751
      
 
1752
      // at this step, PT has been validated and $this->_user has been set,
 
1753
 
 
1754
      phpCAS::traceEnd(TRUE);
 
1755
      return TRUE;
 
1756
    }
 
1757
 
 
1758
  /** @} */
 
1759
 
 
1760
  // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 
1761
  // XX                                                                    XX
 
1762
  // XX                               MISC                                 XX
 
1763
  // XX                                                                    XX
 
1764
  // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 
1765
 
 
1766
  /**
 
1767
   * @addtogroup internalMisc
 
1768
   * @{
 
1769
   */  
 
1770
  
 
1771
  // ########################################################################
 
1772
  //  URL
 
1773
  // ########################################################################
 
1774
  /**
 
1775
   * the URL of the current request (without any ticket CGI parameter). Written 
 
1776
   * and read by CASClient::getURL().
 
1777
   *
 
1778
   * @hideinitializer
 
1779
   * @private
 
1780
   */
 
1781
  var $_url = '';
 
1782
 
 
1783
  /**
 
1784
   * This method returns the URL of the current request (without any ticket
 
1785
   * CGI parameter).
 
1786
   *
 
1787
   * @return The URL
 
1788
   *
 
1789
   * @private
 
1790
   */
 
1791
  function getURL()
 
1792
    {
 
1793
      phpCAS::traceBegin();
 
1794
      // the URL is built when needed only
 
1795
      if ( empty($this->_url) ) {
 
1796
        // remove the ticket if present in the URL
 
1797
        $this->_url = ($_SERVER['HTTPS'] == 'on') ? 'https' : 'http';
 
1798
        $this->_url .= '://';
 
1799
        /* replaced by Julien Marchal - v0.4.6
 
1800
         * $this->_url .= $_SERVER['SERVER_NAME'];
 
1801
         */
 
1802
        if(empty($_SERVER['HTTP_X_FORWARDED_SERVER'])){
 
1803
          /* replaced by teedog - v0.4.12
 
1804
           * $this->_url .= $_SERVER['SERVER_NAME'];
 
1805
           */
 
1806
          if (empty($_SERVER['SERVER_NAME'])) {
 
1807
            $this->_url .= $_SERVER['HTTP_HOST'];
 
1808
          } else {
 
1809
            $this->_url .= $_SERVER['SERVER_NAME'];
 
1810
          }
 
1811
        } else {
 
1812
          $this->_url .= $_SERVER['HTTP_X_FORWARDED_SERVER'];
 
1813
        }
 
1814
        if ( ($_SERVER['HTTPS']=='on' && $_SERVER['SERVER_PORT']!=443)
 
1815
             || ($_SERVER['HTTPS']!='on' && $_SERVER['SERVER_PORT']!=80) ) {
 
1816
          $this->_url .= ':';
 
1817
          $this->_url .= $_SERVER['SERVER_PORT'];
 
1818
        }
 
1819
 
 
1820
        $this->_url .= strtok($_SERVER['REQUEST_URI'],"?");
 
1821
        $cgi_params = '?'.strtok("?");
 
1822
        // remove the ticket if present in the CGI parameters
 
1823
        $cgi_params = preg_replace('/&ticket=[^&]*/','',$cgi_params);
 
1824
        $cgi_params = preg_replace('/\?ticket=[^&]*/','?',$cgi_params);
 
1825
        $cgi_params = preg_replace('/\?$/','',$cgi_params);
 
1826
        $this->_url .= $cgi_params;
 
1827
      }
 
1828
      phpCAS::traceEnd($this->_url);
 
1829
      return $this->_url;
 
1830
    }
 
1831
 
 
1832
 
 
1833
  // ########################################################################
 
1834
  //  AUTHENTICATION ERROR HANDLING
 
1835
  // ########################################################################
 
1836
  /**
 
1837
   * This method is used to print the HTML output when the user was not authenticated.
 
1838
   *
 
1839
   * @param $failure the failure that occured
 
1840
   * @param $cas_url the URL the CAS server was asked for
 
1841
   * @param $no_response the response from the CAS server (other 
 
1842
   * parameters are ignored if TRUE)
 
1843
   * @param $bad_response bad response from the CAS server ($err_code
 
1844
   * and $err_msg ignored if TRUE)
 
1845
   * @param $cas_response the response of the CAS server
 
1846
   * @param $err_code the error code given by the CAS server
 
1847
   * @param $err_msg the error message given by the CAS server
 
1848
   *
 
1849
   * @private
 
1850
   */
 
1851
  function authError($failure,$cas_url,$no_response,$bad_response='',$cas_response='',$err_code='',$err_msg='')
 
1852
    {
 
1853
      phpCAS::traceBegin();
 
1854
 
 
1855
      $this->printHTMLHeader($this->getString(CAS_STR_AUTHENTICATION_FAILED));
 
1856
      printf($this->getString(CAS_STR_YOU_WERE_NOT_AUTHENTICATED),$this->getURL(),$_SERVER['SERVER_ADMIN']);
 
1857
      phpCAS::trace('CAS URL: '.$cas_url);
 
1858
      phpCAS::trace('Authentication failure: '.$failure);
 
1859
      if ( $no_response ) {
 
1860
        phpCAS::trace('Reason: no response from the CAS server');
 
1861
      } else {
 
1862
        if ( $bad_response ) {
 
1863
            phpCAS::trace('Reason: bad response from the CAS server');
 
1864
        } else {
 
1865
          switch ($this->getServerVersion()) {
 
1866
          case CAS_VERSION_1_0:
 
1867
            phpCAS::trace('Reason: CAS error');
 
1868
            break;
 
1869
          case CAS_VERSION_2_0:
 
1870
            if ( empty($err_code) )
 
1871
              phpCAS::trace('Reason: no CAS error');
 
1872
            else
 
1873
              phpCAS::trace('Reason: ['.$err_code.'] CAS error: '.$err_msg);
 
1874
            break;
 
1875
          }
 
1876
        }
 
1877
        phpCAS::trace('CAS response: '.$cas_response);
 
1878
      }
 
1879
      $this->printHTMLFooter();
 
1880
      phpCAS::traceExit();
 
1881
      exit();
 
1882
    }
 
1883
 
 
1884
  /** @} */
 
1885
}
 
1886
 
 
1887
?>
 
 
b'\\ No newline at end of file'