[WIP] Rewrite to Proxy class: (#5507)

* Rewrite to Proxy class:
- introduced new Friendica\Network\Proxy class for in exchange of proxy_*()
  functions
- moved also all PROXY_* constants there as Proxy::*
- removed now no longer needed mod/proxy.php loading as composer's auto-load
  will do this for us
- renamed those proxy_*() functions to better names:
  + proxy_init()           -> Proxy::init()         (public)
  + proxy_url()            -> Proxy::proxifyUrl()   (public)
  + proxy_parse_html()     -> Proxy::proxifyHtml()  (public)
  + proxy_is_local_image() -> Proxy::isLocalImage() (private)
  + proxy_parse_query()    -> Proxy::parseQuery()   (private)
  + proxy_img_cb()         -> Proxy::replaceUrl()   (private)

* Ops, need to set $a here ...

* CR request:
- moved Proxy class to Friendica\Module
- extended BaseModule

* Ops, no need for own instance of $a when self::getApp() is around.

* Proxy-rewrite:
- proxy_url() and proxy_parse_html() are both non-module functions (now
  methods)
- so they must be splitted into a seperate class
- also the SIZE_* and DEFAULT_TIME constants are both not relevant to module

* No instances from utility classes

* Fixed error:
- proxify*() is now located in `Friendica\Util\ProxyUtils`

* Moved back to original place, ops? How did they move here? Well, it was not
intended by me.

* Removed duplicate (left-over from split) constants and static array. Thank to
MrPetovan finding it.

* Renamed ProxyUtils -> Proxy and aliased it back to ProxyUtils.
This commit is contained in:
Roland Häder 2018-07-31 04:06:22 +02:00 committed by Hypolite Petovan
parent bf87ad4fcf
commit 4d39164c1e
30 changed files with 578 additions and 448 deletions

View file

@ -40,6 +40,7 @@ use Friendica\Object\Image;
use Friendica\Protocol\Diaspora;
use Friendica\Util\DateTimeFormat;
use Friendica\Util\Network;
use Friendica\Util\Proxy as ProxyUtils;
use Friendica\Util\XML;
require_once 'include/conversation.php';
@ -47,7 +48,6 @@ require_once 'mod/share.php';
require_once 'mod/item.php';
require_once 'include/security.php';
require_once 'mod/wall_upload.php';
require_once 'mod/proxy.php';
define('API_METHOD_ANY', '*');
define('API_METHOD_GET', 'GET');
@ -2518,7 +2518,7 @@ function api_get_entitities(&$text, $bbcode)
preg_match_all("/\[img](.*?)\[\/img\]/ism", $bbcode, $images);
foreach ($images[1] as $image) {
$replace = proxy_url($image);
$replace = ProxyUtils::proxifyUrl($image);
$text = str_replace($image, $replace, $text);
}
return [];
@ -2627,7 +2627,7 @@ function api_get_entitities(&$text, $bbcode)
// If image cache is activated, then use the following sizes:
// thumb (150), small (340), medium (600) and large (1024)
if (!Config::get("system", "proxy_disabled")) {
$media_url = proxy_url($url);
$media_url = ProxyUtils::proxifyUrl($url);
$sizes = [];
$scale = Image::getScalingDimensions($image[0], $image[1], 150);

View file

@ -20,6 +20,7 @@ use Friendica\Model\Term;
use Friendica\Object\Post;
use Friendica\Object\Thread;
use Friendica\Util\DateTimeFormat;
use Friendica\Util\Proxy as ProxyUtils;
use Friendica\Util\Temporal;
use Friendica\Util\XML;
@ -407,7 +408,6 @@ function visible_activity($item) {
*
*/
function conversation(App $a, array $items, $mode, $update, $preview = false, $order = 'commented', $uid = 0) {
require_once 'mod/proxy.php';
$ssl_state = ((local_user()) ? true : false);
@ -418,8 +418,10 @@ function conversation(App $a, array $items, $mode, $update, $preview = false, $o
if (local_user()) {
$str_blocked = PConfig::get(local_user(), 'system', 'blocked');
if ($str_blocked) {
$arr_blocked = explode(',', $str_blocked);
for ($x = 0; $x < count($arr_blocked); $x ++) {
$arr_blocked[$x] = trim($arr_blocked[$x]);
}
@ -477,6 +479,7 @@ function conversation(App $a, array $items, $mode, $update, $preview = false, $o
}
} elseif ($mode === 'notes') {
$profile_owner = local_user();
if (!$update) {
$live_update_div = '<div id="live-notes"></div>' . "\r\n"
. "<script> var profile_uid = " . local_user()
@ -484,6 +487,7 @@ function conversation(App $a, array $items, $mode, $update, $preview = false, $o
}
} elseif ($mode === 'display') {
$profile_owner = $a->profile['uid'];
if (!$update) {
$live_update_div = '<div id="live-display"></div>' . "\r\n"
. "<script> var profile_uid = " . defaults($_SESSION, 'uid', 0) . ";"
@ -492,6 +496,7 @@ function conversation(App $a, array $items, $mode, $update, $preview = false, $o
} elseif ($mode === 'community') {
$items = conversation_add_children($items, true, $order, $uid);
$profile_owner = 0;
if (!$update) {
$live_update_div = '<div id="live-community"></div>' . "\r\n"
. "<script> var profile_uid = -1; var netargs = '" . substr($a->cmd, 10)
@ -523,7 +528,7 @@ function conversation(App $a, array $items, $mode, $update, $preview = false, $o
$page_template = get_markup_template("conversation.tpl");
if ($items && count($items)) {
if (!empty($items)) {
if ($mode === 'community') {
$writable = true;
} else {
@ -645,7 +650,7 @@ function conversation(App $a, array $items, $mode, $update, $preview = false, $o
'name' => $profile_name_e,
'sparkle' => $sparkle,
'lock' => $lock,
'thumb' => System::removedBaseUrl(proxy_url($item['author-avatar'], false, PROXY_SIZE_THUMB)),
'thumb' => System::removedBaseUrl(ProxyUtils::proxifyUrl($item['author-avatar'], false, ProxyUtils::SIZE_THUMB)),
'title' => $title_e,
'body' => $body_e,
'tags' => $tags_e,
@ -664,7 +669,7 @@ function conversation(App $a, array $items, $mode, $update, $preview = false, $o
'indent' => '',
'owner_name' => $owner_name_e,
'owner_url' => $owner_url,
'owner_photo' => System::removedBaseUrl(proxy_url($item['owner-avatar'], false, PROXY_SIZE_THUMB)),
'owner_photo' => System::removedBaseUrl(ProxyUtils::proxifyUrl($item['owner-avatar'], false, ProxyUtils::SIZE_THUMB)),
'plink' => get_plink($item),
'edpost' => false,
'isstarred' => $isstarred,

View file

@ -11,6 +11,7 @@ use Friendica\Database\DBA;
*
* @param $args Query parameters (1 to N parameters of different types)
* @return array|bool Query array
* @deprecated
*/
function q($sql) {
$args = func_get_args();

View file

@ -20,8 +20,8 @@ use Friendica\Model\Item;
use Friendica\Render\FriendicaSmarty;
use Friendica\Util\DateTimeFormat;
use Friendica\Util\Map;
use Friendica\Util\Proxy as ProxyUtils;
require_once "mod/proxy.php";
require_once "include/conversation.php";
/**
@ -858,7 +858,7 @@ function micropro($contact, $redirect = false, $class = '', $textmode = false) {
'$click' => defaults($contact, 'click', ''),
'$class' => $class,
'$url' => $url,
'$photo' => proxy_url($contact['thumb'], false, PROXY_SIZE_THUMB),
'$photo' => ProxyUtils::proxifyUrl($contact['thumb'], false, ProxyUtils::SIZE_THUMB),
'$name' => $contact['name'],
'title' => $contact['name'] . ' [' . $contact['addr'] . ']',
'$parkle' => $sparkle,

View file

@ -9,9 +9,9 @@ use Friendica\Core\Addon;
use Friendica\Database\DBA;
use Friendica\Model\Contact;
use Friendica\Model\Item;
use Friendica\Util\Proxy as ProxyUtils;
require_once 'include/dba.php';
require_once 'mod/proxy.php';
function acl_content(App $a)
{
@ -192,7 +192,7 @@ function acl_content(App $a)
$contacts = [];
foreach ($r as $g) {
$contacts[] = [
'photo' => proxy_url($g['photo'], false, PROXY_SIZE_MICRO),
'photo' => ProxyUtils::proxifyUrl($g['photo'], false, ProxyUtils::SIZE_MICRO),
'name' => $g['name'],
'nick' => defaults($g, 'addr', $g['url']),
'network' => $g['network'],
@ -214,7 +214,7 @@ function acl_content(App $a)
foreach ($r as $g) {
$entry = [
'type' => 'c',
'photo' => proxy_url($g['micro'], false, PROXY_SIZE_MICRO),
'photo' => ProxyUtils::proxifyUrl($g['micro'], false, ProxyUtils::SIZE_MICRO),
'name' => htmlentities($g['name']),
'id' => intval($g['id']),
'network' => $g['network'],
@ -275,7 +275,7 @@ function acl_content(App $a)
if (count($contact) > 0) {
$unknown_contacts[] = [
'type' => 'c',
'photo' => proxy_url($contact['micro'], false, PROXY_SIZE_MICRO),
'photo' => ProxyUtils::proxifyUrl($contact['micro'], false, ProxyUtils::SIZE_MICRO),
'name' => htmlentities($contact['name']),
'id' => intval($contact['cid']),
'network' => $contact['network'],

View file

@ -11,6 +11,7 @@ use Friendica\Database\DBA;
use Friendica\Model\Contact;
use Friendica\Model\GContact;
use Friendica\Model\Profile;
use Friendica\Util\Proxy as ProxyUtils;
require_once 'include/dba.php';
require_once 'mod/contacts.php';
@ -80,7 +81,7 @@ function allfriends_content(App $a)
'url' => $rr['url'],
'itemurl' => defaults($contact_details, 'addr', $rr['url']),
'name' => htmlentities($contact_details['name']),
'thumb' => proxy_url($contact_details['thumb'], false, PROXY_SIZE_THUMB),
'thumb' => ProxyUtils::proxifyUrl($contact_details['thumb'], false, ProxyUtils::SIZE_THUMB),
'img_hover' => htmlentities($contact_details['name']),
'details' => $contact_details['location'],
'tags' => $contact_details['keywords'],

View file

@ -10,6 +10,7 @@ use Friendica\Database\DBA;
use Friendica\Model\Contact;
use Friendica\Model\GContact;
use Friendica\Model\Profile;
use Friendica\Util\Proxy as ProxyUtils;
require_once 'include/dba.php';
require_once 'mod/contacts.php';
@ -120,7 +121,7 @@ function common_content(App $a)
'url' => $rr['url'],
'itemurl' => defaults($contact_details, 'addr', $rr['url']),
'name' => $contact_details['name'],
'thumb' => proxy_url($contact_details['thumb'], false, PROXY_SIZE_THUMB),
'thumb' => ProxyUtils::proxifyUrl($contact_details['thumb'], false, ProxyUtils::SIZE_THUMB),
'img_hover' => htmlentities($contact_details['name']),
'details' => $contact_details['location'],
'tags' => $contact_details['keywords'],

View file

@ -19,8 +19,7 @@ use Friendica\Model\Group;
use Friendica\Model\Profile;
use Friendica\Network\Probe;
use Friendica\Util\DateTimeFormat;
require_once 'mod/proxy.php';
use Friendica\Util\Proxy as ProxyUtils;
function contacts_init(App $a)
{
@ -970,7 +969,7 @@ function _contact_detail_for_template(array $rr)
'id' => $rr['id'],
'alt_text' => $alt_text,
'dir_icon' => $dir_icon,
'thumb' => proxy_url($rr['thumb'], false, PROXY_SIZE_THUMB),
'thumb' => ProxyUtils::proxifyUrl($rr['thumb'], false, ProxyUtils::SIZE_THUMB),
'name' => htmlentities($rr['name']),
'username' => htmlentities($rr['name']),
'account_type' => Contact::getAccountType($rr),

View file

@ -12,6 +12,7 @@ use Friendica\Core\L10n;
use Friendica\Database\DBA;
use Friendica\Model\Contact;
use Friendica\Model\Profile;
use Friendica\Util\Proxy as ProxyUtils;
function directory_init(App $a)
{
@ -35,8 +36,6 @@ function directory_post(App $a)
function directory_content(App $a)
{
require_once("mod/proxy.php");
if ((Config::get('system', 'block_public') && !local_user() && !remote_user())
|| (Config::get('system', 'block_local_dir') && !local_user() && !remote_user())
) {
@ -165,7 +164,7 @@ function directory_content(App $a)
'id' => $rr['id'],
'url' => $profile_link,
'itemurl' => $itemurl,
'thumb' => proxy_url($rr[$photo], false, PROXY_SIZE_THUMB),
'thumb' => ProxyUtils::proxifyUrl($rr[$photo], false, ProxyUtils::SIZE_THUMB),
'img_hover' => $rr['name'],
'name' => $rr['name'],
'details' => $details,

View file

@ -16,6 +16,7 @@ use Friendica\Model\GContact;
use Friendica\Network\Probe;
use Friendica\Protocol\PortableContact;
use Friendica\Util\Network;
use Friendica\Util\Proxy as ProxyUtils;
require_once 'mod/contacts.php';
@ -226,7 +227,7 @@ function dirfind_content(App $a, $prefix = "") {
'url' => Contact::magicLink($jj->url),
'itemurl' => $itemurl,
'name' => htmlentities($jj->name),
'thumb' => proxy_url($jj->photo, false, PROXY_SIZE_THUMB),
'thumb' => ProxyUtils::proxifyUrl($jj->photo, false, ProxyUtils::SIZE_THUMB),
'img_hover' => $jj->tags,
'conntxt' => $conntxt,
'connlnk' => $connlnk,

View file

@ -10,6 +10,7 @@ use Friendica\Model\Contact;
use Friendica\Model\Profile;
use Friendica\Network\Probe;
use Friendica\Database\DBA;
use Friendica\Util\Proxy as ProxyUtils;
function follow_post(App $a)
{
@ -144,7 +145,7 @@ function follow_content(App $a)
$o = replace_macros($tpl, [
'$header' => htmlentities($header),
//'$photo' => proxy_url($ret['photo'], false, PROXY_SIZE_SMALL),
//'$photo' => ProxyUtils::proxifyUrl($ret['photo'], false, ProxyUtils::SIZE_SMALL),
'$desc' => '',
'$pls_answer' => L10n::t('Please answer the following:'),
'$does_know_you' => ['knowyou', L10n::t('Does %s know you?', $ret['name']), false, '', [L10n::t('No'), L10n::t('Yes')]],

View file

@ -14,6 +14,7 @@ use Friendica\Core\System;
use Friendica\Database\DBA;
use Friendica\Model\Contact;
use Friendica\Model\GContact;
use Friendica\Util\Proxy as ProxyUtils;
function hovercard_init(App $a)
{
@ -96,7 +97,7 @@ function hovercard_content()
'name' => $contact['name'],
'nick' => $contact['nick'],
'addr' => defaults($contact, 'addr', $contact['url']),
'thumb' => proxy_url($contact['thumb'], false, PROXY_SIZE_THUMB),
'thumb' => ProxyUtils::proxifyUrl($contact['thumb'], false, ProxyUtils::SIZE_THUMB),
'url' => Contact::magicLink($contact['url']),
'nurl' => $contact['nurl'], // We additionally store the nurl as identifier
'location' => $contact['location'],

View file

@ -11,9 +11,9 @@ use Friendica\Core\System;
use Friendica\Database\DBA;
use Friendica\Model\Contact;
use Friendica\Util\Network;
use Friendica\Util\Proxy as ProxyUtils;
require_once 'include/text.php';
require_once 'mod/proxy.php';
/**
* @brief Controller for /match.
@ -100,7 +100,7 @@ function match_content(App $a)
'tags' => $contact_details['keywords'],
'about' => $contact_details['about'],
'account_type' => Contact::getAccountType($contact_details),
'thumb' => proxy_url($jj->photo, false, PROXY_SIZE_THUMB),
'thumb' => ProxyUtils::proxifyUrl($jj->photo, false, ProxyUtils::SIZE_THUMB),
'inttxt' => ' ' . L10n::t('is interested in:'),
'conntxt' => L10n::t('Connect'),
'connlnk' => $connlnk,

View file

@ -14,6 +14,7 @@ use Friendica\Database\DBA;
use Friendica\Model\Contact;
use Friendica\Model\Mail;
use Friendica\Util\DateTimeFormat;
use Friendica\Util\Proxy as ProxyUtils;
use Friendica\Util\Temporal;
require_once 'include/conversation.php';
@ -388,7 +389,7 @@ function message_content(App $a)
'from_url' => $from_url,
'from_addr' => $contact['addr'],
'sparkle' => $sparkle,
'from_photo' => proxy_url($from_photo, false, PROXY_SIZE_THUMB),
'from_photo' => ProxyUtils::proxifyUrl($from_photo, false, ProxyUtils::SIZE_THUMB),
'subject' => $subject_e,
'body' => $body_e,
'delete' => L10n::t('Delete message'),
@ -489,7 +490,7 @@ function render_messages(array $msg, $t)
'$from_url' => Contact::magicLink($rr['url']),
'$from_addr' => $contact['addr'],
'$sparkle' => ' sparkle',
'$from_photo' => proxy_url($from_photo, false, PROXY_SIZE_THUMB),
'$from_photo' => ProxyUtils::proxifyUrl($from_photo, false, ProxyUtils::SIZE_THUMB),
'$subject' => $subject_e,
'$delete' => L10n::t('Delete conversation'),
'$body' => $body_e,

View file

@ -22,6 +22,7 @@ use Friendica\Model\Item;
use Friendica\Model\Profile;
use Friendica\Module\Login;
use Friendica\Util\DateTimeFormat;
use Friendica\Util\Proxy as ProxyUtils;
require_once 'include/conversation.php';
require_once 'include/items.php';
@ -652,7 +653,7 @@ function networkThreadedView(App $a, $update, $parent)
'id' => 'network',
'name' => htmlentities($contact['name']),
'itemurl' => defaults($contact, 'addr', $contact['nurl']),
'thumb' => proxy_url($contact['thumb'], false, PROXY_SIZE_THUMB),
'thumb' => ProxyUtils::proxifyUrl($contact['thumb'], false, ProxyUtils::SIZE_THUMB),
'details' => $contact['location'],
];

View file

@ -19,9 +19,9 @@ use Friendica\Model\Group;
use Friendica\Model\Item;
use Friendica\Util\DateTimeFormat;
use Friendica\Util\Temporal;
use Friendica\Util\Proxy as ProxyUtils;
use Friendica\Util\XML;
require_once 'mod/proxy.php';
require_once 'include/enotify.php';
/**
@ -356,9 +356,9 @@ function ping_init(App $a)
$contact = Contact::getDetailsByURL($notif['url']);
if (isset($contact['micro'])) {
$notif['photo'] = proxy_url($contact['micro'], false, PROXY_SIZE_MICRO);
$notif['photo'] = ProxyUtils::proxifyUrl($contact['micro'], false, ProxyUtils::SIZE_MICRO);
} else {
$notif['photo'] = proxy_url($notif['photo'], false, PROXY_SIZE_MICRO);
$notif['photo'] = ProxyUtils::proxifyUrl($notif['photo'], false, ProxyUtils::SIZE_MICRO);
}
$local_time = DateTimeFormat::local($notif['date']);

View file

@ -1,375 +0,0 @@
<?php
/**
* @file mod/proxy.php
* @brief Based upon "Privacy Image Cache" by Tobias Hößl <https://github.com/CatoTH/>
*/
use Friendica\App;
use Friendica\Core\Config;
use Friendica\Core\System;
use Friendica\Database\DBA;
use Friendica\Model\Photo;
use Friendica\Object\Image;
use Friendica\Util\DateTimeFormat;
use Friendica\Util\Network;
define('PROXY_DEFAULT_TIME', 86400); // 1 Day
define('PROXY_SIZE_MICRO', 'micro');
define('PROXY_SIZE_THUMB', 'thumb');
define('PROXY_SIZE_SMALL', 'small');
define('PROXY_SIZE_MEDIUM', 'medium');
define('PROXY_SIZE_LARGE', 'large');
require_once 'include/security.php';
function proxy_init(App $a) {
// Pictures are stored in one of the following ways:
// 1. If a folder "proxy" exists and is writeable, then use this for caching
// 2. If a cache path is defined, use this
// 3. If everything else failed, cache into the database
//
// Question: Do we really need these three methods?
if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
header('HTTP/1.1 304 Not Modified');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s', time()) . ' GMT');
header('Etag: ' . $_SERVER['HTTP_IF_NONE_MATCH']);
header('Expires: ' . gmdate('D, d M Y H:i:s', time() + (31536000)) . ' GMT');
header('Cache-Control: max-age=31536000');
if (function_exists('header_remove')) {
header_remove('Last-Modified');
header_remove('Expires');
header_remove('Cache-Control');
}
exit;
}
if (function_exists('header_remove')) {
header_remove('Pragma');
header_remove('pragma');
}
$thumb = false;
$size = 1024;
$sizetype = '';
$basepath = $a->get_basepath();
// If the cache path isn't there, try to create it
if (!is_dir($basepath . '/proxy') && is_writable($basepath)) {
mkdir($basepath . '/proxy');
}
// Checking if caching into a folder in the webroot is activated and working
$direct_cache = (is_dir($basepath . '/proxy') && is_writable($basepath . '/proxy'));
// Look for filename in the arguments
if ((isset($a->argv[1]) || isset($a->argv[2]) || isset($a->argv[3])) && !isset($_REQUEST['url'])) {
if (isset($a->argv[3])) {
$url = $a->argv[3];
} elseif (isset($a->argv[2])) {
$url = $a->argv[2];
} else {
$url = $a->argv[1];
}
if (isset($a->argv[3]) && ($a->argv[3] == 'thumb')) {
$size = 200;
}
// thumb, small, medium and large.
if (substr($url, -6) == ':micro') {
$size = 48;
$sizetype = ':micro';
$url = substr($url, 0, -6);
} elseif (substr($url, -6) == ':thumb') {
$size = 80;
$sizetype = ':thumb';
$url = substr($url, 0, -6);
} elseif (substr($url, -6) == ':small') {
$size = 175;
$url = substr($url, 0, -6);
$sizetype = ':small';
} elseif (substr($url, -7) == ':medium') {
$size = 600;
$url = substr($url, 0, -7);
$sizetype = ':medium';
} elseif (substr($url, -6) == ':large') {
$size = 1024;
$url = substr($url, 0, -6);
$sizetype = ':large';
}
$pos = strrpos($url, '=.');
if ($pos) {
$url = substr($url, 0, $pos + 1);
}
$url = str_replace(['.jpg', '.jpeg', '.gif', '.png'], ['','','',''], $url);
$url = base64_decode(strtr($url, '-_', '+/'), true);
if ($url) {
$_REQUEST['url'] = $url;
}
} else {
$direct_cache = false;
}
if (!$direct_cache) {
$urlhash = 'pic:' . sha1($_REQUEST['url']);
$cachefile = get_cachefile(hash('md5', $_REQUEST['url']));
if ($cachefile != '' && file_exists($cachefile)) {
$img_str = file_get_contents($cachefile);
$mime = image_type_to_mime_type(exif_imagetype($cachefile));
header('Content-type: ' . $mime);
header('Last-Modified: ' . gmdate('D, d M Y H:i:s', time()) . ' GMT');
header('Etag: "' . md5($img_str) . '"');
header('Expires: ' . gmdate('D, d M Y H:i:s', time() + (31536000)) . ' GMT');
header('Cache-Control: max-age=31536000');
// reduce quality - if it isn't a GIF
if ($mime != 'image/gif') {
$Image = new Image($img_str, $mime);
if ($Image->isValid()) {
$img_str = $Image->asString();
}
}
echo $img_str;
killme();
}
} else {
$cachefile = '';
}
$valid = true;
$photo = null;
if (!$direct_cache && ($cachefile == '')) {
$photo = DBA::selectFirst('photo', ['data', 'desc'], ['resource-id' => $urlhash]);
if (DBA::isResult($photo)) {
$img_str = $photo['data'];
$mime = $photo['desc'];
if ($mime == '') {
$mime = 'image/jpeg';
}
}
}
if (!DBA::isResult($photo)) {
// It shouldn't happen but it does - spaces in URL
$_REQUEST['url'] = str_replace(' ', '+', $_REQUEST['url']);
$redirects = 0;
$img_str = Network::fetchUrl($_REQUEST['url'], true, $redirects, 10);
$tempfile = tempnam(get_temppath(), 'cache');
file_put_contents($tempfile, $img_str);
$mime = image_type_to_mime_type(exif_imagetype($tempfile));
unlink($tempfile);
// If there is an error then return a blank image
if ((substr($a->get_curl_code(), 0, 1) == '4') || (!$img_str)) {
$img_str = file_get_contents('images/blank.png');
$mime = 'image/png';
$cachefile = ''; // Clear the cachefile so that the dummy isn't stored
$valid = false;
$Image = new Image($img_str, 'image/png');
if ($Image->isValid()) {
$Image->scaleDown(10);
$img_str = $Image->asString();
}
} elseif ($mime != 'image/jpeg' && !$direct_cache && $cachefile == '') {
$image = @imagecreatefromstring($img_str);
if ($image === FALSE) {
die();
}
$fields = ['uid' => 0, 'contact-id' => 0, 'guid' => System::createGUID(), 'resource-id' => $urlhash, 'created' => DateTimeFormat::utcNow(), 'edited' => DateTimeFormat::utcNow(),
'filename' => basename($_REQUEST['url']), 'type' => '', 'album' => '', 'height' => imagesy($image), 'width' => imagesx($image),
'datasize' => 0, 'data' => $img_str, 'scale' => 100, 'profile' => 0,
'allow_cid' => '', 'allow_gid' => '', 'deny_cid' => '', 'deny_gid' => '', 'desc' => $mime];
DBA::insert('photo', $fields);
} else {
$Image = new Image($img_str, $mime);
if ($Image->isValid() && !$direct_cache && ($cachefile == '')) {
Photo::store($Image, 0, 0, $urlhash, $_REQUEST['url'], '', 100);
}
}
}
$img_str_orig = $img_str;
// reduce quality - if it isn't a GIF
if ($mime != 'image/gif') {
$Image = new Image($img_str, $mime);
if ($Image->isValid()) {
$Image->scaleDown($size);
$img_str = $Image->asString();
}
}
// If there is a real existing directory then put the cache file there
// advantage: real file access is really fast
// Otherwise write in cachefile
if ($valid && $direct_cache) {
file_put_contents($basepath . '/proxy/' . proxy_url($_REQUEST['url'], true), $img_str_orig);
if ($sizetype != '') {
file_put_contents($basepath . '/proxy/' . proxy_url($_REQUEST['url'], true) . $sizetype, $img_str);
}
} elseif ($cachefile != '') {
file_put_contents($cachefile, $img_str_orig);
}
header('Content-type: ' . $mime);
// Only output the cache headers when the file is valid
if ($valid) {
header('Last-Modified: ' . gmdate('D, d M Y H:i:s', time()) . ' GMT');
header('Etag: "' . md5($img_str) . '"');
header('Expires: ' . gmdate('D, d M Y H:i:s', time() + (31536000)) . ' GMT');
header('Cache-Control: max-age=31536000');
}
echo $img_str;
killme();
}
/**
* @brief Transform a remote URL into a local one
*
* This function only performs the URL replacement on http URL and if the
* provided URL isn't local, "the isn't deactivated" (sic) and if the config
* system.proxy_disabled is set to false.
*
* @param string $url The URL to proxyfy
* @param bool $writemode Returns a local path the remote URL should be saved to
* @param string $size One of the PROXY_SIZE_* constants
*
* @return string The proxyfied URL or relative path
*/
function proxy_url($url, $writemode = false, $size = '') {
$a = get_app();
if (substr($url, 0, strlen('http')) !== 'http') {
return $url;
}
// Only continue if it isn't a local image and the isn't deactivated
if (proxy_is_local_image($url)) {
$url = str_replace(normalise_link(System::baseUrl()) . '/', System::baseUrl() . '/', $url);
return $url;
}
if (Config::get('system', 'proxy_disabled')) {
return $url;
}
// Image URL may have encoded ampersands for display which aren't desirable for proxy
$url = html_entity_decode($url, ENT_NOQUOTES, 'utf-8');
// Creating a sub directory to reduce the amount of files in the cache directory
$basepath = $a->get_basepath() . '/proxy';
$shortpath = hash('md5', $url);
$longpath = substr($shortpath, 0, 2);
if (is_dir($basepath) && $writemode && !is_dir($basepath . '/' . $longpath)) {
mkdir($basepath . '/' . $longpath);
chmod($basepath . '/' . $longpath, 0777);
}
$longpath .= '/' . strtr(base64_encode($url), '+/', '-_');
// Extract the URL extension
$extension = pathinfo(parse_url($url, PHP_URL_PATH), PATHINFO_EXTENSION);
$extensions = ['jpg', 'jpeg', 'gif', 'png'];
if (in_array($extension, $extensions)) {
$shortpath .= '.' . $extension;
$longpath .= '.' . $extension;
}
$proxypath = System::baseUrl() . '/proxy/' . $longpath;
if ($size != '') {
$size = ':' . $size;
}
// Too long files aren't supported by Apache
// Writemode in combination with long files shouldn't be possible
if ((strlen($proxypath) > 250) && $writemode) {
return $shortpath;
} elseif (strlen($proxypath) > 250) {
return System::baseUrl() . '/proxy/' . $shortpath . '?url=' . urlencode($url);
} elseif ($writemode) {
return $longpath;
} else {
return $proxypath . $size;
}
}
/**
* @param $url string
* @return boolean
*/
function proxy_is_local_image($url) {
if ($url[0] == '/') {
return true;
}
if (strtolower(substr($url, 0, 5)) == 'data:') {
return true;
}
// links normalised - bug #431
$baseurl = normalise_link(System::baseUrl());
$url = normalise_link($url);
return (substr($url, 0, strlen($baseurl)) == $baseurl);
}
/**
* @brief Return the array of query string parameters from a URL
*
* @param string $url
* @return array Associative array of query string parameters
*/
function proxy_parse_query($url) {
$query = parse_url($url, PHP_URL_QUERY);
$query = html_entity_decode($query);
$query_list = explode('&', $query);
$arr = [];
foreach ($query_list as $key_value) {
$key_value_list = explode('=', $key_value);
$arr[$key_value_list[0]] = $key_value_list[1];
}
unset($url, $query_list, $url);
return $arr;
}
function proxy_img_cb($matches) {
// if the picture seems to be from another picture cache then take the original source
$queryvar = proxy_parse_query($matches[2]);
if (($queryvar['url'] != '') && (substr($queryvar['url'], 0, 4) == 'http')) {
$matches[2] = urldecode($queryvar['url']);
}
// following line changed per bug #431
if (proxy_is_local_image($matches[2])) {
return $matches[1] . $matches[2] . $matches[3];
}
return $matches[1] . proxy_url(htmlspecialchars_decode($matches[2])) . $matches[3];
}
function proxy_parse_html($html) {
$html = str_replace(normalise_link(System::baseUrl()) . '/', System::baseUrl() . '/', $html);
return preg_replace_callback('/(<img [^>]*src *= *["\'])([^"\']+)(["\'][^>]*>)/siU', 'proxy_img_cb', $html);
}

View file

@ -11,6 +11,7 @@ use Friendica\Core\System;
use Friendica\Database\DBA;
use Friendica\Model\Contact;
use Friendica\Model\GContact;
use Friendica\Util\Proxy as ProxyUtils;
function suggest_init(App $a) {
if (! local_user()) {
@ -51,11 +52,10 @@ function suggest_init(App $a) {
}
function suggest_content(App $a) {
require_once("mod/proxy.php");
function suggest_content(App $a)
{
$o = '';
if (! local_user()) {
notice(L10n::t('Permission denied.') . EOL);
return;
@ -91,7 +91,7 @@ function suggest_content(App $a) {
'itemurl' => (($contact_details['addr'] != "") ? $contact_details['addr'] : $rr['url']),
'img_hover' => $rr['url'],
'name' => $contact_details['name'],
'thumb' => proxy_url($contact_details['thumb'], false, PROXY_SIZE_THUMB),
'thumb' => ProxyUtils::proxifyUrl($contact_details['thumb'], false, ProxyUtils::SIZE_THUMB),
'details' => $contact_details['location'],
'tags' => $contact_details['keywords'],
'about' => $contact_details['about'],

View file

@ -10,6 +10,7 @@ use Friendica\Core\L10n;
use Friendica\Database\DBA;
use Friendica\Model\Contact;
use Friendica\Model\Profile;
use Friendica\Util\Proxy as ProxyUtils;
function viewcontacts_init(App $a)
{
@ -39,8 +40,6 @@ function viewcontacts_init(App $a)
function viewcontacts_content(App $a)
{
require_once("mod/proxy.php");
if ((Config::get('system', 'block_public')) && (! local_user()) && (! remote_user())) {
notice(L10n::t('Public access denied.') . EOL);
return;
@ -102,7 +101,7 @@ function viewcontacts_content(App $a)
'id' => $rr['id'],
'img_hover' => L10n::t('Visit %s\'s profile [%s]', $contact_details['name'], $rr['url']),
'photo_menu' => Contact::photoMenu($rr),
'thumb' => proxy_url($contact_details['thumb'], false, PROXY_SIZE_THUMB),
'thumb' => ProxyUtils::proxifyUrl($contact_details['thumb'], false, ProxyUtils::SIZE_THUMB),
'name' => htmlentities(substr($contact_details['name'], 0, 20)),
'username' => htmlentities($contact_details['name']),
'details' => $contact_details['location'],

View file

@ -10,6 +10,7 @@ use Friendica\Core\L10n;
use Friendica\Core\System;
use Friendica\Database\DBA;
use Friendica\Model\Contact;
use Friendica\Util\Proxy as ProxyUtils;
require_once 'include/dba.php';
@ -110,7 +111,7 @@ class ForumManager
'name' => $contact['name'],
'cid' => $contact['id'],
'selected' => $selected,
'micro' => System::removedBaseUrl(proxy_url($contact['micro'], false, PROXY_SIZE_MICRO)),
'micro' => System::removedBaseUrl(ProxyUtils::proxifyUrl($contact['micro'], false, ProxyUtils::SIZE_MICRO)),
'id' => ++$id,
];
$entries[] = $entry;

View file

@ -19,9 +19,9 @@ use Friendica\Database\DBA;
use Friendica\Util\DateTimeFormat;
use Friendica\Util\Network;
use Friendica\Util\ParseUrl;
use Friendica\Util\Proxy as ProxyUtils;
require_once 'include/dba.php';
require_once 'mod/proxy.php';
/**
* Handles all OEmbed content fetching and replacement
@ -191,13 +191,16 @@ class OEmbed
$ret = $oembed->html;
}
break;
case "photo":
$ret .= '<img width="' . $oembed->width . '" src="' . proxy_url($oembed->url) . '">';
$ret .= '<img width="' . $oembed->width . '" src="' . ProxyUtils::proxifyUrl($oembed->url) . '">';
break;
case "link":
break;
case "rich":
$ret .= proxy_parse_html($oembed->html);
$ret .= ProxyUtils::proxifyHtml($oembed->html);
break;
}

View file

@ -24,10 +24,9 @@ use Friendica\Object\Image;
use Friendica\Util\Map;
use Friendica\Util\Network;
use Friendica\Util\ParseUrl;
use Friendica\Util\Proxy as ProxyUtils;
use League\HTMLToMarkdown\HtmlConverter;
require_once "mod/proxy.php";
class BBCode extends BaseObject
{
/**
@ -361,7 +360,7 @@ class BBCode extends BaseObject
{
// Only send proxied pictures to API and for internal display
if (in_array($simplehtml, [false, 2])) {
return proxy_url($image);
return ProxyUtils::proxifyUrl($image);
} else {
return $image;
}
@ -1033,7 +1032,7 @@ class BBCode extends BaseObject
} else {
$text = trim($share[1]) . "\n";
$avatar = proxy_url($avatar, false, PROXY_SIZE_THUMB);
$avatar = ProxyUtils::proxifyUrl($avatar, false, ProxyUtils::SIZE_THUMB);
$tpl = get_markup_template('shared_content.tpl');
$text .= replace_macros($tpl, [

View file

@ -13,6 +13,7 @@ use Friendica\Database\DBA;
use Friendica\Model\Contact;
use Friendica\Model\Item;
use Friendica\Util\DateTimeFormat;
use Friendica\Util\Proxy as ProxyUtils;
use Friendica\Util\Temporal;
use Friendica\Util\XML;
@ -243,7 +244,7 @@ class NotificationsManager extends BaseObject
case 'system':
$default_item_label = 'notify';
$default_item_link = System::baseUrl(true) . '/notify/view/' . $it['id'];
$default_item_image = proxy_url($it['photo'], false, PROXY_SIZE_MICRO);
$default_item_image = ProxyUtils::proxifyUrl($it['photo'], false, ProxyUtils::SIZE_MICRO);
$default_item_url = $it['url'];
$default_item_text = strip_tags(BBCode::convert($it['msg']));
$default_item_when = DateTimeFormat::local($it['date'], 'r');
@ -253,7 +254,7 @@ class NotificationsManager extends BaseObject
case 'home':
$default_item_label = 'comment';
$default_item_link = System::baseUrl(true) . '/display/' . $it['parent-guid'];
$default_item_image = proxy_url($it['author-avatar'], false, PROXY_SIZE_MICRO);
$default_item_image = ProxyUtils::proxifyUrl($it['author-avatar'], false, ProxyUtils::SIZE_MICRO);
$default_item_url = $it['author-link'];
$default_item_text = L10n::t("%s commented on %s's post", $it['author-name'], $it['parent-author-name']);
$default_item_when = DateTimeFormat::local($it['created'], 'r');
@ -263,7 +264,7 @@ class NotificationsManager extends BaseObject
default:
$default_item_label = (($it['id'] == $it['parent']) ? 'post' : 'comment');
$default_item_link = System::baseUrl(true) . '/display/' . $it['parent-guid'];
$default_item_image = proxy_url($it['author-avatar'], false, PROXY_SIZE_MICRO);
$default_item_image = ProxyUtils::proxifyUrl($it['author-avatar'], false, ProxyUtils::SIZE_MICRO);
$default_item_url = $it['author-link'];
$default_item_text = (($it['id'] == $it['parent'])
? L10n::t("%s created a new post", $it['author-name'])
@ -278,7 +279,7 @@ class NotificationsManager extends BaseObject
$notif = [
'label' => 'like',
'link' => System::baseUrl(true) . '/display/' . $it['parent-guid'],
'image' => proxy_url($it['author-avatar'], false, PROXY_SIZE_MICRO),
'image' => ProxyUtils::proxifyUrl($it['author-avatar'], false, ProxyUtils::SIZE_MICRO),
'url' => $it['author-link'],
'text' => L10n::t("%s liked %s's post", $it['author-name'], $it['parent-author-name']),
'when' => $default_item_when,
@ -291,7 +292,7 @@ class NotificationsManager extends BaseObject
$notif = [
'label' => 'dislike',
'link' => System::baseUrl(true) . '/display/' . $it['parent-guid'],
'image' => proxy_url($it['author-avatar'], false, PROXY_SIZE_MICRO),
'image' => ProxyUtils::proxifyUrl($it['author-avatar'], false, ProxyUtils::SIZE_MICRO),
'url' => $it['author-link'],
'text' => L10n::t("%s disliked %s's post", $it['author-name'], $it['parent-author-name']),
'when' => $default_item_when,
@ -304,7 +305,7 @@ class NotificationsManager extends BaseObject
$notif = [
'label' => 'attend',
'link' => System::baseUrl(true) . '/display/' . $it['parent-guid'],
'image' => proxy_url($it['author-avatar'], false, PROXY_SIZE_MICRO),
'image' => ProxyUtils::proxifyUrl($it['author-avatar'], false, ProxyUtils::SIZE_MICRO),
'url' => $it['author-link'],
'text' => L10n::t("%s is attending %s's event", $it['author-name'], $it['parent-author-name']),
'when' => $default_item_when,
@ -317,7 +318,7 @@ class NotificationsManager extends BaseObject
$notif = [
'label' => 'attendno',
'link' => System::baseUrl(true) . '/display/' . $it['parent-guid'],
'image' => proxy_url($it['author-avatar'], false, PROXY_SIZE_MICRO),
'image' => ProxyUtils::proxifyUrl($it['author-avatar'], false, ProxyUtils::SIZE_MICRO),
'url' => $it['author-link'],
'text' => L10n::t("%s is not attending %s's event", $it['author-name'], $it['parent-author-name']),
'when' => $default_item_when,
@ -330,7 +331,7 @@ class NotificationsManager extends BaseObject
$notif = [
'label' => 'attendmaybe',
'link' => System::baseUrl(true) . '/display/' . $it['parent-guid'],
'image' => proxy_url($it['author-avatar'], false, PROXY_SIZE_MICRO),
'image' => ProxyUtils::proxifyUrl($it['author-avatar'], false, ProxyUtils::SIZE_MICRO),
'url' => $it['author-link'],
'text' => L10n::t("%s may attend %s's event", $it['author-name'], $it['parent-author-name']),
'when' => $default_item_when,
@ -347,7 +348,7 @@ class NotificationsManager extends BaseObject
$notif = [
'label' => 'friend',
'link' => System::baseUrl(true) . '/display/' . $it['parent-guid'],
'image' => proxy_url($it['author-avatar'], false, PROXY_SIZE_MICRO),
'image' => ProxyUtils::proxifyUrl($it['author-avatar'], false, ProxyUtils::SIZE_MICRO),
'url' => $it['author-link'],
'text' => L10n::t("%s is now friends with %s", $it['author-name'], $it['fname']),
'when' => $default_item_when,
@ -622,7 +623,7 @@ class NotificationsManager extends BaseObject
'madeby_zrl' => Contact::magicLink($it['url']),
'madeby_addr' => $it['addr'],
'contact_id' => $it['contact-id'],
'photo' => ((x($it, 'fphoto')) ? proxy_url($it['fphoto'], false, PROXY_SIZE_SMALL) : "images/person-175.jpg"),
'photo' => ((x($it, 'fphoto')) ? ProxyUtils::proxifyUrl($it['fphoto'], false, ProxyUtils::SIZE_SMALL) : "images/person-175.jpg"),
'name' => $it['fname'],
'url' => $it['furl'],
'zrl' => Contact::magicLink($it['furl']),
@ -650,7 +651,7 @@ class NotificationsManager extends BaseObject
'uid' => $_SESSION['uid'],
'intro_id' => $it['intro_id'],
'contact_id' => $it['contact-id'],
'photo' => ((x($it, 'photo')) ? proxy_url($it['photo'], false, PROXY_SIZE_SMALL) : "images/person-175.jpg"),
'photo' => ((x($it, 'photo')) ? ProxyUtils::proxifyUrl($it['photo'], false, ProxyUtils::SIZE_SMALL) : "images/person-175.jpg"),
'name' => $it['name'],
'location' => BBCode::convert($it['glocation'], false),
'about' => BBCode::convert($it['gabout'], false),

View file

@ -20,10 +20,10 @@ use Friendica\Model\Contact;
use Friendica\Protocol\Diaspora;
use Friendica\Util\DateTimeFormat;
use Friendica\Util\Network;
use Friendica\Util\Proxy as ProxyUtils;
use Friendica\Util\Temporal;
require_once 'include/dba.php';
require_once 'mod/proxy.php';
class Profile
{
@ -495,7 +495,7 @@ class Profile
}
if (isset($p['photo'])) {
$p['photo'] = proxy_url($p['photo'], false, PROXY_SIZE_SMALL);
$p['photo'] = ProxyUtils::proxifyUrl($p['photo'], false, ProxyUtils::SIZE_SMALL);
}
$p['url'] = Contact::magicLink(defaults($p, 'url', $profile_url));

268
src/Module/Proxy.php Normal file
View file

@ -0,0 +1,268 @@
<?php
/**
* @file src/Module/Proxy.php
* @brief Based upon "Privacy Image Cache" by Tobias Hößl <https://github.com/CatoTH/>
*/
namespace Friendica\Module;
use Friendica\App;
use Friendica\BaseModule;
use Friendica\Core\Config;
use Friendica\Core\System;
use Friendica\Database\DBA;
use Friendica\Model\Photo;
use Friendica\Object\Image;
use Friendica\Util\DateTimeFormat;
use Friendica\Util\Network;
use Friendica\Util\Proxy as ProxyUtils;
require_once 'include/security.php';
/**
* @brief Module Proxy
*/
class Proxy extends BaseModule
{
/**
* @brief Initializer method for this class.
*
* Sets application instance and checks if /proxy/ path is writable.
*
* @param \Friendica\App $app Application instance
*/
public static function init()
{
// Set application instance here
$a = self::getApp();
/*
* Pictures are stored in one of the following ways:
*
* 1. If a folder "proxy" exists and is writeable, then use this for caching
* 2. If a cache path is defined, use this
* 3. If everything else failed, cache into the database
*
* Question: Do we really need these three methods?
*/
if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
header('HTTP/1.1 304 Not Modified');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s', time()) . ' GMT');
header('Etag: ' . $_SERVER['HTTP_IF_NONE_MATCH']);
header('Expires: ' . gmdate('D, d M Y H:i:s', time() + (31536000)) . ' GMT');
header('Cache-Control: max-age=31536000');
if (function_exists('header_remove')) {
header_remove('Last-Modified');
header_remove('Expires');
header_remove('Cache-Control');
}
/// @TODO Stop here?
exit();
}
if (function_exists('header_remove')) {
header_remove('Pragma');
header_remove('pragma');
}
$thumb = false;
$size = 1024;
$sizetype = '';
$basepath = $a->get_basepath();
// If the cache path isn't there, try to create it
if (!is_dir($basepath . '/proxy') && is_writable($basepath)) {
mkdir($basepath . '/proxy');
}
// Checking if caching into a folder in the webroot is activated and working
$direct_cache = (is_dir($basepath . '/proxy') && is_writable($basepath . '/proxy'));
// Look for filename in the arguments
if ((isset($a->argv[1]) || isset($a->argv[2]) || isset($a->argv[3])) && !isset($_REQUEST['url'])) {
if (isset($a->argv[3])) {
$url = $a->argv[3];
} elseif (isset($a->argv[2])) {
$url = $a->argv[2];
} else {
$url = $a->argv[1];
}
if (isset($a->argv[3]) && ($a->argv[3] == 'thumb')) {
$size = 200;
}
// thumb, small, medium and large.
if (substr($url, -6) == ':micro') {
$size = 48;
$sizetype = ':micro';
$url = substr($url, 0, -6);
} elseif (substr($url, -6) == ':thumb') {
$size = 80;
$sizetype = ':thumb';
$url = substr($url, 0, -6);
} elseif (substr($url, -6) == ':small') {
$size = 175;
$url = substr($url, 0, -6);
$sizetype = ':small';
} elseif (substr($url, -7) == ':medium') {
$size = 600;
$url = substr($url, 0, -7);
$sizetype = ':medium';
} elseif (substr($url, -6) == ':large') {
$size = 1024;
$url = substr($url, 0, -6);
$sizetype = ':large';
}
$pos = strrpos($url, '=.');
if ($pos) {
$url = substr($url, 0, $pos + 1);
}
$url = str_replace(['.jpg', '.jpeg', '.gif', '.png'], ['','','',''], $url);
$url = base64_decode(strtr($url, '-_', '+/'), true);
if ($url) {
$_REQUEST['url'] = $url;
}
} else {
$direct_cache = false;
}
if (!$direct_cache) {
$urlhash = 'pic:' . sha1($_REQUEST['url']);
$cachefile = get_cachefile(hash('md5', $_REQUEST['url']));
if ($cachefile != '' && file_exists($cachefile)) {
$img_str = file_get_contents($cachefile);
$mime = image_type_to_mime_type(exif_imagetype($cachefile));
header('Content-type: ' . $mime);
header('Last-Modified: ' . gmdate('D, d M Y H:i:s', time()) . ' GMT');
header('Etag: "' . md5($img_str) . '"');
header('Expires: ' . gmdate('D, d M Y H:i:s', time() + (31536000)) . ' GMT');
header('Cache-Control: max-age=31536000');
// reduce quality - if it isn't a GIF
if ($mime != 'image/gif') {
$image = new Image($img_str, $mime);
if ($image->isValid()) {
$img_str = $image->asString();
}
}
echo $img_str;
exit();
}
} else {
$cachefile = '';
}
$valid = true;
$photo = null;
if (!$direct_cache && ($cachefile == '')) {
$photo = DBA::selectFirst('photo', ['data', 'desc'], ['resource-id' => $urlhash]);
if (DBA::isResult($photo)) {
$img_str = $photo['data'];
$mime = $photo['desc'];
if ($mime == '') {
$mime = 'image/jpeg';
}
}
}
if (!DBA::isResult($photo)) {
// It shouldn't happen but it does - spaces in URL
$_REQUEST['url'] = str_replace(' ', '+', $_REQUEST['url']);
$redirects = 0;
$img_str = Network::fetchUrl($_REQUEST['url'], true, $redirects, 10);
$tempfile = tempnam(get_temppath(), 'cache');
file_put_contents($tempfile, $img_str);
$mime = image_type_to_mime_type(exif_imagetype($tempfile));
unlink($tempfile);
// If there is an error then return a blank image
if ((substr($a->get_curl_code(), 0, 1) == '4') || (!$img_str)) {
$img_str = file_get_contents('images/blank.png');
$mime = 'image/png';
$cachefile = ''; // Clear the cachefile so that the dummy isn't stored
$valid = false;
$image = new Image($img_str, 'image/png');
if ($image->isValid()) {
$image->scaleDown(10);
$img_str = $image->asString();
}
} elseif ($mime != 'image/jpeg' && !$direct_cache && $cachefile == '') {
$image = @imagecreatefromstring($img_str);
if ($image === FALSE) {
die();
}
$fields = ['uid' => 0, 'contact-id' => 0, 'guid' => System::createGUID(), 'resource-id' => $urlhash, 'created' => DateTimeFormat::utcNow(), 'edited' => DateTimeFormat::utcNow(),
'filename' => basename($_REQUEST['url']), 'type' => '', 'album' => '', 'height' => imagesy($image), 'width' => imagesx($image),
'datasize' => 0, 'data' => $img_str, 'scale' => 100, 'profile' => 0,
'allow_cid' => '', 'allow_gid' => '', 'deny_cid' => '', 'deny_gid' => '', 'desc' => $mime];
DBA::insert('photo', $fields);
} else {
$image = new Image($img_str, $mime);
if ($image->isValid() && !$direct_cache && ($cachefile == '')) {
Photo::store($image, 0, 0, $urlhash, $_REQUEST['url'], '', 100);
}
}
}
$img_str_orig = $img_str;
// reduce quality - if it isn't a GIF
if ($mime != 'image/gif') {
$image = new Image($img_str, $mime);
if ($image->isValid()) {
$image->scaleDown($size);
$img_str = $image->asString();
}
}
/*
* If there is a real existing directory then put the cache file there
* advantage: real file access is really fast
* Otherwise write in cachefile
*/
if ($valid && $direct_cache) {
file_put_contents($basepath . '/proxy/' . ProxyUtils::proxifyUrl($_REQUEST['url'], true), $img_str_orig);
if ($sizetype != '') {
file_put_contents($basepath . '/proxy/' . ProxyUtils::proxifyUrl($_REQUEST['url'], true) . $sizetype, $img_str);
}
} elseif ($cachefile != '') {
file_put_contents($cachefile, $img_str_orig);
}
header('Content-type: ' . $mime);
// Only output the cache headers when the file is valid
if ($valid) {
header('Last-Modified: ' . gmdate('D, d M Y H:i:s', time()) . ' GMT');
header('Etag: "' . md5($img_str) . '"');
header('Expires: ' . gmdate('D, d M Y H:i:s', time() + (31536000)) . ' GMT');
header('Cache-Control: max-age=31536000');
}
echo $img_str;
exit();
}
}

View file

@ -16,6 +16,7 @@ use Friendica\Model\Contact;
use Friendica\Model\Item;
use Friendica\Model\Term;
use Friendica\Util\DateTimeFormat;
use Friendica\Util\Proxy as ProxyUtils;
use Friendica\Util\Temporal;
require_once 'include/dba.php';
@ -112,8 +113,6 @@ class Post extends BaseObject
*/
public function getTemplateData($conv_responses, $thread_level = 1)
{
require_once "mod/proxy.php";
$result = [];
$a = self::getApp();
@ -150,6 +149,7 @@ class Post extends BaseObject
|| strlen($item['deny_cid']) || strlen($item['deny_gid']))))
? L10n::t('Private Message')
: false);
$shareable = in_array($conv->getProfileOwner(), [0, local_user()]) && $item['private'] != 1;
if (local_user() && link_compare($a->contact['url'], $item['author-link'])) {
@ -367,7 +367,7 @@ class Post extends BaseObject
'profile_url' => $profile_link,
'item_photo_menu' => item_photo_menu($item),
'name' => $name_e,
'thumb' => $a->remove_baseurl(proxy_url($item['author-avatar'], false, PROXY_SIZE_THUMB)),
'thumb' => $a->remove_baseurl(ProxyUtils::proxifyUrl($item['author-avatar'], false, ProxyUtils::SIZE_THUMB)),
'osparkle' => $osparkle,
'sparkle' => $sparkle,
'title' => $title_e,
@ -380,7 +380,7 @@ class Post extends BaseObject
'indent' => $indent,
'shiny' => $shiny,
'owner_url' => $this->getOwnerUrl(),
'owner_photo' => $a->remove_baseurl(proxy_url($item['owner-avatar'], false, PROXY_SIZE_THUMB)),
'owner_photo' => $a->remove_baseurl(ProxyUtils::proxifyUrl($item['owner-avatar'], false, ProxyUtils::SIZE_THUMB)),
'owner_name' => htmlentities($owner_name_e),
'plink' => get_plink($item),
'edpost' => Feature::isEnabled($conv->getProfileOwner(), 'edit_posts') ? $edpost : '',

View file

@ -23,6 +23,7 @@ use Friendica\Network\Probe;
use Friendica\Object\Image;
use Friendica\Util\DateTimeFormat;
use Friendica\Util\Network;
use Friendica\Util\Proxy as ProxyUtils;
use Friendica\Util\XML;
require_once 'include/dba.php';
@ -30,7 +31,6 @@ require_once 'include/items.php';
require_once 'mod/share.php';
require_once 'include/enotify.php';
require_once 'include/api.php';
require_once 'mod/proxy.php';
/**
* @brief This class contain functions for the OStatus protocol
@ -1214,7 +1214,7 @@ class OStatus
}
// Is it a remote picture? Then make a smaller preview here
$preview = proxy_url($preview, false, PROXY_SIZE_SMALL);
$preview = ProxyUtils::proxifyUrl($preview, false, ProxyUtils::SIZE_SMALL);
// Is it a local picture? Then make it smaller here
$preview = str_replace(["-0.jpg", "-0.png"], ["-2.jpg", "-2.png"], $preview);

221
src/Util/Proxy.php Normal file
View file

@ -0,0 +1,221 @@
<?php
namespace Friendica\Util;
use Friendica\BaseModule;
use Friendica\BaseObject;
use Friendica\Core\Config;
use Friendica\Core\System;
require_once 'include/security.php';
/**
* @brief Proxy utilities class
*/
class Proxy
{
/**
* Default time to keep images in proxy storage
*/
const DEFAULT_TIME = 86400; // 1 Day
/**
* Sizes constants
*/
const SIZE_MICRO = 'micro';
const SIZE_THUMB = 'thumb';
const SIZE_SMALL = 'small';
const SIZE_MEDIUM = 'medium';
const SIZE_LARGE = 'large';
/**
* Accepted extensions
*
* @var array
* @todo Make this configurable?
*/
private static $extensions = [
'jpg',
'jpeg',
'gif',
'png',
];
/**
* @brief Private constructor
*/
private function __construct () {
// No instances from utilities classes
}
/**
* @brief Transform a remote URL into a local one.
*
* This function only performs the URL replacement on http URL and if the
* provided URL isn't local, "the isn't deactivated" (sic) and if the config
* system.proxy_disabled is set to false.
*
* @param string $url The URL to proxyfy
* @param bool $writemode Returns a local path the remote URL should be saved to
* @param string $size One of the ProxyUtils::SIZE_* constants
*
* @return string The proxyfied URL or relative path
*/
public static function proxifyUrl($url, $writemode = false, $size = '')
{
// Get application instance
$a = BaseObject::getApp();
// Trim URL first
$url = trim($url);
// Is no http in front of it?
/// @TODO To weak test for being a valid URL
if (substr($url, 0, 4) !== 'http') {
return $url;
}
// Only continue if it isn't a local image and the isn't deactivated
if (self::isLocalImage($url)) {
$url = str_replace(normalise_link(System::baseUrl()) . '/', System::baseUrl() . '/', $url);
return $url;
}
// Is the proxy disabled?
if (Config::get('system', 'proxy_disabled')) {
return $url;
}
// Image URL may have encoded ampersands for display which aren't desirable for proxy
$url = html_entity_decode($url, ENT_NOQUOTES, 'utf-8');
// Creating a sub directory to reduce the amount of files in the cache directory
$basepath = $a->get_basepath() . '/proxy';
$shortpath = hash('md5', $url);
$longpath = substr($shortpath, 0, 2);
if (is_dir($basepath) && $writemode && !is_dir($basepath . '/' . $longpath)) {
mkdir($basepath . '/' . $longpath);
chmod($basepath . '/' . $longpath, 0777);
}
$longpath .= '/' . strtr(base64_encode($url), '+/', '-_');
// Extract the URL extension
$extension = pathinfo(parse_url($url, PHP_URL_PATH), PATHINFO_EXTENSION);
if (in_array($extension, self::$extensions)) {
$shortpath .= '.' . $extension;
$longpath .= '.' . $extension;
}
$proxypath = System::baseUrl() . '/proxy/' . $longpath;
if ($size != '') {
$size = ':' . $size;
}
// Too long files aren't supported by Apache
// Writemode in combination with long files shouldn't be possible
if ((strlen($proxypath) > 250) && $writemode) {
return $shortpath;
} elseif (strlen($proxypath) > 250) {
return System::baseUrl() . '/proxy/' . $shortpath . '?url=' . urlencode($url);
} elseif ($writemode) {
return $longpath;
} else {
return $proxypath . $size;
}
}
/**
* @brief "Proxifies" HTML code's image tags
*
* "Proxifies", means replaces image URLs in given HTML code with those from
* proxy storage directory.
*
* @param string $html Un-proxified HTML code
*
* @return string Proxified HTML code
*/
public static function proxifyHtml($html)
{
$html = str_replace(normalise_link(System::baseUrl()) . '/', System::baseUrl() . '/', $html);
return preg_replace_callback('/(<img [^>]*src *= *["\'])([^"\']+)(["\'][^>]*>)/siU', 'self::replaceUrl', $html);
}
/**
* @brief Checks if the URL is a local URL.
*
* @param string $url
* @return boolean
*/
private static function isLocalImage($url)
{
if (substr($url, 0, 1) == '/') {
return true;
}
if (strtolower(substr($url, 0, 5)) == 'data:') {
return true;
}
// links normalised - bug #431
$baseurl = normalise_link(System::baseUrl());
$url = normalise_link($url);
return (substr($url, 0, strlen($baseurl)) == $baseurl);
}
/**
* @brief Return the array of query string parameters from a URL
*
* @param string $url URL to parse
* @return array Associative array of query string parameters
*/
private static function parseQuery($url)
{
$query = parse_url($url, PHP_URL_QUERY);
$query = html_entity_decode($query);
$query_list = explode('&', $query);
$arr = [];
foreach ($query_list as $key_value) {
$key_value_list = explode('=', $key_value);
$arr[$key_value_list[0]] = $key_value_list[1];
}
unset($url, $query_list, $url);
return $arr;
}
/**
* @brief Call-back method to replace the UR
*
* @param array $matches Matches from preg_replace_callback()
* @return string Proxified HTML image tag
*/
private static function replaceUrl(array $matches)
{
// if the picture seems to be from another picture cache then take the original source
$queryvar = self::parseQuery($matches[2]);
if (!empty($queryvar['url']) && substr($queryvar['url'], 0, 4) == 'http') {
$matches[2] = urldecode($queryvar['url']);
}
// Following line changed per bug #431
if (self::isLocalImage($matches[2])) {
return $matches[1] . $matches[2] . $matches[3];
}
// Return proxified HTML
return $matches[1] . self::proxifyUrl(htmlspecialchars_decode($matches[2])) . $matches[3];
}
}

View file

@ -9,13 +9,14 @@ use Friendica\BaseObject;
use Friendica\Core\Cache;
use Friendica\Core\Config;
use Friendica\Database\DBA;
use Friendica\Database\PostUpdate;
use Friendica\Model\Contact;
use Friendica\Model\GContact;
use Friendica\Model\Photo;
use Friendica\Model\User;
use Friendica\Network\Probe;
use Friendica\Protocol\PortableContact;
use Friendica\Database\PostUpdate;
use Friendica\Util\Proxy as ProxyUtils;
require_once 'include/dba.php';
require_once 'mod/nodeinfo.php';
@ -159,9 +160,11 @@ class CronJobs
clear_cache($a->get_basepath(), $a->get_basepath() . "/proxy");
$cachetime = Config::get('system', 'proxy_cache_time');
if (!$cachetime) {
$cachetime = PROXY_DEFAULT_TIME;
$cachetime = ProxyUtils::DEFAULT_TIME;
}
$condition = ['`uid` = 0 AND `resource-id` LIKE "pic:%" AND `created` < NOW() - INTERVAL ? SECOND', $cachetime];
DBA::delete('photo', $condition);
}

View file

@ -19,8 +19,7 @@ use Friendica\Core\System;
use Friendica\Database\DBA;
use Friendica\Model\Contact;
use Friendica\Model\GContact;
require_once "mod/proxy.php";
use Friendica\Util\Proxy as ProxyUtils;
function vier_init(App $a)
{
@ -154,7 +153,7 @@ function vier_community_info()
$entry = replace_macros($tpl, [
'$id' => $rr['id'],
'$profile_link' => 'follow/?url='.urlencode($rr['url']),
'$photo' => proxy_url($rr['photo'], false, PROXY_SIZE_MICRO),
'$photo' => ProxyUtils::proxifyUrl($rr['photo'], false, ProxyUtils::SIZE_MICRO),
'$alt_text' => $rr['name'],
]);
$aside['$comunity_profiles_items'][] = $entry;
@ -237,7 +236,7 @@ function vier_community_info()
'name' => $contact['name'],
'cid' => $contact['id'],
'selected' => $selected,
'micro' => System::removedBaseUrl(proxy_url($contact['micro'], false, PROXY_SIZE_MICRO)),
'micro' => System::removedBaseUrl(ProxyUtils::proxifyUrl($contact['micro'], false, ProxyUtils::SIZE_MICRO)),
'id' => ++$id,
];
$entries[] = $entry;