Subversion Repositories Applications.gtt

Rev

Rev 94 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
94 jpm 1
<?php
2
//
3
// +----------------------------------------------------------------------+
4
// | PHP Version 4                                                        |
5
// +----------------------------------------------------------------------+
6
// | Copyright (c) 1997-2004 The PHP Group                                |
7
// +----------------------------------------------------------------------+
8
// | This source file is subject to version 2.02 of the PHP license,      |
9
// | that is bundled with this package in the file LICENSE, and is        |
10
// | available at through the world-wide-web at                           |
11
// | http://www.php.net/license/2_02.txt.                                 |
12
// | If you did not receive a copy of the PHP license and are unable to   |
13
// | obtain it through the world-wide-web, please send a note to          |
14
// | license@php.net so we can mail you a copy immediately.               |
15
// +----------------------------------------------------------------------+
16
// | Authors: Martin Jansen <mj@php.net>                                  |
17
// |          Rui Hirokawa <hirokawa@php.net>                             |
18
// |          David Costa  <gurugeek@php.net>                             |
19
// +----------------------------------------------------------------------+
20
//
21
//  $Id: Auth_HTTP.php,v 1.27 2005/04/04 12:48:33 hirokawa Exp $
22
//
23
 
24
require_once "Auth/Auth.php";
25
 
26
define('AUTH_HTTP_NONCE_TIME_LEN', 16);
27
define('AUTH_HTTP_NONCE_HASH_LEN', 32);
28
 
29
// {{{ class Auth_HTTP
30
 
31
/**
32
 * PEAR::Auth_HTTP
33
 *
34
 * The PEAR::Auth_HTTP class provides methods for creating an
35
 * HTTP authentication system based on RFC-2617 using PHP.
36
 *
37
 * Instead of generating an HTML driven form like PEAR::Auth
38
 * does, this class sends header commands to the clients which
39
 * cause them to present a login box like they are e.g. used
40
 * in Apache's .htaccess mechanism.
41
 *
42
 * This class requires the PEAR::Auth package.
43
 *
44
 * @notes The HTTP Digest Authentication part is based on
45
 *  authentication class written by Tom Pike <tom.pike@xiven.com>
46
 *
47
 * @author  Martin Jansen <mj@php.net>
48
 * @author  Rui Hirokawa <hirokawa@php.net>
49
 * @author  David Costa <gurugeek@php.net>
50
 * @package Auth_HTTP
51
 * @extends Auth
52
 * @version $Revision: 1.27 $
53
 */
54
class Auth_HTTP extends Auth
55
{
56
 
57
    // {{{ properties
58
 
59
    /**
60
     * Authorization method: 'basic' or 'digest'
61
     *
62
     * @access public
63
     * @var    string
64
     */
65
    var $authType = 'basic';
66
 
67
    /**
68
     * Name of the realm for Basic Authentication
69
     *
70
     * @access public
71
     * @var    string
72
     * @see    drawLogin()
73
     */
74
    var $realm = "protected area";
75
 
76
    /**
77
     * Text to send if user hits cancel button
78
     *
79
     * @access public
80
     * @var    string
81
     * @see    drawLogin()
82
     */
83
    var $CancelText = "Error 401 - Access denied";
84
 
85
    /**
86
     * option array
87
     *
88
     * @access public
89
     * @var    array
90
     */
91
    var $options = array();
92
 
93
    /**
94
     * flag to indicate the nonce was stale.
95
     *
96
     * @access public
97
     * @var    bool
98
     */
99
    var $stale = false;
100
 
101
    /**
102
     * opaque string for digest authentication
103
     *
104
     * @access public
105
     * @var    string
106
     */
107
    var $opaque = 'dummy';
108
 
109
    /**
110
     * digest URI
111
     *
112
     * @access public
113
     * @var    string
114
     */
115
    var $uri = '';
116
 
117
    /**
118
     * authorization info returned by the client
119
     *
120
     * @access public
121
     * @var    array
122
     */
123
    var $auth = array();
124
 
125
    /**
126
     * next nonce value
127
     *
128
     * @access public
129
     * @var    string
130
     */
131
    var $nextNonce = '';
132
 
133
    /**
134
     * nonce value
135
     *
136
     * @access public
137
     * @var    string
138
     */
139
    var $nonce = '';
140
 
141
    /**
142
     * Holds a reference to the global server variable
143
     * @var array
144
     */
145
    var $server;
146
 
147
    /**
148
     * Holds a reference to the global post variable
149
     * @var array
150
     */
151
    var $post;
152
 
153
    /**
154
     * Holds a reference to the global cookie variable
155
     * @var array
156
     */
157
    var $cookie;
158
 
159
 
160
    // }}}
161
    // {{{ Constructor
162
 
163
    /**
164
     * Constructor
165
     *
166
     * @param string    Type of the storage driver
167
     * @param mixed     Additional options for the storage driver
168
     *                  (example: if you are using DB as the storage
169
     *                   driver, you have to pass the dsn string here)
170
     *
171
     * @return void
172
     */
173
    function Auth_HTTP($storageDriver, $options = '')
174
    {
175
        /* set default values for options */
176
        $this->options = array('cryptType' => 'md5',
177
                               'algorithm' => 'MD5',
178
                               'qop' => 'auth-int,auth',
179
                               'opaquekey' => 'moo',
180
                               'noncekey' => 'moo',
181
                               'digestRealm' => 'protected area',
182
                               'forceDigestOnly' => false,
183
                               'nonceLife' => 300,
184
                               'sessionSharing' => true,
185
                               );
186
 
187
        if (!empty($options['authType'])) {
188
            $this->authType = strtolower($options['authType']);
189
        }
190
 
191
        if (is_array($options)) {
192
            foreach($options as $key => $value) {
193
                if (array_key_exists( $key, $this->options)) {
194
                    $this->options[$key] = $value;
195
                }
196
            }
197
 
198
            if (!empty($this->options['opaquekey'])) {
199
                $this->opaque = md5($this->options['opaquekey']);
200
            }
201
        }
202
 
203
		$this->Auth($storageDriver, $options);
204
	}
205
 
206
	// }}}
207
    // {{{ assignData()
208
 
209
    /**
210
     * Assign values from $PHP_AUTH_USER and $PHP_AUTH_PW or 'Authorization' header
211
     * to internal variables and sets the session id based
212
     * on them
213
     *
214
     * @access public
215
     * @return void
216
     */
217
    function assignData()
218
    {
219
        if (method_exists($this, '_importGlobalVariable')) {
220
            $this->server = &$this->_importGlobalVariable('server');
221
        }
222
 
223
 
224
        if ($this->authType == 'basic') {
225
            if (!empty($this->server['PHP_AUTH_USER'])) {
226
                $this->username = $this->server['PHP_AUTH_USER'];
227
            }
228
 
229
            if (!empty($this->server['PHP_AUTH_PW'])) {
230
                $this->password = $this->server['PHP_AUTH_PW'];
231
            }
232
 
233
            /**
234
             * Try to get authentication information from IIS
235
             */
236
            if  (empty($this->username) && empty($this->password)) {
237
                if (!empty($this->server['HTTP_AUTHORIZATION'])) {
238
                    list($this->username, $this->password) =
239
                        explode(':', base64_decode(substr($this->server['HTTP_AUTHORIZATION'], 6)));
240
                }
241
            }
242
        } elseif ($this->authType == 'digest') {
243
            $this->username = '';
244
            $this->password = '';
245
 
246
            $this->digest_header = null;
247
            if (!empty($this->server['PHP_AUTH_DIGEST'])) {
248
                $this->digest_header = substr($this->server['PHP_AUTH_DIGEST'],
249
                                              strpos($this->server['PHP_AUTH_DIGEST'],' ')+1);
250
            } else {
251
                $headers = getallheaders();
252
                if(isset($headers['Authorization']) && !empty($headers['Authorization'])) {
253
                    $this->digest_header = substr($headers['Authorization'],
254
                                                  strpos($headers['Authorization'],' ')+1);
255
                }
256
            }
257
 
258
            if($this->digest_header) {
259
                $authtemp = explode(',', $this->digest_header);
260
                $auth = array();
261
                foreach($authtemp as $key => $value) {
262
                    $value = trim($value);
263
                    if(strpos($value,'=') !== false) {
264
                        $lhs = substr($value,0,strpos($value,'='));
265
                        $rhs = substr($value,strpos($value,'=')+1);
266
                        if(substr($rhs,0,1) == '"' && substr($rhs,-1,1) == '"') {
267
                            $rhs = substr($rhs,1,-1);
268
                        }
269
                        $auth[$lhs] = $rhs;
270
                    }
271
                }
272
            }
273
            if (!isset($auth['uri']) || !isset($auth['realm'])) {
274
                return;
275
            }
276
 
277
            if ($this->selfURI() == $auth['uri']) {
278
                $this->uri = $auth['uri'];
279
                if (substr($headers['Authorization'],0,7) == 'Digest ') {
280
 
281
                    $this->authType = 'digest';
282
 
283
                    if (!isset($auth['nonce']) || !isset($auth['username']) ||
284
                  !isset($auth['response']) || !isset($auth['qop']) ||
285
                  !isset($auth['nc']) || !isset($auth['cnonce'])){
286
                        return;
287
                    }
288
 
289
               if ($auth['qop'] != 'auth' && $auth['qop'] != 'auth-int') {
290
                        return;
291
               }
292
 
293
                    $this->stale = $this->_judgeStale($auth['nonce']);
294
 
295
               if ($this->nextNonce == false) {
296
                  return;
297
               }
298
 
299
                    $this->username = $auth['username'];
300
                    $this->password = $auth['response'];
301
                    $this->auth['nonce'] = $auth['nonce'];
302
 
303
               $this->auth['qop'] = $auth['qop'];
304
               $this->auth['nc'] = $auth['nc'];
305
               $this->auth['cnonce'] = $auth['cnonce'];
306
 
307
                    if (isset($auth['opaque'])) {
308
                        $this->auth['opaque'] = $auth['opaque'];
309
                    }
310
 
311
                } elseif (substr($headers['Authorization'],0,6) == 'Basic ') {
312
                    if ($this->options['forceDigestOnly']) {
313
                        return; // Basic authentication is not allowed.
314
                    }
315
 
316
                    $this->authType = 'basic';
317
                    list($username, $password) =
318
                        explode(':',base64_decode(substr($headers['Authorization'],6)));
319
                    $this->username = $username;
320
                    $this->password = $password;
321
                }
322
            }
323
        } else {
324
            return PEAR::raiseError('authType is invalid.');
325
        }
326
 
327
        if ($this->options['sessionSharing'] &&
328
            isset($this->username) && isset($this->password)) {
329
            session_id(md5('Auth_HTTP' . $this->username . $this->password));
330
        }
331
 
332
        /**
333
         * set sessionName for AUTH, so that the sessionName is different
334
         * for distinct realms
335
         */
336
         $this->_sessionName = "_authhttp".md5($this->realm);
337
    }
338
 
339
    // }}}
340
    // {{{ login()
341
 
342
    /**
343
     * Login function
344
     *
345
     * @access private
346
     * @return void
347
     */
348
    function login()
349
    {
350
        $login_ok = false;
351
        if (method_exists($this, '_loadStorage')) {
352
            $this->_loadStorage();
353
        }
354
        $this->storage->_auth_obj->_sessionName =& $this->_sessionName;
355
 
356
        /**
357
         * When the user has already entered a username,
358
         * we have to validate it.
359
         */
360
        if (!empty($this->username) && !empty($this->password)) {
361
            if ($this->authType == 'basic' && !$this->options['forceDigestOnly']) {
362
                if (true === $this->storage->fetchData($this->username, $this->password)) {
363
                    $login_ok = true;
364
                }
365
            } else { /* digest authentication */
366
 
367
                if (!$this->getAuth() || $this->getAuthData('a1') == null) {
368
                    /*
369
                     * note:
370
                     *  - only PEAR::DB is supported as container.
371
                     *  - password should be stored in container as plain-text
372
                     *    (if $options['cryptType'] == 'none') or
373
                     *     A1 hashed form (md5('username:realm:password'))
374
                     *    (if $options['cryptType'] == 'md5')
375
                     */
376
                    $dbs = $this->storage;
377
                    if (!DB::isConnection($dbs->db)) {
378
                        $dbs->_connect($dbs->options['dsn']);
379
                    }
380
 
381
                    $query = 'SELECT '.$dbs->options['passwordcol']." FROM ".$dbs->options['table'].
382
                        ' WHERE '.$dbs->options['usernamecol']." = '".
383
                        $dbs->db->quoteString($this->username)."' ";
384
 
385
                    $pwd = $dbs->db->getOne($query); // password stored in container.
386
 
387
                    if (DB::isError($pwd)) {
388
                        return PEAR::raiseError($pwd->getMessage(), $pwd->getCode());
389
                    }
390
 
391
                    if ($this->options['cryptType'] == 'none') {
392
                        $a1 = md5($this->username.':'.$this->options['digestRealm'].':'.$pwd);
393
                    } else {
394
                        $a1 = $pwd;
395
                    }
396
 
397
                    $this->setAuthData('a1', $a1, true);
398
                } else {
399
                    $a1 = $this->getAuthData('a1');
400
                }
401
 
402
                $login_ok = $this->validateDigest($this->password, $a1);
403
                if ($this->nextNonce == false) {
404
                    $login_ok = false;
405
                }
406
            }
407
 
408
            if (!$login_ok && is_callable($this->loginFailedCallback)) {
409
                call_user_func($this->loginFailedCallback,$this->username, $this);
410
            }
411
        }
412
 
413
        if (!empty($this->username) && $login_ok) {
414
            $this->setAuth($this->username);
415
            if (is_callable($this->loginCallback)) {
416
                call_user_func($this->loginCallback,$this->username, $this);
417
            }
418
        }
419
 
420
        /**
421
         * If the login failed or the user entered no username,
422
         * output the login screen again.
423
         */
424
        if (!empty($this->username) && !$login_ok) {
425
            $this->status = AUTH_WRONG_LOGIN;
426
        }
427
 
428
        if ((empty($this->username) || !$login_ok) && $this->showLogin) {
429
            $this->drawLogin($this->storage->activeUser);
430
            return;
431
        }
432
 
433
      if (!empty($this->username) && $login_ok && $this->authType == 'digest'
434
         && $this->auth['qop'] == 'auth') {
435
         $this->authenticationInfo();
436
      }
437
    }
438
 
439
    // }}}
440
    // {{{ drawLogin()
441
 
442
    /**
443
     * Launch the login box
444
     *
445
     * @param  string $username  Username
446
     * @return void
447
     * @access private
448
     */
449
    function drawLogin($username = "")
450
    {
451
        /**
452
         * Send the header commands
453
         */
454
        if ($this->authType == 'basic') {
455
            header("WWW-Authenticate: Basic realm=\"".$this->realm."\"");
456
            header('HTTP/1.0 401 Unauthorized');
457
        } else if ($this->authType == 'digest') {
458
            $this->nonce = $this->_getNonce();
459
 
460
            $wwwauth = 'WWW-Authenticate: Digest ';
461
            $wwwauth .= 'qop="'.$this->options['qop'].'", ';
462
            $wwwauth .= 'algorithm='.$this->options['algorithm'].', ';
463
            $wwwauth .= 'realm="'.$this->options['digestRealm'].'", ';
464
            $wwwauth .= 'nonce="'.$this->nonce.'", ';
465
            if ($this->stale) {
466
                $wwwauth .= 'stale=true, ';
467
            }
468
            if (!empty($this->opaque)) {
469
                $wwwauth .= 'opaque="'.$this->opaque.'"' ;
470
            }
471
            $wwwauth .= "\r\n";
472
            if (!$this->options['forceDigestOnly']) {
473
                $wwwauth .= 'WWW-Authenticate: Basic realm="'.$this->realm.'"';
474
            }
475
            header($wwwauth);
476
            header('HTTP/1.0 401 Unauthorized');
477
        }
478
 
479
        /**
480
         * This code is only executed if the user hits the cancel
481
         * button or if he enters wrong data 3 times.
482
         */
483
        if ($this->stale) {
484
            echo 'Stale nonce value, please re-authenticate.';
485
        } else {
486
            echo $this->CancelText;
487
        }
488
        exit;
489
    }
490
 
491
    // }}}
492
    // {{{ setRealm()
493
 
494
    /**
495
     * Set name of the current realm
496
     *
497
     * @access public
498
     * @param  string $realm  Name of the realm
499
     * @param  string $digestRealm  Name of the realm for digest authentication
500
     * @return void
501
     */
502
    function setRealm($realm, $digestRealm = '')
503
    {
504
        $this->realm = $realm;
505
        if (!empty($digestRealm)) {
506
            $this->options['digestRealm'] = $digestRealm;
507
        }
508
    }
509
 
510
    // }}}
511
    // {{{ setCancelText()
512
 
513
    /**
514
     * Set the text to send if user hits the cancel button
515
     *
516
     * @access public
517
     * @param  string $text  Text to send
518
     * @return void
519
     */
520
    function setCancelText($text)
521
    {
522
        $this->CancelText = $text;
523
    }
524
 
525
    // }}}
526
    // {{{ validateDigest()
527
 
528
    /**
529
     * judge if the client response is valid.
530
     *
531
     * @access private
532
     * @param  string $response  client response
533
     * @param  string $a1 password or hashed password stored in container
534
     * @return bool true if success, false otherwise
535
     */
536
    function validateDigest($response, $a1)
537
    {
538
        if (method_exists($this, '_importGlobalVariable')) {
539
            $this->server = &$this->_importGlobalVariable('server');
540
        }
541
 
542
        $a2unhashed = $this->server['REQUEST_METHOD'].":".$this->selfURI();
543
        if($this->auth['qop'] == 'auth-int') {
544
            if(isset($GLOBALS["HTTP_RAW_POST_DATA"])) {
545
                // In PHP < 4.3 get raw POST data from this variable
546
                $body = $GLOBALS["HTTP_RAW_POST_DATA"];
547
            } else if($lines = @file('php://input')) {
548
                // In PHP >= 4.3 get raw POST data from this file
549
                $body = implode("\n", $lines);
550
            } else {
551
                if (method_exists($this, '_importGlobalVariable')) {
552
                    $this->post = &$this->_importGlobalVariable('post');
553
                }
554
                $body = '';
555
                foreach($this->post as $key => $value) {
556
                    if($body != '') $body .= '&';
557
                    $body .= rawurlencode($key) . '=' . rawurlencode($value);
558
                }
559
            }
560
 
561
            $a2unhashed .= ':'.md5($body);
562
        }
563
 
564
        $a2 = md5($a2unhashed);
565
        $combined = $a1.':'.
566
            $this->auth['nonce'].':'.
567
            $this->auth['nc'].':'.
568
            $this->auth['cnonce'].':'.
569
            $this->auth['qop'].':'.
570
            $a2;
571
        $expectedResponse = md5($combined);
572
 
573
        if(!isset($this->auth['opaque']) || $this->auth['opaque'] == $this->opaque) {
574
            if($response == $expectedResponse) { // password is valid
575
                if(!$this->stale) {
576
                    return true;
577
                } else {
578
                    $this->drawLogin();
579
                }
580
            }
581
        }
582
 
583
        return false;
584
    }
585
 
586
    // }}}
587
    // {{{ _judgeStale()
588
 
589
    /**
590
     * judge if nonce from client is stale.
591
     *
592
     * @access private
593
     * @param  string $nonce  nonce value from client
594
     * @return bool stale
595
     */
596
    function _judgeStale($nonce)
597
    {
598
        $stale = false;
599
 
600
        if(!$this->_decodeNonce($nonce, $time, $hash_cli)) {
601
         $this->nextNonce = false;
602
         $stale = true;
603
            return $stale;
604
        }
605
 
606
        if ($time < time() - $this->options['nonceLife']) {
607
         $this->nextNonce = $this->_getNonce();
608
            $stale = true;
609
        } else {
610
         $this->nextNonce = $nonce;
611
      }
612
 
613
        return $stale;
614
    }
615
 
616
    // }}}
617
    // {{{ _nonceDecode()
618
 
619
    /**
620
     * decode nonce string
621
     *
622
     * @access private
623
     * @param  string $nonce nonce value from client
624
     * @param  string $time decoded time
625
     * @param  string $hash decoded hash
626
     * @return bool false if nonce is invalid
627
     */
628
    function _decodeNonce($nonce, &$time, &$hash)
629
    {
630
        if (method_exists($this, '_importGlobalVariable')) {
631
            $this->server = &$this->_importGlobalVariable('server');
632
        }
633
 
634
        if (strlen($nonce) != AUTH_HTTP_NONCE_TIME_LEN + AUTH_HTTP_NONCE_HASH_LEN) {
635
            return false;
636
        }
637
 
638
        $time =  base64_decode(substr($nonce, 0, AUTH_HTTP_NONCE_TIME_LEN));
639
        $hash_cli = substr($nonce, AUTH_HTTP_NONCE_TIME_LEN, AUTH_HTTP_NONCE_HASH_LEN);
640
 
641
        $hash = md5($time . $this->server['HTTP_USER_AGENT'] . $this->options['noncekey']);
642
 
643
        if ($hash_cli != $hash) {
644
            return false;
645
        }
646
 
647
        return true;
648
    }
649
 
650
    // }}}
651
    // {{{ _getNonce()
652
 
653
    /**
654
     * return nonce to detect timeout
655
     *
656
     * @access private
657
     * @return string nonce value
658
     */
659
    function _getNonce()
660
    {
661
        if (method_exists($this, '_importGlobalVariable')) {
662
            $this->server = &$this->_importGlobalVariable('server');
663
        }
664
 
665
        $time = time();
666
        $hash = md5($time . $this->server['HTTP_USER_AGENT'] . $this->options['noncekey']);
667
 
668
        return base64_encode($time) . $hash;
669
    }
670
 
671
    // }}}
672
    // {{{ authenticationInfo()
673
 
674
    /**
675
     * output HTTP Authentication-Info header
676
     *
677
     * @notes md5 hash of contents is required if 'qop' is 'auth-int'
678
     *
679
     * @access private
680
     * @param string MD5 hash of content
681
     */
682
    function authenticationInfo($contentMD5 = '') {
683
 
684
        if($this->getAuth() && ($this->getAuthData('a1') != null)) {
685
            $a1 = $this->getAuthData('a1');
686
 
687
            // Work out authorisation response
688
            $a2unhashed = ":".$this->selfURI();
689
            if($this->auth['qop'] == 'auth-int') {
690
                $a2unhashed .= ':'.$contentMD5;
691
            }
692
            $a2 = md5($a2unhashed);
693
            $combined = $a1.':'.
694
                        $this->nonce.':'.
695
                        $this->auth['nc'].':'.
696
                        $this->auth['cnonce'].':'.
697
                        $this->auth['qop'].':'.
698
                        $a2;
699
 
700
            // Send authentication info
701
            $wwwauth = 'Authentication-Info: ';
702
            if($this->nonce != $this->nextNonce) {
703
                $wwwauth .= 'nextnonce="'.$this->nextNonce.'", ';
704
            }
705
            $wwwauth .= 'qop='.$this->auth['qop'].', ';
706
            $wwwauth .= 'rspauth="'.md5($combined).'", ';
707
            $wwwauth .= 'cnonce="'.$this->auth['cnonce'].'", ';
708
            $wwwauth .= 'nc='.$this->auth['nc'].'';
709
            header($wwwauth);
710
        }
711
    }
712
    // }}}
713
    // {{{ setOption()
714
    /**
715
     * set authentication option
716
     *
717
     * @access public
718
     * @param mixed $name key of option
719
     * @param mixed $value value of option
720
     * @return void
721
     */
722
    function setOption($name, $value = null)
723
    {
724
        if (is_array($name)) {
725
            foreach($name as $key => $value) {
726
                if (array_key_exists( $key, $this->options)) {
727
                    $this->options[$key] = $value;
728
                }
729
            }
730
        } else {
731
            if (array_key_exists( $name, $this->options)) {
732
                    $this->options[$name] = $value;
733
            }
734
        }
735
    }
736
 
737
    // }}}
738
    // {{{ getOption()
739
    /**
740
     * get authentication option
741
     *
742
     * @access public
743
     * @param string $name key of option
744
     * @return mixed option value
745
     */
746
    function getOption($name)
747
    {
748
        if (array_key_exists( $name, $this->options)) {
749
            return $this->options[$name];
750
        }
751
        if ($name == 'CancelText') {
752
            return $this->CancelText;
753
        }
754
        if ($name == 'Realm') {
755
            return $this->realm;
756
        }
757
        return false;
758
    }
759
 
760
    // }}}
761
    // {{{ selfURI()
762
    /**
763
     * get self URI
764
     *
765
     * @access public
766
     * @return string self URI
767
     */
768
    function selfURI()
769
    {
770
        if (method_exists($this, '_importGlobalVariable')) {
771
            $this->server = &$this->_importGlobalVariable('server');
772
        }
773
 
774
        if (preg_match("/MSIE/",$this->server['HTTP_USER_AGENT'])) {
775
            // query string should be removed for MSIE
776
            $uri = preg_replace("/^(.*)\?/","\\1",$this->server['REQUEST_URI']);
777
        } else {
778
            $uri = $this->server['REQUEST_URI'];
779
        }
780
        return $uri;
781
    }
782
 
783
    // }}}
784
 
785
}
786
 
787
// }}}
788
 
789
/*
790
 * Local variables:
791
 * tab-width: 4
792
 * c-basic-offset: 4
793
 * End:
794
 */
795
?>