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