Mercurial > hg > rc2
comparison program/lib/Roundcube/rcube_string_replacer.php @ 0:4681f974d28b
vanilla 1.3.3 distro, I hope
| author | Charlie Root |
|---|---|
| date | Thu, 04 Jan 2018 15:52:31 -0500 |
| parents | |
| children | c32b53434b47 |
comparison
equal
deleted
inserted
replaced
| -1:000000000000 | 0:4681f974d28b |
|---|---|
| 1 <?php | |
| 2 | |
| 3 /** | |
| 4 +-----------------------------------------------------------------------+ | |
| 5 | This file is part of the Roundcube Webmail client | | |
| 6 | Copyright (C) 2009-2012, The Roundcube Dev Team | | |
| 7 | | | |
| 8 | Licensed under the GNU General Public License version 3 or | | |
| 9 | any later version with exceptions for skins & plugins. | | |
| 10 | See the README file for a full license statement. | | |
| 11 | | | |
| 12 | PURPOSE: | | |
| 13 | Handle string replacements based on preg_replace_callback | | |
| 14 +-----------------------------------------------------------------------+ | |
| 15 | Author: Thomas Bruederli <roundcube@gmail.com> | | |
| 16 +-----------------------------------------------------------------------+ | |
| 17 */ | |
| 18 | |
| 19 /** | |
| 20 * Helper class for string replacements based on preg_replace_callback | |
| 21 * | |
| 22 * @package Framework | |
| 23 * @subpackage Utils | |
| 24 */ | |
| 25 class rcube_string_replacer | |
| 26 { | |
| 27 public static $pattern = '/##str_replacement_(\d+)##/'; | |
| 28 public $mailto_pattern; | |
| 29 public $link_pattern; | |
| 30 public $linkref_index; | |
| 31 public $linkref_pattern; | |
| 32 | |
| 33 protected $values = array(); | |
| 34 protected $options = array(); | |
| 35 protected $linkrefs = array(); | |
| 36 protected $urls = array(); | |
| 37 protected $noword = '[^\w@.#-]'; | |
| 38 | |
| 39 | |
| 40 function __construct($options = array()) | |
| 41 { | |
| 42 // Simplified domain expression for UTF8 characters handling | |
| 43 // Support unicode/punycode in top-level domain part | |
| 44 $utf_domain = '[^?&@"\'\\/()<>\s\r\t\n]+\\.?([^\\x00-\\x2f\\x3b-\\x40\\x5b-\\x60\\x7b-\\x7f]{2,}|xn--[a-zA-Z0-9]{2,})'; | |
| 45 $url1 = '.:;,'; | |
| 46 $url2 = 'a-zA-Z0-9%=#$@+?|!&\\/_~\\[\\]\\(\\){}\*\x80-\xFE-'; | |
| 47 | |
| 48 // Supported link prefixes | |
| 49 $link_prefix = "([\w]+:\/\/|{$this->noword}[Ww][Ww][Ww]\.|^[Ww][Ww][Ww]\.)"; | |
| 50 | |
| 51 $this->options = $options; | |
| 52 $this->linkref_index = '/\[([^\]#]+)\](:?\s*##str_replacement_(\d+)##)/'; | |
| 53 $this->linkref_pattern = '/\[([^\]#]+)\]/'; | |
| 54 $this->link_pattern = "/$link_prefix($utf_domain([$url1]*[$url2]+)*)/"; | |
| 55 $this->mailto_pattern = "/(" | |
| 56 ."[-\w!\#\$%&\'*+~\/^`|{}=]+(?:\.[-\w!\#\$%&\'*+~\/^`|{}=]+)*" // local-part | |
| 57 ."@$utf_domain" // domain-part | |
| 58 ."(\?[$url1$url2]+)?" // e.g. ?subject=test... | |
| 59 .")/"; | |
| 60 } | |
| 61 | |
| 62 /** | |
| 63 * Add a string to the internal list | |
| 64 * | |
| 65 * @param string String value | |
| 66 * | |
| 67 * @return int Index of value for retrieval | |
| 68 */ | |
| 69 public function add($str) | |
| 70 { | |
| 71 $i = count($this->values); | |
| 72 $this->values[$i] = $str; | |
| 73 return $i; | |
| 74 } | |
| 75 | |
| 76 /** | |
| 77 * Build replacement string | |
| 78 */ | |
| 79 public function get_replacement($i) | |
| 80 { | |
| 81 return '##str_replacement_' . $i . '##'; | |
| 82 } | |
| 83 | |
| 84 /** | |
| 85 * Callback function used to build HTML links around URL strings | |
| 86 * | |
| 87 * @param array Matches result from preg_replace_callback | |
| 88 * @return int Index of saved string value | |
| 89 */ | |
| 90 public function link_callback($matches) | |
| 91 { | |
| 92 $i = -1; | |
| 93 $scheme = strtolower($matches[1]); | |
| 94 | |
| 95 if (preg_match('!^(http|ftp|file)s?://!i', $scheme)) { | |
| 96 $url = $matches[1] . $matches[2]; | |
| 97 } | |
| 98 else if (preg_match("/^({$this->noword}*)(www\.)$/i", $matches[1], $m)) { | |
| 99 $url = $m[2] . $matches[2]; | |
| 100 $url_prefix = 'http://'; | |
| 101 $prefix = $m[1]; | |
| 102 } | |
| 103 | |
| 104 if ($url) { | |
| 105 $suffix = $this->parse_url_brackets($url); | |
| 106 $attrib = (array)$this->options['link_attribs']; | |
| 107 $attrib['href'] = $url_prefix . $url; | |
| 108 | |
| 109 $i = $this->add(html::a($attrib, rcube::Q($url)) . $suffix); | |
| 110 $this->urls[$i] = $attrib['href']; | |
| 111 } | |
| 112 | |
| 113 // Return valid link for recognized schemes, otherwise | |
| 114 // return the unmodified string for unrecognized schemes. | |
| 115 return $i >= 0 ? $prefix . $this->get_replacement($i) : $matches[0]; | |
| 116 } | |
| 117 | |
| 118 /** | |
| 119 * Callback to add an entry to the link index | |
| 120 */ | |
| 121 public function linkref_addindex($matches) | |
| 122 { | |
| 123 $key = $matches[1]; | |
| 124 $this->linkrefs[$key] = $this->urls[$matches[3]]; | |
| 125 | |
| 126 return $this->get_replacement($this->add('['.$key.']')) . $matches[2]; | |
| 127 } | |
| 128 | |
| 129 /** | |
| 130 * Callback to replace link references with real links | |
| 131 */ | |
| 132 public function linkref_callback($matches) | |
| 133 { | |
| 134 $i = 0; | |
| 135 if ($url = $this->linkrefs[$matches[1]]) { | |
| 136 $attrib = (array)$this->options['link_attribs']; | |
| 137 $attrib['href'] = $url; | |
| 138 $i = $this->add(html::a($attrib, rcube::Q($matches[1]))); | |
| 139 } | |
| 140 | |
| 141 return $i > 0 ? '['.$this->get_replacement($i).']' : $matches[0]; | |
| 142 } | |
| 143 | |
| 144 /** | |
| 145 * Callback function used to build mailto: links around e-mail strings | |
| 146 * | |
| 147 * @param array Matches result from preg_replace_callback | |
| 148 * | |
| 149 * @return int Index of saved string value | |
| 150 */ | |
| 151 public function mailto_callback($matches) | |
| 152 { | |
| 153 $href = $matches[1]; | |
| 154 $suffix = $this->parse_url_brackets($href); | |
| 155 $i = $this->add(html::a('mailto:' . $href, rcube::Q($href)) . $suffix); | |
| 156 | |
| 157 return $i >= 0 ? $this->get_replacement($i) : ''; | |
| 158 } | |
| 159 | |
| 160 /** | |
| 161 * Look up the index from the preg_replace matches array | |
| 162 * and return the substitution value. | |
| 163 * | |
| 164 * @param array Matches result from preg_replace_callback | |
| 165 * @return string Value at index $matches[1] | |
| 166 */ | |
| 167 public function replace_callback($matches) | |
| 168 { | |
| 169 return $this->values[$matches[1]]; | |
| 170 } | |
| 171 | |
| 172 /** | |
| 173 * Replace all defined (link|mailto) patterns with replacement string | |
| 174 * | |
| 175 * @param string $str Text | |
| 176 * | |
| 177 * @return string Text | |
| 178 */ | |
| 179 public function replace($str) | |
| 180 { | |
| 181 // search for patterns like links and e-mail addresses | |
| 182 $str = preg_replace_callback($this->link_pattern, array($this, 'link_callback'), $str); | |
| 183 $str = preg_replace_callback($this->mailto_pattern, array($this, 'mailto_callback'), $str); | |
| 184 // resolve link references | |
| 185 $str = preg_replace_callback($this->linkref_index, array($this, 'linkref_addindex'), $str); | |
| 186 $str = preg_replace_callback($this->linkref_pattern, array($this, 'linkref_callback'), $str); | |
| 187 | |
| 188 return $str; | |
| 189 } | |
| 190 | |
| 191 /** | |
| 192 * Replace substituted strings with original values | |
| 193 */ | |
| 194 public function resolve($str) | |
| 195 { | |
| 196 return preg_replace_callback(self::$pattern, array($this, 'replace_callback'), $str); | |
| 197 } | |
| 198 | |
| 199 /** | |
| 200 * Fixes bracket characters in URL handling | |
| 201 */ | |
| 202 public static function parse_url_brackets(&$url) | |
| 203 { | |
| 204 // #1487672: special handling of square brackets, | |
| 205 // URL regexp allows [] characters in URL, for example: | |
| 206 // "http://example.com/?a[b]=c". However we need to handle | |
| 207 // properly situation when a bracket is placed at the end | |
| 208 // of the link e.g. "[http://example.com]" | |
| 209 // Yes, this is not perfect handles correctly only paired characters | |
| 210 // but it should work for common cases | |
| 211 | |
| 212 if (preg_match('/(\\[|\\])/', $url)) { | |
| 213 $in = false; | |
| 214 for ($i=0, $len=strlen($url); $i<$len; $i++) { | |
| 215 if ($url[$i] == '[') { | |
| 216 if ($in) | |
| 217 break; | |
| 218 $in = true; | |
| 219 } | |
| 220 else if ($url[$i] == ']') { | |
| 221 if (!$in) | |
| 222 break; | |
| 223 $in = false; | |
| 224 } | |
| 225 } | |
| 226 | |
| 227 if ($i < $len) { | |
| 228 $suffix = substr($url, $i); | |
| 229 $url = substr($url, 0, $i); | |
| 230 } | |
| 231 } | |
| 232 | |
| 233 // Do the same for parentheses | |
| 234 if (preg_match('/(\\(|\\))/', $url)) { | |
| 235 $in = false; | |
| 236 for ($i=0, $len=strlen($url); $i<$len; $i++) { | |
| 237 if ($url[$i] == '(') { | |
| 238 if ($in) | |
| 239 break; | |
| 240 $in = true; | |
| 241 } | |
| 242 else if ($url[$i] == ')') { | |
| 243 if (!$in) | |
| 244 break; | |
| 245 $in = false; | |
| 246 } | |
| 247 } | |
| 248 | |
| 249 if ($i < $len) { | |
| 250 $suffix = substr($url, $i); | |
| 251 $url = substr($url, 0, $i); | |
| 252 } | |
| 253 } | |
| 254 | |
| 255 return $suffix; | |
| 256 } | |
| 257 } |
