From 910592b49a86273a30150bfdb67d9b49409e32f2 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Fri, 2 Aug 2013 14:04:14 +0400 Subject: add plugin to cache images in starred articles; pass article_id to sanitize --- plugins/cache_starred_images/init.php | 168 ++++++++++++++++++++++++++++++++++ 1 file changed, 168 insertions(+) create mode 100644 plugins/cache_starred_images/init.php (limited to 'plugins/cache_starred_images/init.php') diff --git a/plugins/cache_starred_images/init.php b/plugins/cache_starred_images/init.php new file mode 100644 index 000000000..2366a1e2b --- /dev/null +++ b/plugins/cache_starred_images/init.php @@ -0,0 +1,168 @@ +host = $host; + + $this->cache_dir = CACHE_DIR . "/starred-images/"; + + if (!is_dir($this->cache_dir)) { + mkdir($this->cache_dir); + } + + if (is_dir($this->cache_dir)) { + chmod($this->cache_dir, 0777); + + if (is_writable($this->cache_dir)) { + $host->add_hook($host::HOOK_UPDATE_TASK, $this); + $host->add_hook($host::HOOK_SANITIZE, $this); + } else { + user_error("Starred cache directory is not writable.", E_USER_WARNING); + } + + } else { + user_error("Unable to create starred cache directory.", E_USER_WARNING); + } + } + + function image() { + $hash = basename($_REQUEST["hash"]); + + if ($hash) { + + $filename = $this->cache_dir . "/" . $hash . '.png'; + + if (file_exists($filename)) { + /* See if we can use X-Sendfile */ + $xsendfile = false; + if (function_exists('apache_get_modules') && + array_search('mod_xsendfile', apache_get_modules())) + $xsendfile = true; + + if ($xsendfile) { + header("X-Sendfile: $filename"); + header("Content-type: application/octet-stream"); + header('Content-Disposition: attachment; filename="' . basename($filename) . '"'); + } else { + header("Content-type: image/png"); + $stamp = gmdate("D, d M Y H:i:s", filemtime($filename)). " GMT"; + header("Last-Modified: $stamp", true); + ob_clean(); // discard any data in the output buffer (if possible) + flush(); // flush headers (if possible) + readfile($filename); + } + } else { + header($_SERVER["SERVER_PROTOCOL"]." 404 Not Found"); + echo "File not found."; + } + } + } + + function hook_sanitize($doc, $site_url, $allowed_elements, $disallowed_attributes, $article_id) { + $xpath = new DOMXpath($doc); + + if ($article_id) { + $entries = $xpath->query('(//img[@src])'); + + foreach ($entries as $entry) { + if ($entry->hasAttribute('src')) { + $src = rewrite_relative_url($site_url, $entry->getAttribute('src')); + + $local_filename = $this->cache_dir . $article_id . "-" . sha1($src) . ".png"; + + if (file_exists($local_filename)) { + $entry->setAttribute("src", get_self_url_prefix() . + "/backend.php?op=pluginhandler&plugin=cache_starred_images&method=image&hash=" . + $article_id . "-" . sha1($src)); + } + + } + } + } + + return $doc; + } + + function hook_update_task() { + header("Content-type: text/plain"); + + $result = db_query("SELECT content, ttrss_user_entries.owner_uid, link, site_url, ttrss_entries.id, plugin_data + FROM ttrss_entries, ttrss_user_entries LEFT JOIN ttrss_feeds ON + (ttrss_user_entries.feed_id = ttrss_feeds.id) + WHERE ref_id = ttrss_entries.id AND + marked = true AND + UPPER(content) LIKE '%cache_article_images($line["content"], $line["site_url"], $line["owner_uid"], $line["id"]); + + if ($success) { + $plugin_data = db_escape_string("starred_cache_images,${line['owner_uid']}:" . $line["plugin_data"]); + + db_query("UPDATE ttrss_entries SET plugin_data = '$plugin_data' WHERE id = " . $line["id"]); + } + } + } + } + + function cache_article_images($content, $site_url, $owner_uid, $article_id) { + libxml_use_internal_errors(true); + + $charset_hack = ' + + '; + + $doc = new DOMDocument(); + $doc->loadHTML($charset_hack . $content); + $xpath = new DOMXPath($doc); + + $entries = $xpath->query('(//img[@src])'); + + $success = false; + $has_images = false; + + foreach ($entries as $entry) { + if ($entry->hasAttribute('src')) { + $has_images = true; + $src = rewrite_relative_url($site_url, $entry->getAttribute('src')); + + $local_filename = $this->cache_dir . $article_id . "-" . sha1($src) . ".png"; + + //_debug("cache_images: downloading: $src to $local_filename"); + + if (!file_exists($local_filename)) { + $file_content = fetch_file_contents($src); + + if ($file_content && strlen($file_content) > 0) { + file_put_contents($local_filename, $file_content); + $success = true; + } + } else { + $success = true; + } + } + } + + return $success || !$has_images; + } + + function api_version() { + return 2; + } +} +?> -- cgit v1.2.3-54-g00ecf From 4e5ddeafa655d91e60a27f7aa7dcb9e98fdba853 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Fri, 2 Aug 2013 14:12:56 +0400 Subject: make cache starred plugin use hook_house_keeping --- classes/handler/public.php | 5 ++++- plugins/cache_starred_images/init.php | 27 +++++++++++++++++++++++++-- 2 files changed, 29 insertions(+), 3 deletions(-) (limited to 'plugins/cache_starred_images/init.php') diff --git a/classes/handler/public.php b/classes/handler/public.php index 9c0359507..7fb2771bd 100644 --- a/classes/handler/public.php +++ b/classes/handler/public.php @@ -391,10 +391,13 @@ class Handler_Public extends Handler { } function updateTask() { - PluginHost::getInstance()->run_hooks(PluginHost::HOOK_UPDATE_TASK, "hook_update_task", $op); } + function housekeepingTask() { + PluginHost::getInstance()->run_hooks(PluginHost::HOOK_HOUSE_KEEPING, "hook_house_keeping", $op); + } + function globalUpdateFeeds() { RPC::updaterandomfeed_real($this->dbh); diff --git a/plugins/cache_starred_images/init.php b/plugins/cache_starred_images/init.php index 2366a1e2b..6899aa5cf 100644 --- a/plugins/cache_starred_images/init.php +++ b/plugins/cache_starred_images/init.php @@ -25,6 +25,7 @@ class Cache_Starred_Images extends Plugin { if (is_writable($this->cache_dir)) { $host->add_hook($host::HOOK_UPDATE_TASK, $this); + $host->add_hook($host::HOOK_HOUSE_KEEPING, $this); $host->add_hook($host::HOOK_SANITIZE, $this); } else { user_error("Starred cache directory is not writable.", E_USER_WARNING); @@ -68,6 +69,30 @@ class Cache_Starred_Images extends Plugin { } } + function hook_house_keeping() { + $files = glob($this->cache_dir . "/*.png"); + + $last_article_id = 0; + $article_exists = 1; + + foreach ($files as $file) { + list ($article_id, $hash) = explode("-", basename($file)); + + if ($article_id != $last_article_id) { + $last_article_id = $article_id; + $article_id = db_escape_string($article_id); + + $result = db_query("SELECT id FROM ttrss_entries WHERE id = " . $article_id); + + $article_exists = db_num_rows($result) > 0; + } + + if (!$article_exists) { + unlink($file); + } + } + } + function hook_sanitize($doc, $site_url, $allowed_elements, $disallowed_attributes, $article_id) { $xpath = new DOMXpath($doc); @@ -94,8 +119,6 @@ class Cache_Starred_Images extends Plugin { } function hook_update_task() { - header("Content-type: text/plain"); - $result = db_query("SELECT content, ttrss_user_entries.owner_uid, link, site_url, ttrss_entries.id, plugin_data FROM ttrss_entries, ttrss_user_entries LEFT JOIN ttrss_feeds ON (ttrss_user_entries.feed_id = ttrss_feeds.id) -- cgit v1.2.3-54-g00ecf From f5967cf825bbcc26ac3c8ac1ba5ce86ef3be9855 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Fri, 6 Sep 2013 09:21:17 +0400 Subject: cache starred: only try to chmod cache directory if it is not writable --- plugins/cache_starred_images/init.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'plugins/cache_starred_images/init.php') diff --git a/plugins/cache_starred_images/init.php b/plugins/cache_starred_images/init.php index 6899aa5cf..f721b3892 100644 --- a/plugins/cache_starred_images/init.php +++ b/plugins/cache_starred_images/init.php @@ -21,7 +21,9 @@ class Cache_Starred_Images extends Plugin { } if (is_dir($this->cache_dir)) { - chmod($this->cache_dir, 0777); + + if (!is_writable($this->cache_dir)) + chmod($this->cache_dir, 0777); if (is_writable($this->cache_dir)) { $host->add_hook($host::HOOK_UPDATE_TASK, $this); -- cgit v1.2.3-54-g00ecf From ae3851b1b5c569fb0b626237731b23376ef1bbf5 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Sat, 5 Oct 2013 20:28:26 +0400 Subject: discard gzipped buffer in cache starred image sender method --- plugins/cache_starred_images/init.php | 2 ++ 1 file changed, 2 insertions(+) (limited to 'plugins/cache_starred_images/init.php') diff --git a/plugins/cache_starred_images/init.php b/plugins/cache_starred_images/init.php index f721b3892..6e62bde76 100644 --- a/plugins/cache_starred_images/init.php +++ b/plugins/cache_starred_images/init.php @@ -39,6 +39,8 @@ class Cache_Starred_Images extends Plugin { } function image() { + ob_end_clean(); + $hash = basename($_REQUEST["hash"]); if ($hash) { -- cgit v1.2.3-54-g00ecf From 849c66b783ae97871c658c9db1a62abb0372e000 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Thu, 21 Aug 2014 13:20:23 +0400 Subject: remove ob_clean/flush shit as unneeded hacks from cached image output routines --- image.php | 2 -- plugins/cache_starred_images/init.php | 2 -- 2 files changed, 4 deletions(-) (limited to 'plugins/cache_starred_images/init.php') diff --git a/image.php b/image.php index a134e658e..dcc7d806d 100644 --- a/image.php +++ b/image.php @@ -41,8 +41,6 @@ header("Content-type: image/png"); $stamp = gmdate("D, d M Y H:i:s", filemtime($filename)). " GMT"; header("Last-Modified: $stamp", true); - ob_clean(); // discard any data in the output buffer (if possible) - flush(); // flush headers (if possible) readfile($filename); } } else { diff --git a/plugins/cache_starred_images/init.php b/plugins/cache_starred_images/init.php index 6e62bde76..4e5f2d4f7 100644 --- a/plugins/cache_starred_images/init.php +++ b/plugins/cache_starred_images/init.php @@ -62,8 +62,6 @@ class Cache_Starred_Images extends Plugin { header("Content-type: image/png"); $stamp = gmdate("D, d M Y H:i:s", filemtime($filename)). " GMT"; header("Last-Modified: $stamp", true); - ob_clean(); // discard any data in the output buffer (if possible) - flush(); // flush headers (if possible) readfile($filename); } } else { -- cgit v1.2.3-54-g00ecf