]> git.mxchange.org Git - friendica.git/blob - src/Util/Proxy.php
16e3e221ff5c0da7dab239eb27e089b3aa80bb05
[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                 // Trim URL first
91                 $url = trim($url);
92
93                 // Quit if not an HTTP/HTTPS link or if local
94                 if (!in_array(parse_url($url, PHP_URL_SCHEME), ['http', 'https']) || self::isLocalImage($url)) {
95                         return $url;
96                 }
97
98                 // Is the proxy disabled?
99                 if (DI::config()->get('system', 'proxy_disabled')) {
100                         return $url;
101                 }
102
103                 // Image URL may have encoded ampersands for display which aren't desirable for proxy
104                 $url = html_entity_decode($url, ENT_NOQUOTES, 'utf-8');
105
106                 $shortpath = hash('md5', $url);
107                 $longpath = substr($shortpath, 0, 2);
108
109                 $longpath .= '/' . strtr(base64_encode($url), '+/', '-_');
110
111                 // Extract the URL extension
112                 $extension = pathinfo(parse_url($url, PHP_URL_PATH), PATHINFO_EXTENSION);
113
114                 if (in_array($extension, self::$extensions)) {
115                         $shortpath .= '.' . $extension;
116                         $longpath .= '.' . $extension;
117                 }
118
119                 $proxypath = DI::baseUrl() . '/proxy/' . $longpath;
120
121                 if ($size != '') {
122                         $size = ':' . $size;
123                 }
124
125                 // Too long files aren't supported by Apache
126                 if (strlen($proxypath) > 250) {
127                         return DI::baseUrl() . '/proxy/' . $shortpath . '?url=' . urlencode($url);
128                 } else {
129                         return $proxypath . $size;
130                 }
131         }
132
133         /**
134          * "Proxifies" HTML code's image tags
135          *
136          * "Proxifies", means replaces image URLs in given HTML code with those from
137          * proxy storage directory.
138          *
139          * @param string $html Un-proxified HTML code
140          *
141          * @return string Proxified HTML code
142          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
143          */
144         public static function proxifyHtml($html)
145         {
146                 $html = str_replace(Strings::normaliseLink(DI::baseUrl()) . '/', DI::baseUrl() . '/', $html);
147
148                 return preg_replace_callback('/(<img [^>]*src *= *["\'])([^"\']+)(["\'][^>]*>)/siU', 'self::replaceUrl', $html);
149         }
150
151         /**
152          * Checks if the URL is a local URL.
153          *
154          * @param string $url
155          * @return boolean
156          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
157          */
158         public static function isLocalImage($url)
159         {
160                 if (substr($url, 0, 1) == '/') {
161                         return true;
162                 }
163
164                 if (strtolower(substr($url, 0, 5)) == 'data:') {
165                         return true;
166                 }
167
168                 return Network::isLocalLink($url);
169         }
170
171         /**
172          * Return the array of query string parameters from a URL
173          *
174          * @param string $url URL to parse
175          * @return array Associative array of query string parameters
176          */
177         private static function parseQuery($url)
178         {
179                 $query = parse_url($url, PHP_URL_QUERY);
180                 $query = html_entity_decode($query);
181
182                 parse_str($query, $arr);
183
184                 return $arr;
185         }
186
187         /**
188          * Call-back method to replace the UR
189          *
190          * @param array $matches Matches from preg_replace_callback()
191          * @return string Proxified HTML image tag
192          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
193          */
194         private static function replaceUrl(array $matches)
195         {
196                 // if the picture seems to be from another picture cache then take the original source
197                 $queryvar = self::parseQuery($matches[2]);
198
199                 if (!empty($queryvar['url']) && substr($queryvar['url'], 0, 4) == 'http') {
200                         $matches[2] = urldecode($queryvar['url']);
201                 }
202
203                 // Following line changed per bug #431
204                 if (self::isLocalImage($matches[2])) {
205                         return $matches[1] . $matches[2] . $matches[3];
206                 }
207
208                 // Return proxified HTML
209                 return $matches[1] . self::proxifyUrl(htmlspecialchars_decode($matches[2])) . $matches[3];
210         }
211
212 }