PluginProbe
s2Member – Excellent for All Kinds of Memberships, Content Restriction Paywalls & Member Access Subscriptions / trunk
s2Member – Excellent for All Kinds of Memberships, Content Restriction Paywalls & Member Access Subscriptions vtrunk
260909 260829 260814 260805 110710 110731 110812 110815 110912 110913 110915 110926 110927 111002 111003 111011 111017 111029 111105 111206 111216 111220 120213 120219 120301 All 187 releases
s2member / src / includes / classes / utils-strings.inc.php

utils-strings.inc.php in s2Member – Excellent for All Kinds of Memberships, Content Restriction Paywalls & Member Access Subscriptions trunk, at src/includes/classes/utils-strings.inc.php

752 lines 28.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 // @codingStandardsIgnoreFile
3 /**
4 * String utilities.
5 *
6 * Copyright: © 2009-2011
7 * {@link http://websharks-inc.com/ WebSharks, Inc.}
8 * (coded in the USA)
9 *
10 * Released under the terms of the GNU General Public License.
11 * You should have received a copy of the GNU General Public License,
12 * along with this software. In the main directory, see: /licensing/
13 * If not, see: {@link http://www.gnu.org/licenses/}.
14 *
15 * @package s2Member\Utilities
16 * @since 3.5
17 */
18 if(!defined('WPINC')) // MUST have WordPress.
19 exit('Do not access this file directly.');
20
21 if(!class_exists('c_ws_plugin__s2member_utils_strings'))
22 {
23 /**
24 * String utilities.
25 *
26 * @package s2Member\Utilities
27 * @since 3.5
28 */
29 class c_ws_plugin__s2member_utils_strings
30 {
31 /**
32 * Array of all ampersand entities.
33 *
34 * Array keys are actually regex patterns *(very useful)*.
35 *
36 * @package s2Member\Utilities
37 * @since 111106
38 *
39 * @var array
40 */
41 public static $ampersand_entities = array(
42 '&amp;' => '&amp;',
43 '&#0*38;' => '&#38;',
44 '&#[xX]0*26;' => '&#x26;'
45 );
46
47 /**
48 * Array of all quote entities *(and entities for quote variations)*.
49 *
50 * Array keys are actually regex patterns *(very useful)*.
51 *
52 * @package s2Member\Utilities
53 * @since 111106
54 *
55 * @var array
56 */
57 public static $quote_entities_w_variations = array(
58 '&apos;' => '&apos;',
59 '&#0*39;' => '&#39;',
60 '&#[xX]0*27;' => '&#x27;',
61 '&lsquo;' => '&lsquo;',
62 '&#0*8216;' => '&#8216;',
63 '&#[xX]0*2018;' => '&#x2018;',
64 '&rsquo;' => '&rsquo;',
65 '&#0*8217;' => '&#8217;',
66 '&#[xX]0*2019;' => '&#x2019;',
67 '&quot;' => '&quot;',
68 '&#0*34;' => '&#34;',
69 '&#[xX]0*22;' => '&#x22;',
70 '&ldquo;' => '&ldquo;',
71 '&#0*8220;' => '&#8220;',
72 '&#[xX]0*201[cC];' => '&#x201C;',
73 '&rdquo;' => '&rdquo;',
74 '&#0*8221;' => '&#8221;',
75 '&#[xX]0*201[dD];' => '&#x201D;',
76 //260829.0025 Literal smart quotes can reach shortcode attributes unchanged; normalize them like their HTML entity forms.
77 '' => '',
78 '' => '',
79 '' => '',
80 '' => ''
81 );
82
83 /**
84 * Escapes double quotes.
85 *
86 * @package s2Member\Utilities
87 * @since 3.5
88 *
89 * @param string $string Input string.
90 * @param int $times Number of escapes. Defaults to 1.
91 * @param string $escape_char The character to be used in escapes.
92 *
93 * @return string Output string after double quotes are escaped.
94 */
95 public static function esc_dq($string = '', $times = NULL, $escape_char = '\\')
96 {
97 $times = (is_numeric($times) && $times >= 0) ? (int)$times : 1;
98
99 return str_replace('"', str_repeat($escape_char, $times).'"', (string)$string);
100 }
101
102 /**
103 * Escapes single quotes.
104 *
105 * @package s2Member\Utilities
106 * @since 3.5
107 *
108 * @param string $string Input string.
109 * @param int $times Number of escapes. Defaults to 1.
110 *
111 * @return string Output string after single quotes are escaped.
112 */
113 public static function esc_sq($string = '', $times = NULL)
114 {
115 $times = (is_numeric($times) && $times >= 0) ? (int)$times : 1;
116
117 return str_replace("'", str_repeat('\\', $times)."'", (string)$string);
118 }
119
120 /**
121 * Escapes JavaScript and single quotes.
122 *
123 * @package s2Member\Utilities
124 * @since 110901
125 *
126 * @param string $string Input string.
127 * @param int $times Number of escapes. Defaults to 1.
128 *
129 * @return string Output string after JavaScript and single quotes are escaped.
130 */
131 public static function esc_js_sq($string = '', $times = NULL)
132 {
133 $times = (is_numeric($times) && $times >= 0) ? (int)$times : 1;
134
135 return str_replace("'", str_repeat('\\', $times)."'", str_replace(array("\r", "\n"), array('', '\\n'), str_replace("\\'", "'", (string)$string)));
136 }
137
138 /**
139 * Escapes dollars signs (for regex patterns).
140 *
141 * @package s2Member\Utilities
142 * @since 3.5
143 *
144 * @param string $string Input string.
145 * @param int $times Number of escapes. Defaults to 1.
146 *
147 * @return string Output string after dollar signs are escaped.
148 *
149 * @deprecated Starting with s2Member v120103, please use:
150 * ``c_ws_plugin__s2member_utils_strings::esc_refs()``.
151 */
152 public static function esc_ds($string = '', $times = NULL)
153 {
154 $times = (is_numeric($times) && $times >= 0) ? (int)$times : 1;
155
156 return str_replace('$', str_repeat('\\', $times).'$', (string)$string);
157 }
158
159 /**
160 * Escapes backreferences (for regex patterns).
161 *
162 * @package s2Member\Utilities
163 * @since 120103
164 *
165 * @param string $string Input string.
166 * @param int $times Number of escapes. Defaults to 1.
167 *
168 * @return string Output string after backreferences are escaped.
169 */
170 public static function esc_refs($string = NULL, $times = NULL)
171 {
172 $times = (is_numeric($times) && $times >= 0) ? (int)$times : 1;
173
174 return str_replace(array('\\', '$'), array(str_repeat('\\', $times).'\\', str_repeat('\\', $times).'$'), (string)$string);
175 }
176
177 /**
178 * Sanitizes a string; by stripping characters NOT on a standard U.S. keyboard.
179 *
180 * @package s2Member\Utilities
181 * @since 111106
182 *
183 * @param string $string Input string.
184 *
185 * @return string Output string, after characters NOT on a standard U.S. keyboard have been stripped.
186 */
187 public static function strip_2_kb_chars($string = '')
188 {
189 return preg_replace('/[^0-9A-Z'."\r\n\t".'\s`\=\[\]\\\;\',\.\/~\!@#\$%\^&\*\(\)_\+\|\}\{\:"\?\>\<\-]/i', '', remove_accents((string)$string));
190 }
191
192 /**
193 * Sanitizes a string by breaking PHP opening tags (including encoded/mixed/double-encoded forms).
194 *
195 * @package s2Member\Utilities
196 * @since 241207
197 *
198 * @param string $input Input string to sanitize.
199 * @return string Original input when safe, otherwise decoded string with PHP opening tags removed.
200 */
201 public static function strip_php_tags($input = '') {
202 $copy = (string) $input;
203
204 //251002 Collapse mixed/double encodings (HTML entities + URL encodings).
205 $flags = ENT_QUOTES | (defined('ENT_HTML5') ? ENT_HTML5 : 0);
206 for ($i = 0; $i < 3; $i++) {
207 $copy = html_entity_decode($copy, $flags, 'UTF-8'); // &lt;, &#60;, &#x3C;, etc.
208 $copy = rawurldecode($copy); // %3C, %3F, %3E, etc.
209 }
210
211 // If no opener after normalization, return the original.
212 if (strpos($copy, '<?') === false) {
213 return $input;
214 }
215
216 // Break all openers by removing all occurrences of '<?'.
217 while (strpos($copy, '<?') !== false) {
218 $copy = str_replace('<?', 'NEUTERED_', $copy);
219 }
220
221 return $copy;
222 }
223
224 /**
225 * Recursively breaks PHP opening tags from strings in arrays/objects using strip_php_tags().
226 *
227 * @package s2Member\Utilities
228 * @since 251002
229 *
230 * @param mixed $input A scalar, array, or object to sanitize.
231 * @return mixed The sanitized value with PHP opening tags removed from all scalar leaves.
232 */
233 public static function strip_php_tags_deep($input) {
234 if (is_array($input)) {
235 foreach ($input as $k => $v)
236 $input[$k] = self::strip_php_tags_deep($v);
237 return $input;
238 }
239 if (is_object($input)) {
240 foreach (get_object_vars($input) as $k => $v)
241 $input->$k = self::strip_php_tags_deep($v);
242 return $input;
243 }
244 return self::strip_php_tags($input);
245 }
246
247 /**
248 * Trims deeply; alias of ``trim_deep``.
249 *
250 * @package s2Member\Utilities
251 * @since 111106
252 *
253 * @see s2Member\Utilities\c_ws_plugin__s2member_utils_strings::trim_deep()
254 * @see http://php.net/manual/en/function.trim.php
255 *
256 * @param string|array $value Either a string, an array, or a multi-dimensional array, filled with integer and/or string values.
257 * @param string|bool $chars Optional. Defaults to false, indicating the default trim chars ` \t\n\r\0\x0B`. Or, set to a specific string of chars.
258 * @param string|bool $extra_chars Optional. This is NOT possible with PHP alone, but here you can specify extra chars; in addition to ``$chars``.
259 *
260 * @return string|array Either the input string, or the input array; after all data is trimmed up according to arguments passed in.
261 */
262 public static function trim($value = '', $chars = FALSE, $extra_chars = FALSE)
263 {
264 return c_ws_plugin__s2member_utils_strings::trim_deep($value, $chars, $extra_chars);
265 }
266
267 /**
268 * Trims deeply; or use {@link s2Member\Utilities\c_ws_plugin__s2member_utils_strings::trim()}.
269 *
270 * @package s2Member\Utilities
271 * @since 3.5
272 *
273 * @see s2Member\Utilities\c_ws_plugin__s2member_utils_strings::trim()
274 * @see http://php.net/manual/en/function.trim.php
275 *
276 * @param string|array $value Either a string, an array, or a multi-dimensional array, filled with integer and/or string values.
277 * @param string|bool $chars Optional. Defaults to false, indicating the default trim chars ` \t\n\r\0\x0B`. Or, set to a specific string of chars.
278 * @param string|bool $extra_chars Optional. This is NOT possible with PHP alone, but here you can specify extra chars; in addition to ``$chars``.
279 *
280 * @return string|array Either the input string, or the input array; after all data is trimmed up according to arguments passed in.
281 */
282 public static function trim_deep($value = '', $chars = FALSE, $extra_chars = FALSE)
283 {
284 $chars = (is_string($chars)) ? $chars : " \t\n\r\0\x0B";
285 $chars = (is_string($extra_chars)) ? $chars.$extra_chars : $chars;
286
287 if(is_array($value)) /* Handles all types of arrays.
288 Note, we do NOT use ``array_map()`` here, because multiple args to ``array_map()`` causes a loss of string keys.
289 For further details, see: <http://php.net/manual/en/function.array-map.php>. */
290 {
291 foreach($value as &$r) // Reference.
292 $r = c_ws_plugin__s2member_utils_strings::trim_deep($r, $chars);
293 return $value; // Return modified array.
294 }
295 return trim((string)$value, $chars);
296 }
297
298 /**
299 * Trims double quotes deeply.
300 *
301 * @package s2Member\Utilities
302 * @since 3.5
303 *
304 * @param string|array $value Either a string, an array, or a multi-dimensional array, filled with integer and/or string values.
305 *
306 * @return string|array Either the input string, or the input array; after all data is trimmed up.
307 */
308 public static function trim_dq_deep($value = '')
309 {
310 return c_ws_plugin__s2member_utils_strings::trim_deep($value, FALSE, '"');
311 }
312
313 /**
314 * Trims single quotes deeply.
315 *
316 * @package s2Member\Utilities
317 * @since 111106
318 *
319 * @param string|array $value Either a string, an array, or a multi-dimensional array, filled with integer and/or string values.
320 *
321 * @return string|array Either the input string, or the input array; after all data is trimmed up.
322 */
323 public static function trim_sq_deep($value = '')
324 {
325 return c_ws_plugin__s2member_utils_strings::trim_deep($value, FALSE, "'");
326 }
327
328 /**
329 * Trims double and single quotes deeply.
330 *
331 * @package s2Member\Utilities
332 * @since 111106
333 *
334 * @param string|array $value Either a string, an array, or a multi-dimensional array, filled with integer and/or string values.
335 *
336 * @return string|array Either the input string, or the input array; after all data is trimmed up.
337 */
338 public static function trim_dsq_deep($value = '')
339 {
340 return c_ws_plugin__s2member_utils_strings::trim_deep($value, FALSE, "'".'"');
341 }
342
343 /**
344 * Trims all single/double quote entity variations deeply.
345 *
346 * This is useful on Shortcode attributes mangled by a Visual Editor.
347 *
348 * @package s2Member\Utilities
349 * @since 111011
350 *
351 * @param string|array $value Either a string, an array, or a multi-dimensional array, filled with integer and/or string values.
352 *
353 * @return string|array Either the input string, or the input array; after all data is trimmed up.
354 */
355 public static function trim_qts_deep($value = '')
356 {
357 $qts = implode('|', array_keys(c_ws_plugin__s2member_utils_strings::$quote_entities_w_variations));
358
359 return is_array($value) ? array_map('c_ws_plugin__s2member_utils_strings::trim_qts_deep', $value) : preg_replace('/^(?:'.$qts.')+|(?:'.$qts.')+$/', '', (string)$value);
360 }
361
362 /**
363 * Trims HTML whitespace.
364 *
365 * This is useful on Shortcode content.
366 *
367 * @package s2Member\Utilities
368 * @since 140124
369 *
370 * @param string $string Input string to trim.
371 *
372 * @return string Output string with all HTML whitespace trimmed away.
373 */
374 public static function trim_html($string = '')
375 {
376 $whitespace = '&nbsp;|\<br\>|\<br\s*\/\>|\<p\>(?:&nbsp;)*\<\/p\>';
377 return preg_replace('/^(?:'.$whitespace.')+|(?:'.$whitespace.')+$/', '', (string)$string);
378 }
379
380 /**
381 * Wraps a string with the characters provided.
382 *
383 * This is useful when preparing an input array for ``c_ws_plugin__s2member_utils_arrays::in_regex_array()``.
384 *
385 * @package s2Member\Utilities
386 * @since 3.5
387 *
388 * @param string|array $value Either a string, an array, or a multi-dimensional array, filled with integer and/or string values.
389 * @param string $beg Optional. A string value to wrap at the beginning of each value.
390 * @param string $end Optional. A string value to wrap at the ending of each value.
391 * @param bool $wrap_e Optional. Defaults to false. Should empty strings be wrapped too?
392 *
393 * @return string|array Either the input string, or the input array; after all data is wrapped up.
394 */
395 public static function wrap_deep($value = '', $beg = '', $end = '', $wrap_e = FALSE)
396 {
397 if(is_array($value)) /* Handles all types of arrays.
398 Note, we do NOT use ``array_map()`` here, because multiple args to ``array_map()`` causes a loss of string keys.
399 For further details, see: <http://php.net/manual/en/function.array-map.php>. */
400 {
401 foreach($value as &$r) // Reference.
402 $r = c_ws_plugin__s2member_utils_strings::wrap_deep($r, $beg, $end, $wrap_e);
403 return $value; // Return modified array.
404 }
405 return (strlen((string)$value) || $wrap_e) ? (string)$beg.(string)$value.(string)$end : (string)$value;
406 }
407
408 /**
409 * Escapes meta characters with ``preg_quote()`` deeply.
410 *
411 * @package s2Member\Utilities
412 * @since 110926
413 *
414 * @param string|array $value Either a string, an array, or a multi-dimensional array, filled with integer and/or string values.
415 * @param string $delimiter Optional. If a delimiting character is specified, it will also be escaped via ``preg_quote()``.
416 *
417 * @return string|array Either the input string, or the input array; after all data is escaped with ``preg_quote()``.
418 */
419 public static function preg_quote_deep($value = '', $delimiter = '')
420 {
421 if(is_array($value)) /* Handles all types of arrays.
422 Note, we do NOT use ``array_map()`` here, because multiple args to ``array_map()`` causes a loss of string keys.
423 For further details, see: <http://php.net/manual/en/function.array-map.php>. */
424 {
425 foreach($value as &$r) // Reference.
426 $r = c_ws_plugin__s2member_utils_strings::preg_quote_deep($r, $delimiter);
427 return $value; // Return modified array.
428 }
429 return preg_quote((string)$value, (string)$delimiter);
430 }
431
432 /**
433 * Generates a random string with letters/numbers/symbols.
434 *
435 * @package s2Member\Utilities
436 * @since 3.5
437 *
438 * @param int $length Optional. Defaults to `12`. Length of the random string.
439 * @param bool $special_chars Defaults to true. If false, special chars are NOT included.
440 * @param bool $extra_special_chars Defaults to false. If true, extra special chars are included.
441 *
442 * @return string A randomly generated string, based on parameter configuration.
443 */
444 public static function random_str_gen($length = 0, $special_chars = TRUE, $extra_special_chars = FALSE)
445 {
446 $length = (is_numeric($length) && $length >= 0) ? (int)$length : 12;
447
448 $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
449 $chars .= ($extra_special_chars) ? '-_ []{}<>~`+=,.;:/?|' : '';
450 $chars .= ($special_chars) ? '!@#$%^&*()' : '';
451
452 for($i = 0, $random_str = ''; $i < $length; $i++)
453 $random_str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
454
455 return $random_str;
456 }
457
458 /**
459 * Highlights PHP, and also Shortcodes.
460 *
461 * @package s2Member\Utilities
462 * @since 3.5
463 *
464 * @param string $string Input string to be highlighted.
465 *
466 * @return string The highlighted string.
467 */
468 public static function highlight_php($string = '')
469 {
470 $string = highlight_string(trim((string)$string), TRUE); // Start with PHP syntax, then Shortcodes.
471 $string = preg_replace('/\[\/?_*s2[a-z0-9_\-]+.*?\]/i', '<span style="color:#164A61;">$0</span>', $string);
472 return str_replace('<code>', '<code class="highlight-php">', $string);
473 }
474
475 /**
476 * Parses email addresses from a string or array.
477 *
478 * @package s2Member\Utilities
479 * @since 111009
480 *
481 * @param string|array $value Input string or an array is also fine.
482 *
483 * @return array Array of parsed email addresses.
484 */
485 public static function parse_emails($value = '')
486 {
487 if(is_array($value)) /* Handles all types of arrays.
488 Note, we do NOT use ``array_map()`` here, because multiple args to ``array_map()`` causes a loss of string keys.
489 For further details, see: <http://php.net/manual/en/function.array-map.php>. */
490 {
491 $emails = array(); // Initialize array.
492 foreach($value as $v) // Loop through array.
493 $emails = array_merge($emails, c_ws_plugin__s2member_utils_strings::parse_emails($v));
494 return $emails; // Return array.
495 }
496 $delimiter = (strpos((string)$value, ';') !== FALSE) ? ';' : ',';
497 foreach(c_ws_plugin__s2member_utils_strings::trim_deep(preg_split('/'.preg_quote($delimiter, '/').'+/', (string)$value)) as $section)
498 {
499 if(preg_match('/\<(.+?)\>/', $section, $m) && strpos($m[1], '@') !== FALSE)
500 $emails[] = $m[1]; // Email inside <brackets>.
501
502 else if(strpos($section, '@') !== FALSE)
503 $emails[] = $section;
504 }
505 return (!empty($emails)) ? $emails : array();
506 }
507
508 /**
509 * Base64 URL-safe encoding.
510 *
511 * @package s2Member\Utilities
512 * @since 110913
513 *
514 * @param string $string Input string to be base64 encoded.
515 * @param array $url_unsafe_chars Optional. An array of un-safe characters. Defaults to: ``array('+', '/')``.
516 * @param array $url_safe_chars Optional. An array of safe character replacements. Defaults to: ``array('-', '_')``.
517 * @param string $trim_padding_chars Optional. A string of padding chars to rtrim. Defaults to: `=~.`.
518 *
519 * @return string The base64 URL-safe encoded string.
520 */
521 public static function base64_url_safe_encode($string = '', $url_unsafe_chars = array('+', '/'), $url_safe_chars = array('-', '_'), $trim_padding_chars = '=~.')
522 {
523 $string = (string)$string; // Force string values here. String MUST be a string.
524 $trim_padding_chars = (string)$trim_padding_chars; // And force this one too.
525
526 $base64_url_safe = str_replace((array)$url_unsafe_chars, (array)$url_safe_chars, (string)base64_encode($string));
527 $base64_url_safe = (strlen($trim_padding_chars)) ? rtrim($base64_url_safe, $trim_padding_chars) : $base64_url_safe;
528
529 return $base64_url_safe; // Base64 encoded, with URL-safe replacements.
530 }
531
532 /**
533 * Base64 URL-safe decoding.
534 *
535 * Note, this function is backward compatible with routines supplied by s2Member in the past;
536 * where padding characters were replaced with `~` or `.`, instead of being stripped completely.
537 *
538 * @package s2Member\Utilities
539 * @since 110913
540 *
541 * @param string $base64_url_safe Input string to be base64 decoded.
542 * @param array $url_unsafe_chars Optional. An array of un-safe character replacements. Defaults to: ``array('+', '/')``.
543 * @param array $url_safe_chars Optional. An array of safe characters. Defaults to: ``array('-', '_')``.
544 * @param string $trim_padding_chars Optional. A string of padding chars to rtrim. Defaults to: `=~.`.
545 *
546 * @return string The decoded string.
547 */
548 public static function base64_url_safe_decode($base64_url_safe = '', $url_unsafe_chars = array('+', '/'), $url_safe_chars = array('-', '_'), $trim_padding_chars = '=~.')
549 {
550 $base64_url_safe = (string)$base64_url_safe; // Force string values here. This MUST be a string.
551 $trim_padding_chars = (string)$trim_padding_chars; // And force this one too.
552
553 $string = (strlen($trim_padding_chars)) ? rtrim($base64_url_safe, $trim_padding_chars) : $base64_url_safe;
554 $string = (strlen($trim_padding_chars)) ? str_pad($string, strlen($string) % 4, '=', STR_PAD_RIGHT) : $string;
555 $string = (string)base64_decode(str_replace((array)$url_safe_chars, (array)$url_unsafe_chars, $string));
556
557 return $string; // Base64 decoded, with URL-safe replacements.
558 }
559
560 /**
561 * Generates an RSA-SHA1 signature.
562 *
563 * @package s2Member\Utilities
564 * @since 111017
565 *
566 * @param string $string Input string/data, to be signed by this routine.
567 * @param string $key The secret key that will be used in this signature.
568 *
569 * @return string|bool An RSA-SHA1 signature string, or false on failure.
570 */
571 public static function rsa_sha1_sign($string = '', $key = '')
572 {
573 $key = c_ws_plugin__s2member_utils_strings::_rsa_sha1_key_fix_wrappers((string)$key);
574
575 $signature = c_ws_plugin__s2member_utils_strings::_rsa_sha1_shell_sign((string)$string, (string)$key);
576
577 if(empty($signature) && stripos(PHP_OS, 'win') === 0 && file_exists(($openssl = 'c:\\openssl-win32\\bin\\openssl.exe')))
578 $signature = c_ws_plugin__s2member_utils_strings::_rsa_sha1_shell_sign((string)$string, (string)$key, $openssl);
579
580 if(empty($signature) && stripos(PHP_OS, 'win') === 0 && file_exists(($openssl = 'c:\\openssl-win64\\bin\\openssl.exe')))
581 $signature = c_ws_plugin__s2member_utils_strings::_rsa_sha1_shell_sign((string)$string, (string)$key, $openssl);
582
583 //260816 PHP 8 returns OpenSSLAsymmetricKey objects instead of resources; false indicates key-loading failure on all supported PHP versions.
584 if(empty($signature) && function_exists('openssl_get_privatekey') && function_exists('openssl_sign') && ($private_key = openssl_get_privatekey((string)$key)) !== FALSE)
585 {
586 openssl_sign((string)$string, $signature, $private_key, OPENSSL_ALGO_SHA1);
587 //260816 openssl_free_key() is unnecessary on PHP 8+ and deprecated there; PHP 5-7 still use resource cleanup.
588 if(PHP_VERSION_ID < 80000 && is_resource($private_key))
589 openssl_free_key($private_key);
590 }
591
592 if(empty($signature)) // Now, if we're still empty, trigger an error here.
593 {
594 $error = 's2Member was unable to generate an RSA-SHA1 signature.'.
595 ' Please make sure your installation of PHP is compiled with OpenSSL: `openssl_sign()`.'.
596 ' See: http://php.net/manual/en/function.openssl-sign.php';
597
598 //260816 PHP 8.4 deprecates trigger_error(..., E_USER_ERROR); preserve the previous fatal-style path on older PHP.
599 if(PHP_VERSION_ID >= 80400)
600 throw new \RuntimeException($error);
601 else
602 trigger_error($error, E_USER_ERROR);
603 }
604
605 return (!empty($signature)) ? $signature : FALSE;
606 }
607
608 /**
609 * Generates an RSA-SHA1 signature from the command line.
610 *
611 * Used by {@link s2Member\Utilities\c_ws_plugin__s2member_utils_strings::rsa_sha1_sign()}.
612 *
613 * @package s2Member\Utilities
614 * @since 111017
615 *
616 * @param string $string Input string/data, to be signed by this routine.
617 * @param string $key The secret key that will be used in this signature.
618 * @param string $openssl Optional. Defaults to `openssl`. Path to OpenSSL executable.
619 *
620 * @return string|bool An RSA-SHA1 signature string, or false on failure.
621 */
622 public static function _rsa_sha1_shell_sign($string = '', $key = '', $openssl = '')
623 {
624 if(function_exists('shell_exec') && ($esa = 'escapeshellarg') && ($openssl = (($openssl && is_string($openssl)) ? $openssl : 'openssl')) && ($temp_dir = c_ws_plugin__s2member_utils_dirs::get_temp_dir()))
625 {
626 file_put_contents(($string_file = $temp_dir.'/'.md5(uniqid('', TRUE).'rsa-sha1-string').'.tmp'), (string)$string);
627 file_put_contents(($private_key_file = $temp_dir.'/'.md5(uniqid('', TRUE).'rsa-sha1-private-key').'.tmp'), (string)$key);
628 file_put_contents(($rsa_sha1_sig_file = $temp_dir.'/'.md5(uniqid('', TRUE).'rsa-sha1-sig').'.tmp'), '');
629
630 @shell_exec($esa($openssl).' sha1 -sign '.$esa($private_key_file).' -out '.$esa($rsa_sha1_sig_file).' '.$esa($string_file));
631 $signature = file_get_contents($rsa_sha1_sig_file); // Do NOT trim here. Was the signature was written?
632 unlink($rsa_sha1_sig_file).unlink($private_key_file).unlink($string_file); // Cleanup.
633 }
634 return (!empty($signature)) ? $signature : FALSE;
635 }
636
637 /**
638 * Fixes incomplete private key wrappers for RSA-SHA1 signing.
639 *
640 * Used by {@link s2Member\Utilities\c_ws_plugin__s2member_utils_strings::rsa_sha1_sign()}.
641 *
642 * @package s2Member\Utilities
643 * @since 111017
644 *
645 * @param string $key The secret key to be used in an RSA-SHA1 signature.
646 *
647 * @return string Key with incomplete wrappers corrected, when/if possible.
648 *
649 * @see http://www.faqs.org/qa/qa-14736.html
650 */
651 public static function _rsa_sha1_key_fix_wrappers($key = '')
652 {
653 if(($key = trim((string)$key)) && (strpos($key, '-----BEGIN RSA PRIVATE KEY-----') === FALSE || strpos($key, '-----END RSA PRIVATE KEY-----') === FALSE))
654 {
655 foreach(($lines = c_ws_plugin__s2member_utils_strings::trim_deep(preg_split('/['."\r\n".']+/', $key))) as $line => $value)
656 if(strpos($value, '-') === 0) // Begins with a boundary identifying character ( a hyphen `-` )?
657 {
658 $boundaries = (empty($boundaries)) ? 1 : $boundaries + 1; // Counter.
659 unset($lines[$line]); // Remove this boundary line. We'll fix these below.
660 }
661 if(empty($boundaries) || $boundaries <= 2) // Do NOT modify keys with more than 2 boundaries.
662 $key = '-----BEGIN RSA PRIVATE KEY-----'."\n".implode("\n", $lines)."\n".'-----END RSA PRIVATE KEY-----';
663 }
664 return $key; // Always a trimmed string here.
665 }
666
667 /**
668 * Generates an HMAC-SHA1 signature.
669 *
670 * @package s2Member\Utilities
671 * @since 111017
672 *
673 * @param string $string Input string/data, to be signed by this routine.
674 * @param string $key The secret key that will be used in this signature.
675 *
676 * @return string An HMAC-SHA1 signature string.
677 */
678 public static function hmac_sha1_sign($string = '', $key = '')
679 {
680 $key_64 = str_pad(((strlen((string)$key) > 64) ? pack('H*', sha1((string)$key)) : (string)$key), 64, chr(0x00));
681
682 return pack('H*', sha1(($key_64 ^ str_repeat(chr(0x5c), 64)).pack('H*', sha1(($key_64 ^ str_repeat(chr(0x36), 64)).(string)$string))));
683 }
684
685 /**
686 * Generates an HMAC-SHA256 signature.
687 *
688 * @package s2Member\Utilities
689 * @since 111017
690 *
691 * @param string $string Input string/data, to be signed by this routine.
692 * @param string $key The secret key that will be used in this signature.
693 * @param boolean $binary Return binary format?
694 *
695 * @return string An HMAC-SHA256 signature string.
696 */
697 public static function hmac_sha256_sign($string = '', $key = '', $binary = FALSE)
698 {
699 return hash_hmac('sha256', $string, $key, $binary);
700 }
701
702 /**
703 * Decodes unreserved chars encoded by PHP's ``urlencode()``, deeply.
704 *
705 * For further details regarding unreserved chars, see: {@link http://www.faqs.org/rfcs/rfc3986.html}.
706 *
707 * @package s2Member\Utilities
708 * @since 111017
709 *
710 * @see http://www.faqs.org/rfcs/rfc3986.html
711 *
712 * @param string|array $value Either a string, an array, or a multi-dimensional array, filled with integer and/or string values.
713 *
714 * @return string|array Either the input string, or the input array; after all unreserved chars are decoded properly.
715 */
716 public static function urldecode_ur_chars_deep($value = array())
717 {
718 if(is_array($value)) /* Handles all types of arrays.
719 Note, we do NOT use ``array_map()`` here, because multiple args to ``array_map()`` causes a loss of string keys.
720 For further details, see: <http://php.net/manual/en/function.array-map.php>. */
721 {
722 foreach($value as &$r) // Reference.
723 $r = c_ws_plugin__s2member_utils_strings::urldecode_ur_chars_deep($r);
724 return $value; // Return modified array.
725 }
726 return str_replace(array('%2D', '%2E', '%5F', '%7E'), array('-', '.', '_', '~'), (string)$value);
727 }
728
729 public static function like_escape($string)
730 {
731 global $wpdb; // Global DB object reference.
732
733 if(method_exists($wpdb, 'esc_like'))
734 return $wpdb->esc_like($string);
735
736 return like_escape($string); // Deprecated in WP v4.0.
737 }
738
739 public static function fill_cvs($string, $custom, $urlencode = false)
740 {
741 $string = (string)$string;
742 $custom = (string)$custom;
743
744 foreach (preg_split('/\|/', $custom) as $_key => $_value) {
745 $string = str_ireplace('%%cv'.$_key.'%%', $urlencode ? urlencode(trim($_value)) : trim($_value), $string);
746 } // unset($_key, $_value); // Housekeeping.
747
748 return $string;
749 }
750 }
751 }
752