| ... | @@ -249,6 +249,24 @@ fn Validity_Criteria( | ... | @@ -249,6 +249,24 @@ fn Validity_Criteria( |
| 249 | var zwnj_state: enum { start, dual_joining, transparent1, zwnj, transparent2, right_joining } = .start; | 249 | var zwnj_state: enum { start, dual_joining, transparent1, zwnj, transparent2, right_joining } = .start; |
| 250 | var check_zwnj_state: ?bool = null; | 250 | var check_zwnj_state: ?bool = null; |
| 251 | | 251 | |
| | 252 | // 9. If CheckBidi, and if the domain name is a Bidi domain name, then the label must satisfy all six of the numbered conditions in RFC 5893 Section 2. |
| | 253 | // 9.1. The first character must be a character with Bidi property L, R, or AL. If it has the R or AL property, it is an RTL label; if it has the L property, it is an LTR label. |
| | 254 | // 9.2. In an RTL label, only characters with the Bidi properties R, AL, AN, EN, ES, CS, ET, ON, BN, or NSM are allowed. |
| | 255 | // 9.3. In an RTL label, the end of the label must be a character with Bidi property R, AL, EN, or AN, followed by zero or more characters with Bidi property NSM. |
| | 256 | // 9.4. In an RTL label, if an EN is present, no AN may be present, and vice versa. |
| | 257 | // 9.5. In an LTR label, only characters with the Bidi properties L, EN, ES, CS, ET, ON, BN, or NSM are allowed. |
| | 258 | // 9.6. In an LTR label, the end of the label must be a character with Bidi property L or EN, followed by zero or more characters with Bidi property NSM. |
| | 259 | var seen_en = false; |
| | 260 | var seen_an = false; |
| | 261 | var last_bidi_class: ucd.BidiClass = .NSM; |
| | 262 | if (CheckBidi) { |
| | 263 | switch (first_p[4]) { |
| | 264 | .L => {}, |
| | 265 | .R, .AL => {}, |
| | 266 | else => return error.IDNAFailure, |
| | 267 | } |
| | 268 | } |
| | 269 | |
| 252 | while (it.nextCodepointSlice()) |sl| { | 270 | while (it.nextCodepointSlice()) |sl| { |
| 253 | const cp = std.unicode.utf8Decode(sl) catch unreachable; | 271 | const cp = std.unicode.utf8Decode(sl) catch unreachable; |
| 254 | const status, _, _ = mappingRow(cp); | 272 | const status, _, _ = mappingRow(cp); |
| ... | @@ -293,10 +311,10 @@ fn Validity_Criteria( | ... | @@ -293,10 +311,10 @@ fn Validity_Criteria( |
| 293 | | 311 | |
| 294 | // 3. In addition, if UseSTD3ASCIIRules=true and the code point is an ASCII code point (U+0000..U+007F), then it must be a lowercase letter (a-z), a digit (0-9), or a hyphen-minus (U+002D). | 312 | // 3. In addition, if UseSTD3ASCIIRules=true and the code point is an ASCII code point (U+0000..U+007F), then it must be a lowercase letter (a-z), a digit (0-9), or a hyphen-minus (U+002D). |
| 295 | // (Note: This excludes uppercase ASCII A-Z which are mapped in UTS #46 and disallowed in IDNA2008.) | 313 | // (Note: This excludes uppercase ASCII A-Z which are mapped in UTS #46 and disallowed in IDNA2008.) |
| 296 | if (UseSTD3ASCIIRules and std.ascii.isAscii(sl[0])) { | 314 | if (UseSTD3ASCIIRules and std.ascii.isAscii(sl[0])) blk: { |
| 297 | if (std.ascii.isLower(sl[0])) continue; | 315 | if (std.ascii.isLower(sl[0])) break :blk; |
| 298 | if (std.ascii.isDigit(sl[0])) continue; | 316 | if (std.ascii.isDigit(sl[0])) break :blk; |
| 299 | if (sl[0] == '-') continue; | 317 | if (sl[0] == '-') break :blk; |
| 300 | return error.IDNAFailure; | 318 | return error.IDNAFailure; |
| 301 | } | 319 | } |
| 302 | | 320 | |
| ... | @@ -380,61 +398,49 @@ fn Validity_Criteria( | ... | @@ -380,61 +398,49 @@ fn Validity_Criteria( |
| 380 | } | 398 | } |
| 381 | point_2before = point_before; | 399 | point_2before = point_before; |
| 382 | point_before = p; | 400 | point_before = p; |
| | 401 | |
| | 402 | if (CheckBidi) { |
| | 403 | switch (first_p[4]) { |
| | 404 | .L => { |
| | 405 | switch (p[4]) { |
| | 406 | .L, .EN, .ES, .CS, .ET, .ON, .BN => |cl| last_bidi_class = cl, |
| | 407 | .NSM => {}, |
| | 408 | else => return error.IDNAFailure, |
| | 409 | } |
| | 410 | }, |
| | 411 | .R, .AL => { |
| | 412 | switch (p[4]) { |
| | 413 | .R, .AL, .AN, .EN, .ES, .CS, .ET, .ON, .BN => |cl| last_bidi_class = cl, |
| | 414 | .NSM => {}, |
| | 415 | else => return error.IDNAFailure, |
| | 416 | } |
| | 417 | if (p[4] == .EN and seen_an) return error.IDNAFailure; |
| | 418 | if (p[4] == .EN) seen_en = true; |
| | 419 | if (p[4] == .AN and seen_en) return error.IDNAFailure; |
| | 420 | if (p[4] == .AN) seen_an = true; |
| | 421 | }, |
| | 422 | else => unreachable, |
| | 423 | } |
| | 424 | } |
| 383 | } | 425 | } |
| 384 | if (CheckJoiners) { | 426 | if (CheckJoiners) { |
| 385 | if (check_zwnj_state == true and zwnj_state != .right_joining) return error.IDNAFailure; | 427 | if (check_zwnj_state == true and zwnj_state != .right_joining) return error.IDNAFailure; |
| 386 | } | 428 | } |
| 387 | | | |
| 388 | // 9. If CheckBidi, and if the domain name is a Bidi domain name, then the label must satisfy all six of the numbered conditions in RFC 5893 Section 2. | | |
| 389 | if (CheckBidi) { | 429 | if (CheckBidi) { |
| 390 | // 1. The first character must be a character with Bidi property L, R, or AL. If it has the R or AL property, it is an RTL label; if it has the L property, it is an LTR label. | | |
| 391 | // 2. In an RTL label, only characters with the Bidi properties R, AL, AN, EN, ES, CS, ET, ON, BN, or NSM are allowed. | | |
| 392 | // 3. In an RTL label, the end of the label must be a character with Bidi property R, AL, EN, or AN, followed by zero or more characters with Bidi property NSM. | | |
| 393 | // 4. In an RTL label, if an EN is present, no AN may be present, and vice versa. | | |
| 394 | // 5. In an LTR label, only characters with the Bidi properties L, EN, ES, CS, ET, ON, BN, or NSM are allowed. | | |
| 395 | // 6. In an LTR label, the end of the label must be a character with Bidi property L or EN, followed by zero or more characters with Bidi property NSM. | | |
| 396 | switch (first_p[4]) { | 430 | switch (first_p[4]) { |
| 397 | .L => { | 431 | .L => { |
| 398 | it.i = 0; | | |
| 399 | var last_bidi_class: ucd.BidiClass = .NSM; | | |
| 400 | while (it.nextCodepointSlice()) |sl| { | | |
| 401 | const cp = std.unicode.utf8Decode(sl) catch unreachable; | | |
| 402 | const point = ucd.unicode_data.find(cp); | | |
| 403 | switch (point[4]) { | | |
| 404 | .L, .EN, .ES, .CS, .ET, .ON, .BN => |cl| last_bidi_class = cl, | | |
| 405 | .NSM => {}, | | |
| 406 | else => return error.IDNAFailure, | | |
| 407 | } | | |
| 408 | } | | |
| 409 | switch (last_bidi_class) { | 432 | switch (last_bidi_class) { |
| 410 | .L, .EN => {}, | 433 | .L, .EN => {}, |
| 411 | else => return error.IDNAFailure, | 434 | else => return error.IDNAFailure, |
| 412 | } | 435 | } |
| 413 | }, | 436 | }, |
| 414 | .R, .AL => { | 437 | .R, .AL => { |
| 415 | it.i = 0; | | |
| 416 | var seen_en = false; | | |
| 417 | var seen_an = false; | | |
| 418 | var last_bidi_class: ucd.BidiClass = .NSM; | | |
| 419 | while (it.nextCodepointSlice()) |sl| { | | |
| 420 | const cp = std.unicode.utf8Decode(sl) catch unreachable; | | |
| 421 | const point = ucd.unicode_data.find(cp); | | |
| 422 | switch (point[4]) { | | |
| 423 | .R, .AL, .AN, .EN, .ES, .CS, .ET, .ON, .BN => |cl| last_bidi_class = cl, | | |
| 424 | .NSM => {}, | | |
| 425 | else => return error.IDNAFailure, | | |
| 426 | } | | |
| 427 | if (point[4] == .EN and seen_an) return error.IDNAFailure; | | |
| 428 | if (point[4] == .EN) seen_en = true; | | |
| 429 | if (point[4] == .AN and seen_en) return error.IDNAFailure; | | |
| 430 | if (point[4] == .AN) seen_an = true; | | |
| 431 | } | | |
| 432 | switch (last_bidi_class) { | 438 | switch (last_bidi_class) { |
| 433 | .R, .AL, .EN, .AN => {}, | 439 | .R, .AL, .EN, .AN => {}, |
| 434 | else => return error.IDNAFailure, | 440 | else => return error.IDNAFailure, |
| 435 | } | 441 | } |
| 436 | }, | 442 | }, |
| 437 | else => return error.IDNAFailure, | 443 | else => unreachable, |
| 438 | } | 444 | } |
| 439 | } | 445 | } |
| 440 | } | 446 | } |