Use UTS46 instead of IDNA2003

This commit is contained in:
Marvin W 2019-12-23 04:01:25 +01:00
parent a0a956ee08
commit 53d467938c
No known key found for this signature in database
GPG key ID: 072E9235DB996F2A
3 changed files with 38 additions and 38 deletions

View file

@ -62,47 +62,31 @@ public class Jid {
} }
private static string idna_decode(string src) throws InvalidJidError { private static string idna_decode(string src) throws InvalidJidError {
try {
ICU.ErrorCode status = ICU.ErrorCode.ZERO_ERROR; ICU.ErrorCode status = ICU.ErrorCode.ZERO_ERROR;
long src16_length = 0; ICU.IDNAInfo info;
string16 src16 = src.to_utf16(-1, null, out src16_length); char[] dest = new char[src.length * 2];
ICU.Char[] dest16 = new ICU.Char[src16_length]; ICU.IDNA.openUTS46(ICU.IDNAOptions.DEFAULT, ref status).nameToUnicodeUTF8(src, -1, dest, out info, ref status);
ICU.ParseError error;
long dest16_length = ICU.IDNA.IDNToUnicode(src16, (int32) src16_length, dest16, dest16.length, ICU.IDNAOptions.DEFAULT, 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.ZERO_ERROR) { } else if (status.is_failure() || info.errors > 0) {
throw new InvalidJidError.UNKNOWN(@"Unknown error: $(status.errorName())"); 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);
} catch (ConvertError e) {
throw new InvalidJidError.INVALID_CHAR(@"Conversion error: $(e.message)");
} }
return (string) dest;
} }
private static void idna_verify(string src) throws InvalidJidError { private static void idna_verify(string src) throws InvalidJidError {
try {
ICU.ErrorCode status = ICU.ErrorCode.ZERO_ERROR; ICU.ErrorCode status = ICU.ErrorCode.ZERO_ERROR;
long src16_length = 0; ICU.IDNAInfo info;
string16 src16 = src.to_utf16(-1, null, out src16_length); char[] dest = new char[src.length * 2];
ICU.Char[] dest16 = new ICU.Char[256]; ICU.IDNA.openUTS46(ICU.IDNAOptions.DEFAULT, ref status).nameToASCII_UTF8(src, -1, dest, out info, ref status);
ICU.ParseError error;
long dest16_length = ICU.IDNA.IDNToASCII(src16, (int32) src16_length, dest16, dest16.length, ICU.IDNAOptions.DEFAULT, 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.ZERO_ERROR) { } else if (status.is_failure() || info.errors > 0) {
throw new InvalidJidError.UNKNOWN(@"Unknown error: $(status.errorName())"); throw new InvalidJidError.UNKNOWN(@"Unknown error: $(status.errorName())");
} else if (dest16_length < 0) {
throw new InvalidJidError.UNKNOWN("Unknown error");
}
} catch (ConvertError e) {
throw new InvalidJidError.INVALID_CHAR(@"Conversion error: $(e.message)");
} }
} }
private static string? prepare(string? src, ICU.PrepType type) 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.ErrorCode status = ICU.ErrorCode.ZERO_ERROR; ICU.ErrorCode status = ICU.ErrorCode.ZERO_ERROR;
@ -111,7 +95,7 @@ public class Jid {
string16 src16 = src.to_utf16(-1, null, out src16_length); string16 src16 = src.to_utf16(-1, null, out src16_length);
ICU.Char[] dest16 = new ICU.Char[src16_length * 2]; ICU.Char[] dest16 = new ICU.Char[src16_length * 2];
ICU.ParseError error; ICU.ParseError error;
long dest16_length = profile.prepare((ICU.Char*) src16, (int32) src16_length, dest16, dest16.length, 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.ZERO_ERROR) { } else if (status != ICU.ErrorCode.ZERO_ERROR) {

View file

@ -9,11 +9,11 @@ class JidTest : Gee.TestCase {
add_test("jid_valid_domain_with_resource", () => { test_jid_valid("example.com/test"); }); add_test("jid_valid_domain_with_resource", () => { test_jid_valid("example.com/test"); });
add_test("jid_valid_full", () => { test_jid_valid("test@example.com/test"); }); add_test("jid_valid_full", () => { test_jid_valid("test@example.com/test"); });
// Should those actually be valid? // These should not be valid in "strict-mode"
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_invalid_emoji_domain", () => { test_jid_invalid("test@😅.com"); });
add_test("jid_invalid_bidi_local", () => { test_jid_invalid("test@example.com"); }); add_test("jid_invalid_bidi_local", () => { test_jid_invalid("test@example.com"); });
add_test("jid_invalid_bidi_resource", () => { test_jid_invalid("test@example.com/test"); }); add_test("jid_invalid_bidi_resource", () => { test_jid_invalid("test@example.com/test"); });
add_test("jid_invalid_bidi_domain", () => { test_jid_invalid("test@example.com"); }); add_test("jid_invalid_bidi_domain", () => { test_jid_invalid("test@example.com"); });
@ -43,7 +43,7 @@ class JidTest : Gee.TestCase {
try { try {
new Jid(jid); new Jid(jid);
} catch (Error e) { } catch (Error e) {
fail_if_reached(); fail_if_reached(@"Throws $(e.message)");
} }
} }

View file

@ -15,6 +15,10 @@ enum ErrorCode {
; ;
[CCode (cname = "u_errorName")] [CCode (cname = "u_errorName")]
public unowned string errorName(); public unowned string errorName();
[CCode (cname = "U_SUCCESS")]
public bool is_success();
[CCode (cname = "U_FAILURE")]
public bool is_failure();
} }
[CCode (cname = "UErrorCode", cprefix = "U_", cheader_filename = "unicode/parseerr.h")] [CCode (cname = "UErrorCode", cprefix = "U_", cheader_filename = "unicode/parseerr.h")]
@ -42,8 +46,20 @@ enum PrepOptions {
[CCode (cname = "UIDNA", cprefix = "uidna_", free_function = "uidna_close", cheader_filename = "unicode/uidna.h")] [CCode (cname = "UIDNA", cprefix = "uidna_", free_function = "uidna_close", cheader_filename = "unicode/uidna.h")]
[Compact] [Compact]
class IDNA { class IDNA {
public static IDNA openUTS46(IDNAOptions options, ref ErrorCode status);
public static int32 IDNToUnicode(Char* src, int32 src_length, Char* dest, int32 dest_capacity, IDNAOptions options, out ParseError parse_error, ref ErrorCode status); public static int32 IDNToUnicode(Char* src, int32 src_length, Char* dest, int32 dest_capacity, IDNAOptions options, out ParseError parse_error, ref ErrorCode status);
public static int32 IDNToASCII(Char* src, int32 src_length, Char* dest, int32 dest_capacity, IDNAOptions options, out ParseError parse_error, ref ErrorCode status); public static int32 IDNToASCII(Char* src, int32 src_length, Char* dest, int32 dest_capacity, IDNAOptions options, out ParseError parse_error, ref ErrorCode status);
public int32 nameToUnicode(Char* src, int32 src_length, Char* dest, int32 dest_capacity, out IDNAInfo info, ref ErrorCode status);
public int32 nameToASCII(Char* src, int32 src_length, Char* dest, int32 dest_capacity, out IDNAInfo info, ref ErrorCode status);
public int32 nameToASCII_UTF8(string name, int32 name_length, char[] dest, out IDNAInfo info, ref ErrorCode status);
public int32 nameToUnicodeUTF8(string name, int32 name_length, char[] dest, out IDNAInfo info, ref ErrorCode status);
}
[CCode (cname = "UIDNAInfo", default_value = "UIDNA_INFO_INITIALIZER", has_type_id = false, cheader_filename = "unicode/uidna.h")]
struct IDNAInfo {
public static IDNAInfo INITIAL;
public uint32 errors;
public bool isTransitionalDifferent;
} }
[CCode (cname = "uint32_t", cprefix = "UIDNA_")] [CCode (cname = "uint32_t", cprefix = "UIDNA_")]