diff --git a/library/HTMLPurifier/Lexer.php b/library/HTMLPurifier/Lexer.php
index cb4f0fcc..4f35c441 100644
--- a/library/HTMLPurifier/Lexer.php
+++ b/library/HTMLPurifier/Lexer.php
@@ -306,6 +306,145 @@ class HTMLPurifier_Lexer
}
}
+ /**
+ * Currently converts UTF8 into an array of Unicode codepoints. (changing)
+ *
+ * We're going to convert this into a multi-purpose UTF-8 well-formedness
+ * checker as well as handler for the control characters that are illegal
+ * in SGML documents. But *after* we draw up some unit-tests. This means
+ * that the function, in the end, will not return an array of codepoints
+ * but a valid UTF8 string, with non-SGML codepoints excluded.
+ *
+ * @note Just for reference, the non-SGML code points are 0 to 31 and
+ * 127 to 159, inclusive.
+ *
+ * @note The functionality provided by the original function could be
+ * implemented with iconv using 'UTF-8//IGNORE', mbstring, or
+ * even the PCRE modifier 'u', these do not allow us to strip
+ * control characters or disallowed code points, and the latter
+ * does not allow invalid UTF8 characters to be ignored.
+ *
+ * @note Decomposing the string into Unicode code points is necessary
+ * because SGML disallows the use of specific code points, not
+ * necessarily bytes. A naive implementation that simply strtr
+ * disallowed code points as bytes will break other Unicode
+ * characters in which using such bytes is valid.
+ *
+ * @note Code adapted from utf8ToUnicode by Henri Sivonen and
+ * hsivonen@iki.fi at under the
+ * LGPL license.
+ */
+ function cleanUTF8($str) {
+ $mState = 0; // cached expected number of octets after the current octet
+ // until the beginning of the next UTF8 character sequence
+ $mUcs4 = 0; // cached Unicode character
+ $mBytes = 1; // cached expected number of octets in the current sequence
+
+ $out = array();
+
+ $len = strlen($str);
+ for($i = 0; $i < $len; $i++) {
+ $in = ord($str{$i});
+ if (0 == $mState) {
+ // When mState is zero we expect either a US-ASCII character
+ // or a multi-octet sequence.
+ if (0 == (0x80 & ($in))) {
+ // US-ASCII, pass straight through.
+ $out[] = $in;
+ $mBytes = 1;
+ } elseif (0xC0 == (0xE0 & ($in))) {
+ // First octet of 2 octet sequence
+ $mUcs4 = ($in);
+ $mUcs4 = ($mUcs4 & 0x1F) << 6;
+ $mState = 1;
+ $mBytes = 2;
+ } elseif (0xE0 == (0xF0 & ($in))) {
+ // First octet of 3 octet sequence
+ $mUcs4 = ($in);
+ $mUcs4 = ($mUcs4 & 0x0F) << 12;
+ $mState = 2;
+ $mBytes = 3;
+ } elseif (0xF0 == (0xF8 & ($in))) {
+ // First octet of 4 octet sequence
+ $mUcs4 = ($in);
+ $mUcs4 = ($mUcs4 & 0x07) << 18;
+ $mState = 3;
+ $mBytes = 4;
+ } elseif (0xF8 == (0xFC & ($in))) {
+ // First octet of 5 octet sequence.
+ //
+ // This is illegal because the encoded codepoint must be
+ // either:
+ // (a) not the shortest form or
+ // (b) outside the Unicode range of 0-0x10FFFF.
+ // Rather than trying to resynchronize, we will carry on
+ // until the end of the sequence and let the later error
+ // handling code catch it.
+ $mUcs4 = ($in);
+ $mUcs4 = ($mUcs4 & 0x03) << 24;
+ $mState = 4;
+ $mBytes = 5;
+ } elseif (0xFC == (0xFE & ($in))) {
+ // First octet of 6 octet sequence, see comments for 5
+ // octet sequence.
+ $mUcs4 = ($in);
+ $mUcs4 = ($mUcs4 & 1) << 30;
+ $mState = 5;
+ $mBytes = 6;
+ } else {
+ // Current octet is neither in the US-ASCII range nor a
+ // legal first octet of a multi-octet sequence.
+ return false;
+ }
+ } else {
+ // When mState is non-zero, we expect a continuation of the
+ // multi-octet sequence
+ if (0x80 == (0xC0 & ($in))) {
+ // Legal continuation.
+ $shift = ($mState - 1) * 6;
+ $tmp = $in;
+ $tmp = ($tmp & 0x0000003F) << $shift;
+ $mUcs4 |= $tmp;
+
+ if (0 == --$mState) {
+ // End of the multi-octet sequence. mUcs4 now contains
+ // the final Unicode codepoint to be output
+
+ // Check for illegal sequences and codepoints.
+
+ // From Unicode 3.1, non-shortest form is illegal
+ if (((2 == $mBytes) && ($mUcs4 < 0x0080)) ||
+ ((3 == $mBytes) && ($mUcs4 < 0x0800)) ||
+ ((4 == $mBytes) && ($mUcs4 < 0x10000)) ||
+ (4 < $mBytes) ||
+ // From Unicode 3.2, surrogate characters = illegal
+ (($mUcs4 & 0xFFFFF800) == 0xD800) ||
+ // Codepoints outside the Unicode range are illegal
+ ($mUcs4 > 0x10FFFF)
+ ) {
+ return false;
+ }
+ if (0xFEFF != $mUcs4) {
+ // BOM is legal but we don't want to output it
+ $out[] = $mUcs4;
+ }
+ //initialize UTF8 cache
+ $mState = 0;
+ $mUcs4 = 0;
+ $mBytes = 1;
+ }
+ } else {
+ // ((0xC0 & (*in) != 0x80) && (mState != 0))
+ *
+ * Incomplete multi-octet sequence.
+ */
+ return false;
+ }
+ }
+ }
+ return $out;
+ }
+
}
?>
\ No newline at end of file