The SMTP conversation
Send message data
Enter DATA mode, terminate a message correctly, and understand why lines beginning with a dot are escaped.
The envelope is built. At least one recipient said 250. Now the client sends DATA, and the server answers 354, which means “go ahead, I’m listening”.
From this point the client sends the actual message: headers, a blank line, the body. The server doesn’t interpret any of it as commands. It just collects lines.
How the message ends
The client marks the end with a line containing only a dot. On the wire that’s <CRLF>.<CRLF>.
This raises a question. What if a real line in my message starts with a dot? Say I write .gitignore is missing at the start of a line. The server would think the message ended there.
The fix is dot-stuffing. The client adds a second dot in front of any line that starts with one. The server removes it on receipt.
Message line: .gitignore is missing
Wire line: ..gitignore is missing
Terminator: .
The stored message has one dot again. The terminator line is not part of the message at all. It’s SMTP framing.
Every SMTP library does this for you. The mistake I see is application code adding the extra dot itself, so the stored message ends up with two. Let the library handle the wire format.
The reply that matters most
After the terminator, the server sends the result for the whole transaction:
C: .
S: 250 2.0.0 Ok: queued as 4Xk2Qs1Pz7z3
This 250 is the important one. It means the server accepted responsibility for the message. The queue ID at the end is what you’ll search for in the logs.
The server might also reject here. A 552 5.3.4 Message too large after DATA means the content was refused, even though every recipient was accepted earlier. The client must read this reply before it deletes its queued copy or reuses the connection.
The uncertain disconnect
Here’s a realistic failure. The client sends the terminator, then the connection drops before any reply arrives.
Did the server get the message or not? The client has no idea. Maybe the server committed it a millisecond before the network died.
The safe choice is to retry, which can produce a duplicate. This is an at-least-once problem, and email accepts it. Don’t rely on Message-ID as a perfect deduplication key, but it does help you spot repeats in the mailbox.
Try writing a five-line message where one line starts with a dot. Write the stored form, the wire form, and the terminator as three separate blocks.
Lesson completed