]> git.mxchange.org Git - friendica.git/blob - src/Util/Proxy.php
fc65047b937d03241b26da398d066ce32eeaba81
[friendica.git] / src / Util / Proxy.php
1 <?php
2
3 namespace Friendica\Util;
4
5 use Friendica\BaseModule;
6 use Friendica\BaseObject;
7 use Friendica\Core\Config;
8 use Friendica\Core\System;
9 use Friendica\Util\Strings;
10
11 /**
12  * @brief Proxy utilities class
13  */
14 class Proxy
15 {
16
17         /**
18          * Default time to keep images in proxy storage
19          */
20         const DEFAULT_TIME = 86400; // 1 Day
21
22         /**
23          * Sizes constants
24          */
25         const SIZE_MICRO  = 'micro';
26         const SIZE_THUMB  = 'thumb';
27         const SIZE_SMALL  = 'small';
28         const SIZE_MEDIUM = 'medium';
29         const SIZE_LARGE  = 'large';
30
31         /**
32          * Accepted extensions
33          *
34          * @var array
35          * @todo Make this configurable?
36          */
37         private static $extensions = [
38                 'jpg',
39                 'jpeg',
40                 'gif',
41                 'png',
42         ];
43
44         /**
45          * @brief Private constructor
46          */
47         private function __construct () {
48                 // No instances from utilities classes
49         }
50
51         /**
52          * @brief Transform a remote URL into a local one.
53          *
54          * This function only performs the URL replacement on http URL and if the
55          * provided URL isn't local, "the isn't deactivated" (sic) and if the config
56          * system.proxy_disabled is set to false.
57          *
58          * @param string $url       The URL to proxyfy
59          * @param bool   $writemode Returns a local path the remote URL should be saved to
60          * @param string $size      One of the ProxyUtils::SIZE_* constants
61          *
62          * @return string The proxyfied URL or relative path
63          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
64          */
65         public static function proxifyUrl($url, $writemode = false, $size = '')
66         {
67                 // Get application instance
68                 $a = BaseObject::getApp();
69
70                 // Trim URL first
71                 $url = trim($url);
72
73                 // Is no http in front of it?
74                 /// @TODO To weak test for being a valid URL
75                 if (substr($url, 0, 4) !== 'http') {
76                         return $url;
77                 }
78
79                 // Only continue if it isn't a local image and the isn't deactivated
80                 if (self::isLocalImage($url)) {
81                         $url = str_replace(Strings::normaliseLink(System::baseUrl()) . '/', System::baseUrl() . '/', $url);
82                         return $url;
83                 }
84
85                 // Is the proxy disabled?
86                 if (Config::get('system', 'proxy_disabled')) {
87                         return $url;
88                 }
89
90                 // Image URL may have encoded ampersands for display which aren't desirable for proxy
91                 $url = html_entity_decode($url, ENT_NOQUOTES, 'utf-8');
92
93                 // Creating a sub directory to reduce the amount of files in the cache directory
94                 $basepath = $a->getBasePath() . '/proxy';
95
96                 $shortpath = hash('md5', $url);
97                 $longpath = substr($shortpath, 0, 2);
98
99                 if (is_dir($basepath) && $writemode && !is_dir($basepath . '/' . $longpath)) {
100                         mkdir($basepath . '/' . $longpath);
101                         chmod($basepath . '/' . $longpath, 0777);
102                 }
103
104                 $longpath .= '/' . strtr(base64_encode($url), '+/', '-_');
105
106                 // Extract the URL extension
107                 $extension = pathinfo(parse_url($url, PHP_URL_PATH), PATHINFO_EXTENSION);
108
109                 if (in_array($extension, self::$extensions)) {
110                         $shortpath .= '.' . $extension;
111                         $longpath .= '.' . $extension;
112                 }
113
114                 $proxypath = System::baseUrl() . '/proxy/' . $longpath;
115
116                 if ($size != '') {
117                         $size = ':' . $size;
118                 }
119
120                 // Too long files aren't supported by Apache
121                 // Writemode in combination with long files shouldn't be possible
122                 if ((strlen($proxypath) > 250) && $writemode) {
123                         return $shortpath;
124                 } elseif (strlen($proxypath) > 250) {
125                         return System::baseUrl() . '/proxy/' . $shortpath . '?url=' . urlencode($url);
126                 } elseif ($writemode) {
127                         return $longpath;
128                 } else {
129                         return $proxypath . $size;
130                 }
131         }
132
133         /**
134          * @brief "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(System::baseUrl()) . '/', System::baseUrl() . '/', $html);
147
148                 return preg_replace_callback('/(<img [^>]*src *= *["\'])([^"\']+)(["\'][^>]*>)/siU', 'self::replaceUrl', $html);
149         }
150
151         /**
152          * @brief Checks if the URL is a local URL.
153          *
154          * @param string $url
155          * @return boolean
156          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
157          */
158         private 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                 // links normalised - bug #431
169                 $baseurl = Strings::normaliseLink(System::baseUrl());
170                 $url = Strings::normaliseLink($url);
171
172                 return (substr($url, 0, strlen($baseurl)) == $baseurl);
173         }
174
175         /**
176          * @brief Return the array of query string parameters from a URL
177          *
178          * @param string $url URL to parse
179          * @return array Associative array of query string parameters
180          */
181         private static function parseQuery($url)
182         {
183                 $query = parse_url($url, PHP_URL_QUERY);
184                 $query = html_entity_decode($query);
185
186                 parse_str($query, $arr);
187
188                 return $arr;
189         }
190
191         /**
192          * @brief Call-back method to replace the UR
193          *
194          * @param array $matches Matches from preg_replace_callback()
195          * @return string Proxified HTML image tag
196          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
197          */
198         private static function replaceUrl(array $matches)
199         {
200                 // if the picture seems to be from another picture cache then take the original source
201                 $queryvar = self::parseQuery($matches[2]);
202
203                 if (!empty($queryvar['url']) && substr($queryvar['url'], 0, 4) == 'http') {
204                         $matches[2] = urldecode($queryvar['url']);
205                 }
206
207                 // Following line changed per bug #431
208                 if (self::isLocalImage($matches[2])) {
209                         return $matches[1] . $matches[2] . $matches[3];
210                 }
211
212                 // Return proxified HTML
213                 return $matches[1] . self::proxifyUrl(htmlspecialchars_decode($matches[2])) . $matches[3];
214         }
215
216 }