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