aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarvin W <git@larma.de>2026-05-30 16:00:14 +0200
committerMarvin W <git@larma.de>2026-06-07 23:13:06 +0200
commitafcf44928748f7ac049f1ba9f5c51c6dedbf4ec2 (patch)
treeb33c4e7edaa00880bd622a71907d285f706d38b6
parentcf8e81e26862b463c6d42fc6359557db85eb8d90 (diff)
Use single regex to process xml encoded entities
-rw-r--r--xmpp-vala/src/core/stanza_node.vala58
-rw-r--r--xmpp-vala/tests/stanza.vala11
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("&", "&amp;").replace("\"", "&quot;").replace("'", "&apos;").replace("<", "&lt;").replace(">", "&gt;");
+ 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("&amp;"); break;
+ case '"': result_builder.append("&quot;"); break;
+ case '\'': result_builder.append("&apos;"); break;
+ case '<': result_builder.append("&lt;"); break;
+ case '>': result_builder.append("&gt;"); break;
+ default: error("Unreachable"); break;
+ }
+ return false;
+ });
}
set {
if (value == null) {
val = null;
return;
}
- string tmp = ((!)value).replace("&gt;", ">").replace("&lt;", "<").replace("&apos;","'").replace("&quot;","\"");
- 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("&amp;", "&");
+ 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&quot;a&lt;&apos;&gt;r&amp;me");
+
+ attr.encoded_val = "b&quot;a&lt;&apos;&gt;r&amp;me";
+ fail_if_not_eq_str(attr.val, "b\"a<'>r&me");
+ fail_if_not_eq_str(attr.encoded_val, "b&quot;a&lt;&apos;&gt;r&amp;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);