@@ -1662,3 +1662,46 @@ func TestNewSMTPServiceWithOAuth2(t *testing.T) {
16621662 assert .Equal (t , log , service .logger )
16631663 assert .Equal (t , mockProvider , service .oauth2Provider )
16641664}
1665+
1666+ // TestDotStuffingIntegration tests that emails with URLs crossing line boundaries
1667+ // are properly handled by the SMTP sending logic using textproto.DotWriter.
1668+ // This verifies the fix for the URL corruption bug where dots after soft line
1669+ // breaks in quoted-printable encoded content were being stripped by SMTP servers.
1670+ func TestDotStuffingIntegration (t * testing.T ) {
1671+ // Create a mock server that captures the raw message data
1672+ // The server starts serving automatically in newMockSMTPServer
1673+ server := newMockSMTPServer (t , true )
1674+ defer server .Close ()
1675+
1676+ // Wait for server to start
1677+ time .Sleep (50 * time .Millisecond )
1678+
1679+ host , portStr , _ := net .SplitHostPort (server .Addr ())
1680+ port := 0
1681+ fmt .Sscanf (portStr , "%d" , & port )
1682+
1683+ // Create a message with content that would trigger the bug:
1684+ // After quoted-printable encoding, a soft line break before ".com" results in
1685+ // a line starting with ".com". Without proper dot-stuffing, SMTP servers
1686+ // strip the leading dot, corrupting URLs.
1687+ //
1688+ // The test message simulates this by having a line that starts with a period.
1689+ testMessage := []byte ("Content-Type: text/html\r \n \r \n " +
1690+ "Line one\r \n " +
1691+ ".com/path/to/image.png\r \n " + // This line starts with a dot
1692+ "Line three\r \n " )
1693+
1694+ err := sendRawEmail (
host ,
port ,
"user" ,
"pass" ,
false ,
"[email protected] " , []
string {
"[email protected] " },
testMessage )
1695+ require .NoError (t , err )
1696+
1697+ // Verify the message was received
1698+ require .Len (t , server .messages , 1 )
1699+ receivedData := string (server .messages [0 ].data )
1700+
1701+ // The SMTP server should have received the message with the dot doubled (dot-stuffed).
1702+ // textproto.DotWriter automatically handles this per RFC 5321.
1703+ // When the server receives "..com", it strips one dot to get ".com" back.
1704+ // Our mock server stores the raw data as received (with dot-stuffing applied).
1705+ assert .Contains (t , receivedData , "..com/path/to/image.png" ,
1706+ "Expected dot-stuffed content (double dot) but got: %s" , receivedData )
1707+ }
0 commit comments