3 * StatusNet, the distributed open-source microblogging tool
5 * Show notice attachments
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.
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.
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/>.
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/
30 if (!defined('GNUSOCIAL')) { exit(1); }
33 * Show notice attachments
37 * @author Evan Prodromou <evan@status.net>
38 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
39 * @link http://status.net/
41 class AttachmentAction extends ManagedAction
44 * Attachment File object to show
46 var $attachment = null;
49 * Load attributes based on database arguments
51 * Loads all the DB stuff
53 * @param array $args $_REQUEST array
55 * @return success flag
58 protected function prepare(array $args=array())
60 parent::prepare($args);
63 if (!empty($id = $this->trimmed('attachment'))) {
64 $this->attachment = File::getByID($id);
65 } elseif (!empty($filehash = $this->trimmed('filehash'))) {
66 $this->attachment = File::getByHash($filehash);
68 } catch (Exception $e) {
71 if (!$this->attachment instanceof File) {
72 // TRANS: Client error displayed trying to get a non-existing attachment.
73 $this->clientError(_('No such attachment.'), 404);
76 $filename = $this->attachment->getFileOrThumbnailPath();
78 if (empty($filename)) {
79 $this->clientError(_('Requested local URL for a file that is not stored locally.'), 404);
85 * Is this action read-only?
87 * @return boolean true
89 function isReadOnly($args)
97 * @return string title of the page
101 $a = new Attachment($this->attachment);
105 public function showPage()
107 if (empty($this->attachment->getFileOrThumbnailPath())) {
108 // if it's not a local file, gtfo
109 common_redirect($this->attachment->getUrl(), 303);
116 * Fill the content area of the page
118 * Shows a single notice list item.
122 function showContent()
124 $ali = new Attachment($this->attachment, $this);
129 * Don't show page notice
133 function showPageNoticeBlock()
138 * Show aside: this attachments appears in what notices
142 function showSections() {
143 $ns = new AttachmentNoticeSection($this);
148 * Last-modified date for file
150 * @return int last-modified date as unix timestamp
152 public function lastModified()
154 if (common_config('site', 'use_x_sendfile')) {
157 $path = $this->attachment->getFileOrThumbnailPath();
159 return filemtime($path);
166 * etag header for file
168 * This returns the same data (inode, size, mtime) as Apache would,
169 * but in decimal instead of hex.
171 * @return string etag http header
175 if (common_config('site', 'use_x_sendfile')) {
180 $path = $this->attachment->getFileOrThumbnailPath();
182 $cache = Cache::instance();
187 $key = Cache::key('attachments:etag:' . $path);
188 $etag = $cache->get($key);
189 if($etag === false) {
190 $etag = crc32(file_get_contents($path));
191 $cache->set($key,$etag);
198 return '"' . $stat['ino'] . '-' . $stat['size'] . '-' . $stat['mtime'] . '"';
205 * Include $filepath in the response, for viewing and downloading.
206 * If provided, $filesize is used to size the HTTP request,
207 * otherwise it's value is calculated
208 * @param string $filepath the absolute path to the file to send
209 * @param $filesize optional, calculated if unkown
211 static function sendFile(string $filepath, $filesize) {
212 if (is_string(common_config('site', 'x-static-delivery'))) {
213 $tmp = explode(INSTALLDIR, $filepath);
214 $relative_path = end($tmp);
215 common_debug("Using Static Delivery with header: '" .
216 common_config('site', 'x-static-delivery') . ": {$relative_path}'");
217 header(common_config('site', 'x-static-delivery') . ": {$relative_path}");
219 if (empty($filesize)) {
220 $filesize = filesize($filepath);
222 header("Content-Length: {$filesize}");
223 // header('Cache-Control: private, no-transform, no-store, must-revalidate');
225 $ret = @readfile($filepath);
227 if ($ret === false) {
228 common_log(LOG_ERR, "Couldn't read file at {$filepath}.");
229 } elseif ($ret !== $filesize) {
230 common_log(LOG_ERR, "The lengths of the file as recorded on the DB (or on disk) for the file " .
231 "{$filepath} differ from what was sent to the user ({$filesize} vs {$ret}).");