[ Index ] |
PHP Cross Reference of Phabricator |
[Summary view] [Print] [Text view]
1 <?php 2 3 final class PhabricatorFileDataController extends PhabricatorFileController { 4 5 private $phid; 6 private $key; 7 private $token; 8 9 public function willProcessRequest(array $data) { 10 $this->phid = $data['phid']; 11 $this->key = $data['key']; 12 $this->token = idx($data, 'token'); 13 } 14 15 public function shouldRequireLogin() { 16 return false; 17 } 18 19 protected function checkFileAndToken($file) { 20 if (!$file) { 21 return new Aphront404Response(); 22 } 23 24 if (!$file->validateSecretKey($this->key)) { 25 return new Aphront403Response(); 26 } 27 28 return null; 29 } 30 31 public function processRequest() { 32 $request = $this->getRequest(); 33 34 $alt = PhabricatorEnv::getEnvConfig('security.alternate-file-domain'); 35 $base_uri = PhabricatorEnv::getEnvConfig('phabricator.base-uri'); 36 $alt_uri = new PhutilURI($alt); 37 $alt_domain = $alt_uri->getDomain(); 38 $req_domain = $request->getHost(); 39 $main_domain = id(new PhutilURI($base_uri))->getDomain(); 40 41 $cache_response = true; 42 43 if (empty($alt) || $main_domain == $alt_domain) { 44 // Alternate files domain isn't configured or it's set 45 // to the same as the default domain 46 47 // load the file with permissions checks; 48 $file = id(new PhabricatorFileQuery()) 49 ->setViewer($request->getUser()) 50 ->withPHIDs(array($this->phid)) 51 ->executeOne(); 52 53 $error_response = $this->checkFileAndToken($file); 54 if ($error_response) { 55 return $error_response; 56 } 57 58 // when the file is not CDNable, don't allow cache 59 $cache_response = $file->getCanCDN(); 60 } else if ($req_domain != $alt_domain) { 61 // Alternate domain is configured but this request isn't using it 62 63 // load the file with permissions checks; 64 $file = id(new PhabricatorFileQuery()) 65 ->setViewer($request->getUser()) 66 ->withPHIDs(array($this->phid)) 67 ->executeOne(); 68 69 $error_response = $this->checkFileAndToken($file); 70 if ($error_response) { 71 return $error_response; 72 } 73 74 // if the user can see the file, generate a token; 75 // redirect to the alt domain with the token; 76 return id(new AphrontRedirectResponse()) 77 ->setIsExternal(true) 78 ->setURI($file->getCDNURIWithToken()); 79 80 } else { 81 // We are using the alternate domain 82 83 // load the file, bypassing permission checks; 84 $file = id(new PhabricatorFileQuery()) 85 ->setViewer(PhabricatorUser::getOmnipotentUser()) 86 ->withPHIDs(array($this->phid)) 87 ->executeOne(); 88 89 $error_response = $this->checkFileAndToken($file); 90 if ($error_response) { 91 return $error_response; 92 } 93 94 $acquire_token_uri = id(new PhutilURI($file->getViewURI())) 95 ->setDomain($main_domain); 96 97 98 if ($this->token) { 99 // validate the token, if it is valid, continue 100 $validated_token = $file->validateOneTimeToken($this->token); 101 102 if (!$validated_token) { 103 $dialog = $this->newDialog() 104 ->setShortTitle(pht('Expired File')) 105 ->setTitle(pht('File Link Has Expired')) 106 ->appendParagraph( 107 pht( 108 'The link you followed to view this file is invalid or '. 109 'expired.')) 110 ->appendParagraph( 111 pht( 112 'Continue to generate a new link to the file. You may be '. 113 'required to log in.')) 114 ->addCancelButton( 115 $acquire_token_uri, 116 pht('Continue')); 117 118 // Build an explicit response so we can respond with HTTP/403 instead 119 // of HTTP/200. 120 $response = id(new AphrontDialogResponse()) 121 ->setDialog($dialog) 122 ->setHTTPResponseCode(403); 123 124 return $response; 125 } 126 // return the file data without cache headers 127 $cache_response = false; 128 } else if (!$file->getCanCDN()) { 129 // file cannot be served via cdn, and no token given 130 // redirect to the main domain to aquire a token 131 132 // This is marked as an "external" URI because it is fully qualified. 133 return id(new AphrontRedirectResponse()) 134 ->setIsExternal(true) 135 ->setURI($acquire_token_uri); 136 } 137 } 138 139 $data = $file->loadFileData(); 140 $response = new AphrontFileResponse(); 141 $response->setContent($data); 142 if ($cache_response) { 143 $response->setCacheDurationInSeconds(60 * 60 * 24 * 30); 144 } 145 146 // NOTE: It's important to accept "Range" requests when playing audio. 147 // If we don't, Safari has difficulty figuring out how long sounds are 148 // and glitches when trying to loop them. In particular, Safari sends 149 // an initial request for bytes 0-1 of the audio file, and things go south 150 // if we can't respond with a 206 Partial Content. 151 $range = $request->getHTTPHeader('range'); 152 if ($range) { 153 $matches = null; 154 if (preg_match('/^bytes=(\d+)-(\d+)$/', $range, $matches)) { 155 $response->setHTTPResponseCode(206); 156 $response->setRange((int)$matches[1], (int)$matches[2]); 157 } 158 } else if (isset($validated_token)) { 159 // consume the one-time token if we have one. 160 $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); 161 $validated_token->delete(); 162 unset($unguarded); 163 } 164 165 $is_viewable = $file->isViewableInBrowser(); 166 $force_download = $request->getExists('download'); 167 168 if ($is_viewable && !$force_download) { 169 $response->setMimeType($file->getViewableMimeType()); 170 } else { 171 if (!$request->isHTTPPost() && !$alt_domain) { 172 // NOTE: Require POST to download files from the primary domain. We'd 173 // rather go full-bore and do a real CSRF check, but can't currently 174 // authenticate users on the file domain. This should blunt any 175 // attacks based on iframes, script tags, applet tags, etc., at least. 176 // Send the user to the "info" page if they're using some other method. 177 178 // This is marked as "external" because it is fully qualified. 179 return id(new AphrontRedirectResponse()) 180 ->setIsExternal(true) 181 ->setURI(PhabricatorEnv::getProductionURI($file->getBestURI())); 182 } 183 $response->setMimeType($file->getMimeType()); 184 $response->setDownload($file->getName()); 185 } 186 187 return $response; 188 } 189 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Sun Nov 30 09:20:46 2014 | Cross-referenced by PHPXref 0.7.1 |