attachment.php 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. <?php
  2. /**
  3. * StatusNet, the distributed open-source microblogging tool
  4. *
  5. * Show notice attachments
  6. *
  7. * PHP version 5
  8. *
  9. * LICENCE: This program is free software: you can redistribute it and/or modify
  10. * it under the terms of the GNU Affero General Public License as published by
  11. * the Free Software Foundation, either version 3 of the License, or
  12. * (at your option) any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU Affero General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU Affero General Public License
  20. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  21. *
  22. * @category Personal
  23. * @package StatusNet
  24. * @author Evan Prodromou <evan@status.net>
  25. * @copyright 2008-2009 StatusNet, Inc.
  26. * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
  27. * @link http://status.net/
  28. */
  29. if (!defined('GNUSOCIAL')) { exit(1); }
  30. /**
  31. * Show notice attachments
  32. *
  33. * @category Personal
  34. * @package StatusNet
  35. * @author Evan Prodromou <evan@status.net>
  36. * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
  37. * @link http://status.net/
  38. */
  39. class AttachmentAction extends ManagedAction
  40. {
  41. /**
  42. * Attachment File object to show
  43. */
  44. var $attachment = null;
  45. /**
  46. * Load attributes based on database arguments
  47. *
  48. * Loads all the DB stuff
  49. *
  50. * @param array $args $_REQUEST array
  51. *
  52. * @return success flag
  53. */
  54. protected function prepare(array $args=array())
  55. {
  56. parent::prepare($args);
  57. try {
  58. if (!empty($id = $this->trimmed('attachment'))) {
  59. $this->attachment = File::getByID($id);
  60. } elseif (!empty($filehash = $this->trimmed('filehash'))) {
  61. $this->attachment = File::getByHash($filehash);
  62. }
  63. } catch (Exception $e) {
  64. // Not found
  65. }
  66. if (!$this->attachment instanceof File) {
  67. // TRANS: Client error displayed trying to get a non-existing attachment.
  68. $this->clientError(_('No such attachment.'), 404);
  69. }
  70. $filename = $this->attachment->getFileOrThumbnailPath();
  71. if (empty($filename)) {
  72. $this->clientError(_('Requested local URL for a file that is not stored locally.'), 404);
  73. }
  74. return true;
  75. }
  76. /**
  77. * Is this action read-only?
  78. *
  79. * @return boolean true
  80. */
  81. function isReadOnly($args)
  82. {
  83. return true;
  84. }
  85. /**
  86. * Title of the page
  87. *
  88. * @return string title of the page
  89. */
  90. function title()
  91. {
  92. $a = new Attachment($this->attachment);
  93. return $a->title();
  94. }
  95. public function showPage()
  96. {
  97. if (empty($this->attachment->getFileOrThumbnailPath())) {
  98. // if it's not a local file, gtfo
  99. common_redirect($this->attachment->getUrl(), 303);
  100. }
  101. parent::showPage();
  102. }
  103. /**
  104. * Fill the content area of the page
  105. *
  106. * Shows a single notice list item.
  107. *
  108. * @return void
  109. */
  110. function showContent()
  111. {
  112. $ali = new Attachment($this->attachment, $this);
  113. $cnt = $ali->show();
  114. }
  115. /**
  116. * Don't show page notice
  117. *
  118. * @return void
  119. */
  120. function showPageNoticeBlock()
  121. {
  122. }
  123. /**
  124. * Show aside: this attachments appears in what notices
  125. *
  126. * @return void
  127. */
  128. function showSections() {
  129. $ns = new AttachmentNoticeSection($this);
  130. $ns->show();
  131. }
  132. /**
  133. * Last-modified date for file
  134. *
  135. * @return int last-modified date as unix timestamp
  136. */
  137. public function lastModified()
  138. {
  139. if (common_config('site', 'use_x_sendfile')) {
  140. return null;
  141. }
  142. $path = $this->attachment->getFileOrThumbnailPath();
  143. if (!empty($path)) {
  144. return filemtime($path);
  145. } else {
  146. return null;
  147. }
  148. }
  149. /**
  150. * etag header for file
  151. *
  152. * This returns the same data (inode, size, mtime) as Apache would,
  153. * but in decimal instead of hex.
  154. *
  155. * @return string etag http header
  156. */
  157. function etag()
  158. {
  159. if (common_config('site', 'use_x_sendfile')) {
  160. return null;
  161. }
  162. $path = $this->attachment->getFileOrThumbnailPath();
  163. $cache = Cache::instance();
  164. if($cache) {
  165. if (empty($path)) {
  166. return null;
  167. }
  168. $key = Cache::key('attachments:etag:' . $path);
  169. $etag = $cache->get($key);
  170. if($etag === false) {
  171. $etag = crc32(file_get_contents($path));
  172. $cache->set($key,$etag);
  173. }
  174. return $etag;
  175. }
  176. if (!empty($path)) {
  177. $stat = stat($path);
  178. return '"' . $stat['ino'] . '-' . $stat['size'] . '-' . $stat['mtime'] . '"';
  179. } else {
  180. return null;
  181. }
  182. }
  183. /**
  184. * Include $filepath in the response, for viewing and downloading.
  185. * If provided, $filesize is used to size the HTTP request,
  186. * otherwise it's value is calculated
  187. * @param string $filepath the absolute path to the file to send
  188. * @param $filesize optional, calculated if unkown
  189. */
  190. static function sendFile(string $filepath, $filesize) {
  191. if (is_string(common_config('site', 'x-static-delivery'))) {
  192. $tmp = explode(INSTALLDIR, $filepath);
  193. $relative_path = end($tmp);
  194. common_debug("Using Static Delivery with header: '" .
  195. common_config('site', 'x-static-delivery') . ": {$relative_path}'");
  196. header(common_config('site', 'x-static-delivery') . ": {$relative_path}");
  197. } else {
  198. if (empty($filesize)) {
  199. $filesize = filesize($filepath);
  200. }
  201. header("Content-Length: {$filesize}");
  202. // header('Cache-Control: private, no-transform, no-store, must-revalidate');
  203. $ret = @readfile($filepath);
  204. if ($ret === false) {
  205. common_log(LOG_ERR, "Couldn't read file at {$filepath}.");
  206. } elseif ($ret !== $filesize) {
  207. common_log(LOG_ERR, "The lengths of the file as recorded on the DB (or on disk) for the file " .
  208. "{$filepath} differ from what was sent to the user ({$filesize} vs {$ret}).");
  209. }
  210. }
  211. }
  212. }