X-Git-Url: http://git.megabrutal.com/?p=mgsmtp.git;a=blobdiff_plain;f=Relay.pas;fp=Relay.pas;h=713db8d8641af8593d6703d4a9999bd6c548efd1;hp=cea792d52c5237a026db4f17fb390c413e16fd1f;hb=7e2904763170597795d8c117d938cb6e4b86b3b7;hpb=6f8e6026b5d35a3fa41a124032caed6621a9f86e diff --git a/Relay.pas b/Relay.pas index cea792d..713db8d 100644 --- a/Relay.pas +++ b/Relay.pas @@ -1,6 +1,6 @@ { MegaBrutal's SMTP Server (MgSMTP) - Copyright (C) 2010 MegaBrutal + Copyright (C) 2010-2015 MegaBrutal This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by @@ -88,6 +88,7 @@ type protected FEnvelope: TEnvelope; FEMailProperties: TEMailProperties; + FTransactionComplete: boolean; FRoutingTarget: TRoutingTarget; RoutingTable: TRoutingTable; TCP: TTCPRFCConnection; @@ -99,6 +100,7 @@ type public property Envelope: TEnvelope read FEnvelope; property EMailProperties: TEMailProperties read FEMailProperties; + property IsTransactionComplete: boolean read FTransactionComplete; property RelayServerName: string read GetRelayServerName; property RelayServerPort: integer read GetRelayServerPort; function OpenConnection: boolean; @@ -171,6 +173,7 @@ begin Self.RoutingTable:= RoutingTable; FEnvelope:= Envelope; FEMailProperties:= EMailProperties; + FTransactionComplete:= false; FRoutingTarget:= RoutingTable.GetRouteInfo(Envelope.RelayHost); Response:= TRFCReply.Create; FillChar(SMTPExtensions, SizeOf(TSMTPExtensions), #0); @@ -302,10 +305,8 @@ end; procedure TRelayer.AdministerMassFailure(var Result: boolean); -var i: integer; begin - for i:= 0 to Envelope.GetNumberOfRecipients - 1 do - Envelope.SetRecipientData(i, Response.GetNumericCode, Response.ReplyText.Text); + Envelope.SetAllRecipientData(Response.GetNumericCode, Response.ReplyText.Text); Result:= false; end; @@ -341,12 +342,13 @@ begin end else Result:= false; MXList.Free; + FTransactionComplete:= false; end; function TRelayer.Greet: boolean; { This function reads and checks the relay server's greeting. + Then identifies this server with an EHLO. Then, if necessary, authenticates at the connected relay server. - Then identifies this server with a HELO. The function returns true, if the authentication and the EHLO command were successful. } var @@ -360,7 +362,9 @@ begin Response.Clear; AdministerMassFailure(Result); TCP.ReadResponse(Response); - if Response.GetNumericCode = SMTP_R_READY then begin + + { Expect 2xx reply. } + if (Response.GetNumericCode div 100) = 2 then begin TCP.SendCommand(SMTP_C_EHLO, MainServerConfig.Name); TCP.ReadResponse(Response); @@ -426,20 +430,32 @@ function TRelayer.SendEnvelope: boolean; { Sends the envelope (that is the return-path and the recipient addresses). The function returns true, if the MAIL command were successful, and the relay server has accepted at least one of the recipient addresses. + This function returns false if a null reply is read, which is considered + as protocol violation. This function is aware of the SMTP extension, named PIPELINING. If it's supported by the server, we send RCPT commands stuffed, without waiting for a response. After all RCPTs are sent, we check all responses. } var - i, c: integer; Prms: string; + i, c: integer; UltimateFail: boolean; Prms: string; procedure ProcessRCPTResponse; begin TCP.ReadResponse(Response); - if Response.GetNumericCode = SMTP_R_OK then Inc(c); + { If we get an "OK" reply code, we increase the count of sucessful + recipients. } + if Response.GetNumericCode = SMTP_R_OK then Inc(c) + { Response code 0 is non-existent in the SMTP protocol. + If we receive something we _perceive_ as 0, then it is likely + that something seriously went wrong. MgSMTP shouldn't treat + this condition as permanent. } + else if Response.GetNumericCode = 0 then UltimateFail:= true; Envelope.SetRecipientData(i, Response.GetNumericCode, Response.ReplyText.Text); end; begin + { The MAIL command is considered the beginning of the transaction. } + FTransactionComplete:= false; + UltimateFail:= false; Response.Clear; Prms:= 'FROM:<' + Envelope.ReturnPath + '>'; @@ -463,8 +479,16 @@ begin for i:= 0 to Envelope.GetNumberOfRecipients - 1 do ProcessRCPTResponse; - Result:= c <> 0; + Result:= (c <> 0) and (not UltimateFail); + + { If there are no accepted recipients, and no protocol failure is + discovered, we can't send the DATA command, and practically we + can do nothing more for this transaction. Therefore it is + considered complete by protocol. } + FTransactionComplete:= (c = 0) and (not UltimateFail); + if not Result then begin + { Either way, try to reset SMTP state in case of failure. } TCP.SendCommand(SMTP_C_RSET); TCP.ReadResponse(Response); end; @@ -499,7 +523,12 @@ var i: integer; begin TCP.WriteLn('.'); TCP.ReadResponse(Response); + + { Mark the transaction complete, if we have a valid response. } + FTransactionComplete:= Response.GetNumericCode <> 0; + for i:= 0 to Envelope.GetNumberOfRecipients - 1 do begin + { Set status code for recipients those were accepted in the envelope stage: } if Envelope.GetRecipient(i).Data = SMTP_R_OK then Envelope.SetRecipientData(i, Response.GetNumericCode, Response.ReplyText.Text); end;