Skip to content

Commit fe5e21f

Browse files
committed
Enforce ASCII-only constraint per RFC 5322 specification
RFC 5322 (Internet Message Format) is strictly an ASCII-only specification. This change ensures compliance by validating that email addresses contain only ASCII characters. Changes: - Add ValidationError.nonASCIICharacters case with error description - Validate ASCII-only in LocalPart.init using String.asciiBytes from INCITS_4_1986 - Replace manual character classification with INCITS_4_1986 utilities (isASCIILetter, isASCIIDigit, isASCIIWhitespace) in String.swift and [UInt8].swift - Add import INCITS_4_1986 to String.swift This prevents non-ASCII characters from being accepted in RFC 5322 contexts, ensuring compliance with the specification and preventing potential issues with email systems that expect ASCII-only message format.
1 parent ae7db4f commit fe5e21f

File tree

3 files changed

+30
-3
lines changed

3 files changed

+30
-3
lines changed

Sources/RFC_5322/RFC_5322.EmailAddress.swift

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,11 @@ extension RFC_5322.EmailAddress {
110110

111111
/// Initialize with a string
112112
public init(_ string: String) throws {
113+
// RFC 5322 is ASCII-only - validate before processing
114+
guard string.asciiBytes != nil else {
115+
throw ValidationError.nonASCIICharacters
116+
}
117+
113118
// Check overall length first
114119
guard string.count <= Limits.maxLength else {
115120
throw ValidationError.localPartTooLong(string.count)
@@ -177,7 +182,7 @@ extension RFC_5322.EmailAddress {
177182

178183
extension RFC_5322.EmailAddress {
179184
/// Just the email address part without display name
180-
public var addressValue: String {
185+
public var address: String {
181186
"\(localPart)@\(domain.name)"
182187
}
183188
}
@@ -191,6 +196,26 @@ extension RFC_5322.EmailAddress {
191196
case localPartTooLong(_ length: Int)
192197
case consecutiveDots
193198
case leadingOrTrailingDot
199+
case nonASCIICharacters
200+
201+
public var errorDescription: String? {
202+
switch self {
203+
case .missingAtSign:
204+
return "Email address must contain @"
205+
case .invalidDotAtom:
206+
return "Invalid local-part format (before @)"
207+
case .invalidQuotedString:
208+
return "Invalid quoted string format in local-part"
209+
case .localPartTooLong(let length):
210+
return "Local-part length \(length) exceeds maximum of \(Limits.maxLength)"
211+
case .consecutiveDots:
212+
return "Local-part cannot contain consecutive dots"
213+
case .leadingOrTrailingDot:
214+
return "Local-part cannot start or end with a dot"
215+
case .nonASCIICharacters:
216+
return "RFC 5322 email addresses must contain only ASCII characters"
217+
}
218+
}
194219
}
195220
}
196221

Sources/RFC_5322/String.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
// Type conversions for RFC 5322 Message
66
//
77

8+
import INCITS_4_1986
9+
810
extension String {
911
/// Creates RFC 5322 message string from a Message
1012
///
@@ -28,7 +30,7 @@ extension String {
2830
if let name = emailAddress.displayName {
2931
// Quote the display name if it contains special characters or non-ASCII
3032
let needsQuoting = name.contains(where: {
31-
!$0.isLetter && !$0.isNumber && !$0.isWhitespace || $0.asciiValue == nil
33+
!$0.isASCIILetter && !$0.isASCIIDigit && !$0.isASCIIWhitespace || $0.asciiValue == nil
3234
})
3335
let quotedName = needsQuoting ? "\"\(name)\"" : name
3436
self = "\(quotedName) <\(emailAddress.localPart)@\(emailAddress.domain.name)>" // Exactly one space before angle bracket

Sources/RFC_5322/[UInt8].swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ extension [UInt8] {
3535
if let displayName = emailAddress.displayName {
3636
// Check if quoting is needed
3737
let needsQuoting = displayName.contains(where: {
38-
!$0.isLetter && !$0.isNumber && !$0.isWhitespace || $0.asciiValue == nil
38+
!$0.isASCIILetter && !$0.isASCIIDigit && !$0.isASCIIWhitespace || $0.asciiValue == nil
3939
})
4040

4141
if needsQuoting {

0 commit comments

Comments
 (0)