Merge branch 'photocache' into 'dev'

Add Photo Cache addon support

See merge request hubzilla/core!1412
This commit is contained in:
M. Dent 2018-12-02 19:33:54 +01:00
commit 0b38ab259c
7 changed files with 132 additions and 38 deletions

View File

@ -94,6 +94,29 @@ class Cron {
@time_sleep_until(microtime(true) + (float) $interval); @time_sleep_until(microtime(true) + (float) $interval);
} }
} }
// Clean expired photos from cache
$age = get_config('system','active_expire_days', '30');
$r = q("SELECT DISTINCT xchan, content FROM photo WHERE photo_usage = %d AND expires < %s - INTERVAL %s",
intval(PHOTO_CACHE),
db_utcnow(),
db_quoteinterval($age . ' DAY')
);
if($r) {
foreach($r as $rr) {
$file = dbunescbin($rr['content']);
if(is_file($file)) {
@unlink($file);
logger('info: deleted cached photo file ' . $file, LOGGER_DEBUG);
}
}
}
q("DELETE FROM photo WHERE photo_usage = %d AND expires < %s - INTERVAL %s",
intval(PHOTO_CACHE),
db_utcnow(),
db_quoteinterval($age . ' DAY')
);
// publish any applicable items that were set to be published in the future // publish any applicable items that were set to be published in the future
// (time travel posts). Restrict to items that have come of age in the last // (time travel posts). Restrict to items that have come of age in the last

View File

@ -1,19 +1,20 @@
<?php <?php
namespace Zotlabs\Module; namespace Zotlabs\Module;
require_once('include/security.php'); require_once('include/security.php');
require_once('include/attach.php'); require_once('include/attach.php');
require_once('include/photo/photo_driver.php'); require_once('include/photo/photo_driver.php');
class Photo extends \Zotlabs\Web\Controller { class Photo extends \Zotlabs\Web\Controller {
function init() { function init() {
$prvcachecontrol = false;
$streaming = null; $streaming = null;
$channel = null; $channel = null;
$person = 0; $person = 0;
$renew = false;
switch(argc()) { switch(argc()) {
case 4: case 4:
@ -29,7 +30,15 @@ class Photo extends \Zotlabs\Web\Controller {
killme(); killme();
// NOTREACHED // NOTREACHED
} }
$cache_mode = array(
'on' => get_config('system','photo_cache_enable', 0),
'age' => 86400,
'exp' => true,
'leak' => false
);
call_hooks('cache_mode_hook', $cache_mode);
$observer_xchan = get_observer_hash(); $observer_xchan = get_observer_hash();
$ismodified = $_SERVER['HTTP_IF_MODIFIED_SINCE']; $ismodified = $_SERVER['HTTP_IF_MODIFIED_SINCE'];
@ -106,13 +115,14 @@ class Photo extends \Zotlabs\Web\Controller {
License link: http://creativecommons.org/licenses/by/3.0/ License link: http://creativecommons.org/licenses/by/3.0/
*/ */
// @FIXME It seems this part doesn't work because we are not setting such cookie
$cookie_value = false; $cookie_value = false;
if (isset($_COOKIE['devicePixelRatio'])) { if (isset($_COOKIE['devicePixelRatio'])) {
$cookie_value = intval($_COOKIE['devicePixelRatio']); $cookie_value = intval($_COOKIE['devicePixelRatio']);
} }
else { else {
// Force revalidation of cache on next request // Force revalidation of cache on next request
$cache_directive = 'no-cache'; // $prvcachecontrol = 'no-cache';
$status = 'no cookie'; $status = 'no cookie';
} }
@ -129,27 +139,43 @@ class Photo extends \Zotlabs\Web\Controller {
$resolution = 1; $resolution = 1;
} }
$r = q("SELECT uid, photo_usage FROM photo WHERE resource_id = '%s' AND imgscale = %d LIMIT 1", $r = q("SELECT uid, photo_usage, expires, display_path FROM photo WHERE resource_id = '%s' AND imgscale = %d LIMIT 1",
dbesc($photo), dbesc($photo),
intval($resolution) intval($resolution)
); );
if($r) { if($r) {
$allowed = (-1); $allowed = (-1);
if(intval($r[0]['photo_usage'])) { $u = intval($r[0]['photo_usage']);
if($u) {
$allowed = 1; $allowed = 1;
if(intval($r[0]['photo_usage']) === PHOTO_COVER) if($u === PHOTO_COVER)
if($resolution < PHOTO_RES_COVER_1200) if($resolution < PHOTO_RES_COVER_1200)
$allowed = (-1); $allowed = (-1);
if(intval($r[0]['photo_usage']) === PHOTO_PROFILE) if($u === PHOTO_PROFILE)
if(! in_array($resolution,[4,5,6])) if(! in_array($resolution,[4,5,6]))
$allowed = (-1); $allowed = (-1);
if($u === PHOTO_CACHE) {
// Cached image leak protection
if(! (local_channel() || $cache_mode['leak'])) {
header("Location: " . $r[0]['display_path']);
killme();
}
// Revalidate cache
if($cache_mode['on'] && strtotime($r[0]['expires']) - 60 < time()) {
$cache = array(
'url' => $r[0]['display_path'],
'uid' => $r[0]['uid']
);
call_hooks('cache_url_hook', $cache);
if(! $cache['status'])
http_status_exit(404,'not found');
}
}
} }
if($allowed === (-1)) { if($allowed === (-1))
$allowed = attach_can_view($r[0]['uid'],$observer_xchan,$photo); $allowed = attach_can_view($r[0]['uid'],$observer_xchan,$photo);
}
$channel = channelx_by_n($r[0]['uid']); $channel = channelx_by_n($r[0]['uid']);
@ -160,16 +186,19 @@ class Photo extends \Zotlabs\Web\Controller {
); );
$exists = (($e) ? true : false); $exists = (($e) ? true : false);
if($exists && $allowed) { if($exists && $allowed) {
$expires = strtotime($e[0]['expires'] . 'Z');
$data = dbunescbin($e[0]['content']); $data = dbunescbin($e[0]['content']);
$filesize = $e[0]['filesize']; $filesize = $e[0]['filesize'];
$mimetype = $e[0]['mimetype']; $mimetype = $e[0]['mimetype'];
$modified = strtotime($e[0]['edited'] . 'Z'); $modified = strtotime($e[0]['edited'] . 'Z');
if(intval($e[0]['os_storage']))
if(intval($e[0]['os_storage'])) {
$streaming = $data; $streaming = $data;
}
if($e[0]['allow_cid'] != '' || $e[0]['allow_gid'] != '' || $e[0]['deny_gid'] != '' || $e[0]['deny_gid'] != '') if($e[0]['allow_cid'] != '' || $e[0]['allow_gid'] != '' || $e[0]['deny_gid'] != '' || $e[0]['deny_gid'] != '')
$prvcachecontrol = true; $prvcachecontrol = 'no-store, no-cache, must-revalidate';
} }
else { else {
if(! $allowed) { if(! $allowed) {
@ -180,9 +209,9 @@ class Photo extends \Zotlabs\Web\Controller {
} }
} }
} else { }
else
http_status_exit(404,'not found'); http_status_exit(404,'not found');
}
} }
header_remove('Pragma'); header_remove('Pragma');
@ -225,24 +254,14 @@ class Photo extends \Zotlabs\Web\Controller {
$mimetype = $ph->getType(); $mimetype = $ph->getType();
} }
} }
// @FIXME Seems never invoked
// Writing in cachefile
if (isset($cachefile) && $cachefile != '') {
file_put_contents($cachefile, $data);
$modified = filemtime($cachefile);
}
if(isset($prvcachecontrol)) {
header("Content-type: " . $mimetype);
if($prvcachecontrol) {
// it is a private photo that they have no permission to view. // it is a private photo that they have no permission to view.
// tell the browser not to cache it, in case they authenticate // tell the browser not to cache it, in case they authenticate
// and subsequently have permission to see it // and subsequently have permission to see it
header("Cache-Control: no-store, no-cache, must-revalidate"); header("Cache-Control: " . $prvcachecontrol);
} }
else { else {
@ -255,18 +274,23 @@ class Photo extends \Zotlabs\Web\Controller {
// This has performance considerations but we highly recommend you // This has performance considerations but we highly recommend you
// leave it alone. // leave it alone.
$cache = get_config('system','photo_cache_time', 86400); // 1 day by default $maxage = $cache_mode['age'];
header("Expires: " . gmdate("D, d M Y H:i:s", time() + $cache) . " GMT"); if($cache_mode['exp'] || (! isset($expires)) || (isset($expires) && $expires - 60 < time()))
header("Cache-Control: max-age=" . $cache); $expires = time() + $maxage;
else
$maxage = $expires - time();
header("Expires: " . gmdate("D, d M Y H:i:s", $expires) . " GMT");
header("Cache-Control: max-age=" . $maxage);
} }
header("Content-type: " . $mimetype);
header("Last-Modified: " . gmdate("D, d M Y H:i:s", $modified) . " GMT"); header("Last-Modified: " . gmdate("D, d M Y H:i:s", $modified) . " GMT");
header("Content-Length: " . (isset($filesize) ? $filesize : strlen($data))); header("Content-Length: " . (isset($filesize) ? $filesize : strlen($data)));
// If it's a file resource, stream it. // If it's a file resource, stream it.
if($streaming && $channel) { if($streaming && $channel) {
if(strpos($streaming,'store') !== false) if(strpos($streaming,'store') !== false)
$istream = fopen($streaming,'rb'); $istream = fopen($streaming,'rb');

32
Zotlabs/Update/_1229.php Normal file
View File

@ -0,0 +1,32 @@
<?php
namespace Zotlabs\Update;
class _1229 {
function run() {
q("START TRANSACTION");
if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) {
$r1 = q("ALTER TABLE photo ADD expires timestamp NOT NULL DEFAULT '0001-01-01 00:00:00' ");
$r2 = q("create index \"photo_expires_idx\" on photo (\"expires\")");
$r = ($r1 && $r2);
}
else {
$r = q("ALTER TABLE `photo` ADD `expires` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' ,
ADD INDEX `expires` (`expires`)");
}
if($r) {
q("COMMIT");
return UPDATE_SUCCESS;
}
q("ROLLBACK");
return UPDATE_FAILED;
}
}

View File

@ -53,7 +53,7 @@ define ( 'PLATFORM_NAME', 'hubzilla' );
define ( 'STD_VERSION', '3.9.5' ); define ( 'STD_VERSION', '3.9.5' );
define ( 'ZOT_REVISION', '6.0a' ); define ( 'ZOT_REVISION', '6.0a' );
define ( 'DB_UPDATE_VERSION', 1228 ); define ( 'DB_UPDATE_VERSION', 1229 );
define ( 'PROJECT_BASE', __DIR__ ); define ( 'PROJECT_BASE', __DIR__ );
@ -217,6 +217,7 @@ define ( 'PHOTO_PROFILE', 0x0001 );
define ( 'PHOTO_XCHAN', 0x0002 ); define ( 'PHOTO_XCHAN', 0x0002 );
define ( 'PHOTO_THING', 0x0004 ); define ( 'PHOTO_THING', 0x0004 );
define ( 'PHOTO_COVER', 0x0010 ); define ( 'PHOTO_COVER', 0x0010 );
define ( 'PHOTO_CACHE', 0x0020 );
define ( 'PHOTO_ADULT', 0x0008 ); define ( 'PHOTO_ADULT', 0x0008 );
define ( 'PHOTO_FLAG_OS', 0x4000 ); define ( 'PHOTO_FLAG_OS', 0x4000 );

View File

@ -1105,23 +1105,33 @@ function linkify($s, $me = false) {
* to a local redirector which uses https and which redirects to the selected content * to a local redirector which uses https and which redirects to the selected content
* *
* @param string $s * @param string $s
* @param int $uid
* @returns string * @returns string
*/ */
function sslify($s) { function sslify($s) {
// Local photo cache
$str = array(
'body' => $s,
'uid' => local_channel()
);
call_hooks('cache_body_hook', $str);
$s = $str['body'];
if (strpos(z_root(),'https:') === false) if (strpos(z_root(),'https:') === false)
return $s; return $s;
// By default we'll only sslify img tags because media files will probably choke. // By default we'll only sslify img tags because media files will probably choke.
// You can set sslify_everything if you want - but it will likely white-screen if it hits your php memory limit. // You can set sslify_everything if you want - but it will likely white-screen if it hits your php memory limit.
// The downside is that http: media files will likely be blocked by your browser // The downside is that http: media files will likely be blocked by your browser
// Complain to your browser maker // Complain to your browser maker
$allow = get_config('system','sslify_everything'); $allow = get_config('system','sslify_everything');
$pattern = (($allow) ? "/\<(.*?)src=[\"|'](http\:.*?)[\"|'](.*?)\>/" : "/\<img(.*?)src=[\"|'](http\:.*?)[\"|'](.*?)\>/" );
$pattern = (($allow) ? "/\<(.*?)src=\"(http\:.*?)\"(.*?)\>/" : "/\<img(.*?)src=\"(http\:.*?)\"(.*?)\>/" );
$matches = null; $matches = null;
$cnt = preg_match_all($pattern,$s,$matches,PREG_SET_ORDER); $cnt = preg_match_all($pattern, $s, $matches, PREG_SET_ORDER);
if ($cnt) { if ($cnt) {
foreach ($matches as $match) { foreach ($matches as $match) {
$filename = basename( parse_url($match[2], PHP_URL_PATH) ); $filename = basename( parse_url($match[2], PHP_URL_PATH) );
@ -3295,7 +3305,7 @@ function cleanup_bbcode($body) {
$body = preg_replace('/\[\/code\]\s*\[code\]/ism',"\n",$body); $body = preg_replace('/\[\/code\]\s*\[code\]/ism',"\n",$body);
$body = scale_external_images($body,false); $body = scale_external_images($body, false);
return $body; return $body;
} }

View File

@ -947,6 +947,7 @@ CREATE TABLE IF NOT EXISTS `photo` (
`resource_id` char(191) NOT NULL DEFAULT '', `resource_id` char(191) NOT NULL DEFAULT '',
`created` datetime NOT NULL DEFAULT '0001-01-01 00:00:00', `created` datetime NOT NULL DEFAULT '0001-01-01 00:00:00',
`edited` datetime NOT NULL DEFAULT '0001-01-01 00:00:00', `edited` datetime NOT NULL DEFAULT '0001-01-01 00:00:00',
`expires` datetime NOT NULL DEFAULT '0001-01-01 00:00:00',
`title` char(191) NOT NULL DEFAULT '', `title` char(191) NOT NULL DEFAULT '',
`description` text NOT NULL, `description` text NOT NULL,
`album` char(191) NOT NULL DEFAULT '', `album` char(191) NOT NULL DEFAULT '',
@ -979,6 +980,7 @@ CREATE TABLE IF NOT EXISTS `photo` (
KEY `xchan` (`xchan`), KEY `xchan` (`xchan`),
KEY `filesize` (`filesize`), KEY `filesize` (`filesize`),
KEY `resource_id` (`resource_id`), KEY `resource_id` (`resource_id`),
KEY `expires` (`expires`),
KEY `is_nsfw` (`is_nsfw`), KEY `is_nsfw` (`is_nsfw`),
KEY `os_storage` (`os_storage`), KEY `os_storage` (`os_storage`),
KEY `photo_usage` (`photo_usage`) KEY `photo_usage` (`photo_usage`)

View File

@ -928,6 +928,7 @@ CREATE TABLE "photo" (
"resource_id" text NOT NULL, "resource_id" text NOT NULL,
"created" timestamp NOT NULL, "created" timestamp NOT NULL,
"edited" timestamp NOT NULL, "edited" timestamp NOT NULL,
"expires" timestamp NOT NULL,
"title" text NOT NULL, "title" text NOT NULL,
"description" text NOT NULL, "description" text NOT NULL,
"album" text NOT NULL, "album" text NOT NULL,
@ -961,6 +962,7 @@ create index "photo_aid" on photo ("aid");
create index "photo_xchan" on photo ("xchan"); create index "photo_xchan" on photo ("xchan");
create index "photo_filesize" on photo ("filesize"); create index "photo_filesize" on photo ("filesize");
create index "photo_resource_id" on photo ("resource_id"); create index "photo_resource_id" on photo ("resource_id");
create index "photo_expires_idx" on photo ("expires");
create index "photo_usage" on photo ("photo_usage"); create index "photo_usage" on photo ("photo_usage");
create index "photo_is_nsfw" on photo ("is_nsfw"); create index "photo_is_nsfw" on photo ("is_nsfw");
create index "photo_os_storage" on photo ("os_storage"); create index "photo_os_storage" on photo ("os_storage");