MediaWiki  REL1_24
HttpFunctions.php
Go to the documentation of this file.
00001 <?php
00032 class Http {
00033     static public $httpEngine = false;
00034 
00060     public static function request( $method, $url, $options = array() ) {
00061         wfDebug( "HTTP: $method: $url\n" );
00062         wfProfileIn( __METHOD__ . "-$method" );
00063 
00064         $options['method'] = strtoupper( $method );
00065 
00066         if ( !isset( $options['timeout'] ) ) {
00067             $options['timeout'] = 'default';
00068         }
00069         if ( !isset( $options['connectTimeout'] ) ) {
00070             $options['connectTimeout'] = 'default';
00071         }
00072 
00073         $req = MWHttpRequest::factory( $url, $options );
00074         $status = $req->execute();
00075 
00076         $content = false;
00077         if ( $status->isOK() ) {
00078             $content = $req->getContent();
00079         }
00080         wfProfileOut( __METHOD__ . "-$method" );
00081         return $content;
00082     }
00083 
00093     public static function get( $url, $timeout = 'default', $options = array() ) {
00094         $options['timeout'] = $timeout;
00095         return Http::request( 'GET', $url, $options );
00096     }
00097 
00106     public static function post( $url, $options = array() ) {
00107         return Http::request( 'POST', $url, $options );
00108     }
00109 
00116     public static function isLocalURL( $url ) {
00117         global $wgCommandLineMode, $wgConf;
00118 
00119         if ( $wgCommandLineMode ) {
00120             return false;
00121         }
00122 
00123         // Extract host part
00124         $matches = array();
00125         if ( preg_match( '!^http://([\w.-]+)[/:].*$!', $url, $matches ) ) {
00126             $host = $matches[1];
00127             // Split up dotwise
00128             $domainParts = explode( '.', $host );
00129             // Check if this domain or any superdomain is listed in $wgConf as a local virtual host
00130             $domainParts = array_reverse( $domainParts );
00131 
00132             $domain = '';
00133             $countParts = count( $domainParts );
00134             for ( $i = 0; $i < $countParts; $i++ ) {
00135                 $domainPart = $domainParts[$i];
00136                 if ( $i == 0 ) {
00137                     $domain = $domainPart;
00138                 } else {
00139                     $domain = $domainPart . '.' . $domain;
00140                 }
00141 
00142                 if ( $wgConf->isLocalVHost( $domain ) ) {
00143                     return true;
00144                 }
00145             }
00146         }
00147 
00148         return false;
00149     }
00150 
00155     public static function userAgent() {
00156         global $wgVersion;
00157         return "MediaWiki/$wgVersion";
00158     }
00159 
00172     public static function isValidURI( $uri ) {
00173         return preg_match(
00174             '/^https?:\/\/[^\/\s]\S*$/D',
00175             $uri
00176         );
00177     }
00178 }
00179 
00187 class MWHttpRequest {
00188     const SUPPORTS_FILE_POSTS = false;
00189 
00190     protected $content;
00191     protected $timeout = 'default';
00192     protected $headersOnly = null;
00193     protected $postData = null;
00194     protected $proxy = null;
00195     protected $noProxy = false;
00196     protected $sslVerifyHost = true;
00197     protected $sslVerifyCert = true;
00198     protected $caInfo = null;
00199     protected $method = "GET";
00200     protected $reqHeaders = array();
00201     protected $url;
00202     protected $parsedUrl;
00203     protected $callback;
00204     protected $maxRedirects = 5;
00205     protected $followRedirects = false;
00206 
00210     protected $cookieJar;
00211 
00212     protected $headerList = array();
00213     protected $respVersion = "0.9";
00214     protected $respStatus = "200 Ok";
00215     protected $respHeaders = array();
00216 
00217     public $status;
00218 
00223     protected function __construct( $url, $options = array() ) {
00224         global $wgHTTPTimeout, $wgHTTPConnectTimeout;
00225 
00226         $this->url = wfExpandUrl( $url, PROTO_HTTP );
00227         $this->parsedUrl = wfParseUrl( $this->url );
00228 
00229         if ( !$this->parsedUrl || !Http::isValidURI( $this->url ) ) {
00230             $this->status = Status::newFatal( 'http-invalid-url' );
00231         } else {
00232             $this->status = Status::newGood( 100 ); // continue
00233         }
00234 
00235         if ( isset( $options['timeout'] ) && $options['timeout'] != 'default' ) {
00236             $this->timeout = $options['timeout'];
00237         } else {
00238             $this->timeout = $wgHTTPTimeout;
00239         }
00240         if ( isset( $options['connectTimeout'] ) && $options['connectTimeout'] != 'default' ) {
00241             $this->connectTimeout = $options['connectTimeout'];
00242         } else {
00243             $this->connectTimeout = $wgHTTPConnectTimeout;
00244         }
00245         if ( isset( $options['userAgent'] ) ) {
00246             $this->setUserAgent( $options['userAgent'] );
00247         }
00248 
00249         $members = array( "postData", "proxy", "noProxy", "sslVerifyHost", "caInfo",
00250                 "method", "followRedirects", "maxRedirects", "sslVerifyCert", "callback" );
00251 
00252         foreach ( $members as $o ) {
00253             if ( isset( $options[$o] ) ) {
00254                 // ensure that MWHttpRequest::method is always
00255                 // uppercased. Bug 36137
00256                 if ( $o == 'method' ) {
00257                     $options[$o] = strtoupper( $options[$o] );
00258                 }
00259                 $this->$o = $options[$o];
00260             }
00261         }
00262 
00263         if ( $this->noProxy ) {
00264             $this->proxy = ''; // noProxy takes precedence
00265         }
00266     }
00267 
00273     public static function canMakeRequests() {
00274         return function_exists( 'curl_init' ) || wfIniGetBool( 'allow_url_fopen' );
00275     }
00276 
00285     public static function factory( $url, $options = null ) {
00286         if ( !Http::$httpEngine ) {
00287             Http::$httpEngine = function_exists( 'curl_init' ) ? 'curl' : 'php';
00288         } elseif ( Http::$httpEngine == 'curl' && !function_exists( 'curl_init' ) ) {
00289             throw new MWException( __METHOD__ . ': curl (http://php.net/curl) is not installed, but' .
00290                 ' Http::$httpEngine is set to "curl"' );
00291         }
00292 
00293         switch ( Http::$httpEngine ) {
00294             case 'curl':
00295                 return new CurlHttpRequest( $url, $options );
00296             case 'php':
00297                 if ( !wfIniGetBool( 'allow_url_fopen' ) ) {
00298                     throw new MWException( __METHOD__ . ': allow_url_fopen ' .
00299                         'needs to be enabled for pure PHP http requests to ' .
00300                         'work. If possible, curl should be used instead. See ' .
00301                         'http://php.net/curl.'
00302                     );
00303                 }
00304                 return new PhpHttpRequest( $url, $options );
00305             default:
00306                 throw new MWException( __METHOD__ . ': The setting of Http::$httpEngine is not valid.' );
00307         }
00308     }
00309 
00315     public function getContent() {
00316         return $this->content;
00317     }
00318 
00325     public function setData( $args ) {
00326         $this->postData = $args;
00327     }
00328 
00334     public function proxySetup() {
00335         global $wgHTTPProxy;
00336 
00337         // If there is an explicit proxy set and proxies are not disabled, then use it
00338         if ( $this->proxy && !$this->noProxy ) {
00339             return;
00340         }
00341 
00342         // Otherwise, fallback to $wgHTTPProxy/http_proxy (when set) if this is not a machine
00343         // local URL and proxies are not disabled
00344         if ( Http::isLocalURL( $this->url ) || $this->noProxy ) {
00345             $this->proxy = '';
00346         } elseif ( $wgHTTPProxy ) {
00347             $this->proxy = $wgHTTPProxy;
00348         } elseif ( getenv( "http_proxy" ) ) {
00349             $this->proxy = getenv( "http_proxy" );
00350         }
00351     }
00352 
00357     public function setUserAgent( $UA ) {
00358         $this->setHeader( 'User-Agent', $UA );
00359     }
00360 
00366     public function setHeader( $name, $value ) {
00367         // I feel like I should normalize the case here...
00368         $this->reqHeaders[$name] = $value;
00369     }
00370 
00375     public function getHeaderList() {
00376         $list = array();
00377 
00378         if ( $this->cookieJar ) {
00379             $this->reqHeaders['Cookie'] =
00380                 $this->cookieJar->serializeToHttpRequest(
00381                     $this->parsedUrl['path'],
00382                     $this->parsedUrl['host']
00383                 );
00384         }
00385 
00386         foreach ( $this->reqHeaders as $name => $value ) {
00387             $list[] = "$name: $value";
00388         }
00389 
00390         return $list;
00391     }
00392 
00411     public function setCallback( $callback ) {
00412         if ( !is_callable( $callback ) ) {
00413             throw new MWException( 'Invalid MwHttpRequest callback' );
00414         }
00415         $this->callback = $callback;
00416     }
00417 
00426     public function read( $fh, $content ) {
00427         $this->content .= $content;
00428         return strlen( $content );
00429     }
00430 
00436     public function execute() {
00437         wfProfileIn( __METHOD__ );
00438 
00439         $this->content = "";
00440 
00441         if ( strtoupper( $this->method ) == "HEAD" ) {
00442             $this->headersOnly = true;
00443         }
00444 
00445         $this->proxySetup(); // set up any proxy as needed
00446 
00447         if ( !$this->callback ) {
00448             $this->setCallback( array( $this, 'read' ) );
00449         }
00450 
00451         if ( !isset( $this->reqHeaders['User-Agent'] ) ) {
00452             $this->setUserAgent( Http::userAgent() );
00453         }
00454 
00455         wfProfileOut( __METHOD__ );
00456     }
00457 
00463     protected function parseHeader() {
00464         wfProfileIn( __METHOD__ );
00465 
00466         $lastname = "";
00467 
00468         foreach ( $this->headerList as $header ) {
00469             if ( preg_match( "#^HTTP/([0-9.]+) (.*)#", $header, $match ) ) {
00470                 $this->respVersion = $match[1];
00471                 $this->respStatus = $match[2];
00472             } elseif ( preg_match( "#^[ \t]#", $header ) ) {
00473                 $last = count( $this->respHeaders[$lastname] ) - 1;
00474                 $this->respHeaders[$lastname][$last] .= "\r\n$header";
00475             } elseif ( preg_match( "#^([^:]*):[\t ]*(.*)#", $header, $match ) ) {
00476                 $this->respHeaders[strtolower( $match[1] )][] = $match[2];
00477                 $lastname = strtolower( $match[1] );
00478             }
00479         }
00480 
00481         $this->parseCookies();
00482 
00483         wfProfileOut( __METHOD__ );
00484     }
00485 
00494     protected function setStatus() {
00495         if ( !$this->respHeaders ) {
00496             $this->parseHeader();
00497         }
00498 
00499         if ( (int)$this->respStatus > 399 ) {
00500             list( $code, $message ) = explode( " ", $this->respStatus, 2 );
00501             $this->status->fatal( "http-bad-status", $code, $message );
00502         }
00503     }
00504 
00512     public function getStatus() {
00513         if ( !$this->respHeaders ) {
00514             $this->parseHeader();
00515         }
00516 
00517         return (int)$this->respStatus;
00518     }
00519 
00525     public function isRedirect() {
00526         if ( !$this->respHeaders ) {
00527             $this->parseHeader();
00528         }
00529 
00530         $status = (int)$this->respStatus;
00531 
00532         if ( $status >= 300 && $status <= 303 ) {
00533             return true;
00534         }
00535 
00536         return false;
00537     }
00538 
00547     public function getResponseHeaders() {
00548         if ( !$this->respHeaders ) {
00549             $this->parseHeader();
00550         }
00551 
00552         return $this->respHeaders;
00553     }
00554 
00561     public function getResponseHeader( $header ) {
00562         if ( !$this->respHeaders ) {
00563             $this->parseHeader();
00564         }
00565 
00566         if ( isset( $this->respHeaders[strtolower( $header )] ) ) {
00567             $v = $this->respHeaders[strtolower( $header )];
00568             return $v[count( $v ) - 1];
00569         }
00570 
00571         return null;
00572     }
00573 
00579     public function setCookieJar( $jar ) {
00580         $this->cookieJar = $jar;
00581     }
00582 
00588     public function getCookieJar() {
00589         if ( !$this->respHeaders ) {
00590             $this->parseHeader();
00591         }
00592 
00593         return $this->cookieJar;
00594     }
00595 
00605     public function setCookie( $name, $value = null, $attr = null ) {
00606         if ( !$this->cookieJar ) {
00607             $this->cookieJar = new CookieJar;
00608         }
00609 
00610         $this->cookieJar->setCookie( $name, $value, $attr );
00611     }
00612 
00616     protected function parseCookies() {
00617         wfProfileIn( __METHOD__ );
00618 
00619         if ( !$this->cookieJar ) {
00620             $this->cookieJar = new CookieJar;
00621         }
00622 
00623         if ( isset( $this->respHeaders['set-cookie'] ) ) {
00624             $url = parse_url( $this->getFinalUrl() );
00625             foreach ( $this->respHeaders['set-cookie'] as $cookie ) {
00626                 $this->cookieJar->parseCookieResponseHeader( $cookie, $url['host'] );
00627             }
00628         }
00629 
00630         wfProfileOut( __METHOD__ );
00631     }
00632 
00649     public function getFinalUrl() {
00650         $headers = $this->getResponseHeaders();
00651 
00652         //return full url (fix for incorrect but handled relative location)
00653         if ( isset( $headers['location'] ) ) {
00654             $locations = $headers['location'];
00655             $domain = '';
00656             $foundRelativeURI = false;
00657             $countLocations = count( $locations );
00658 
00659             for ( $i = $countLocations - 1; $i >= 0; $i-- ) {
00660                 $url = parse_url( $locations[$i] );
00661 
00662                 if ( isset( $url['host'] ) ) {
00663                     $domain = $url['scheme'] . '://' . $url['host'];
00664                     break; //found correct URI (with host)
00665                 } else {
00666                     $foundRelativeURI = true;
00667                 }
00668             }
00669 
00670             if ( $foundRelativeURI ) {
00671                 if ( $domain ) {
00672                     return $domain . $locations[$countLocations - 1];
00673                 } else {
00674                     $url = parse_url( $this->url );
00675                     if ( isset( $url['host'] ) ) {
00676                         return $url['scheme'] . '://' . $url['host'] .
00677                             $locations[$countLocations - 1];
00678                     }
00679                 }
00680             } else {
00681                 return $locations[$countLocations - 1];
00682             }
00683         }
00684 
00685         return $this->url;
00686     }
00687 
00693     public function canFollowRedirects() {
00694         return true;
00695     }
00696 }
00697 
00701 class CurlHttpRequest extends MWHttpRequest {
00702     const SUPPORTS_FILE_POSTS = true;
00703 
00704     protected $curlOptions = array();
00705     protected $headerText = "";
00706 
00712     protected function readHeader( $fh, $content ) {
00713         $this->headerText .= $content;
00714         return strlen( $content );
00715     }
00716 
00717     public function execute() {
00718         wfProfileIn( __METHOD__ );
00719 
00720         parent::execute();
00721 
00722         if ( !$this->status->isOK() ) {
00723             wfProfileOut( __METHOD__ );
00724             return $this->status;
00725         }
00726 
00727         $this->curlOptions[CURLOPT_PROXY] = $this->proxy;
00728         $this->curlOptions[CURLOPT_TIMEOUT] = $this->timeout;
00729 
00730         // Only supported in curl >= 7.16.2
00731         if ( defined( 'CURLOPT_CONNECTTIMEOUT_MS' ) ) {
00732             $this->curlOptions[CURLOPT_CONNECTTIMEOUT_MS] = $this->connectTimeout * 1000;
00733         }
00734 
00735         $this->curlOptions[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_1_0;
00736         $this->curlOptions[CURLOPT_WRITEFUNCTION] = $this->callback;
00737         $this->curlOptions[CURLOPT_HEADERFUNCTION] = array( $this, "readHeader" );
00738         $this->curlOptions[CURLOPT_MAXREDIRS] = $this->maxRedirects;
00739         $this->curlOptions[CURLOPT_ENCODING] = ""; # Enable compression
00740 
00741         $this->curlOptions[CURLOPT_USERAGENT] = $this->reqHeaders['User-Agent'];
00742 
00743         $this->curlOptions[CURLOPT_SSL_VERIFYHOST] = $this->sslVerifyHost ? 2 : 0;
00744         $this->curlOptions[CURLOPT_SSL_VERIFYPEER] = $this->sslVerifyCert;
00745 
00746         if ( $this->caInfo ) {
00747             $this->curlOptions[CURLOPT_CAINFO] = $this->caInfo;
00748         }
00749 
00750         if ( $this->headersOnly ) {
00751             $this->curlOptions[CURLOPT_NOBODY] = true;
00752             $this->curlOptions[CURLOPT_HEADER] = true;
00753         } elseif ( $this->method == 'POST' ) {
00754             $this->curlOptions[CURLOPT_POST] = true;
00755             $this->curlOptions[CURLOPT_POSTFIELDS] = $this->postData;
00756             // Suppress 'Expect: 100-continue' header, as some servers
00757             // will reject it with a 417 and Curl won't auto retry
00758             // with HTTP 1.0 fallback
00759             $this->reqHeaders['Expect'] = '';
00760         } else {
00761             $this->curlOptions[CURLOPT_CUSTOMREQUEST] = $this->method;
00762         }
00763 
00764         $this->curlOptions[CURLOPT_HTTPHEADER] = $this->getHeaderList();
00765 
00766         $curlHandle = curl_init( $this->url );
00767 
00768         if ( !curl_setopt_array( $curlHandle, $this->curlOptions ) ) {
00769             wfProfileOut( __METHOD__ );
00770             throw new MWException( "Error setting curl options." );
00771         }
00772 
00773         if ( $this->followRedirects && $this->canFollowRedirects() ) {
00774             wfSuppressWarnings();
00775             if ( !curl_setopt( $curlHandle, CURLOPT_FOLLOWLOCATION, true ) ) {
00776                 wfDebug( __METHOD__ . ": Couldn't set CURLOPT_FOLLOWLOCATION. " .
00777                     "Probably safe_mode or open_basedir is set.\n" );
00778                 // Continue the processing. If it were in curl_setopt_array,
00779                 // processing would have halted on its entry
00780             }
00781             wfRestoreWarnings();
00782         }
00783 
00784         $curlRes = curl_exec( $curlHandle );
00785         if ( curl_errno( $curlHandle ) == CURLE_OPERATION_TIMEOUTED ) {
00786             $this->status->fatal( 'http-timed-out', $this->url );
00787         } elseif ( $curlRes === false ) {
00788             $this->status->fatal( 'http-curl-error', curl_error( $curlHandle ) );
00789         } else {
00790             $this->headerList = explode( "\r\n", $this->headerText );
00791         }
00792 
00793         curl_close( $curlHandle );
00794 
00795         $this->parseHeader();
00796         $this->setStatus();
00797 
00798         wfProfileOut( __METHOD__ );
00799 
00800         return $this->status;
00801     }
00802 
00806     public function canFollowRedirects() {
00807         if ( strval( ini_get( 'open_basedir' ) ) !== '' || wfIniGetBool( 'safe_mode' ) ) {
00808             wfDebug( "Cannot follow redirects in safe mode\n" );
00809             return false;
00810         }
00811 
00812         $curlVersionInfo = curl_version();
00813         if ( $curlVersionInfo['version_number'] < 0x071304 ) {
00814             wfDebug( "Cannot follow redirects with libcurl < 7.19.4 due to CVE-2009-0037\n" );
00815             return false;
00816         }
00817 
00818         return true;
00819     }
00820 }
00821 
00822 class PhpHttpRequest extends MWHttpRequest {
00823 
00828     protected function urlToTcp( $url ) {
00829         $parsedUrl = parse_url( $url );
00830 
00831         return 'tcp://' . $parsedUrl['host'] . ':' . $parsedUrl['port'];
00832     }
00833 
00834     public function execute() {
00835         wfProfileIn( __METHOD__ );
00836 
00837         parent::execute();
00838 
00839         if ( is_array( $this->postData ) ) {
00840             $this->postData = wfArrayToCgi( $this->postData );
00841         }
00842 
00843         if ( $this->parsedUrl['scheme'] != 'http'
00844             && $this->parsedUrl['scheme'] != 'https' ) {
00845             $this->status->fatal( 'http-invalid-scheme', $this->parsedUrl['scheme'] );
00846         }
00847 
00848         $this->reqHeaders['Accept'] = "*/*";
00849         $this->reqHeaders['Connection'] = 'Close';
00850         if ( $this->method == 'POST' ) {
00851             // Required for HTTP 1.0 POSTs
00852             $this->reqHeaders['Content-Length'] = strlen( $this->postData );
00853             if ( !isset( $this->reqHeaders['Content-Type'] ) ) {
00854                 $this->reqHeaders['Content-Type'] = "application/x-www-form-urlencoded";
00855             }
00856         }
00857 
00858         // Set up PHP stream context
00859         $options = array(
00860             'http' => array(
00861                 'method' => $this->method,
00862                 'header' => implode( "\r\n", $this->getHeaderList() ),
00863                 'protocol_version' => '1.1',
00864                 'max_redirects' => $this->followRedirects ? $this->maxRedirects : 0,
00865                 'ignore_errors' => true,
00866                 'timeout' => $this->timeout,
00867                 // Curl options in case curlwrappers are installed
00868                 'curl_verify_ssl_host' => $this->sslVerifyHost ? 2 : 0,
00869                 'curl_verify_ssl_peer' => $this->sslVerifyCert,
00870             ),
00871             'ssl' => array(
00872                 'verify_peer' => $this->sslVerifyCert,
00873                 'SNI_enabled' => true,
00874             ),
00875         );
00876 
00877         if ( $this->proxy ) {
00878             $options['http']['proxy'] = $this->urlToTCP( $this->proxy );
00879             $options['http']['request_fulluri'] = true;
00880         }
00881 
00882         if ( $this->postData ) {
00883             $options['http']['content'] = $this->postData;
00884         }
00885 
00886         if ( $this->sslVerifyHost ) {
00887             $options['ssl']['CN_match'] = $this->parsedUrl['host'];
00888         }
00889 
00890         if ( is_dir( $this->caInfo ) ) {
00891             $options['ssl']['capath'] = $this->caInfo;
00892         } elseif ( is_file( $this->caInfo ) ) {
00893             $options['ssl']['cafile'] = $this->caInfo;
00894         } elseif ( $this->caInfo ) {
00895             throw new MWException( "Invalid CA info passed: {$this->caInfo}" );
00896         }
00897 
00898         $context = stream_context_create( $options );
00899 
00900         $this->headerList = array();
00901         $reqCount = 0;
00902         $url = $this->url;
00903 
00904         $result = array();
00905 
00906         do {
00907             $reqCount++;
00908             wfSuppressWarnings();
00909             $fh = fopen( $url, "r", false, $context );
00910             wfRestoreWarnings();
00911 
00912             if ( !$fh ) {
00913                 break;
00914             }
00915 
00916             $result = stream_get_meta_data( $fh );
00917             $this->headerList = $result['wrapper_data'];
00918             $this->parseHeader();
00919 
00920             if ( !$this->followRedirects ) {
00921                 break;
00922             }
00923 
00924             # Handle manual redirection
00925             if ( !$this->isRedirect() || $reqCount > $this->maxRedirects ) {
00926                 break;
00927             }
00928             # Check security of URL
00929             $url = $this->getResponseHeader( "Location" );
00930 
00931             if ( !Http::isValidURI( $url ) ) {
00932                 wfDebug( __METHOD__ . ": insecure redirection\n" );
00933                 break;
00934             }
00935         } while ( true );
00936 
00937         $this->setStatus();
00938 
00939         if ( $fh === false ) {
00940             $this->status->fatal( 'http-request-error' );
00941             wfProfileOut( __METHOD__ );
00942             return $this->status;
00943         }
00944 
00945         if ( $result['timed_out'] ) {
00946             $this->status->fatal( 'http-timed-out', $this->url );
00947             wfProfileOut( __METHOD__ );
00948             return $this->status;
00949         }
00950 
00951         // If everything went OK, or we received some error code
00952         // get the response body content.
00953         if ( $this->status->isOK() || (int)$this->respStatus >= 300 ) {
00954             while ( !feof( $fh ) ) {
00955                 $buf = fread( $fh, 8192 );
00956 
00957                 if ( $buf === false ) {
00958                     $this->status->fatal( 'http-read-error' );
00959                     break;
00960                 }
00961 
00962                 if ( strlen( $buf ) ) {
00963                     call_user_func( $this->callback, $fh, $buf );
00964                 }
00965             }
00966         }
00967         fclose( $fh );
00968 
00969         wfProfileOut( __METHOD__ );
00970 
00971         return $this->status;
00972     }
00973 }