MediaWiki  1.23.15
HttpFunctions.php
Go to the documentation of this file.
1 <?php
29 
34 class Http {
35  static public $httpEngine = false;
36 
61  public static function request( $method, $url, $options = array() ) {
62  wfDebug( "HTTP: $method: $url\n" );
63  wfProfileIn( __METHOD__ . "-$method" );
64 
65  $options['method'] = strtoupper( $method );
66 
67  if ( !isset( $options['timeout'] ) ) {
68  $options['timeout'] = 'default';
69  }
70  if ( !isset( $options['connectTimeout'] ) ) {
71  $options['connectTimeout'] = 'default';
72  }
73 
74  $req = MWHttpRequest::factory( $url, $options );
75  $status = $req->execute();
76 
77  $content = false;
78  if ( $status->isOK() ) {
79  $content = $req->getContent();
80  } else {
81  $errors = $status->getErrorsByType( 'error' );
82  $logger = LoggerFactory::getInstance( 'http' );
83  $logger->warning( $status->getWikiText(), array( 'caller' => $caller ) );
84  }
85  wfProfileOut( __METHOD__ . "-$method" );
86  return $content;
87  }
88 
98  public static function get( $url, $timeout = 'default', $options = array() ) {
99  $options['timeout'] = $timeout;
100  return Http::request( 'GET', $url, $options );
101  }
102 
111  public static function post( $url, $options = array() ) {
112  return Http::request( 'POST', $url, $options );
113  }
114 
121  public static function isLocalURL( $url ) {
122  global $wgCommandLineMode, $wgConf;
123 
124  if ( $wgCommandLineMode ) {
125  return false;
126  }
127 
128  // Extract host part
129  $matches = array();
130  if ( preg_match( '!^http://([\w.-]+)[/:].*$!', $url, $matches ) ) {
131  $host = $matches[1];
132  // Split up dotwise
133  $domainParts = explode( '.', $host );
134  // Check if this domain or any superdomain is listed in $wgConf as a local virtual host
135  $domainParts = array_reverse( $domainParts );
136 
137  $domain = '';
138  $countParts = count( $domainParts );
139  for ( $i = 0; $i < $countParts; $i++ ) {
140  $domainPart = $domainParts[$i];
141  if ( $i == 0 ) {
142  $domain = $domainPart;
143  } else {
144  $domain = $domainPart . '.' . $domain;
145  }
146 
147  if ( $wgConf->isLocalVHost( $domain ) ) {
148  return true;
149  }
150  }
151  }
152 
153  return false;
154  }
155 
160  public static function userAgent() {
161  global $wgVersion;
162  return "MediaWiki/$wgVersion";
163  }
164 
177  public static function isValidURI( $uri ) {
178  return preg_match(
179  '/^https?:\/\/[^\/\s]\S*$/D',
180  $uri
181  );
182  }
183 }
184 
193  const SUPPORTS_FILE_POSTS = false;
194 
195  protected $content;
196  protected $timeout = 'default';
197  protected $headersOnly = null;
198  protected $postData = null;
199  protected $proxy = null;
200  protected $noProxy = false;
201  protected $sslVerifyHost = true;
202  protected $sslVerifyCert = true;
203  protected $caInfo = null;
204  protected $method = "GET";
205  protected $reqHeaders = array();
206  protected $url;
207  protected $parsedUrl;
208  protected $callback;
209  protected $maxRedirects = 5;
210  protected $followRedirects = false;
211 
215  protected $cookieJar;
216 
217  protected $headerList = array();
218  protected $respVersion = "0.9";
219  protected $respStatus = "200 Ok";
220  protected $respHeaders = array();
221 
222  public $status;
223 
228  protected function __construct( $url, $options = array() ) {
229  global $wgHTTPTimeout, $wgHTTPConnectTimeout;
230 
231  $this->url = wfExpandUrl( $url, PROTO_HTTP );
232  $this->parsedUrl = wfParseUrl( $this->url );
233 
234  if ( !$this->parsedUrl || !Http::isValidURI( $this->url ) ) {
235  $this->status = Status::newFatal( 'http-invalid-url' );
236  } else {
237  $this->status = Status::newGood( 100 ); // continue
238  }
239 
240  if ( isset( $options['timeout'] ) && $options['timeout'] != 'default' ) {
241  $this->timeout = $options['timeout'];
242  } else {
243  $this->timeout = $wgHTTPTimeout;
244  }
245  if ( isset( $options['connectTimeout'] ) && $options['connectTimeout'] != 'default' ) {
246  $this->connectTimeout = $options['connectTimeout'];
247  } else {
248  $this->connectTimeout = $wgHTTPConnectTimeout;
249  }
250  if ( isset( $options['userAgent'] ) ) {
251  $this->setUserAgent( $options['userAgent'] );
252  }
253 
254  $members = array( "postData", "proxy", "noProxy", "sslVerifyHost", "caInfo",
255  "method", "followRedirects", "maxRedirects", "sslVerifyCert", "callback" );
256 
257  foreach ( $members as $o ) {
258  if ( isset( $options[$o] ) ) {
259  // ensure that MWHttpRequest::method is always
260  // uppercased. Bug 36137
261  if ( $o == 'method' ) {
262  $options[$o] = strtoupper( $options[$o] );
263  }
264  $this->$o = $options[$o];
265  }
266  }
267 
268  if ( $this->noProxy ) {
269  $this->proxy = ''; // noProxy takes precedence
270  }
271  }
272 
278  public static function canMakeRequests() {
279  return function_exists( 'curl_init' ) || wfIniGetBool( 'allow_url_fopen' );
280  }
281 
290  public static function factory( $url, $options = null ) {
291  if ( !Http::$httpEngine ) {
292  Http::$httpEngine = function_exists( 'curl_init' ) ? 'curl' : 'php';
293  } elseif ( Http::$httpEngine == 'curl' && !function_exists( 'curl_init' ) ) {
294  throw new MWException( __METHOD__ . ': curl (http://php.net/curl) is not installed, but' .
295  ' Http::$httpEngine is set to "curl"' );
296  }
297 
298  switch ( Http::$httpEngine ) {
299  case 'curl':
300  return new CurlHttpRequest( $url, $options );
301  case 'php':
302  if ( !wfIniGetBool( 'allow_url_fopen' ) ) {
303  throw new MWException( __METHOD__ . ': allow_url_fopen ' .
304  'needs to be enabled for pure PHP http requests to ' .
305  'work. If possible, curl should be used instead. See ' .
306  'http://php.net/curl.'
307  );
308  }
309  return new PhpHttpRequest( $url, $options );
310  default:
311  throw new MWException( __METHOD__ . ': The setting of Http::$httpEngine is not valid.' );
312  }
313  }
314 
320  public function getContent() {
321  return $this->content;
322  }
323 
330  public function setData( $args ) {
331  $this->postData = $args;
332  }
333 
339  public function proxySetup() {
340  global $wgHTTPProxy;
341 
342  // If there is an explicit proxy set and proxies are not disabled, then use it
343  if ( $this->proxy && !$this->noProxy ) {
344  return;
345  }
346 
347  // Otherwise, fallback to $wgHTTPProxy (when set) if this is not a machine
348  // local URL and proxies are not disabled
349  if ( Http::isLocalURL( $this->url ) || $this->noProxy ) {
350  $this->proxy = '';
351  } elseif ( $wgHTTPProxy ) {
352  $this->proxy = $wgHTTPProxy;
353  }
354  }
355 
360  public function setUserAgent( $UA ) {
361  $this->setHeader( 'User-Agent', $UA );
362  }
363 
369  public function setHeader( $name, $value ) {
370  // I feel like I should normalize the case here...
371  $this->reqHeaders[$name] = $value;
372  }
373 
378  public function getHeaderList() {
379  $list = array();
380 
381  if ( $this->cookieJar ) {
382  $this->reqHeaders['Cookie'] =
383  $this->cookieJar->serializeToHttpRequest(
384  $this->parsedUrl['path'],
385  $this->parsedUrl['host']
386  );
387  }
388 
389  foreach ( $this->reqHeaders as $name => $value ) {
390  $list[] = "$name: $value";
391  }
392 
393  return $list;
394  }
395 
414  public function setCallback( $callback ) {
415  if ( !is_callable( $callback ) ) {
416  throw new MWException( 'Invalid MwHttpRequest callback' );
417  }
418  $this->callback = $callback;
419  }
420 
429  public function read( $fh, $content ) {
430  $this->content .= $content;
431  return strlen( $content );
432  }
433 
439  public function execute() {
440  wfProfileIn( __METHOD__ );
441 
442  $this->content = "";
443 
444  if ( strtoupper( $this->method ) == "HEAD" ) {
445  $this->headersOnly = true;
446  }
447 
448  $this->proxySetup(); // set up any proxy as needed
449 
450  if ( !$this->callback ) {
451  $this->setCallback( array( $this, 'read' ) );
452  }
453 
454  if ( !isset( $this->reqHeaders['User-Agent'] ) ) {
455  $this->setUserAgent( Http::userAgent() );
456  }
457 
458  wfProfileOut( __METHOD__ );
459  }
460 
466  protected function parseHeader() {
467  wfProfileIn( __METHOD__ );
468 
469  $lastname = "";
470 
471  foreach ( $this->headerList as $header ) {
472  if ( preg_match( "#^HTTP/([0-9.]+) (.*)#", $header, $match ) ) {
473  $this->respVersion = $match[1];
474  $this->respStatus = $match[2];
475  } elseif ( preg_match( "#^[ \t]#", $header ) ) {
476  $last = count( $this->respHeaders[$lastname] ) - 1;
477  $this->respHeaders[$lastname][$last] .= "\r\n$header";
478  } elseif ( preg_match( "#^([^:]*):[\t ]*(.*)#", $header, $match ) ) {
479  $this->respHeaders[strtolower( $match[1] )][] = $match[2];
480  $lastname = strtolower( $match[1] );
481  }
482  }
483 
484  $this->parseCookies();
485 
486  wfProfileOut( __METHOD__ );
487  }
488 
497  protected function setStatus() {
498  if ( !$this->respHeaders ) {
499  $this->parseHeader();
500  }
501 
502  if ( (int)$this->respStatus > 399 ) {
503  list( $code, $message ) = explode( " ", $this->respStatus, 2 );
504  $this->status->fatal( "http-bad-status", $code, $message );
505  }
506  }
507 
515  public function getStatus() {
516  if ( !$this->respHeaders ) {
517  $this->parseHeader();
518  }
519 
520  return (int)$this->respStatus;
521  }
522 
528  public function isRedirect() {
529  if ( !$this->respHeaders ) {
530  $this->parseHeader();
531  }
532 
533  $status = (int)$this->respStatus;
534 
535  if ( $status >= 300 && $status <= 303 ) {
536  return true;
537  }
538 
539  return false;
540  }
541 
550  public function getResponseHeaders() {
551  if ( !$this->respHeaders ) {
552  $this->parseHeader();
553  }
554 
555  return $this->respHeaders;
556  }
557 
564  public function getResponseHeader( $header ) {
565  if ( !$this->respHeaders ) {
566  $this->parseHeader();
567  }
568 
569  if ( isset( $this->respHeaders[strtolower( $header )] ) ) {
570  $v = $this->respHeaders[strtolower( $header )];
571  return $v[count( $v ) - 1];
572  }
573 
574  return null;
575  }
576 
582  public function setCookieJar( $jar ) {
583  $this->cookieJar = $jar;
584  }
585 
591  public function getCookieJar() {
592  if ( !$this->respHeaders ) {
593  $this->parseHeader();
594  }
595 
596  return $this->cookieJar;
597  }
598 
608  public function setCookie( $name, $value = null, $attr = null ) {
609  if ( !$this->cookieJar ) {
610  $this->cookieJar = new CookieJar;
611  }
612 
613  $this->cookieJar->setCookie( $name, $value, $attr );
614  }
615 
619  protected function parseCookies() {
620  wfProfileIn( __METHOD__ );
621 
622  if ( !$this->cookieJar ) {
623  $this->cookieJar = new CookieJar;
624  }
625 
626  if ( isset( $this->respHeaders['set-cookie'] ) ) {
627  $url = parse_url( $this->getFinalUrl() );
628  foreach ( $this->respHeaders['set-cookie'] as $cookie ) {
629  $this->cookieJar->parseCookieResponseHeader( $cookie, $url['host'] );
630  }
631  }
632 
633  wfProfileOut( __METHOD__ );
634  }
635 
652  public function getFinalUrl() {
653  $headers = $this->getResponseHeaders();
654 
655  //return full url (fix for incorrect but handled relative location)
656  if ( isset( $headers['location'] ) ) {
657  $locations = $headers['location'];
658  $domain = '';
659  $foundRelativeURI = false;
660  $countLocations = count( $locations );
661 
662  for ( $i = $countLocations - 1; $i >= 0; $i-- ) {
663  $url = parse_url( $locations[$i] );
664 
665  if ( isset( $url['host'] ) ) {
666  $domain = $url['scheme'] . '://' . $url['host'];
667  break; //found correct URI (with host)
668  } else {
669  $foundRelativeURI = true;
670  }
671  }
672 
673  if ( $foundRelativeURI ) {
674  if ( $domain ) {
675  return $domain . $locations[$countLocations - 1];
676  } else {
677  $url = parse_url( $this->url );
678  if ( isset( $url['host'] ) ) {
679  return $url['scheme'] . '://' . $url['host'] .
680  $locations[$countLocations - 1];
681  }
682  }
683  } else {
684  return $locations[$countLocations - 1];
685  }
686  }
687 
688  return $this->url;
689  }
690 
696  public function canFollowRedirects() {
697  return true;
698  }
699 }
700 
705  const SUPPORTS_FILE_POSTS = true;
706 
707  protected $curlOptions = array();
708  protected $headerText = "";
709 
715  protected function readHeader( $fh, $content ) {
716  $this->headerText .= $content;
717  return strlen( $content );
718  }
719 
720  public function execute() {
721  wfProfileIn( __METHOD__ );
722 
723  parent::execute();
724 
725  if ( !$this->status->isOK() ) {
726  wfProfileOut( __METHOD__ );
727  return $this->status;
728  }
729 
730  $this->curlOptions[CURLOPT_PROXY] = $this->proxy;
731  $this->curlOptions[CURLOPT_TIMEOUT] = $this->timeout;
732 
733  // Only supported in curl >= 7.16.2
734  if ( defined( 'CURLOPT_CONNECTTIMEOUT_MS' ) ) {
735  $this->curlOptions[CURLOPT_CONNECTTIMEOUT_MS] = $this->connectTimeout * 1000;
736  }
737 
738  $this->curlOptions[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_1_0;
739  $this->curlOptions[CURLOPT_WRITEFUNCTION] = $this->callback;
740  $this->curlOptions[CURLOPT_HEADERFUNCTION] = array( $this, "readHeader" );
741  $this->curlOptions[CURLOPT_MAXREDIRS] = $this->maxRedirects;
742  $this->curlOptions[CURLOPT_ENCODING] = ""; # Enable compression
743 
744  $this->curlOptions[CURLOPT_USERAGENT] = $this->reqHeaders['User-Agent'];
745 
746  $this->curlOptions[CURLOPT_SSL_VERIFYHOST] = $this->sslVerifyHost ? 2 : 0;
747  $this->curlOptions[CURLOPT_SSL_VERIFYPEER] = $this->sslVerifyCert;
748 
749  if ( $this->caInfo ) {
750  $this->curlOptions[CURLOPT_CAINFO] = $this->caInfo;
751  }
752 
753  if ( $this->headersOnly ) {
754  $this->curlOptions[CURLOPT_NOBODY] = true;
755  $this->curlOptions[CURLOPT_HEADER] = true;
756  } elseif ( $this->method == 'POST' ) {
757  $this->curlOptions[CURLOPT_POST] = true;
759  // Don't interpret POST parameters starting with '@' as file uploads, because this
760  // makes it impossible to POST plain values starting with '@' (and causes security
761  // issues potentially exposing the contents of local files).
762  // The PHP manual says this option was introduced in PHP 5.5 defaults to true in PHP 5.6,
763  // but we support lower versions, and the option doesn't exist in HHVM 5.6.99.
764  if ( defined( 'CURLOPT_SAFE_UPLOAD' ) ) {
765  $this->curlOptions[CURLOPT_SAFE_UPLOAD] = true;
766  } else if ( is_array( $postData ) ) {
767  // In PHP 5.2 and later, '@' is interpreted as a file upload if POSTFIELDS
768  // is an array, but not if it's a string. So convert $req['body'] to a string
769  // for safety.
771  }
772  $this->curlOptions[CURLOPT_POSTFIELDS] = $postData;
773 
774  // Suppress 'Expect: 100-continue' header, as some servers
775  // will reject it with a 417 and Curl won't auto retry
776  // with HTTP 1.0 fallback
777  $this->reqHeaders['Expect'] = '';
778  } else {
779  $this->curlOptions[CURLOPT_CUSTOMREQUEST] = $this->method;
780  }
781 
782  $this->curlOptions[CURLOPT_HTTPHEADER] = $this->getHeaderList();
783 
784  $curlHandle = curl_init( $this->url );
785 
786  if ( !curl_setopt_array( $curlHandle, $this->curlOptions ) ) {
787  wfProfileOut( __METHOD__ );
788  throw new MWException( "Error setting curl options." );
789  }
790 
791  if ( $this->followRedirects && $this->canFollowRedirects() ) {
793  if ( ! curl_setopt( $curlHandle, CURLOPT_FOLLOWLOCATION, true ) ) {
794  wfDebug( __METHOD__ . ": Couldn't set CURLOPT_FOLLOWLOCATION. " .
795  "Probably safe_mode or open_basedir is set.\n" );
796  // Continue the processing. If it were in curl_setopt_array,
797  // processing would have halted on its entry
798  }
800  }
801 
802  $curlRes = curl_exec( $curlHandle );
803  if ( curl_errno( $curlHandle ) == CURLE_OPERATION_TIMEOUTED ) {
804  $this->status->fatal( 'http-timed-out', $this->url );
805  } elseif ( $curlRes === false ) {
806  $this->status->fatal( 'http-curl-error', curl_error( $curlHandle ) );
807  } else {
808  $this->headerList = explode( "\r\n", $this->headerText );
809  }
810 
811  curl_close( $curlHandle );
812 
813  $this->parseHeader();
814  $this->setStatus();
815 
816  wfProfileOut( __METHOD__ );
817 
818  return $this->status;
819  }
820 
824  public function canFollowRedirects() {
825  if ( strval( ini_get( 'open_basedir' ) ) !== '' || wfIniGetBool( 'safe_mode' ) ) {
826  wfDebug( "Cannot follow redirects in safe mode\n" );
827  return false;
828  }
829 
830  if ( !defined( 'CURLOPT_REDIR_PROTOCOLS' ) ) {
831  wfDebug( "Cannot follow redirects with libcurl < 7.19.4 due to CVE-2009-0037\n" );
832  return false;
833  }
834 
835  return true;
836  }
837 }
838 
839 class PhpHttpRequest extends MWHttpRequest {
840 
841  private $fopenErrors = array();
842 
847  protected function urlToTcp( $url ) {
848  $parsedUrl = parse_url( $url );
849 
850  return 'tcp://' . $parsedUrl['host'] . ':' . $parsedUrl['port'];
851  }
852 
860  protected function getCertOptions() {
861  $certOptions = array();
862  $certLocations = array();
863  if ( $this->caInfo ) {
864  $certLocations = array( 'manual' => $this->caInfo );
865  } elseif ( version_compare( PHP_VERSION, '5.6.0', '<' ) ) {
866  // Default locations, based on
867  // https://www.happyassassin.net/2015/01/12/a-note-about-ssltls-trusted-certificate-stores-and-platforms/
868  // PHP 5.5 and older doesn't have any defaults, so we try to guess ourselves. PHP 5.6+ gets the CA location
869  // from OpenSSL as long as it is not set manually, so we should leave capath/cafile empty there.
870  $certLocations = array_filter( array(
871  getenv( 'SSL_CERT_DIR' ),
872  getenv( 'SSL_CERT_PATH' ),
873  '/etc/pki/tls/certs/ca-bundle.crt', # Fedora et al
874  '/etc/ssl/certs', # Debian et al
875  '/etc/pki/tls/certs/ca-bundle.trust.crt',
876  '/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem',
877  '/System/Library/OpenSSL', # OSX
878  ) );
879  }
880 
881  foreach( $certLocations as $key => $cert ) {
882  if ( is_dir( $cert ) ) {
883  $certOptions['capath'] = $cert;
884  break;
885  } elseif ( is_file( $cert ) ) {
886  $certOptions['cafile'] = $cert;
887  break;
888  } elseif ( $key === 'manual' ) {
889  // fail more loudly if a cert path was manually configured and it is not valid
890  throw new DomainException( "Invalid CA info passed: $cert" );
891  }
892  }
893 
894  return $certOptions;
895  }
896 
902  public function errorHandler( $errno, $errstr ) {
903  $n = count( $this->fopenErrors ) + 1;
904  $this->fopenErrors += array( "errno$n" => $errno, "errstr$n" => $errstr );
905  }
906 
907  public function execute() {
908  wfProfileIn( __METHOD__ );
909 
910  parent::execute();
911 
912  if ( is_array( $this->postData ) ) {
913  $this->postData = wfArrayToCgi( $this->postData );
914  }
915 
916  if ( $this->parsedUrl['scheme'] != 'http'
917  && $this->parsedUrl['scheme'] != 'https' ) {
918  $this->status->fatal( 'http-invalid-scheme', $this->parsedUrl['scheme'] );
919  }
920 
921  $this->reqHeaders['Accept'] = "*/*";
922  $this->reqHeaders['Connection'] = 'Close';
923  if ( $this->method == 'POST' ) {
924  // Required for HTTP 1.0 POSTs
925  $this->reqHeaders['Content-Length'] = strlen( $this->postData );
926  if ( !isset( $this->reqHeaders['Content-Type'] ) ) {
927  $this->reqHeaders['Content-Type'] = "application/x-www-form-urlencoded";
928  }
929  }
930 
931  // Set up PHP stream context
932  $options = array(
933  'http' => array(
934  'method' => $this->method,
935  'header' => implode( "\r\n", $this->getHeaderList() ),
936  'protocol_version' => '1.1',
937  'max_redirects' => $this->followRedirects ? $this->maxRedirects : 0,
938  'ignore_errors' => true,
939  'timeout' => $this->timeout,
940  // Curl options in case curlwrappers are installed
941  'curl_verify_ssl_host' => $this->sslVerifyHost ? 2 : 0,
942  'curl_verify_ssl_peer' => $this->sslVerifyCert,
943  ),
944  'ssl' => array(
945  'verify_peer' => $this->sslVerifyCert,
946  'SNI_enabled' => true,
947  ),
948  );
949 
950  if ( $this->proxy ) {
951  $options['http']['proxy'] = $this->urlToTCP( $this->proxy );
952  $options['http']['request_fulluri'] = true;
953  }
954 
955  if ( $this->postData ) {
956  $options['http']['content'] = $this->postData;
957  }
958 
959  if ( $this->sslVerifyHost ) {
960  // PHP 5.6.0 deprecates CN_match, in favour of peer_name which
961  // actually checks SubjectAltName properly.
962  if ( version_compare( PHP_VERSION, '5.6.0', '>=' ) ) {
963  $options['ssl']['peer_name'] = $this->parsedUrl['host'];
964  } else {
965  $options['ssl']['CN_match'] = $this->parsedUrl['host'];
966  }
967  }
968 
969  $options['ssl'] += $this->getCertOptions();
970 
971  $context = stream_context_create( $options );
972 
973  $this->headerList = array();
974  $reqCount = 0;
975  $url = $this->url;
976 
977  $result = array();
978 
979  do {
980  $reqCount++;
981  $this->fopenErrors = array();
982  set_error_handler( array( $this, 'errorHandler' ) );
983  $fh = fopen( $url, "r", false, $context );
984  restore_error_handler();
985 
986  if ( !$fh ) {
987  // HACK for instant commons.
988  // If we are contacting (commons|upload).wikimedia.org
989  // try again with CN_match for en.wikipedia.org
990  // as php does not handle SubjectAltName properly
991  // prior to "peer_name" option in php 5.6
992  if ( isset( $options['ssl']['CN_match'] )
993  && ( $options['ssl']['CN_match'] === 'commons.wikimedia.org'
994  || $options['ssl']['CN_match'] === 'upload.wikimedia.org' )
995  ) {
996  $options['ssl']['CN_match'] = 'en.wikipedia.org';
997  $context = stream_context_create( $options );
998  continue;
999  }
1000  break;
1001  }
1002 
1003  $result = stream_get_meta_data( $fh );
1004  $this->headerList = $result['wrapper_data'];
1005  $this->parseHeader();
1006 
1007  if ( !$this->followRedirects ) {
1008  break;
1009  }
1010 
1011  # Handle manual redirection
1012  if ( !$this->isRedirect() || $reqCount > $this->maxRedirects ) {
1013  break;
1014  }
1015  # Check security of URL
1016  $url = $this->getResponseHeader( "Location" );
1017 
1018  if ( !Http::isValidURI( $url ) ) {
1019  wfDebug( __METHOD__ . ": insecure redirection\n" );
1020  break;
1021  }
1022  } while ( true );
1023 
1024  $this->setStatus();
1025 
1026  if ( $fh === false ) {
1027  if ( $this->fopenErrors ) {
1028  LoggerFactory::getInstance( 'http' )->warning( __CLASS__
1029  . ': error opening connection: {errstr1}', $this->fopenErrors );
1030  }
1031  $this->status->fatal( 'http-request-error' );
1032  wfProfileOut( __METHOD__ );
1033  return $this->status;
1034  }
1035 
1036  if ( $result['timed_out'] ) {
1037  $this->status->fatal( 'http-timed-out', $this->url );
1038  wfProfileOut( __METHOD__ );
1039  return $this->status;
1040  }
1041 
1042  // If everything went OK, or we received some error code
1043  // get the response body content.
1044  if ( $this->status->isOK() || (int)$this->respStatus >= 300 ) {
1045  while ( !feof( $fh ) ) {
1046  $buf = fread( $fh, 8192 );
1047 
1048  if ( $buf === false ) {
1049  $this->status->fatal( 'http-read-error' );
1050  break;
1051  }
1052 
1053  if ( strlen( $buf ) ) {
1054  call_user_func( $this->callback, $fh, $buf );
1055  }
1056  }
1057  }
1058  fclose( $fh );
1059 
1060  wfProfileOut( __METHOD__ );
1061 
1062  return $this->status;
1063  }
1064 }
MWHttpRequest\$headerList
$headerList
Definition: HttpFunctions.php:216
$result
The index of the header message $result[1]=The index of the body text message $result[2 through n]=Parameters passed to body text message. Please note the header message cannot receive/use parameters. 'ImportHandleLogItemXMLTag':When parsing a XML tag in a log item. $reader:XMLReader object $logInfo:Array of information Return false to stop further processing of the tag 'ImportHandlePageXMLTag':When parsing a XML tag in a page. $reader:XMLReader object $pageInfo:Array of information Return false to stop further processing of the tag 'ImportHandleRevisionXMLTag':When parsing a XML tag in a page revision. $reader:XMLReader object $pageInfo:Array of page information $revisionInfo:Array of revision information Return false to stop further processing of the tag 'ImportHandleToplevelXMLTag':When parsing a top level XML tag. $reader:XMLReader object Return false to stop further processing of the tag 'ImportHandleUploadXMLTag':When parsing a XML tag in a file upload. $reader:XMLReader object $revisionInfo:Array of information Return false to stop further processing of the tag 'InfoAction':When building information to display on the action=info page. $context:IContextSource object & $pageInfo:Array of information 'InitializeArticleMaybeRedirect':MediaWiki check to see if title is a redirect. $title:Title object for the current page $request:WebRequest $ignoreRedirect:boolean to skip redirect check $target:Title/string of redirect target $article:Article object 'InterwikiLoadPrefix':When resolving if a given prefix is an interwiki or not. Return true without providing an interwiki to continue interwiki search. $prefix:interwiki prefix we are looking for. & $iwData:output array describing the interwiki with keys iw_url, iw_local, iw_trans and optionally iw_api and iw_wikiid. 'InternalParseBeforeSanitize':during Parser 's internalParse method just before the parser removes unwanted/dangerous HTML tags and after nowiki/noinclude/includeonly/onlyinclude and other processings. Ideal for syntax-extensions after template/parser function execution which respect nowiki and HTML-comments. & $parser:Parser object & $text:string containing partially parsed text & $stripState:Parser 's internal StripState object 'InternalParseBeforeLinks':during Parser 's internalParse method before links but after nowiki/noinclude/includeonly/onlyinclude and other processings. & $parser:Parser object & $text:string containing partially parsed text & $stripState:Parser 's internal StripState object 'InvalidateEmailComplete':Called after a user 's email has been invalidated successfully. $user:user(object) whose email is being invalidated 'IRCLineURL':When constructing the URL to use in an IRC notification. Callee may modify $url and $query, URL will be constructed as $url . $query & $url:URL to index.php & $query:Query string $rc:RecentChange object that triggered url generation 'IsFileCacheable':Override the result of Article::isFileCacheable()(if true) $article:article(object) being checked 'IsTrustedProxy':Override the result of wfIsTrustedProxy() $ip:IP being check $result:Change this value to override the result of wfIsTrustedProxy() 'IsUploadAllowedFromUrl':Override the result of UploadFromUrl::isAllowedUrl() $url:URL used to upload from & $allowed:Boolean indicating if uploading is allowed for given URL 'isValidEmailAddr':Override the result of User::isValidEmailAddr(), for instance to return false if the domain name doesn 't match your organization. $addr:The e-mail address entered by the user & $result:Set this and return false to override the internal checks 'isValidPassword':Override the result of User::isValidPassword() $password:The password entered by the user & $result:Set this and return false to override the internal checks $user:User the password is being validated for 'Language::getMessagesFileName':$code:The language code or the language we 're looking for a messages file for & $file:The messages file path, you can override this to change the location. 'LanguageGetNamespaces':Provide custom ordering for namespaces or remove namespaces. Do not use this hook to add namespaces. Use CanonicalNamespaces for that. & $namespaces:Array of namespaces indexed by their numbers 'LanguageGetMagic':DEPRECATED, use $magicWords in a file listed in $wgExtensionMessagesFiles instead. Use this to define synonyms of magic words depending of the language $magicExtensions:associative array of magic words synonyms $lang:language code(string) 'LanguageGetSpecialPageAliases':DEPRECATED, use $specialPageAliases in a file listed in $wgExtensionMessagesFiles instead. Use to define aliases of special pages names depending of the language $specialPageAliases:associative array of magic words synonyms $lang:language code(string) 'LanguageGetTranslatedLanguageNames':Provide translated language names. & $names:array of language code=> language name $code language of the preferred translations 'LanguageLinks':Manipulate a page 's language links. This is called in various places to allow extensions to define the effective language links for a page. $title:The page 's Title. & $links:Associative array mapping language codes to prefixed links of the form "language:title". & $linkFlags:Associative array mapping prefixed links to arrays of flags. Currently unused, but planned to provide support for marking individual language links in the UI, e.g. for featured articles. 'LinkBegin':Used when generating internal and interwiki links in Linker::link(), before processing starts. Return false to skip default processing and return $ret. See documentation for Linker::link() for details on the expected meanings of parameters. $skin:the Skin object $target:the Title that the link is pointing to & $html:the contents that the< a > tag should have(raw HTML) $result
Definition: hooks.txt:1528
MWHttpRequest\$headersOnly
$headersOnly
Definition: HttpFunctions.php:197
PhpHttpRequest\$fopenErrors
$fopenErrors
Definition: HttpFunctions.php:840
Http\$httpEngine
static $httpEngine
Definition: HttpFunctions.php:35
php
skin txt MediaWiki includes four core it has been set as the default in MediaWiki since the replacing Monobook it had been been the default skin since before being replaced by Vector largely rewritten in while keeping its appearance Several legacy skins were removed in the as the burden of supporting them became too heavy to bear Those in etc for skin dependent CSS etc for skin dependent JavaScript These can also be customised on a per user by etc This feature has led to a wide variety of user styles becoming that gallery is a good place to ending in php
Definition: skin.txt:62
content
per default it will return the text for text based content
Definition: contenthandler.txt:107
MWHttpRequest\setStatus
setStatus()
Sets HTTPRequest status member to a fatal value with the error message if the returned integer value ...
Definition: HttpFunctions.php:496
MWHttpRequest\$respVersion
$respVersion
Definition: HttpFunctions.php:217
MWHttpRequest\proxySetup
proxySetup()
Take care of setting up the proxy (do nothing if "noProxy" is set)
Definition: HttpFunctions.php:338
$last
$last
Definition: profileinfo.php:365
MWHttpRequest\$maxRedirects
$maxRedirects
Definition: HttpFunctions.php:209
MWHttpRequest\$sslVerifyCert
$sslVerifyCert
Definition: HttpFunctions.php:202
Http\userAgent
static userAgent()
A standard user-agent we can use for external requests.
Definition: HttpFunctions.php:160
MWHttpRequest\$followRedirects
$followRedirects
Definition: HttpFunctions.php:210
wfProfileIn
wfProfileIn( $functionname)
Begin profiling of a function.
Definition: Profiler.php:33
$n
$n
Definition: RandomTest.php:76
wfSuppressWarnings
wfSuppressWarnings( $end=false)
Reference-counted warning suppression.
Definition: GlobalFunctions.php:2434
Debian
MediaWiki has optional support for a high distributed memory object caching system For general information on but for a larger site with heavy like it should help lighten the load on the database servers by caching data and objects in Debian
Definition: memcached.txt:10
Status\newGood
static newGood( $value=null)
Factory function for good results.
Definition: Status.php:77
Http\isLocalURL
static isLocalURL( $url)
Check if the URL can be served by localhost.
Definition: HttpFunctions.php:121
MWHttpRequest\setData
setData( $args)
Set the parameters of the request.
Definition: HttpFunctions.php:329
MWHttpRequest\$content
$content
Definition: HttpFunctions.php:195
CurlHttpRequest\$headerText
$headerText
Definition: HttpFunctions.php:707
CurlHttpRequest
MWHttpRequest implemented using internal curl compiled into PHP.
Definition: HttpFunctions.php:703
MWHttpRequest\$timeout
$timeout
Definition: HttpFunctions.php:196
PhpHttpRequest\errorHandler
errorHandler( $errno, $errstr)
Custom error handler for dealing with fopen() errors.
Definition: HttpFunctions.php:901
MWHttpRequest\$status
$status
Definition: HttpFunctions.php:221
PhpHttpRequest\urlToTcp
urlToTcp( $url)
Definition: HttpFunctions.php:846
MWHttpRequest\getStatus
getStatus()
Get the integer value of the HTTP status code (e.g.
Definition: HttpFunctions.php:514
MWHttpRequest\$noProxy
$noProxy
Definition: HttpFunctions.php:200
MWHttpRequest\__construct
__construct( $url, $options=array())
Definition: HttpFunctions.php:227
MWHttpRequest\parseHeader
parseHeader()
Parses the headers, including the HTTP status code and any Set-Cookie headers.
Definition: HttpFunctions.php:465
wfParseUrl
wfParseUrl( $url)
parse_url() work-alike, but non-broken.
Definition: GlobalFunctions.php:802
MWHttpRequest\$postData
$postData
Definition: HttpFunctions.php:198
Http\request
static request( $method, $url, $options=array())
Perform an HTTP request.
Definition: HttpFunctions.php:61
MWHttpRequest\getContent
getContent()
Get the body, or content, of the response to the request.
Definition: HttpFunctions.php:319
MWException
MediaWiki exception.
Definition: MWException.php:26
MWHttpRequest\SUPPORTS_FILE_POSTS
const SUPPORTS_FILE_POSTS
Definition: HttpFunctions.php:193
MediaWiki\Logger\LoggerFactory
Backwards compatible PSR-3 logger instance factory.
Definition: LoggerFactory.php:36
wfRestoreWarnings
wfRestoreWarnings()
Restore error level to previous value.
Definition: GlobalFunctions.php:2464
MWHttpRequest\parseCookies
parseCookies()
Parse the cookies in the response headers and store them in the cookie jar.
Definition: HttpFunctions.php:618
MWHttpRequest\$respStatus
$respStatus
Definition: HttpFunctions.php:218
CookieJar\setCookie
setCookie( $name, $value, $attr)
Set a cookie in the cookie jar.
Definition: Cookie.php:214
$wgCommandLineMode
global $wgCommandLineMode
Definition: Setup.php:408
MWHttpRequest\isRedirect
isRedirect()
Returns true if the last status code was a redirect.
Definition: HttpFunctions.php:527
wfProfileOut
wfProfileOut( $functionname='missing')
Stop profiling of a function.
Definition: Profiler.php:46
MWHttpRequest\getCookieJar
getCookieJar()
Returns the cookie jar in use.
Definition: HttpFunctions.php:590
MWHttpRequest\$reqHeaders
$reqHeaders
Definition: HttpFunctions.php:205
MWHttpRequest\setCookieJar
setCookieJar( $jar)
Tells the MWHttpRequest object to use this pre-loaded CookieJar.
Definition: HttpFunctions.php:581
array
the array() calling protocol came about after MediaWiki 1.4rc1.
List of Api Query prop modules.
MWHttpRequest\$respHeaders
$respHeaders
Definition: HttpFunctions.php:219
global
when a variable name is used in a it is silently declared as a new masking the global
Definition: design.txt:93
MWHttpRequest\$cookieJar
CookieJar $cookieJar
Definition: HttpFunctions.php:214
PhpHttpRequest\execute
execute()
Take care of whatever is necessary to perform the URI request.
Definition: HttpFunctions.php:906
list
deferred txt A few of the database updates required by various functions here can be deferred until after the result page is displayed to the user For updating the view updating the linked to tables after a etc PHP does not yet have any way to tell the server to actually return and disconnect while still running these but it might have such a feature in the future We handle these by creating a deferred update object and putting those objects on a global list
Definition: deferred.txt:11
$options
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped & $options
Definition: hooks.txt:1530
MWHttpRequest\read
read( $fh, $content)
A generic callback to read the body of the response from a remote server.
Definition: HttpFunctions.php:428
execute
$batch execute()
wfDebug
wfDebug( $text, $dest='all')
Sends a line to the debug log if enabled or, optionally, to a comment in output.
Definition: GlobalFunctions.php:980
CurlHttpRequest\execute
execute()
Take care of whatever is necessary to perform the URI request.
Definition: HttpFunctions.php:719
$name
Allows to change the fields on the form that will be generated $name
Definition: hooks.txt:336
$matches
if(!defined( 'MEDIAWIKI')) if(!isset( $wgVersion)) $matches
Definition: NoLocalSettings.php:33
$value
$value
Definition: styleTest.css.php:45
MWHttpRequest\$method
$method
Definition: HttpFunctions.php:204
MWHttpRequest
This wrapper class will call out to curl (if available) or fallback to regular PHP if necessary for h...
Definition: HttpFunctions.php:192
MWHttpRequest\setCallback
setCallback( $callback)
Set a read callback to accept data read from the HTTP request.
Definition: HttpFunctions.php:413
MWHttpRequest\setHeader
setHeader( $name, $value)
Set an arbitrary header.
Definition: HttpFunctions.php:368
PROTO_HTTP
const PROTO_HTTP
Definition: Defines.php:267
MWHttpRequest\getHeaderList
getHeaderList()
Get an array of the headers.
Definition: HttpFunctions.php:377
MWHttpRequest\$parsedUrl
$parsedUrl
Definition: HttpFunctions.php:207
MWHttpRequest\setCookie
setCookie( $name, $value=null, $attr=null)
Sets a cookie.
Definition: HttpFunctions.php:607
MWHttpRequest\getResponseHeaders
getResponseHeaders()
Returns an associative array of response headers after the request has been executed.
Definition: HttpFunctions.php:549
wfIniGetBool
wfIniGetBool( $setting)
Safety wrapper around ini_get() for boolean settings.
Definition: GlobalFunctions.php:2732
MWHttpRequest\$sslVerifyHost
$sslVerifyHost
Definition: HttpFunctions.php:201
$args
if( $line===false) $args
Definition: cdb.php:62
CurlHttpRequest\$curlOptions
$curlOptions
Definition: HttpFunctions.php:706
PhpHttpRequest\getCertOptions
getCertOptions()
Returns an array with a 'capath' or 'cafile' key that is suitable to be merged into the 'ssl' sub-arr...
Definition: HttpFunctions.php:859
CookieJar
Definition: Cookie.php:207
MWHttpRequest\$caInfo
$caInfo
Definition: HttpFunctions.php:203
MWHttpRequest\canMakeRequests
static canMakeRequests()
Simple function to test if we can make any sort of requests at all, using cURL or fopen()
Definition: HttpFunctions.php:277
CurlHttpRequest\canFollowRedirects
canFollowRedirects()
Definition: HttpFunctions.php:823
as
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such as
Definition: distributors.txt:9
CurlHttpRequest\SUPPORTS_FILE_POSTS
const SUPPORTS_FILE_POSTS
Definition: HttpFunctions.php:704
MWHttpRequest\getResponseHeader
getResponseHeader( $header)
Returns the value of the given response header.
Definition: HttpFunctions.php:563
MWHttpRequest\setUserAgent
setUserAgent( $UA)
Set the user agent.
Definition: HttpFunctions.php:359
MWHttpRequest\execute
execute()
Take care of whatever is necessary to perform the URI request.
Definition: HttpFunctions.php:438
Http\isValidURI
static isValidURI( $uri)
Checks that the given URI is a valid one.
Definition: HttpFunctions.php:177
CurlHttpRequest\readHeader
readHeader( $fh, $content)
Definition: HttpFunctions.php:714
MWHttpRequest\canFollowRedirects
canFollowRedirects()
Returns true if the backend can follow redirects.
Definition: HttpFunctions.php:695
PhpHttpRequest
Definition: HttpFunctions.php:838
MWHttpRequest\$callback
$callback
Definition: HttpFunctions.php:208
MWHttpRequest\$proxy
$proxy
Definition: HttpFunctions.php:199
MWHttpRequest\factory
static factory( $url, $options=null)
Generate a new request object.
Definition: HttpFunctions.php:289
MWHttpRequest\getFinalUrl
getFinalUrl()
Returns the final URL after all redirections.
Definition: HttpFunctions.php:651
Http
Various HTTP related functions.
Definition: HttpFunctions.php:34
MWHttpRequest\$url
$url
Definition: HttpFunctions.php:206
wfExpandUrl
wfExpandUrl( $url, $defaultProto=PROTO_CURRENT)
Expand a potentially local URL to a fully-qualified URL.
Definition: GlobalFunctions.php:544
Http\post
static post( $url, $options=array())
Simple wrapper for Http::request( 'POST' )
Definition: HttpFunctions.php:111
Status\newFatal
static newFatal( $message)
Factory function for fatal errors.
Definition: Status.php:63
wfArrayToCgi
wfArrayToCgi( $array1, $array2=null, $prefix='')
This function takes two arrays as input, and returns a CGI-style string, e.g.
Definition: GlobalFunctions.php:414