PageRenderTime 46ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/functions/string_api.php

https://bitbucket.org/pfernandez/testlink1.9.6
PHP | 492 lines | 257 code | 87 blank | 148 comment | 41 complexity | bc734c9d205eed7fe77d3c15fd22b8b4 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, GPL-3.0
  1. <?php
  2. /**
  3. * TestLink Open Source Project - http://testlink.sourceforge.net/
  4. * This script is distributed under the GNU General Public License 2 or later.
  5. *
  6. * String Processing functions
  7. *
  8. * @package TestLink
  9. * @copyright 2007-2009, TestLink community
  10. * @copyright Copyright (C) 2002 - 2004 Mantis Team
  11. * The base for certain code was adapted from Mantis - a php based bugtracking system
  12. * @version CVS: $Id: string_api.php,v 1.12 2009/09/04 19:22:37 schlundus Exp $
  13. * @link http://www.teamst.org/index.php
  14. *
  15. * @internal rev:
  16. * 20080822 - franciscom - restored missed string_email_links()
  17. * 20080606 - havlatm - remove useles mantis related code
  18. * 20071104 - franciscom - changes to string_email_links()
  19. *
  20. **/
  21. /**
  22. * Preserve spaces at beginning of lines.
  23. * Lines must be separated by \n rather than < br / >
  24. **/
  25. function string_preserve_spaces_at_bol( $p_string )
  26. {
  27. $lines = explode( "\n", $p_string );
  28. $line_count = count( $lines );
  29. for ( $i = 0; $i < $line_count; $i++ ) {
  30. $count = 0;
  31. $prefix = '';
  32. $t_char = substr( $lines[$i], $count, 1 );
  33. $spaces = 0;
  34. while ( ( $t_char == ' ' ) || ( $t_char == "\t" ) ) {
  35. if ( $t_char == ' ' )
  36. $spaces++;
  37. else
  38. $spaces += 4; // 1 tab = 4 spaces, can be configurable.
  39. $count++;
  40. $t_char = substr( $lines[$i], $count, 1 );
  41. }
  42. for ( $j = 0; $j < $spaces; $j++ ) {
  43. $prefix .= '&nbsp;';
  44. }
  45. $lines[$i] = $prefix . substr( $lines[$i], $count );
  46. }
  47. return implode( "\n", $lines );
  48. }
  49. /**
  50. * Prepare a string to be printed without being broken into multiple lines
  51. **/
  52. function string_no_break( $p_string ) {
  53. if ( strpos( $p_string, ' ' ) !== false ) {
  54. return '<span class="nowrap">' . $p_string . "</span>";
  55. } else {
  56. return $p_string;
  57. }
  58. }
  59. /**
  60. * Similar to nl2br, but fixes up a problem where new lines are doubled between < pre > tags.
  61. * additionally, wrap the text an $p_wrap character intervals if the config is set
  62. *
  63. * @author Mantis BT team
  64. */
  65. function string_nl2br( $p_string, $p_wrap = 100 )
  66. {
  67. $p_string = nl2br( $p_string );
  68. // fix up eols within <pre> tags
  69. $pre2 = array();
  70. preg_match_all("/<pre[^>]*?>(.|\n)*?<\/pre>/", $p_string, $pre1);
  71. for ( $x = 0; $x < count($pre1[0]); $x++ )
  72. {
  73. $pre2[$x] = preg_replace("/<br[^>]*?>/", "", $pre1[0][$x]);
  74. // this may want to be replaced by html_entity_decode (or equivalent)
  75. // if other encoded characters are a problem
  76. $pre2[$x] = preg_replace("/&nbsp;/", " ", $pre2[$x]);
  77. if ( ON == config_get( 'wrap_in_preformatted_text' ) )
  78. {
  79. $pre2[$x] = preg_replace("/([^\n]{".$p_wrap."})(?!<\/pre>)/", "$1\n", $pre2[$x]);
  80. }
  81. $pre1[0][$x] = "/" . preg_quote($pre1[0][$x], "/") . "/";
  82. }
  83. return preg_replace( $pre1[0], $pre2, $p_string );
  84. }
  85. /**
  86. * Prepare a multiple line string for display to HTML
  87. **/
  88. function string_display( $p_string )
  89. {
  90. $p_string = string_strip_hrefs( $p_string );
  91. $p_string = string_html_specialchars( $p_string );
  92. $p_string = string_restore_valid_html_tags( $p_string, /* multiline = */ true );
  93. $p_string = string_preserve_spaces_at_bol( $p_string );
  94. $p_string = string_nl2br( $p_string );
  95. return $p_string;
  96. }
  97. /** Prepare a single line string for display to HTML */
  98. function string_display_line( $p_string )
  99. {
  100. $p_string = string_strip_hrefs( $p_string );
  101. $p_string = string_html_specialchars( $p_string );
  102. $p_string = string_restore_valid_html_tags( $p_string, /* multiline = */ false );
  103. return $p_string;
  104. }
  105. /**
  106. * Prepare a string for display to HTML and add href anchors for URLs, emails,
  107. * bug references, and cvs references
  108. */
  109. function string_display_links( $p_string )
  110. {
  111. $p_string = string_display( $p_string );
  112. $p_string = string_insert_hrefs( $p_string );
  113. return $p_string;
  114. }
  115. /**
  116. * Prepare a single line string for display to HTML and add href anchors for
  117. * URLs, emails, bug references, and cvs references
  118. */
  119. function string_display_line_links( $p_string )
  120. {
  121. $p_string = string_display_line( $p_string );
  122. $p_string = string_insert_hrefs( $p_string );
  123. return $p_string;
  124. }
  125. /** Prepare a string for display in rss */
  126. function string_rss_links( $p_string )
  127. {
  128. // rss can not start with &nbsp; which spaces will be replaced into by string_display().
  129. $t_string = trim( $p_string );
  130. // same steps as string_display_links() without the preservation of spaces since &nbsp; is undefined in XML.
  131. $t_string = string_strip_hrefs( $t_string );
  132. $t_string = string_html_specialchars( $t_string );
  133. $t_string = string_restore_valid_html_tags( $t_string );
  134. $t_string = string_nl2br( $t_string );
  135. $t_string = string_insert_hrefs( $t_string );
  136. $t_string = string_process_bug_link( $t_string, /* anchor */ true, /* detailInfo */ false, /* fqdn */ true );
  137. $t_string = string_process_bugnote_link( $t_string, /* anchor */ true, /* detailInfo */ false, /* fqdn */ true );
  138. $t_string = string_process_cvs_link( $t_string );
  139. # another escaping to escape the special characters created by the generated links
  140. $t_string = string_html_specialchars( $t_string );
  141. return $t_string;
  142. }
  143. /**
  144. * Prepare a string for plain text display in email
  145. **/
  146. function string_email( $p_string )
  147. {
  148. $p_string = string_strip_hrefs( $p_string );
  149. return $p_string;
  150. }
  151. /**
  152. * Prepare a string for plain text display in email and add URLs for bug
  153. * links and cvs links
  154. */
  155. function string_email_links( $p_string ) {
  156. $p_string = string_email( $p_string );
  157. return $p_string;
  158. }
  159. /**
  160. * Process a string for display in a textarea box
  161. **/
  162. function string_textarea( $p_string )
  163. {
  164. $p_string = string_html_specialchars( $p_string );
  165. return $p_string;
  166. }
  167. /**
  168. * Process a string for display in a text box
  169. */
  170. function string_attribute( $p_string )
  171. {
  172. $p_string = string_html_specialchars( $p_string );
  173. return $p_string;
  174. }
  175. /**
  176. * Process a string for inclusion in a URL as a GET parameter
  177. */
  178. function string_url( $p_string )
  179. {
  180. $p_string = rawurlencode( $p_string );
  181. return $p_string;
  182. }
  183. /**
  184. * validate the url as part of this site before continuing
  185. **/
  186. function string_sanitize_url( $p_url ) {
  187. $t_url = strip_tags( urldecode( $p_url ) );
  188. if ( preg_match( '?http(s)*://?', $t_url ) > 0 ) {
  189. // no embedded addresses
  190. if ( preg_match( '?^' . config_get( 'path' ) . '?', $t_url ) == 0 ) {
  191. // url is ok if it begins with our path, if not, replace it
  192. $t_url = 'index.php';
  193. }
  194. }
  195. if ( $t_url == '' ) {
  196. $t_url = 'index.php';
  197. }
  198. // split and encode parameters
  199. if ( strpos( $t_url, '?' ) !== FALSE ) {
  200. list( $t_path, $t_param ) = split( '\?', $t_url, 2 );
  201. if ( $t_param !== "" ) {
  202. $t_vals = array();
  203. parse_str( $t_param, $t_vals );
  204. $t_param = '';
  205. foreach($t_vals as $k => $v) {
  206. if ($t_param != '') {
  207. $t_param .= '&';
  208. }
  209. $t_param .= "$k=" . urlencode( strip_tags( urldecode( $v ) ) );
  210. }
  211. return $t_path . '?' . $t_param;
  212. } else {
  213. return $t_path;
  214. }
  215. } else {
  216. return $t_url;
  217. }
  218. }
  219. // ----- Tag Processing -------------------------------------------------------
  220. /**
  221. * Detect URLs and email addresses in the string and replace them with href anchors
  222. **/
  223. function string_insert_hrefs( $p_string )
  224. {
  225. if ( !config_get('html_make_links') ) {
  226. return $p_string;
  227. }
  228. $t_change_quotes = false;
  229. if( ini_get_bool( 'magic_quotes_sybase' ) ) {
  230. $t_change_quotes = true;
  231. ini_set( 'magic_quotes_sybase', false );
  232. }
  233. // Find any URL in a string and replace it by a clickable link
  234. $p_string = preg_replace( '/(([[:alpha:]][-+.[:alnum:]]*):\/\/(%[[:digit:]A-Fa-f]{2}|[-_.!~*\';\/?%^\\\\:@&={\|}+$#\(\),\[\][:alnum:]])+)/se',
  235. "'<a href=\"'.rtrim('\\1','.').'\">\\1</a> [<a href=\"'.rtrim('\\1','.').'\" target=\"_blank\">^</a>]'", $p_string);
  236. if( $t_change_quotes ) {
  237. ini_set( 'magic_quotes_sybase', true );
  238. }
  239. # Set up a simple subset of RFC 822 email address parsing
  240. # We don't allow domain literals or quoted strings
  241. # We also don't allow the & character in domains even though the RFC
  242. # appears to do so. This was to prevent &gt; etc from being included.
  243. # Note: we could use email_get_rfc822_regex() but it doesn't work well
  244. # when applied to data that has already had entities inserted.
  245. #
  246. # bpfennig: '@' doesn't accepted anymore
  247. # achumakov: characters 0x80-0xFF aren't acceptable, too
  248. $t_atom = '[^\'@\'](?:[^()<>@,;:\\\".\[\]\000-\037\177-\377 &]+)';
  249. # In order to avoid selecting URLs containing @ characters as email
  250. # addresses we limit our selection to addresses that are preceded by:
  251. # * the beginning of the string
  252. # * a &lt; entity (allowing '<foo@bar.baz>')
  253. # * whitespace
  254. # * a : (allowing 'send email to:foo@bar.baz')
  255. # * a \n, \r, or > (because newlines have been replaced with <br />
  256. # and > isn't valid in URLs anyway
  257. #
  258. # At the end of the string we allow the opposite:
  259. # * the end of the string
  260. # * a &gt; entity
  261. # * whitespace
  262. # * a , character (allowing 'email foo@bar.baz, or ...')
  263. # * a \n, \r, or <
  264. $p_string = preg_replace( '/(?<=^|&quot;|&lt;|[\s\:\>\n\r])('.$t_atom.'(?:\.'.$t_atom.')*\@'.$t_atom.'(?:\.'.$t_atom.')*)(?=$|&quot;|&gt;|[\s\,\<\n\r])/s',
  265. '<a href="mailto:\1">\1</a>', $p_string);
  266. return $p_string;
  267. }
  268. /**
  269. * Detect href anchors in the string and replace them with URLs and email addresses
  270. **/
  271. function string_strip_hrefs( $p_string )
  272. {
  273. # First grab mailto: hrefs. We don't care whether the URL is actually
  274. # correct - just that it's inside an href attribute.
  275. $p_string = preg_replace( '/<a\s[^\>]*href="mailto:([^\"]+)"[^\>]*>[^\<]*<\/a>/si',
  276. '\1', $p_string);
  277. # Then grab any other href
  278. $p_string = preg_replace( '/<a\s[^\>]*href="([^\"]+)"[^\>]*>[^\<]*<\/a>/si',
  279. '\1', $p_string);
  280. return $p_string;
  281. }
  282. /**
  283. * This function looks for text with htmlentities
  284. * like &lt;b&gt; and converts is into corresponding
  285. * html &lt;b&gt; based on the configuration presets
  286. */
  287. function string_restore_valid_html_tags( $p_string, $p_multiline = true )
  288. {
  289. $t_html_valid_tags = config_get( $p_multiline ? 'html_valid_tags' : 'html_valid_tags_single_line' );
  290. if ( OFF === $t_html_valid_tags || is_blank( $t_html_valid_tags ) ) {
  291. return $p_string;
  292. }
  293. $tags = explode( ',', $t_html_valid_tags );
  294. foreach ($tags as $key => $value)
  295. {
  296. if ( !is_blank( $value ) ) {
  297. $tags[$key] = trim($value);
  298. }
  299. }
  300. $tags = implode( '|', $tags);
  301. $p_string = preg_replace( '/&lt;(' . $tags . ')\s*&gt;/ui', '<\\1>', $p_string );
  302. $p_string = preg_replace( '/&lt;\/(' . $tags . ')\s*&gt;/ui', '</\\1>', $p_string );
  303. $p_string = preg_replace( '/&lt;(' . $tags . ')\s*\/&gt;/ui', '<\\1 />', $p_string );
  304. return $p_string;
  305. }
  306. /**
  307. * Return a string with the $p_character pattern repeated N times.
  308. *
  309. * @param string $p_character - pattern to repeat
  310. * @param integer $p_repeats - number of times to repeat.
  311. */
  312. function string_repeat_char( $p_character, $p_repeats ) {
  313. return str_pad( '', $p_repeats, $p_character );
  314. }
  315. /**
  316. * Format date for display
  317. */
  318. function string_format_complete_date( $p_date ) {
  319. $t_timestamp = db_unixtimestamp( $p_date );
  320. return date( config_get( 'complete_date_format' ), $t_timestamp );
  321. }
  322. /**
  323. * Shorten a string for display on a dropdown to prevent the page rendering too wide
  324. */
  325. function string_shorten( $p_string ) {
  326. $t_max = config_get( 'max_dropdown_length' );
  327. if ( ( tlStrLen($p_string ) > $t_max ) && ( $t_max > 0 ) ){
  328. $t_pattern = '/([\s|.|,|\-|_|\/|\?]+)/';
  329. $t_bits = preg_split( $t_pattern, $p_string, -1, PREG_SPLIT_DELIM_CAPTURE );
  330. $t_string = '';
  331. $t_last = $t_bits[ count( $t_bits ) - 1 ];
  332. $t_last_len = tlStrLen( $t_last );
  333. foreach ( $t_bits as $t_bit ) {
  334. if ( ( tlStrLen( $t_string ) + tlStrLen( $t_bit ) + $t_last_len + 3 <= $t_max )
  335. || ( strpos( $t_bit, '.,-/?' ) > 0 ) ) {
  336. $t_string .= $t_bit;
  337. } else {
  338. break;
  339. }
  340. }
  341. $t_string .= '...' . $t_last;
  342. return $t_string;
  343. } else {
  344. return $p_string;
  345. }
  346. }
  347. /**
  348. * remap a field name to a string name (for sort filter)
  349. */
  350. function string_get_field_name( $p_string ) {
  351. $t_map = array(
  352. 'last_updated' => 'last_update',
  353. 'id' => 'email_bug'
  354. );
  355. $t_string = $p_string;
  356. if ( isset( $t_map[ $p_string ] ) ) {
  357. $t_string = $t_map[ $p_string ];
  358. }
  359. return lang_get_defaulted( $t_string );
  360. }
  361. /**
  362. * Calls htmlentities on the specified string, passing along
  363. * the current charset.
  364. */
  365. function string_html_entities( $p_string ) {
  366. return htmlentities( $p_string, ENT_COMPAT, config_get('charset') );
  367. }
  368. /**
  369. * Calls htmlspecialchars on the specified string, passing along
  370. * the current charset, if the current PHP version supports it.
  371. */
  372. function string_html_specialchars( $p_string ) {
  373. # achumakov: @ added to avoid warning output in unsupported codepages
  374. # e.g. 8859-2, windows-1257, Korean, which are treated as 8859-1.
  375. # This is VERY important for Eastern European, Baltic and Korean languages
  376. return preg_replace("/&amp;(#[0-9]+|[a-z]+);/i", "&$1;", @htmlspecialchars( $p_string, ENT_COMPAT, config_get('charset') ) );
  377. }
  378. /**
  379. * Prepares a string to be used as part of header().
  380. */
  381. function string_prepare_header( $p_string ) {
  382. $t_string = $p_string;
  383. $t_truncate_pos = strpos($p_string, "\n");
  384. if ($t_truncate_pos !== false ) {
  385. $t_string = substr($t_string, 0, $t_truncate_pos);
  386. }
  387. $t_truncate_pos = strpos($p_string, "\r");
  388. if ($t_truncate_pos !== false ) {
  389. $t_string = substr($t_string, 0, $t_truncate_pos);
  390. }
  391. return $t_string;
  392. }
  393. /**
  394. * Checks the supplied string for scripting characters, if it contains any, then return true, otherwise return false.
  395. *
  396. * @param string $p_string
  397. * @return boolean
  398. */
  399. function string_contains_scripting_chars( $p_string ) {
  400. if ( ( strstr( $p_string, '<' ) !== false ) || ( strstr( $p_string, '>' ) !== false ) ) {
  401. return true;
  402. }
  403. return false;
  404. }
  405. ?>