Fix issues in ICU usage

This commit is contained in:
Marvin W 2019-12-25 18:17:23 +01:00
parent 4197b589d0
commit 11a118d53d
No known key found for this signature in database
GPG key ID: 072E9235DB996F2A
3 changed files with 56 additions and 11 deletions

View file

@ -89,21 +89,23 @@ public class Jid {
private static string? prepare(string? src, ICU.PrepType type, bool strict = false) throws InvalidJidError { private static string? prepare(string? src, ICU.PrepType type, bool strict = false) throws InvalidJidError {
if (src == null) return src; if (src == null) return src;
try { try {
ICU.ParseError error;
ICU.ErrorCode status = ICU.ErrorCode.ZERO_ERROR; ICU.ErrorCode status = ICU.ErrorCode.ZERO_ERROR;
ICU.PrepProfile profile = ICU.PrepProfile.openByType(type, ref status); ICU.PrepProfile profile = ICU.PrepProfile.openByType(type, ref status);
long src16_length = 0; ICU.String src16 = ICU.String.from_string(src);
string16 src16 = src.to_utf16(-1, null, out src16_length); int32 dest16_capacity = src16.len() * 2 + 1;
ICU.Char[] dest16 = new ICU.Char[src16_length * 2]; ICU.String dest16 = ICU.String.alloc(dest16_capacity);
ICU.ParseError error; long dest16_length = profile.prepare(src16, src16.len(), dest16, dest16_capacity, strict ? ICU.PrepOptions.DEFAULT : ICU.PrepOptions.ALLOW_UNASSIGNED, out error, ref status);
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);
if (status == ICU.ErrorCode.INVALID_CHAR_FOUND) { if (status == ICU.ErrorCode.INVALID_CHAR_FOUND) {
throw new InvalidJidError.INVALID_CHAR("Found invalid character"); 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) { } else if (status != ICU.ErrorCode.ZERO_ERROR) {
throw new InvalidJidError.UNKNOWN(@"Unknown error: $(status.errorName())"); throw new InvalidJidError.UNKNOWN(@"Unknown error: $(status.errorName())");
} else if (dest16_length < 0) { } else if (dest16_length < 0) {
throw new InvalidJidError.UNKNOWN("Unknown error"); throw new InvalidJidError.UNKNOWN("Unknown error");
} }
return ((string16) dest16).to_utf8(dest16_length, null, null); return dest16.to_string();
} catch (ConvertError e) { } catch (ConvertError e) {
throw new InvalidJidError.INVALID_CHAR(@"Conversion error: $(e.message)"); throw new InvalidJidError.INVALID_CHAR(@"Conversion error: $(e.message)");
} }

View file

@ -10,6 +10,7 @@ class JidTest : Gee.TestCase {
add_test("jid_valid_full", () => { test_jid_valid("test@example.com/test"); }); add_test("jid_valid_full", () => { test_jid_valid("test@example.com/test"); });
// These should not be valid in "strict-mode" // 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_local", () => { test_jid_valid("😅@example.com"); });
add_test("jid_valid_emoji_resource", () => { test_jid_valid("test@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"); }); add_test("jid_valid_emoji_domain", () => { test_jid_valid("test@😅.com"); });
@ -66,7 +67,7 @@ class JidTest : Gee.TestCase {
var t2 = new Jid(jid2); var t2 = new Jid(jid2);
fail_if_not_eq_str(t1.to_string(), t2.to_string()); fail_if_not_eq_str(t1.to_string(), t2.to_string());
} catch (Error e) { } 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); var t1 = new Jid(jid1);
fail_if_not_eq_str(t1.to_string(), jid2); fail_if_not_eq_str(t1.to_string(), jid2);
} catch (Error e) { } 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); var t2 = new Jid(jid2);
fail_if_eq_str(t1.to_string(), t2.to_string()); fail_if_eq_str(t1.to_string(), t2.to_string());
} catch (Error e) { } catch (Error e) {
fail_if_reached(); fail_if_reached(@"Throws $(e.message)");
} }
} }
} }

View file

@ -1,15 +1,57 @@
[CCode (cprefix="u_")]
namespace ICU { namespace ICU {
[CCode (cname = "UChar")] [CCode (cname = "UChar")]
[IntegerType (rank = 5, min = 0, max = 65535)] [IntegerType (rank = 5, min = 0, max = 65535)]
struct Char {} 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")] [CCode (cname = "UErrorCode", cprefix = "U_", cheader_filename = "unicode/utypes.h")]
enum ErrorCode { enum ErrorCode {
ZERO_ERROR, ZERO_ERROR,
INVALID_CHAR_FOUND, INVALID_CHAR_FOUND,
INDEX_OUTOFBOUNDS_ERROR, INDEX_OUTOFBOUNDS_ERROR,
BUFFER_OVERFLOW_ERROR, BUFFER_OVERFLOW_ERROR,
STRINGPREP_PROHIBITED_ERROR,
UNASSIGNED_CODE_POINT_FOUND, UNASSIGNED_CODE_POINT_FOUND,
IDNA_STD3_ASCII_RULES_ERROR IDNA_STD3_ASCII_RULES_ERROR
; ;
@ -21,7 +63,7 @@ enum ErrorCode {
public bool is_failure(); 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 {} struct ParseError {}
[CCode (cname = "UStringPrepProfile", cprefix = "usprep_", free_function = "usprep_close", cheader_filename = "unicode/usprep.h")] [CCode (cname = "UStringPrepProfile", cprefix = "usprep_", free_function = "usprep_close", cheader_filename = "unicode/usprep.h")]
@ -29,7 +71,7 @@ struct ParseError {}
class PrepProfile { class PrepProfile {
public static PrepProfile open(string path, string file_name, ref ErrorCode status); public static PrepProfile open(string path, string file_name, ref ErrorCode status);
public static PrepProfile openByType(PrepType type, 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_")] [CCode (cname = "UStringPrepProfileType", cprefix = "USPREP_")]
enum PrepType { enum PrepType {