From afcf44928748f7ac049f1ba9f5c51c6dedbf4ec2 Mon Sep 17 00:00:00 2001 From: Marvin W Date: Sat, 30 May 2026 16:00:14 +0200 Subject: Use single regex to process xml encoded entities --- xmpp-vala/src/core/stanza_node.vala | 58 ++++++++++++++++++++++++++++--------- xmpp-vala/tests/stanza.vala | 11 +++++++ 2 files changed, 55 insertions(+), 14 deletions(-) diff --git a/xmpp-vala/src/core/stanza_node.vala b/xmpp-vala/src/core/stanza_node.vala index 6911b4b2..f44657f1 100644 --- a/xmpp-vala/src/core/stanza_node.vala +++ b/xmpp-vala/src/core/stanza_node.vala @@ -8,6 +8,9 @@ public abstract class StanzaEntry { protected const string ANSI_COLOR_YELLOW = "\x1b[33m"; protected const string ANSI_COLOR_GRAY = "\x1b[37m"; + private static Regex ENCODABLE_VALUE_CHARS = /[&"'<>]/; + private static Regex DECODABLE_VALUE_SEQUENCES = /&(gt|lt|apos|quot|amp|#(x[0-9a-fA-F]+|[0-9]+));/; + public string? ns_uri; public string name; public string? val; @@ -15,27 +18,54 @@ public abstract class StanzaEntry { public string? encoded_val { owned get { if (val == null) return null; - return ((!)val).replace("&", "&").replace("\"", """).replace("'", "'").replace("<", "<").replace(">", ">"); + return ENCODABLE_VALUE_CHARS.replace_eval(val, -1, 0, 0, (match_info, result_builder) => { + int start; + match_info.fetch_pos(0, out start, null); + unowned string src = match_info.get_string(); + switch (src[start]) { + case '&': result_builder.append("&"); break; + case '"': result_builder.append("""); break; + case '\'': result_builder.append("'"); break; + case '<': result_builder.append("<"); break; + case '>': result_builder.append(">"); break; + default: error("Unreachable"); break; + } + return false; + }); } set { if (value == null) { val = null; return; } - string tmp = ((!)value).replace(">", ">").replace("<", "<").replace("'","'").replace(""","\""); - while (tmp.contains("&#")) { - int start = tmp.index_of("&#"); - int end = tmp.index_of(";", start); - if (end < start) break; - unichar num = -1; - if (tmp[start+2]=='x') { - tmp.substring(start+3, start-end-3).scanf("%x", &num); - } else { - num = int.parse(tmp.substring(start+2, start-end-2)); + val = DECODABLE_VALUE_SEQUENCES.replace_eval(value, -1, 0, 0, (match_info, result_builder) => { + int start, end; + match_info.fetch_pos(0, out start, out end); + unowned string src = match_info.get_string(); + switch (src[start+1]) { + case 'g': result_builder.append_c('>'); break; + case 'l': result_builder.append_c('<'); break; + case 'q': result_builder.append_c('"'); break; + case 'a': + switch (src[start+2]) { + case 'p': result_builder.append_c('\''); break; + case 'm': result_builder.append_c('&'); break; + default: critical("Invalid encoding: %s in %s", match_info.fetch(0), src); break; + } + break; + case '#': + unichar num = -1; + if (src[start+2] == 'x') { + src.substring(start+3, start-end-3).scanf("%x", &num); + } else { + num = int.parse(src.substring(start+2, start-end-2)); + } + result_builder.append_unichar(num); + break; + default: critical("Invalid encoding: %s in %s", match_info.fetch(0), src); break; } - tmp = tmp.splice(start, end, num.to_string()); - } - val = tmp.replace("&", "&"); + return false; + }); } } diff --git a/xmpp-vala/tests/stanza.vala b/xmpp-vala/tests/stanza.vala index 7326a668..42e19f9a 100644 --- a/xmpp-vala/tests/stanza.vala +++ b/xmpp-vala/tests/stanza.vala @@ -8,6 +8,7 @@ class StanzaTest : Gee.TestCase { add_async_test("typical_stream", (cb) => { test_typical_stream.begin(cb); }); add_async_test("ack_stream", (cb) => { test_ack_stream.begin(cb); }); add_test("get_attribute_(u)int", test_get_attribute_int); + add_test("test_encoded_val", test_encoded_val); } private async void test_node_one(Gee.TestFinishedCallback cb) { @@ -113,6 +114,16 @@ class StanzaTest : Gee.TestCase { cb(); } + private void test_encoded_val() { + var attr = new StanzaAttribute.build("ns", "foo", "b\"a<'>r&me"); + fail_if_not_eq_str(attr.val, "b\"a<'>r&me"); + fail_if_not_eq_str(attr.encoded_val, "b"a<'>r&me"); + + attr.encoded_val = "b"a<'>r&me"; + fail_if_not_eq_str(attr.val, "b\"a<'>r&me"); + fail_if_not_eq_str(attr.encoded_val, "b"a<'>r&me"); + } + private void test_get_attribute_int() { var stanza_node = new StanzaNode.build("test", "ns").add_self_xmlns().put_attribute("bar", "42"); fail_if_not_eq_int(stanza_node.get_attribute_int("bar", -2), 42); -- cgit v1.3