@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
at upstream/main 177 lines 5.6 kB view raw
1<?php 2 3/** 4 * TODO: Should be final but isn't because of AphrontReloadResponse. 5 */ 6class AphrontRedirectResponse extends AphrontResponse { 7 8 private $uri; 9 private $stackWhenCreated; 10 private $isExternal; 11 private $closeDialogBeforeRedirect; 12 13 public function setIsExternal($external) { 14 $this->isExternal = $external; 15 return $this; 16 } 17 18 public function __construct() { 19 if ($this->shouldStopForDebugging()) { 20 // If we're going to stop, capture the stack so we can print it out. 21 $this->stackWhenCreated = id(new Exception())->getTrace(); 22 } 23 } 24 25 public function setURI($uri) { 26 $this->uri = $uri; 27 return $this; 28 } 29 30 public function getURI() { 31 // NOTE: When we convert a RedirectResponse into an AjaxResponse, we pull 32 // the URI through this method. Make sure it passes checks before we 33 // hand it over to callers. 34 return self::getURIForRedirect($this->uri, $this->isExternal); 35 } 36 37 public function shouldStopForDebugging() { 38 return PhabricatorEnv::getEnvConfig('debug.stop-on-redirect'); 39 } 40 41 public function setCloseDialogBeforeRedirect($close) { 42 $this->closeDialogBeforeRedirect = $close; 43 return $this; 44 } 45 46 public function getCloseDialogBeforeRedirect() { 47 return $this->closeDialogBeforeRedirect; 48 } 49 50 public function getHeaders() { 51 $headers = array(); 52 if (!$this->shouldStopForDebugging()) { 53 $uri = self::getURIForRedirect($this->uri, $this->isExternal); 54 $headers[] = array('Location', $uri); 55 } 56 $headers = array_merge(parent::getHeaders(), $headers); 57 return $headers; 58 } 59 60 public function buildResponseString() { 61 if ($this->shouldStopForDebugging()) { 62 $request = $this->getRequest(); 63 $viewer = $request->getUser(); 64 65 $view = new PhabricatorStandardPageView(); 66 $view->setRequest($this->getRequest()); 67 $view->setApplicationName(pht('Debug')); 68 $view->setTitle(pht('Stopped on Redirect')); 69 70 $dialog = new AphrontDialogView(); 71 $dialog->setUser($viewer); 72 $dialog->setTitle(pht('Stopped on Redirect')); 73 74 $dialog->appendParagraph( 75 pht( 76 'You were stopped here because %s is set in your configuration.', 77 phutil_tag('tt', array(), 'debug.stop-on-redirect'))); 78 79 $dialog->appendParagraph( 80 pht( 81 'You are being redirected to: %s', 82 phutil_tag('tt', array(), $this->getURI()))); 83 84 $dialog->addCancelButton($this->getURI(), pht('Continue')); 85 86 $dialog->appendChild(phutil_tag('br')); 87 88 $dialog->appendChild( 89 id(new AphrontStackTraceView()) 90 ->setUser($viewer) 91 ->setTrace($this->stackWhenCreated)); 92 93 $dialog->setIsStandalone(true); 94 $dialog->setWidth(AphrontDialogView::WIDTH_FULL); 95 96 $box = id(new PHUIBoxView()) 97 ->addMargin(PHUI::MARGIN_LARGE) 98 ->appendChild($dialog); 99 100 $view->appendChild($box); 101 102 return $view->render(); 103 } 104 105 return ''; 106 } 107 108 109 /** 110 * Format a URI for use in a "Location:" header. 111 * 112 * Verifies that a URI redirects to the expected type of resource (local or 113 * remote) and formats it for use in a "Location:" header. 114 * 115 * The HTTP spec says "Location:" headers must use absolute URIs. Although 116 * browsers work with relative URIs, we return absolute URIs to avoid 117 * ambiguity. For example, Chrome interprets "Location: /\evil.com" to mean 118 * "perform a protocol-relative redirect to evil.com". 119 * 120 * @param string $uri URI to redirect to. 121 * @param bool $is_external True if this URI identifies a remote 122 * resource. 123 * @return string URI for use in a "Location:" header. 124 */ 125 public static function getURIForRedirect($uri, $is_external) { 126 $uri_object = new PhutilURI($uri); 127 if ($is_external) { 128 // If this is a remote resource it must have a domain set. This 129 // would also be caught below, but testing for it explicitly first allows 130 // us to raise a better error message. 131 if (!strlen($uri_object->getDomain())) { 132 throw new Exception( 133 pht( 134 'Refusing to redirect to external URI "%s". This URI '. 135 'is not fully qualified, and is missing a domain name. To '. 136 'redirect to a local resource, remove the external flag.', 137 (string)$uri)); 138 } 139 140 // Check that it's a valid remote resource. 141 if (!PhabricatorEnv::isValidURIForLink($uri)) { 142 throw new Exception( 143 pht( 144 'Refusing to redirect to external URI "%s". This URI '. 145 'is not a valid remote web resource.', 146 (string)$uri)); 147 } 148 } else { 149 // If this is a local resource, it must not have a domain set. This allows 150 // us to raise a better error message than the check below can. 151 if (strlen($uri_object->getDomain())) { 152 throw new Exception( 153 pht( 154 'Refusing to redirect to local resource "%s". The URI has a '. 155 'domain, but the redirect is not marked external. Mark '. 156 'redirects as external to allow redirection off the local '. 157 'domain.', 158 (string)$uri)); 159 } 160 161 // If this is a local resource, it must be a valid local resource. 162 if (!PhabricatorEnv::isValidLocalURIForLink($uri)) { 163 throw new Exception( 164 pht( 165 'Refusing to redirect to local resource "%s". This URI is not '. 166 'formatted in a recognizable way.', 167 (string)$uri)); 168 } 169 170 // Fully qualify the result URI. 171 $uri = PhabricatorEnv::getURI((string)$uri); 172 } 173 174 return (string)$uri; 175 } 176 177}