diff --git a/README b/README index 6e39890cbf..c26fe786e0 100644 --- a/README +++ b/README @@ -710,11 +710,21 @@ private site, but users of the private site may be able to subscribe to users on a remote site. (Or not... it's not well tested.) The "proper behaviour" hasn't been defined here, so handle with care. -If fancy URLs is enabled, access to file attachments can also be -restricted to logged-in users only. Uncomment the appropriate rewrite -rule in .htaccess or your server's httpd.conf. (This most likely will -not work if you are using a virtual server for attachments, so consider -the performance/security tradeoff.) +Access to file attachments can also be restricted to logged-in users only. +1. Add a directory outside the web root where your file uploads will be + stored. Usually a command like this will work: + + mkdir /var/www/mublog-files + +2. Make the file uploads directory writeable by the web server. An + insecure way to do this is: + + chmod a+x /var/www/mublog-files + +3. Tell StatusNet to use this directory for file uploads. Add a line + like this to your config.php: + + $config['attachments']['dir'] = '/var/www/mublog-files'; Upgrading ========= diff --git a/actions/getfile.php b/actions/getfile.php index ecda34c0f6..cd327e4100 100644 --- a/actions/getfile.php +++ b/actions/getfile.php @@ -1,13 +1,13 @@ . * - * @category Personal + * @category PrivateAttachments * @package StatusNet * @author Jeffery To - * @copyright 2008-2009 StatusNet, Inc. - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @copyright 2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0 * @link http://status.net/ */ -if (!defined('STATUSNET') && !defined('LACONICA')) { +if (!defined('STATUSNET')) { exit(1); } require_once 'MIME/Type.php'; /** - * Action for getting a file attachment + * An action for returning a requested file * - * @category Personal - * @package StatusNet - * @author Jeffery To - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ + * The StatusNet system will do an implicit user check if the site is + * private before allowing this to continue + * + * @category PrivateAttachments + * @package StatusNet + * @author Jeffery To + * @copyright 2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0 + * @link http://status.net/ */ class GetfileAction extends Action @@ -68,7 +72,7 @@ class GetfileAction extends Action $path = null; if ($filename) { - $path = common_config('attachments', 'dir') . $filename; + $path = File::path($filename); } if (empty($path) or !file_exists($path)) { @@ -103,6 +107,10 @@ class GetfileAction extends Action function lastModified() { + if (common_config('site', 'use_x_sendfile')) { + return null; + } + return filemtime($this->path); } @@ -114,8 +122,24 @@ class GetfileAction extends Action * * @return string etag http header */ + function etag() { + if (common_config('site', 'use_x_sendfile')) { + return null; + } + + $cache = common_memcache(); + if($cache) { + $key = common_cache_key('attachments:etag:' . $this->path); + $etag = $cache->get($key); + if($etag === false) { + $etag = crc32(file_get_contents($this->path)); + $cache->set($key,$etag); + } + return $etag; + } + $stat = stat($this->path); return '"' . $stat['ino'] . '-' . $stat['size'] . '-' . $stat['mtime'] . '"'; } @@ -133,13 +157,19 @@ class GetfileAction extends Action // undo headers set by PHP sessions $sec = session_cache_expire() * 60; header('Expires: ' . date(DATE_RFC1123, time() + $sec)); - header('Cache-Control: public, max-age=' . $sec); - header('Pragma: public'); + header('Cache-Control: max-age=' . $sec); parent::handle($args); $path = $this->path; + header('Content-Type: ' . MIME_Type::autoDetect($path)); - readfile($path); + + if (common_config('site', 'use_x_sendfile')) { + header('X-Sendfile: ' . $path); + } else { + header('Content-Length: ' . filesize($path)); + readfile($path); + } } } diff --git a/classes/File.php b/classes/File.php index e04a9d5255..6173f31d6e 100644 --- a/classes/File.php +++ b/classes/File.php @@ -182,25 +182,32 @@ class File extends Memcached_DataObject static function url($filename) { - $path = common_config('attachments', 'path'); + if(common_config('site','private')) { - if ($path[strlen($path)-1] != '/') { - $path .= '/'; + return common_local_url('getfile', + array('filename' => $filename)); + + } else { + $path = common_config('attachments', 'path'); + + if ($path[strlen($path)-1] != '/') { + $path .= '/'; + } + + if ($path[0] != '/') { + $path = '/'.$path; + } + + $server = common_config('attachments', 'server'); + + if (empty($server)) { + $server = common_config('site', 'server'); + } + + // XXX: protocol + + return 'http://'.$server.$path.$filename; } - - if ($path[0] != '/') { - $path = '/'.$path; - } - - $server = common_config('attachments', 'server'); - - if (empty($server)) { - $server = common_config('site', 'server'); - } - - // XXX: protocol - - return 'http://'.$server.$path.$filename; } function getEnclosure(){ diff --git a/config.php.sample b/config.php.sample index 91e6614c06..b8852dc672 100644 --- a/config.php.sample +++ b/config.php.sample @@ -41,6 +41,20 @@ $config['site']['path'] = 'statusnet'; // Make the site invisible to non-logged-in users // $config['site']['private'] = true; +// If your web server supports X-Sendfile (Apache with mod_xsendfile, +// lighttpd, nginx), you can enable X-Sendfile support for better +// performance. Presently, only attachment serving when the site is +// in private mode will use X-Sendfile. +// $config['site']['X-Sendfile'] = false; +// You may also need to enable X-Sendfile support for your web server and +// allow it to access files outside of the web root. For Apache with +// mod_xsendfile, you can add these to your .htaccess or server config: +// +// XSendFile on +// XSendFileAllowAbove on +// +// See http://tn123.ath.cx/mod_xsendfile/ for mod_xsendfile. + // If you want logging sent to a file instead of syslog // $config['site']['logfile'] = '/tmp/statusnet.log'; @@ -265,6 +279,7 @@ $config['sphinx']['port'] = 3312; // $config['attachments']['user_quota'] = 50000000; // $config['attachments']['monthly_quota'] = 15000000; // $config['attachments']['uploads'] = true; -// $config['attachments']['path'] = "/file/"; +// $config['attachments']['path'] = "/file/"; //ignored if site is private +// $config['attachments']['dir'] = INSTALLDIR . '/file/'; // $config['oohembed']['endpoint'] = 'http://oohembed.com/oohembed/'; diff --git a/htaccess.sample b/htaccess.sample index 91ae9da9be..37eb8e01ec 100644 --- a/htaccess.sample +++ b/htaccess.sample @@ -5,14 +5,6 @@ RewriteBase /mublog/ - # If your site is private and want to only allow logged-in users to - # be able to download file attachments, uncomment this rule. - # - # If you have a custom attachment path - # ($config['attachments']['path']), change "file/" to match. - # - #RewriteRule ^file/(.*) getfile/$1 - RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule (.*) index.php?p=$1 [L,QSA] diff --git a/lib/default.php b/lib/default.php index eea11eb2b1..f2e5771490 100644 --- a/lib/default.php +++ b/lib/default.php @@ -54,6 +54,7 @@ $default = 'dupelimit' => 60, # default for same person saying the same thing 'textlimit' => 140, 'indent' => true, + 'use_x_sendfile' => false, ), 'db' => array('database' => 'YOU HAVE TO SET THIS IN config.php',