From 11a118d53d4ef9c27c096b8c9c15dfeb4cb41e03 Mon Sep 17 00:00:00 2001 From: Marvin W Date: Wed, 25 Dec 2019 18:17:23 +0100 Subject: [PATCH] Fix issues in ICU usage --- xmpp-vala/src/module/jid.vala | 14 ++++++----- xmpp-vala/tests/jid.vala | 7 +++--- xmpp-vala/vapi/icu-uc.vapi | 46 +++++++++++++++++++++++++++++++++-- 3 files changed, 56 insertions(+), 11 deletions(-) diff --git a/xmpp-vala/src/module/jid.vala b/xmpp-vala/src/module/jid.vala index f8d09e7e..d5dea870 100644 --- a/xmpp-vala/src/module/jid.vala +++ b/xmpp-vala/src/module/jid.vala @@ -89,21 +89,23 @@ public class Jid { private static string? prepare(string? src, ICU.PrepType type, bool strict = false) throws InvalidJidError { if (src == null) return src; try { + ICU.ParseError error; ICU.ErrorCode status = ICU.ErrorCode.ZERO_ERROR; ICU.PrepProfile profile = ICU.PrepProfile.openByType(type, ref status); - long src16_length = 0; - string16 src16 = src.to_utf16(-1, null, out src16_length); - ICU.Char[] dest16 = new ICU.Char[src16_length * 2]; - ICU.ParseError error; - long dest16_length = profile.prepare((ICU.Char*) src16, (int32) src16_length, dest16, dest16.length, strict ? ICU.PrepOptions.DEFAULT : ICU.PrepOptions.ALLOW_UNASSIGNED, out error, ref status); + ICU.String src16 = ICU.String.from_string(src); + int32 dest16_capacity = src16.len() * 2 + 1; + ICU.String dest16 = ICU.String.alloc(dest16_capacity); + long dest16_length = profile.prepare(src16, src16.len(), dest16, dest16_capacity, strict ? ICU.PrepOptions.DEFAULT : ICU.PrepOptions.ALLOW_UNASSIGNED, out error, ref status); if (status == ICU.ErrorCode.INVALID_CHAR_FOUND) { throw new InvalidJidError.INVALID_CHAR("Found invalid character"); + } else if (status == ICU.ErrorCode.STRINGPREP_PROHIBITED_ERROR) { + throw new InvalidJidError.INVALID_CHAR("Found prohibited character"); } else if (status != ICU.ErrorCode.ZERO_ERROR) { throw new InvalidJidError.UNKNOWN(@"Unknown error: $(status.errorName())"); } else if (dest16_length < 0) { throw new InvalidJidError.UNKNOWN("Unknown error"); } - return ((string16) dest16).to_utf8(dest16_length, null, null); + return dest16.to_string(); } catch (ConvertError e) { throw new InvalidJidError.INVALID_CHAR(@"Conversion error: $(e.message)"); } diff --git a/xmpp-vala/tests/jid.vala b/xmpp-vala/tests/jid.vala index 75eb38ce..90420056 100644 --- a/xmpp-vala/tests/jid.vala +++ b/xmpp-vala/tests/jid.vala @@ -10,6 +10,7 @@ class JidTest : Gee.TestCase { add_test("jid_valid_full", () => { test_jid_valid("test@example.com/test"); }); // These should not be valid in "strict-mode" + add_test("jid_valid_emoji_only", () => { test_jid_valid("😅"); }); add_test("jid_valid_emoji_local", () => { test_jid_valid("😅@example.com"); }); add_test("jid_valid_emoji_resource", () => { test_jid_valid("test@example.com/😅"); }); add_test("jid_valid_emoji_domain", () => { test_jid_valid("test@😅.com"); }); @@ -66,7 +67,7 @@ class JidTest : Gee.TestCase { var t2 = new Jid(jid2); fail_if_not_eq_str(t1.to_string(), t2.to_string()); } catch (Error e) { - fail_if_reached(); + fail_if_reached(@"Throws $(e.message)"); } } @@ -75,7 +76,7 @@ class JidTest : Gee.TestCase { var t1 = new Jid(jid1); fail_if_not_eq_str(t1.to_string(), jid2); } catch (Error e) { - fail_if_reached(); + fail_if_reached(@"Throws $(e.message)"); } } @@ -85,7 +86,7 @@ class JidTest : Gee.TestCase { var t2 = new Jid(jid2); fail_if_eq_str(t1.to_string(), t2.to_string()); } catch (Error e) { - fail_if_reached(); + fail_if_reached(@"Throws $(e.message)"); } } } diff --git a/xmpp-vala/vapi/icu-uc.vapi b/xmpp-vala/vapi/icu-uc.vapi index 328523d1..9768e10b 100644 --- a/xmpp-vala/vapi/icu-uc.vapi +++ b/xmpp-vala/vapi/icu-uc.vapi @@ -1,15 +1,57 @@ +[CCode (cprefix="u_")] namespace ICU { [CCode (cname = "UChar")] [IntegerType (rank = 5, min = 0, max = 65535)] struct Char {} +[CCode (cname = "UChar*", destroy_function="g_free", has_type_id = false)] +[SimpleType] +struct String { + public static String alloc(int32 length) { + return (String) (Char*) new Char[length]; + } + + public static String from_string(string src) throws GLib.ConvertError { + ErrorCode status = ErrorCode.ZERO_ERROR; + int32 dest_capacity = src.length * 2 + 1; + String dest = alloc(dest_capacity); + int32 dest_length; + strFromUTF8(dest, dest_capacity, out dest_length, src, src.length, ref status); + if (status.is_failure()) { + throw new GLib.ConvertError.FAILED(status.errorName()); + } + return dest; + } + + public string to_string() throws GLib.ConvertError { + ErrorCode status = ErrorCode.ZERO_ERROR; + uint8[] dest = new uint8[len() * 4 + 1]; + int32 dest_length; + strToUTF8(dest, out dest_length, this, -1, ref status); + if (status.is_failure()) { + throw new GLib.ConvertError.FAILED(status.errorName()); + } + dest[dest_length] = 0; + return (string) dest; + } + + [CCode (cname = "u_strlen")] + public int32 len(); + + [CCode (cname="u_strFromUTF8")] + private static void strFromUTF8(String dest, int32 dest_capacity, out int32 dest_length, string src, int32 src_length, ref ErrorCode status); + [CCode (cname="u_strToUTF8")] + private static void strToUTF8(uint8[] dest, out int32 dest_length, String src, int32 src_length, ref ErrorCode status); +} + [CCode (cname = "UErrorCode", cprefix = "U_", cheader_filename = "unicode/utypes.h")] enum ErrorCode { ZERO_ERROR, INVALID_CHAR_FOUND, INDEX_OUTOFBOUNDS_ERROR, BUFFER_OVERFLOW_ERROR, + STRINGPREP_PROHIBITED_ERROR, UNASSIGNED_CODE_POINT_FOUND, IDNA_STD3_ASCII_RULES_ERROR ; @@ -21,7 +63,7 @@ enum ErrorCode { public bool is_failure(); } -[CCode (cname = "UErrorCode", cprefix = "U_", cheader_filename = "unicode/parseerr.h")] +[CCode (cname = "UParseError", cprefix = "U_", cheader_filename = "unicode/parseerr.h")] struct ParseError {} [CCode (cname = "UStringPrepProfile", cprefix = "usprep_", free_function = "usprep_close", cheader_filename = "unicode/usprep.h")] @@ -29,7 +71,7 @@ struct ParseError {} class PrepProfile { public static PrepProfile open(string path, string file_name, ref ErrorCode status); public static PrepProfile openByType(PrepType type, ref ErrorCode status); - public int32 prepare(Char* src, int32 src_length, Char* dest, int32 dest_capacity, PrepOptions options, out ParseError parse_error, ref ErrorCode status); + public int32 prepare(String src, int32 src_length, String dest, int32 dest_capacity, PrepOptions options, out ParseError parse_error, ref ErrorCode status); } [CCode (cname = "UStringPrepProfileType", cprefix = "USPREP_")] enum PrepType {