diff --git a/src/aphront/AphrontRequest.php b/src/aphront/AphrontRequest.php index 68c0c16b16..1d940c32d1 100644 --- a/src/aphront/AphrontRequest.php +++ b/src/aphront/AphrontRequest.php @@ -1,433 +1,435 @@ host = $host; $this->path = $path; } final public function setApplicationConfiguration( $application_configuration) { $this->applicationConfiguration = $application_configuration; return $this; } final public function getApplicationConfiguration() { return $this->applicationConfiguration; } final public function setPath($path) { $this->path = $path; return $this; } final public function getPath() { return $this->path; } final public function getHost() { // The "Host" header may include a port number, or may be a malicious // header in the form "realdomain.com:ignored@evil.com". Invoke the full // parser to extract the real domain correctly. See here for coverage of // a similar issue in Django: // // https://www.djangoproject.com/weblog/2012/oct/17/security/ $uri = new PhutilURI('http://'.$this->host); return $uri->getDomain(); } /* -( Accessing Request Data )--------------------------------------------- */ /** * @task data */ final public function setRequestData(array $request_data) { $this->requestData = $request_data; return $this; } /** * @task data */ final public function getRequestData() { return $this->requestData; } /** * @task data */ final public function getInt($name, $default = null) { if (isset($this->requestData[$name])) { return (int)$this->requestData[$name]; } else { return $default; } } /** * @task data */ final public function getBool($name, $default = null) { if (isset($this->requestData[$name])) { if ($this->requestData[$name] === 'true') { return true; } else if ($this->requestData[$name] === 'false') { return false; } else { return (bool)$this->requestData[$name]; } } else { return $default; } } /** * @task data */ final public function getStr($name, $default = null) { if (isset($this->requestData[$name])) { $str = (string)$this->requestData[$name]; // Normalize newline craziness. $str = str_replace( array("\r\n", "\r"), array("\n", "\n"), $str); return $str; } else { return $default; } } /** * @task data */ final public function getArr($name, $default = array()) { if (isset($this->requestData[$name]) && is_array($this->requestData[$name])) { return $this->requestData[$name]; } else { return $default; } } /** * @task data */ final public function getStrList($name, $default = array()) { if (!isset($this->requestData[$name])) { return $default; } $list = $this->getStr($name); $list = preg_split('/[\s,]+/', $list, $limit = -1, PREG_SPLIT_NO_EMPTY); return $list; } /** * @task data */ final public function getExists($name) { return array_key_exists($name, $this->requestData); } final public function getFileExists($name) { return isset($_FILES[$name]) && (idx($_FILES[$name], 'error') !== UPLOAD_ERR_NO_FILE); } final public function isHTTPPost() { return ($_SERVER['REQUEST_METHOD'] == 'POST'); } final public function isAjax() { return $this->getExists(self::TYPE_AJAX); } final public function isJavelinWorkflow() { return $this->getExists(self::TYPE_WORKFLOW); } final public function isConduit() { return $this->getExists(self::TYPE_CONDUIT); } public static function getCSRFTokenName() { return '__csrf__'; } public static function getCSRFHeaderName() { return 'X-Phabricator-Csrf'; } final public function validateCSRF() { $token_name = self::getCSRFTokenName(); $token = $this->getStr($token_name); // No token in the request, check the HTTP header which is added for Ajax // requests. if (empty($token)) { - - // PHP mangles HTTP headers by uppercasing them and replacing hyphens with - // underscores, then prepending 'HTTP_'. - $php_index = self::getCSRFHeaderName(); - $php_index = strtoupper($php_index); - $php_index = str_replace('-', '_', $php_index); - $php_index = 'HTTP_'.$php_index; - - $token = idx($_SERVER, $php_index); + $token = self::getHTTPHeader(self::getCSRFHeaderName()); } $valid = $this->getUser()->validateCSRFToken($token); if (!$valid) { // Add some diagnostic details so we can figure out if some CSRF issues // are JS problems or people accessing Ajax URIs directly with their // browsers. if ($token) { $token_info = "with an invalid CSRF token"; } else { $token_info = "without a CSRF token"; } if ($this->isAjax()) { $more_info = "(This was an Ajax request, {$token_info}.)"; } else { $more_info = "(This was a web request, {$token_info}.)"; } // Give a more detailed explanation of how to avoid the exception // in developer mode. if (PhabricatorEnv::getEnvConfig('phabricator.developer-mode')) { $more_info = $more_info . "To avoid this error, use phabricator_form() to construct forms. " . "If you are already using phabricator_form(), make sure the form " . "'action' uses a relative URI (i.e., begins with a '/'). Forms " . "using absolute URIs do not include CSRF tokens, to prevent " . "leaking tokens to external sites.\n\n" . "If this page performs writes which do not require CSRF " . "protection (usually, filling caches or logging), you can use " . "AphrontWriteGuard::beginScopedUnguardedWrites() to temporarily " . "bypass CSRF protection while writing. You should use this only " . "for writes which can not be protected with normal CSRF " . "mechanisms.\n\n" . "Some UI elements (like PhabricatorActionListView) also have " . "methods which will allow you to render links as forms (like " . "setRenderAsForm(true))."; } // This should only be able to happen if you load a form, pull your // internet for 6 hours, and then reconnect and immediately submit, // but give the user some indication of what happened since the workflow // is incredibly confusing otherwise. throw new AphrontCSRFException( "The form you just submitted did not include a valid CSRF token. ". "This token is a technical security measure which prevents a ". "certain type of login hijacking attack. However, the token can ". "become invalid if you leave a page open for more than six hours ". "without a connection to the internet. To fix this problem: reload ". "the page, and then resubmit it. All data inserted to the form will ". "be lost in some browsers so copy them somewhere before reloading.\n\n". $more_info); } return true; } final public function isFormPost() { $post = $this->getExists(self::TYPE_FORM) && $this->isHTTPPost(); if (!$post) { return false; } return $this->validateCSRF(); } final public function getCookie($name, $default = null) { return idx($_COOKIE, $name, $default); } final public function clearCookie($name) { $this->setCookie($name, '', time() - (60 * 60 * 24 * 30)); } final public function setCookie($name, $value, $expire = null) { $is_secure = false; // If a base URI has been configured, ensure cookies are only set on that // domain. Also, use the URI protocol to control SSL-only cookies. $base_uri = PhabricatorEnv::getEnvConfig('phabricator.base-uri'); if ($base_uri) { $base_uri = new PhutilURI($base_uri); $base_domain = $base_uri->getDomain(); $base_protocol = $base_uri->getProtocol(); $host = $this->getHost(); if ($base_domain != $host) { throw new Exception( "This install of Phabricator is configured as '{$base_domain}' but ". "you are accessing it via '{$host}'. Access Phabricator via ". "the primary configured domain."); } $is_secure = ($base_protocol == 'https'); } else { $base_uri = new PhutilURI(PhabricatorEnv::getRequestBaseURI()); $base_domain = $base_uri->getDomain(); } if ($expire === null) { $expire = time() + (60 * 60 * 24 * 365 * 5); } setcookie( $name, $value, $expire, $path = '/', $base_domain, $is_secure, $http_only = true); return $this; } final public function setUser($user) { $this->user = $user; return $this; } final public function getUser() { return $this->user; } final public function getRequestURI() { $get = $_GET; unset($get['__path__']); $path = phutil_escape_uri($this->getPath()); return id(new PhutilURI($path))->setQueryParams($get); } final public function isDialogFormPost() { return $this->isFormPost() && $this->getStr('__dialog__'); } final public function getRemoteAddr() { return $_SERVER['REMOTE_ADDR']; } public function isHTTPS() { if (empty($_SERVER['HTTPS'])) { return false; } if (!strcasecmp($_SERVER["HTTPS"], "off")) { return false; } return true; } public function isContinueRequest() { return $this->isFormPost() && $this->getStr('__continue__'); } public function isPreviewRequest() { return $this->isFormPost() && $this->getStr('__preview__'); } /** * Get application request parameters in a flattened form suitable for * inclusion in an HTTP request, excluding parameters with special meanings. * This is primarily useful if you want to ask the user for more input and * then resubmit their request. * * @return dict Original request parameters. */ public function getPassthroughRequestParameters() { return self::flattenData($this->getPassthruRequestData()); } /** * Get request data other than "magic" parameters. * * @return dict Request data, with magic filtered out. */ public function getPassthroughRequestData() { $data = $this->getRequestData(); // Remove magic parameters like __dialog__ and __ajax__. foreach ($data as $key => $value) { if (strncmp($key, '__', 2)) { unset($data[$key]); } } return $data; } /** * Flatten an array of key-value pairs (possibly including arrays as values) * into a list of key-value pairs suitable for submitting via HTTP request * (with arrays flattened). * * @param dict Data to flatten. * @return dict Flat data suitable for inclusion in an HTTP * request. */ public static function flattenData(array $data) { $result = array(); foreach ($data as $key => $value) { if (is_array($value)) { foreach (self::flattenData($value) as $fkey => $fvalue) { $fkey = '['.preg_replace('/(?=\[)|$/', ']', $fkey, $limit = 1); $result[$key.$fkey] = $fvalue; } } else { $result[$key] = (string)$value; } } ksort($result); return $result; } + public static function getHTTPHeader($name, $default = null) { + // PHP mangles HTTP headers by uppercasing them and replacing hyphens with + // underscores, then prepending 'HTTP_'. + $php_index = strtoupper($name); + $php_index = str_replace('-', '_', $php_index); + $php_index = 'HTTP_'.$php_index; + + return idx($_SERVER, $php_index, $default); + } + } diff --git a/src/aphront/console/plugin/xhprof/DarkConsoleXHProfPluginAPI.php b/src/aphront/console/plugin/xhprof/DarkConsoleXHProfPluginAPI.php index 2eadc39068..e2f9cad21d 100644 --- a/src/aphront/console/plugin/xhprof/DarkConsoleXHProfPluginAPI.php +++ b/src/aphront/console/plugin/xhprof/DarkConsoleXHProfPluginAPI.php @@ -1,130 +1,127 @@ 'application/xhprof', 'name' => 'profile.xhprof', )); } catch (Exception $ex) { $caught = $ex; } if ($use_scope) { unset($unguarded); } else { AphrontWriteGuard::allowDangerousUnguardedWrites(false); } if ($caught) { throw $caught; } else { return $file->getPHID(); } } else { return null; } } } diff --git a/src/applications/config/check/PhabricatorSetupCheckBaseURI.php b/src/applications/config/check/PhabricatorSetupCheckBaseURI.php index 83c6fb2624..1ad78a6172 100644 --- a/src/applications/config/check/PhabricatorSetupCheckBaseURI.php +++ b/src/applications/config/check/PhabricatorSetupCheckBaseURI.php @@ -1,68 +1,68 @@ newIssue('config.phabricator.domain') ->setShortName(pht('Dotless Domain')) ->setName(pht('No Dot Character in Domain')) ->setSummary($summary) ->setMessage($message) ->setIsFatal(true); } if ($base_uri) { return; } $base_uri_guess = PhabricatorEnv::getRequestBaseURI(); $summary = pht( 'The base URI for this install is not configured. Configuring it will '. 'improve security and enable features.'); $message = pht( 'The base URI for this install is not configured. Configuring it will '. 'improve security and allow background processes (like daemons and '. 'scripts) to generate links.'. "\n\n". 'You should set the base URI to the URI you will use to access '. 'Phabricator, like "http://phabricator.example.com/".'. "\n\n". 'Include the protocol (http or https), domain name, and port number if '. 'you are using a port other than 80 (http) or 443 (https).'. "\n\n". 'Based on this request, it appears that the correct setting is:'. "\n\n". '%s'. "\n\n". 'To configure the base URI, run the command shown below.', $base_uri_guess); $this ->newIssue('config.phabricator.base-uri') ->setShortName(pht('No Base URI')) ->setName(pht("Base URI Not Configured")) ->setSummary($summary) ->setMessage($message) ->addCommand( hsprintf( 'phabricator/ $ %s', csprintf( './bin/config set phabricator.base-uri %s', $base_uri_guess))); } } diff --git a/src/applications/people/storage/PhabricatorUserLog.php b/src/applications/people/storage/PhabricatorUserLog.php index 9887f26911..20c35bbdec 100644 --- a/src/applications/people/storage/PhabricatorUserLog.php +++ b/src/applications/people/storage/PhabricatorUserLog.php @@ -1,103 +1,103 @@ setActorPHID($actor->getPHID()); } if ($user) { $log->setUserPHID($user->getPHID()); } else { $log->setUserPHID(''); } if ($action) { $log->setAction($action); } return $log; } public static function loadRecentEventsFromThisIP($action, $timespan) { return id(new PhabricatorUserLog())->loadAllWhere( 'action = %s AND remoteAddr = %s AND dateCreated > %d ORDER BY dateCreated DESC', $action, idx($_SERVER, 'REMOTE_ADDR'), time() - $timespan); } public function save() { if (!$this->remoteAddr) { $this->remoteAddr = idx($_SERVER, 'REMOTE_ADDR', ''); } if (!$this->session) { $this->setSession(idx($_COOKIE, 'phsid')); } $this->details['host'] = php_uname('n'); - $this->details['user_agent'] = idx($_SERVER, 'HTTP_USER_AGENT'); + $this->details['user_agent'] = AphrontRequest::getHTTPHeader('User-Agent'); return parent::save(); } public function setSession($session) { // Store the hash of the session, not the actual session key, so that // seeing the logs doesn't compromise all the sessions which appear in // them. This just prevents casual leaks, like in a screenshot. if (strlen($session)) { $this->session = PhabricatorHash::digest($session); } return $this; } public function getConfiguration() { return array( self::CONFIG_SERIALIZATION => array( 'oldValue' => self::SERIALIZATION_JSON, 'newValue' => self::SERIALIZATION_JSON, 'details' => self::SERIALIZATION_JSON, ), ) + parent::getConfiguration(); } } diff --git a/src/infrastructure/celerity/CelerityResourceController.php b/src/infrastructure/celerity/CelerityResourceController.php index b3cb96f3bb..8c72134875 100644 --- a/src/infrastructure/celerity/CelerityResourceController.php +++ b/src/infrastructure/celerity/CelerityResourceController.php @@ -1,99 +1,99 @@ getRootDirectory().$to_resource; } protected function serveResource($path, $package_hash = null) { // Sanity checking to keep this from exposing anything sensitive, since it // ultimately boils down to disk reads. if (preg_match('@(//|\.\.)@', $path)) { return new Aphront400Response(); } $type = CelerityResourceTransformer::getResourceType($path); $type_map = $this->getSupportedResourceTypes(); if (empty($type_map[$type])) { throw new Exception("Only static resources may be served."); } - if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && + if (AphrontRequest::getHTTPHeader('If-Modified-Since') && !PhabricatorEnv::getEnvConfig('phabricator.developer-mode')) { // Return a "304 Not Modified". We don't care about the value of this // field since we never change what resource is served by a given URI. return $this->makeResponseCacheable(new Aphront304Response()); } if ($package_hash) { $map = CelerityResourceMap::getInstance(); $paths = $map->resolvePackage($package_hash); if (!$paths) { return new Aphront404Response(); } try { $data = array(); foreach ($paths as $package_path) { $disk_path = $this->getDiskPath($package_path); $data[] = Filesystem::readFile($disk_path); } $data = implode("\n\n", $data); } catch (Exception $ex) { return new Aphront404Response(); } } else { try { $disk_path = $this->getDiskPath($path); $data = Filesystem::readFile($disk_path); } catch (Exception $ex) { return new Aphront404Response(); } } $xformer = $this->buildResourceTransformer(); if ($xformer) { $data = $xformer->transformResource($path, $data); } $response = new AphrontFileResponse(); $response->setContent($data); $response->setMimeType($type_map[$type]); return $this->makeResponseCacheable($response); } protected function getSupportedResourceTypes() { return array( 'css' => 'text/css; charset=utf-8', 'js' => 'text/javascript; charset=utf-8', 'png' => 'image/png', 'gif' => 'image/gif', 'jpg' => 'image/jpg', 'swf' => 'application/x-shockwave-flash', ); } private function makeResponseCacheable(AphrontResponse $response) { $response->setCacheDurationInSeconds(60 * 60 * 24 * 30); $response->setLastModified(time()); return $response; } } diff --git a/src/view/page/PhabricatorStandardPageView.php b/src/view/page/PhabricatorStandardPageView.php index b0ec7a1642..ea9cc314da 100644 --- a/src/view/page/PhabricatorStandardPageView.php +++ b/src/view/page/PhabricatorStandardPageView.php @@ -1,393 +1,393 @@ applicationMenu = $application_menu; return $this; } public function getApplicationMenu() { return $this->applicationMenu; } public function setApplicationName($application_name) { $this->applicationName = $application_name; return $this; } public function setDisableConsole($disable) { $this->disableConsole = $disable; return $this; } public function getApplicationName() { return $this->applicationName; } public function setBaseURI($base_uri) { $this->baseURI = $base_uri; return $this; } public function getBaseURI() { return $this->baseURI; } public function setShowChrome($show_chrome) { $this->showChrome = $show_chrome; return $this; } public function getShowChrome() { return $this->showChrome; } public function setSearchDefaultScope($search_default_scope) { $this->searchDefaultScope = $search_default_scope; return $this; } public function getSearchDefaultScope() { return $this->searchDefaultScope; } public function appendPageObjects(array $objs) { foreach ($objs as $obj) { $this->pageObjects[] = $obj; } } public function getTitle() { $use_glyph = true; $request = $this->getRequest(); if ($request) { $user = $request->getUser(); if ($user && $user->loadPreferences()->getPreference( PhabricatorUserPreferences::PREFERENCE_TITLES) !== 'glyph') { $use_glyph = false; } } return ($use_glyph ? $this->getGlyph() : '['.$this->getApplicationName().']'). ' '.parent::getTitle(); } protected function willRenderPage() { parent::willRenderPage(); if (!$this->getRequest()) { throw new Exception( "You must set the Request to render a PhabricatorStandardPageView."); } $console = $this->getConsole(); require_celerity_resource('phabricator-core-css'); require_celerity_resource('phabricator-zindex-css'); require_celerity_resource('phabricator-core-buttons-css'); require_celerity_resource('sprite-gradient-css'); require_celerity_resource('phabricator-standard-page-view'); Javelin::initBehavior('workflow', array()); $request = $this->getRequest(); $user = null; if ($request) { $user = $request->getUser(); } if ($user) { $default_img_uri = PhabricatorEnv::getCDNURI( '/rsrc/image/icon/fatcow/document_black.png' ); $download_form = phabricator_form( $user, array( 'action' => '#', 'method' => 'POST', 'class' => 'lightbox-download-form', 'sigil' => 'download', ), phutil_tag( 'button', array(), pht('Download'))); Javelin::initBehavior( 'lightbox-attachments', array( 'defaultImageUri' => $default_img_uri, 'downloadForm' => $download_form, )); } Javelin::initBehavior('aphront-form-disable-on-submit'); Javelin::initBehavior('toggle-class', array()); Javelin::initBehavior('konami', array()); $current_token = null; if ($user) { $current_token = $user->getCSRFToken(); } Javelin::initBehavior( 'refresh-csrf', array( 'tokenName' => AphrontRequest::getCSRFTokenName(), 'header' => AphrontRequest::getCSRFHeaderName(), 'current' => $current_token, )); Javelin::initBehavior('device'); if ($console) { require_celerity_resource('aphront-dark-console-css'); $headers = array(); if (DarkConsoleXHProfPluginAPI::isProfilerRequested()) { $headers[DarkConsoleXHProfPluginAPI::getProfilerHeader()] = 'page'; } Javelin::initBehavior( 'dark-console', array( 'uri' => $request ? (string)$request->getRequestURI() : '?', 'selected' => $user ? $user->getConsoleTab() : null, 'visible' => $user ? (int)$user->getConsoleVisible() : true, 'headers' => $headers, )); // Change this to initBehavior when there is some behavior to initialize require_celerity_resource('javelin-behavior-error-log'); } $menu = id(new PhabricatorMainMenuView()) ->setUser($request->getUser()) ->setDefaultSearchScope($this->getSearchDefaultScope()); if ($this->getController()) { $menu->setController($this->getController()); } if ($this->getApplicationMenu()) { $menu->setApplicationMenu($this->getApplicationMenu()); } $this->menuContent = $menu->render(); } protected function getHead() { $monospaced = PhabricatorEnv::getEnvConfig('style.monospace'); $request = $this->getRequest(); if ($request) { $user = $request->getUser(); if ($user) { $monospaced = nonempty( $user->loadPreferences()->getPreference( PhabricatorUserPreferences::PREFERENCE_MONOSPACED), $monospaced); } } $response = CelerityAPI::getStaticResourceResponse(); $head = array( parent::getHead(), '', $response->renderSingleResource('javelin-magical-init'), ); return implode("\n", $head); } public function setGlyph($glyph) { $this->glyph = $glyph; return $this; } public function getGlyph() { return $this->glyph; } protected function willSendResponse($response) { $request = $this->getRequest(); $response = parent::willSendResponse($response); $console = $request->getApplicationConfiguration()->getConsole(); if ($console) { $response = str_replace( '', $console->render($request), $response); } return $response; } protected function getBody() { $console = $this->getConsole(); $user = null; $request = $this->getRequest(); if ($request) { $user = $request->getUser(); } $header_chrome = null; if ($this->getShowChrome()) { $header_chrome = $this->menuContent; } $developer_warning = null; if (PhabricatorEnv::getEnvConfig('phabricator.developer-mode') && DarkConsoleErrorLogPluginAPI::getErrors()) { $developer_warning = phutil_tag( 'div', array( 'class' => 'aphront-developer-error-callout', ), pht( 'This page raised PHP errors. Find them in DarkConsole '. 'or the error log.')); } // Render the "you have unresolved setup issues..." warning. $setup_warning = null; if ($user && $user->getIsAdmin()) { $open = PhabricatorSetupCheck::getOpenSetupIssueCount(); if ($open) { $setup_warning = phutil_tag( 'div', array( 'class' => 'setup-warning-callout', ), phutil_tag( 'a', array( 'href' => '/config/issue/', ), pht('You have %d unresolved setup issue(s)...', $open))); } } return phutil_render_tag( 'div', array( 'id' => 'base-page', 'class' => 'phabricator-standard-page', ), $developer_warning. $setup_warning. $header_chrome. '
'. ($console ? '' : null). parent::getBody(). '
'. '
'); } protected function getTail() { $request = $this->getRequest(); $user = $request->getUser(); $container = null; if ($user->isLoggedIn()) { $aphlict_object_id = celerity_generate_unique_node_id(); $aphlict_container_id = celerity_generate_unique_node_id(); $client_uri = PhabricatorEnv::getEnvConfig('notification.client-uri'); $client_uri = new PhutilURI($client_uri); if ($client_uri->getDomain() == 'localhost') { $this_host = $this->getRequest()->getHost(); $this_host = new PhutilURI('http://'.$this_host.'/'); $client_uri->setDomain($this_host->getDomain()); } $enable_debug = PhabricatorEnv::getEnvConfig('notification.debug'); Javelin::initBehavior( 'aphlict-listen', array( 'id' => $aphlict_object_id, 'containerID' => $aphlict_container_id, 'server' => $client_uri->getDomain(), 'port' => $client_uri->getPort(), 'debug' => $enable_debug, 'pageObjects' => array_fill_keys($this->pageObjects, true), )); $container = phutil_tag( 'div', array( 'id' => $aphlict_container_id, 'style' => 'position: absolute; width: 0; height: 0;', ), ''); } $response = CelerityAPI::getStaticResourceResponse(); $tail = array( parent::getTail(), $container, $response->renderHTMLFooter(), ); return implode("\n", $tail); } protected function getBodyClasses() { $classes = array(); if (!$this->getShowChrome()) { $classes[] = 'phabricator-chromeless-page'; } - $agent = idx($_SERVER, 'HTTP_USER_AGENT'); + $agent = AphrontRequest::getHTTPHeader('User-Agent'); // Try to guess the device resolution based on UA strings to avoid a flash // of incorrectly-styled content. $device_guess = 'device-desktop'; if (preg_match('@iPhone|iPod|(Android.*Chrome/[.0-9]* Mobile)@', $agent)) { $device_guess = 'device-phone device'; } else if (preg_match('@iPad|(Android.*Chrome/)@', $agent)) { $device_guess = 'device-tablet device'; } $classes[] = $device_guess; return implode(' ', $classes); } private function getConsole() { if ($this->disableConsole) { return null; } return $this->getRequest()->getApplicationConfiguration()->getConsole(); } } diff --git a/webroot/index.php b/webroot/index.php index 774ad58d7a..38e776f241 100644 --- a/webroot/index.php +++ b/webroot/index.php @@ -1,158 +1,158 @@ setData( array( - 'R' => idx($_SERVER, 'HTTP_REFERER', '-'), + 'R' => AphrontRequest::getHTTPHeader('Referer', '-'), 'r' => idx($_SERVER, 'REMOTE_ADDR', '-'), 'M' => idx($_SERVER, 'REQUEST_METHOD', '-'), )); } DarkConsoleXHProfPluginAPI::hookProfiler(); PhutilErrorHandler::setErrorListener( array('DarkConsoleErrorLogPluginAPI', 'handleErrors')); $sink = new AphrontPHPHTTPSink(); $response = PhabricatorSetupCheck::willProcessRequest(); if ($response) { $sink->writeResponse($response); return; } - $host = $_SERVER['HTTP_HOST']; + $host = AphrontRequest::getHTTPHeader('Host'); $path = $_REQUEST['__path__']; switch ($host) { default: $config_key = 'aphront.default-application-configuration-class'; $application = PhabricatorEnv::newObjectFromConfig($config_key); break; } $application->setHost($host); $application->setPath($path); $application->willBuildRequest(); $request = $application->buildRequest(); // Until an administrator sets "phabricator.base-uri", assume it is the same // as the request URI. This will work fine in most cases, it just breaks down // when daemons need to do things. $request_protocol = ($request->isHTTPS() ? 'https' : 'http'); $request_base_uri = "{$request_protocol}://{$host}/"; PhabricatorEnv::setRequestBaseURI($request_base_uri); $write_guard = new AphrontWriteGuard(array($request, 'validateCSRF')); $application->setRequest($request); list($controller, $uri_data) = $application->buildController(); if ($access_log) { $access_log->setData( array( 'U' => (string)$request->getRequestURI()->getPath(), 'C' => get_class($controller), )); } // If execution throws an exception and then trying to render that exception // throws another exception, we want to show the original exception, as it is // likely the root cause of the rendering exception. $original_exception = null; try { $response = $controller->willBeginExecution(); if ($access_log) { if ($request->getUser() && $request->getUser()->getPHID()) { $access_log->setData( array( 'u' => $request->getUser()->getUserName(), )); } } if (!$response) { $controller->willProcessRequest($uri_data); $response = $controller->processRequest(); } } catch (AphrontRedirectException $ex) { $response = id(new AphrontRedirectResponse()) ->setURI($ex->getURI()); } catch (Exception $ex) { $original_exception = $ex; $response = $application->handleException($ex); } try { $response = $controller->didProcessRequest($response); $response = $application->willSendResponse($response, $controller); $response->setRequest($request); $sink->writeResponse($response); } catch (Exception $ex) { $write_guard->dispose(); if ($access_log) { $access_log->write(); } if ($original_exception) { $ex = new PhutilAggregateException( "Multiple exceptions during processing and rendering.", array( $original_exception, $ex, )); } PhabricatorStartup::didFatal('[Rendering Exception] '.$ex->getMessage()); } $write_guard->dispose(); if ($access_log) { $request_start = PhabricatorStartup::getStartTime(); $access_log->setData( array( 'c' => $response->getHTTPResponseCode(), 'T' => (int)(1000000 * (microtime(true) - $request_start)), )); $access_log->write(); } if (DarkConsoleXHProfPluginAPI::isProfilerRequested()) { $profile = DarkConsoleXHProfPluginAPI::stopProfiler(); $profile_sample = id(new PhabricatorXHProfSample()) ->setFilePHID($profile); if (empty($_REQUEST['__profile__'])) { $sample_rate = PhabricatorEnv::getEnvConfig('debug.profile-rate'); } else { $sample_rate = 0; } $profile_sample->setSampleRate($sample_rate); if ($access_log) { $profile_sample->setUsTotal($access_log->getData('T')) ->setHostname($access_log->getData('h')) ->setRequestPath($access_log->getData('U')) ->setController($access_log->getData('C')) ->setUserPHID($request->getUser()->getPHID()); } $profile_sample->save(); } } catch (Exception $ex) { PhabricatorStartup::didFatal("[Exception] ".$ex->getMessage()); }