07-09-2024, 11:50 PM
(07-09-2024, 06:04 PM)Justin Case Wrote: but there was some architectural bug in it involving the dots - from memory it needed two dots on some lines for some reason..
That is simply how the SMTP protocol works. Inside the DATA command that the client sends to the server to deliver the email content, any line of text that begins with a dot must be escaped with an extra dot, because the command is terminated by a single dot on a line by itself. But TIdSMTPServer handles those dots for you, you shouldn't need to deal with them manually on the server side.
(07-09-2024, 06:04 PM)Justin Case Wrote: I'm using 10.2.3
Why? That is a very old version. The current version is 10.6.3.3, which you can download from Indy's GitHub repo.
(07-09-2024, 06:04 PM)Justin Case Wrote: So with the online documentation being down on ww2.indyproject.org, I'm kind of flying blind.
This blog post has some links to archived copies of the documentation. However, even so, the documentation was never updated with all of the latest developments in later Indy 10 releases, but the basics should be there.
(07-09-2024, 06:04 PM)Justin Case Wrote: To get started, I double clicked all the events in object inspector and put in a ShowMessage() so that i knew what order they fire in (yes I had exceptions regarding drawing - but it got me what i needed after multiple program resets).
Like all of Indy's servers, TIdSMTPServer is multi-threaded. Its events are fired in worker threads, not in the main UI thread. But ShowMessage() is not thread-safe, so don't use it outside of the main UI thread. You should instead use the Win32 MessageBox() (which is thread-safe), or better use OutputDebugString() and then view the messages in the IDE's debug log, or in SysInternals DebugView when running your server outside of the IDE.
(07-09-2024, 06:04 PM)Justin Case Wrote: So I have these in this order (I'm manually typing these in - can't copy from the VM - sorry for any typo's but you'll get the idea):
You are missing a few events in that list (perils of typing manually).
You should be getting OnBeforeCommandHandler and OnAfterCommandHandler events for every command the client sends.
And you should be getting a 2nd OnSPFCheck event before the OnMailFrom event.
But that is besides the point...
(07-09-2024, 06:04 PM)Justin Case Wrote: My client (Outlook express - yes it's old but ok for testing) sends my machine name (Asuras) which obviously isn't a domain and as it's a SMTP client, not another SMTP server, that obviously won't resolve.
If it were a proper hostname, it should resolve to an actual IP on their ISP.
(07-09-2024, 06:04 PM)Justin Case Wrote: I suspect a connecting server will send a domain?
A relaying SMTP server should send its own DNS name, just like any other client.
(07-09-2024, 06:04 PM)Justin Case Wrote: So how do I proceed with this?
You could simply ignore it. You don't have to implement SPF if you don't want to. The default action is spfNeutral if you don't assign an OnSPFCheck handler. Also, Indy does not implement the actual SPF validation itself, so you would have to perform the necessary DNS validation yourself in the OnSPFCheck event. If you are not prepared to do that, then don't use that event. Otherwise, you could just validate what you can and reject what you can't, or be neutral about it.
(07-09-2024, 06:04 PM)Justin Case Wrote: obviously if another server connects then the SPFCheck is a great idea but if it's a client then I'm in a fix and it will fail (as a bodge I'm using "If Pos('.', ADomain) then.." to test for a domain).
Indy 10 has IsDomain() and IsFQDN() functions in the IdGlobalProtocols unit which may help you.
In any case, you could always just reverse-DNS the client's IP address to get its hostname. You can use Indy's GStack.HostByAddress() method for that purpose.
(07-09-2024, 06:04 PM)Justin Case Wrote: Will IdSMTPServer cause a disconnect / client abort if I use spfNone / spfNeutral or will it just continue?
It will continue, as those two values are treated as success cases, as are spfPass and spfSoftFail too. Only spfFail, spfTempError, and spfPermError will fail the current SMTP command being validated. The connection will not be closed either way. The client can send further commands if it wants to, though it will likely disconnect if the validating command fails.
(07-09-2024, 06:04 PM)Justin Case Wrote: EG I'm with yahoo. If my email client were to connect to them, it would send "HELO/EHLO Asuras". Based on the indy logic, Asuras means nothing to Yahoo and so it would probably perform a spfCheck against my IP. My IP is part of my ISPs network and thus Yahoo has nothing to pass or fail me with until I login - but in the sequence above, that comes two events later.
The first OnSPFCheck event comes during the HELO/EHLO command. But there is another OnSPFCheck event that comes during the MAIL FROM command. So, you could pass the 1st check if you don't have enough information to perform the validation with, and then pass/fail the 2nd check as needed.
(07-09-2024, 06:04 PM)Justin Case Wrote: Also if a SMTP client connects, it has to send a username and password. How do SMTP servers communicate mail to each other without this?
Relaying between servers does not use authentication. They rely on SPF/DKIM/DMARC, whitelists/blacklists, secure tunnels, etc to verify each other and block out unwanted senders. SMTP servers that receive the initial email from users will handle authentication before accepting emails and beginning the relay process.
(07-09-2024, 06:04 PM)Justin Case Wrote: As the sequence above suggests it seems like a inbound connection has got to login first before it can specify the MailFrom details and trigger that event.
That is not correct. The only requirement TIdSMTPServer has for accepting a MAIL FROM command is that a previous HELO/EHLO command must have been accepted first. There is no authentication requirement because of relaying from other servers.
In your case, you are getting the OnUserLogin event triggered because your Outlook client is authenticating itself when sending an email.
Although you can't force authentication, you can have your OnMailFrom event reject the sender when AContext.LoggedIn is False. Or, you can have your OnSPFCheck event reject the sender when AContext.HELO or AContext.EHLO are True and AContext.LoggedIn is False.
(07-09-2024, 06:04 PM)Justin Case Wrote: How can I know in the UserLogin() event that the connecting user will be sending to a local address in order that I can set VAuthenticated := True ?
You can't, since that information is not known yet at that time. The OnUserLogin event only tells you who is authenticating with your server, nothing more. And the OnMailFrom event only tells you who is sending the email, nothing more.
(07-09-2024, 06:04 PM)Justin Case Wrote: those details don't come through until RcptTo()!!
Exactly true. They are stored in the AContext.RCTPList property, which you can then use during email processing in the OnBeforeMsg, OnReceived, and OnMsgReceive events.
(07-09-2024, 06:04 PM)Justin Case Wrote: AThread has vanished.. now we have AContext. Whats the difference here? Presumably they still run like threads?
Yes, Indy still uses threads. The data model was decoupled from the threading model in Indy 10 so client data could more easily be passed around between threads, as a client could be serviced by multiple threads, and a thread could service multiple clients. At least, that was the plan, but that proved to be a failed experiment and was later abandoned. In practice, each client is still serviced by 1 thread, and there is 1 TIdContext active per thread (however, threads can now be pooled and reused between connections, at least. Indy 9 did not support that).
(07-09-2024, 06:04 PM)Justin Case Wrote: Oh one other thing, whats IdSMTPRelay all about?
TIdSMTPRelay is an SMTP client similar to TIdSMTP, except that instead of connecting to a single SMTP server, it can connect to several servers.
TIdSMTP connects to 1 specified server and sends 1 email at a time, specifying all of the recipients of the email to that server. That server will receive the email and deliver it or relay it to other servers as needed.
TIdSMTPRelay, on the other hand, groups an email's recipients together by their domains, then does a DNS lookup on each domain to discover its registered SMTP server, and then connects to each server and delivers the email to only the recipients of that server.
Basically, TIdSMTP is what an end client would use, and TIdSMTPRelay is what a relaying server would use to forward emails to another server. Of course, nothing stops an end user from trying to use TIdSMTPRelay directly to bypss their own ISP, but they will likely be blocked by the receiving servers since they are not themselves a validated relay server.

