]> git.mxchange.org Git - friendica.git/blob - src/Util/Strings.php
Fix security vulnerbilities.
[friendica.git] / src / Util / Strings.php
1 <?php
2
3 /**
4  * @file src/Util/Strings.php
5  */
6
7 namespace Friendica\Util;
8
9 use Friendica\Content\ContactSelector;
10 use Friendica\Core\Logger;
11
12 /**
13  * @brief This class handles string functions
14  */
15 class Strings
16 {
17     /**
18      * @brief Generates a pseudo-random string of hexadecimal characters
19      *
20      * @param int $size
21      * @return string
22      * @throws \Exception
23      */
24     public static function getRandomHex($size = 64)
25     {
26         $byte_size = ceil($size / 2);
27
28         $bytes = random_bytes($byte_size);
29
30         $return = substr(bin2hex($bytes), 0, $size);
31
32         return $return;
33     }
34
35     /**
36      * Checks, if the given string is a valid hexadecimal code
37      *
38      * @param string $hexCode
39      *
40      * @return bool
41      */
42     public static function isHex($hexCode)
43     {
44         return !empty($hexCode) ? @preg_match("/^[a-f0-9]{2,}$/i", $hexCode) && !(strlen($hexCode) & 1) : false;
45     }
46
47     /**
48      * @brief This is our primary input filter.
49      *
50      * Use this on any text input where angle chars are not valid or permitted
51      * They will be replaced with safer brackets. This may be filtered further
52      * if these are not allowed either.
53      *
54      * @param string $string Input string
55      * @return string Filtered string
56      */
57     public static function escapeTags($string)
58     {
59         return str_replace(["<", ">"], ['[', ']'], $string);
60     }
61
62     /**
63      * @brief Use this on "body" or "content" input where angle chars shouldn't be removed,
64      * and allow them to be safely displayed.
65      * @param string $string
66      *
67      * @return string
68      */
69     public static function escapeHtml($string)
70     {
71         return htmlentities($string, ENT_QUOTES | ENT_HTML5, "UTF-8", false);
72     }
73
74     /**
75      * @brief Generate a string that's random, but usually pronounceable. Used to generate initial passwords
76      *
77      * @param int $len  length
78      *
79      * @return string
80      */
81     public static function getRandomName($len)
82     {
83         if ($len <= 0) {
84             return '';
85         }
86
87         $vowels = ['a', 'a', 'ai', 'au', 'e', 'e', 'e', 'ee', 'ea', 'i', 'ie', 'o', 'ou', 'u'];
88
89         if (random_int(0, 5) == 4) {
90             $vowels[] = 'y';
91         }
92
93         $cons = [
94             'b', 'bl', 'br',
95             'c', 'ch', 'cl', 'cr',
96             'd', 'dr',
97             'f', 'fl', 'fr',
98             'g', 'gh', 'gl', 'gr',
99             'h',
100             'j',
101             'k', 'kh', 'kl', 'kr',
102             'l',
103             'm',
104             'n',
105             'p', 'ph', 'pl', 'pr',
106             'qu',
107             'r', 'rh',
108             's', 'sc', 'sh', 'sm', 'sp', 'st',
109             't', 'th', 'tr',
110             'v',
111             'w', 'wh',
112             'x',
113             'z', 'zh'
114         ];
115
116         $midcons = [
117             'ck', 'ct', 'gn', 'ld', 'lf', 'lm', 'lt', 'mb', 'mm', 'mn', 'mp',
118             'nd', 'ng', 'nk', 'nt', 'rn', 'rp', 'rt'
119         ];
120
121         $noend = [
122             'bl', 'br', 'cl', 'cr', 'dr', 'fl', 'fr', 'gl', 'gr',
123             'kh', 'kl', 'kr', 'mn', 'pl', 'pr', 'rh', 'tr', 'qu', 'wh', 'q'
124         ];
125
126         $start = random_int(0, 2);
127         if ($start == 0) {
128             $table = $vowels;
129         } else {
130             $table = $cons;
131         }
132
133         $word = '';
134
135         for ($x = 0; $x < $len; $x++) {
136             $r = random_int(0, count($table) - 1);
137             $word .= $table[$r];
138
139             if ($table == $vowels) {
140                 $table = array_merge($cons, $midcons);
141             } else {
142                 $table = $vowels;
143             }
144         }
145
146         $word = substr($word, 0, $len);
147
148         foreach ($noend as $noe) {
149             $noelen = strlen($noe);
150             if ((strlen($word) > $noelen) && (substr($word, -$noelen) == $noe)) {
151                 $word = self::getRandomName($len);
152                 break;
153             }
154         }
155
156         return $word;
157     }
158
159     /**
160      * Translate and format the network name of a contact
161      *
162      * @param string $network Network name of the contact (e.g. dfrn, rss and so on)
163      * @param string $url     The contact url
164      *
165      * @return string Formatted network name
166      * @throws \Friendica\Network\HTTPException\InternalServerErrorException
167      */
168     public static function formatNetworkName($network, $url = '')
169     {
170         if ($network != '') {
171             if ($url != '') {
172                 $network_name = '<a href="' . $url . '">' . ContactSelector::networkToName($network, $url) . '</a>';
173             } else {
174                 $network_name = ContactSelector::networkToName($network);
175             }
176
177             return $network_name;
178         }
179     }
180
181     /**
182      * @brief Remove indentation from a text
183      *
184      * @param string $text  String to be transformed.
185      * @param string $chr   Optional. Indentation tag. Default tab (\t).
186      * @param int    $count Optional. Default null.
187      *
188      * @return string       Transformed string.
189      */
190     public static function deindent($text, $chr = "[\t ]", $count = NULL)
191     {
192         $lines = explode("\n", $text);
193
194         if (is_null($count)) {
195             $m = [];
196             $k = 0;
197             while ($k < count($lines) && strlen($lines[$k]) == 0) {
198                 $k++;
199             }
200             preg_match("|^" . $chr . "*|", $lines[$k], $m);
201             $count = strlen($m[0]);
202         }
203
204         for ($k = 0; $k < count($lines); $k++) {
205             $lines[$k] = preg_replace("|^" . $chr . "{" . $count . "}|", "", $lines[$k]);
206         }
207
208         return implode("\n", $lines);
209     }
210
211     /**
212      * @brief Get byte size returned in a Data Measurement (KB, MB, GB)
213      *
214      * @param int $bytes    The number of bytes to be measured
215      * @param int $precision    Optional. Default 2.
216      *
217      * @return string   Size with measured units.
218      */
219     public static function formatBytes($bytes, $precision = 2)
220     {
221         $units = ['B', 'KB', 'MB', 'GB', 'TB'];
222         $bytes = max($bytes, 0);
223         $pow = floor(($bytes ? log($bytes) : 0) / log(1024));
224         $pow = min($pow, count($units) - 1);
225         $bytes /= pow(1024, $pow);
226
227         return round($bytes, $precision) . ' ' . $units[$pow];
228     }
229
230     /**
231      * @brief Protect percent characters in sprintf calls
232      *
233      * @param string $s String to transform.
234      *
235      * @return string   Transformed string.
236      */
237     public static function protectSprintf($s)
238     {
239         return str_replace('%', '%%', $s);
240     }
241
242     /**
243      * @brief Base64 Encode URL and translate +/ to -_ Optionally strip padding.
244      *
245      * @param string $s                 URL to encode
246      * @param boolean $strip_padding    Optional. Default false
247      *
248      * @return string   Encoded URL
249      */
250     public static function base64UrlEncode($s, $strip_padding = false)
251     {
252         $s = strtr(base64_encode($s), '+/', '-_');
253
254         if ($strip_padding) {
255             $s = str_replace('=', '', $s);
256         }
257
258         return $s;
259     }
260
261     /**
262      * @brief Decode Base64 Encoded URL and translate -_ to +/
263      * @param string $s URL to decode
264      *
265      * @return string   Decoded URL
266      * @throws \Exception
267      */
268     public static function base64UrlDecode($s)
269     {
270         if (is_array($s)) {
271             Logger::log('base64url_decode: illegal input: ' . print_r(debug_backtrace(), true));
272             return $s;
273         }
274
275         /*
276         *  // Placeholder for new rev of salmon which strips base64 padding.
277         *  // PHP base64_decode handles the un-padded input without requiring this step
278         *  // Uncomment if you find you need it.
279         *
280         *       $l = strlen($s);
281         *       if (!strpos($s,'=')) {
282         *               $m = $l % 4;
283         *               if ($m == 2)
284         *                       $s .= '==';
285         *               if ($m == 3)
286         *                       $s .= '=';
287         *       }
288         *
289         */
290
291         return base64_decode(strtr($s, '-_', '+/'));
292     }
293
294     /**
295      * @brief Normalize url
296      *
297      * @param string $url   URL to be normalized.
298      *
299      * @return string   Normalized URL.
300      */
301     public static function normaliseLink($url)
302     {
303         $ret = str_replace(['https:', '//www.'], ['http:', '//'], $url);
304         return rtrim($ret, '/');
305     }
306
307     /**
308      * @brief Normalize OpenID identity
309      *
310      * @param string $s OpenID Identity
311      *
312      * @return string   normalized OpenId Identity
313      */
314     public static function normaliseOpenID($s)
315     {
316         return trim(str_replace(['http://', 'https://'], ['', ''], $s), '/');
317     }
318
319     /**
320      * @brief Compare two URLs to see if they are the same, but ignore
321      * slight but hopefully insignificant differences such as if one
322      * is https and the other isn't, or if one is www.something and
323      * the other isn't - and also ignore case differences.
324      *
325      * @param string $a first url
326      * @param string $b second url
327      * @return boolean True if the URLs match, otherwise False
328      *
329      */
330     public static function compareLink($a, $b)
331     {
332         return (strcasecmp(self::normaliseLink($a), self::normaliseLink($b)) === 0);
333     }
334
335
336     /**
337      * Ensures the provided URI has its query string punctuation in order.
338      *
339      * @param string $uri
340      * @return string
341      */
342     public static function ensureQueryParameter($uri)
343     {
344         if (strpos($uri, '?') === false && ($pos = strpos($uri, '&')) !== false) {
345             $uri = substr($uri, 0, $pos) . '?' . substr($uri, $pos + 1);
346         }
347
348         return $uri;
349     }
350
351
352     /**
353      * Check if the trimmed provided string is starting with one of the provided characters
354      *
355      * @param string $string
356      * @param array  $chars
357      * @return bool
358      */
359     public static function startsWith($string, array $chars)
360     {
361         $return = in_array(substr(trim($string), 0, 1), $chars);
362
363         return $return;
364     }
365
366     /**
367      * Returns the regular expression string to match URLs in a given text
368      *
369      * @return string
370      * @see https://daringfireball.net/2010/07/improved_regex_for_matching_urls
371      */
372     public static function autoLinkRegEx()
373     {
374         return '@
375 (?<![=\'\]"/])          # Not preceded by [, =, \', ], ", /
376 \b
377 (                              # Capture 1: entire matched URL
378   https?://                            # http or https protocol
379   (?:
380     [^/\s\xA0`!()\[\]{};:\'",<>?«»“”‘’.]    # Domain can\'t start with a .
381     [^/\s\xA0`!()\[\]{};:\'",<>?«»“”‘’]+    # Domain can\'t end with a .
382     \.
383     [^/\s\xA0`!()\[\]{};:\'".,<>?«»“”‘’]+/? # Followed by a slash
384   )
385   (?:                                  # One or more:
386     [^\s\xA0()<>]+                         # Run of non-space, non-()<>
387     |                                  #   or
388     \(([^\s\xA0()<>]+|(\([^\s()<>]+\)))*\) # balanced parens, up to 2 levels
389     |                                  #   or
390     [^\s\xA0`!()\[\]{};:\'".,<>?«»“”‘’]    # not a space or one of these punct chars
391   )*
392 )@xiu';
393     }
394
395     /**
396      * Ensures a single path item doesn't contain any path-traversing characters
397      *
398      * @see https://stackoverflow.com/a/46097713
399      * @param string $pathItem
400      * @return string
401      */
402     public static function sanitizeFilePathItem($pathItem)
403     {
404         $pathItem = str_replace('/', '_', $pathItem);
405         $pathItem = str_replace('\\', '_', $pathItem);
406         $pathItem = str_replace(DIRECTORY_SEPARATOR, '_', $pathItem); // In case it does not equal the standard values
407
408         return $pathItem;
409     }
410 }