]> git.mxchange.org Git - friendica.git/blob - src/Util/Proxy.php
Merge remote-tracking branch 'upstream/develop' into proxy
[friendica.git] / src / Util / Proxy.php
1 <?php
2 /**
3  * @copyright Copyright (C) 2010-2021, the Friendica project
4  *
5  * @license GNU AGPL version 3 or any later version
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU Affero General Public License as
9  * published by the Free Software Foundation, either version 3 of the
10  * License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU Affero General Public License for more details.
16  *
17  * You should have received a copy of the GNU Affero General Public License
18  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
19  *
20  */
21
22 namespace Friendica\Util;
23
24 use Friendica\DI;
25
26 /**
27  * Proxy utilities class
28  */
29 class Proxy
30 {
31
32         /**
33          * Default time to keep images in proxy storage
34          */
35         const DEFAULT_TIME = 86400; // 1 Day
36
37         /**
38          * Sizes constants
39          */
40         const SIZE_MICRO  = 'micro'; // 48
41         const SIZE_THUMB  = 'thumb'; // 80
42         const SIZE_SMALL  = 'small'; // 300
43         const SIZE_MEDIUM = 'medium'; // 600
44         const SIZE_LARGE  = 'large'; // 1024
45
46         /**
47          * Pixel Sizes
48          */
49         const PIXEL_MICRO  = 48;
50         const PIXEL_THUMB  = 80;
51         const PIXEL_SMALL  = 300;
52         const PIXEL_MEDIUM = 600;
53         const PIXEL_LARGE  = 1024;
54
55         /**
56          * Accepted extensions
57          *
58          * @var array
59          * @todo Make this configurable?
60          */
61         private static $extensions = [
62                 'jpg',
63                 'jpeg',
64                 'gif',
65                 'png',
66         ];
67
68         /**
69          * Private constructor
70          */
71         private function __construct () {
72                 // No instances from utilities classes
73         }
74
75         /**
76          * Transform a remote URL into a local one.
77          *
78          * This function only performs the URL replacement on http URL and if the
79          * provided URL isn't local, "the isn't deactivated" (sic) and if the config
80          * system.proxy_disabled is set to false.
81          *
82          * @param string $url       The URL to proxyfy
83          * @param string $size      One of the ProxyUtils::SIZE_* constants
84          *
85          * @return string The proxyfied URL or relative path
86          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
87          */
88         public static function proxifyUrl($url, $size = '')
89         {
90                 // Get application instance
91                 $a = DI::app();
92
93                 // Trim URL first
94                 $url = trim($url);
95
96                 // Is no http in front of it?
97                 /// @TODO To weak test for being a valid URL
98                 if (substr($url, 0, 4) !== 'http') {
99                         return $url;
100                 }
101
102                 // Only continue if it isn't a local image and the isn't deactivated
103                 if (self::isLocalImage($url)) {
104                         $url = str_replace(Strings::normaliseLink(DI::baseUrl()) . '/', DI::baseUrl() . '/', $url);
105                         return $url;
106                 }
107
108                 // Is the proxy disabled?
109                 if (DI::config()->get('system', 'proxy_disabled')) {
110                         return $url;
111                 }
112
113                 // Image URL may have encoded ampersands for display which aren't desirable for proxy
114                 $url = html_entity_decode($url, ENT_NOQUOTES, 'utf-8');
115
116                 $shortpath = hash('md5', $url);
117                 $longpath = substr($shortpath, 0, 2);
118
119                 $longpath .= '/' . strtr(base64_encode($url), '+/', '-_');
120
121                 // Extract the URL extension
122                 $extension = pathinfo(parse_url($url, PHP_URL_PATH), PATHINFO_EXTENSION);
123
124                 if (in_array($extension, self::$extensions)) {
125                         $shortpath .= '.' . $extension;
126                         $longpath .= '.' . $extension;
127                 }
128
129                 $proxypath = DI::baseUrl() . '/proxy/' . $longpath;
130
131                 if ($size != '') {
132                         $size = ':' . $size;
133                 }
134
135                 // Too long files aren't supported by Apache
136                 if (strlen($proxypath) > 250) {
137                         return DI::baseUrl() . '/proxy/' . $shortpath . '?url=' . urlencode($url);
138                 } else {
139                         return $proxypath . $size;
140                 }
141         }
142
143         /**
144          * "Proxifies" HTML code's image tags
145          *
146          * "Proxifies", means replaces image URLs in given HTML code with those from
147          * proxy storage directory.
148          *
149          * @param string $html Un-proxified HTML code
150          *
151          * @return string Proxified HTML code
152          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
153          */
154         public static function proxifyHtml($html)
155         {
156                 $html = str_replace(Strings::normaliseLink(DI::baseUrl()) . '/', DI::baseUrl() . '/', $html);
157
158                 return preg_replace_callback('/(<img [^>]*src *= *["\'])([^"\']+)(["\'][^>]*>)/siU', 'self::replaceUrl', $html);
159         }
160
161         /**
162          * Checks if the URL is a local URL.
163          *
164          * @param string $url
165          * @return boolean
166          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
167          */
168         public static function isLocalImage($url)
169         {
170                 if (substr($url, 0, 1) == '/') {
171                         return true;
172                 }
173
174                 if (strtolower(substr($url, 0, 5)) == 'data:') {
175                         return true;
176                 }
177
178                 // links normalised - bug #431
179                 $baseurl = Strings::normaliseLink(DI::baseUrl());
180                 $url = Strings::normaliseLink($url);
181
182                 return (substr($url, 0, strlen($baseurl)) == $baseurl);
183         }
184
185         /**
186          * Return the array of query string parameters from a URL
187          *
188          * @param string $url URL to parse
189          * @return array Associative array of query string parameters
190          */
191         private static function parseQuery($url)
192         {
193                 $query = parse_url($url, PHP_URL_QUERY);
194                 $query = html_entity_decode($query);
195
196                 parse_str($query, $arr);
197
198                 return $arr;
199         }
200
201         /**
202          * Call-back method to replace the UR
203          *
204          * @param array $matches Matches from preg_replace_callback()
205          * @return string Proxified HTML image tag
206          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
207          */
208         private static function replaceUrl(array $matches)
209         {
210                 // if the picture seems to be from another picture cache then take the original source
211                 $queryvar = self::parseQuery($matches[2]);
212
213                 if (!empty($queryvar['url']) && substr($queryvar['url'], 0, 4) == 'http') {
214                         $matches[2] = urldecode($queryvar['url']);
215                 }
216
217                 // Following line changed per bug #431
218                 if (self::isLocalImage($matches[2])) {
219                         return $matches[1] . $matches[2] . $matches[3];
220                 }
221
222                 // Return proxified HTML
223                 return $matches[1] . self::proxifyUrl(htmlspecialchars_decode($matches[2])) . $matches[3];
224         }
225
226 }