@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 192 lines 4.4 kB view raw
1<?php 2 3final class AphrontFileResponse extends AphrontResponse { 4 5 private $content; 6 private $contentIterator; 7 private $contentLength; 8 private $compressResponse; 9 10 private $mimeType; 11 12 /** 13 * Download filename 14 * 15 * This is NULL as default or a string. 16 * 17 * @var string|null 18 */ 19 private $download; 20 21 private $rangeMin; 22 private $rangeMax; 23 private $allowOrigins = array(); 24 25 public function addAllowOrigin($origin) { 26 $this->allowOrigins[] = $origin; 27 return $this; 28 } 29 30 /** 31 * Set a download filename 32 * 33 * @param string $download 34 * @return self 35 */ 36 public function setDownload($download) { 37 38 // Make sure we have a populated string 39 if (!phutil_nonempty_string($download)) { 40 $download = 'untitled'; 41 } 42 43 $this->download = $download; 44 return $this; 45 } 46 47 /** 48 * Get the download filename 49 * 50 * If this was never set, NULL is given. 51 * 52 * @return string|null 53 */ 54 public function getDownload() { 55 return $this->download; 56 } 57 58 public function setMimeType($mime_type) { 59 $this->mimeType = $mime_type; 60 return $this; 61 } 62 63 public function getMimeType() { 64 return $this->mimeType; 65 } 66 67 public function setContent($content) { 68 $this->setContentLength(strlen($content)); 69 $this->content = $content; 70 return $this; 71 } 72 73 public function setContentIterator($iterator) { 74 $this->contentIterator = $iterator; 75 return $this; 76 } 77 78 public function buildResponseString() { 79 return $this->content; 80 } 81 82 public function getContentIterator() { 83 if ($this->contentIterator) { 84 return $this->contentIterator; 85 } 86 return parent::getContentIterator(); 87 } 88 89 public function setContentLength($length) { 90 $this->contentLength = $length; 91 return $this; 92 } 93 94 public function getContentLength() { 95 return $this->contentLength; 96 } 97 98 public function setCompressResponse($compress_response) { 99 $this->compressResponse = $compress_response; 100 return $this; 101 } 102 103 public function getCompressResponse() { 104 return $this->compressResponse; 105 } 106 107 public function setRange($min, $max) { 108 $this->rangeMin = $min; 109 $this->rangeMax = $max; 110 return $this; 111 } 112 113 public function getHeaders() { 114 $headers = array( 115 array('Content-Type', $this->getMimeType()), 116 // This tells clients that we can support requests with a "Range" header, 117 // which allows downloads to be resumed, in some browsers, some of the 118 // time, if the stars align. 119 array('Accept-Ranges', 'bytes'), 120 ); 121 122 if ($this->rangeMin !== null || $this->rangeMax !== null) { 123 $len = $this->getContentLength(); 124 $min = $this->rangeMin; 125 126 $max = $this->rangeMax; 127 if ($max === null) { 128 $max = ($len - 1); 129 } 130 131 $headers[] = array('Content-Range', "bytes {$min}-{$max}/{$len}"); 132 $content_len = ($max - $min) + 1; 133 } else { 134 $content_len = $this->getContentLength(); 135 } 136 137 if (!$this->shouldCompressResponse()) { 138 $headers[] = array('Content-Length', $content_len); 139 } 140 141 if (phutil_nonempty_string($this->getDownload())) { 142 $headers[] = array('X-Download-Options', 'noopen'); 143 144 $filename = $this->getDownload(); 145 $filename = addcslashes($filename, '"\\'); 146 $headers[] = array( 147 'Content-Disposition', 148 'attachment; filename="'.$filename.'"', 149 ); 150 } 151 152 if ($this->allowOrigins) { 153 $headers[] = array( 154 'Access-Control-Allow-Origin', 155 implode(',', $this->allowOrigins), 156 ); 157 } 158 159 $headers = array_merge(parent::getHeaders(), $headers); 160 return $headers; 161 } 162 163 protected function shouldCompressResponse() { 164 return $this->getCompressResponse(); 165 } 166 167 public function parseHTTPRange($range) { 168 $begin = null; 169 $end = null; 170 171 $matches = null; 172 if (preg_match('/^bytes=(\d+)-(\d*)$/', $range, $matches)) { 173 // Note that the "Range" header specifies bytes differently than 174 // we do internally: the range 0-1 has 2 bytes (byte 0 and byte 1). 175 $begin = (int)$matches[1]; 176 177 // The "Range" may be "200-299" or "200-", meaning "until end of file". 178 if (strlen($matches[2])) { 179 $range_end = (int)$matches[2]; 180 $end = $range_end + 1; 181 } else { 182 $range_end = null; 183 } 184 185 $this->setHTTPResponseCode(206); 186 $this->setRange($begin, $range_end); 187 } 188 189 return array($begin, $end); 190 } 191 192}