Sending mail via DNSMadeEasy

I am trying to send an email out using a dedicated SMTP service called DNSMadeEasy. Unfortunately, I can’t get it to go. Any ideas on how to remedy this or diagnose the issue. This is using xojo 2024r1.1.

Note that we added a message ID because in AP1 we added it in order to get the mail sent.

We use DNSMadeEasy (a paid service) so that our users can submit Feedback requests via email without having to configure their local SMTP server.

Code fragment as follows:

mailsocket = New SMTPSecureSocket
mailsocket.Address = "smtp1.dnsmadeeasy.com"
mailsocket.Port = 7725
MailSocket.SSLConnectionType = SSLSocket.SSLConnectionTypes.TLSv12
MailSocket.SMTPConnectionType = SMTPSecureSocket.SMTPConnectionTypes.SSLTLS
MailSocket.SSLEnabled = True
mailsocket.Username = "my username"
mailsocket.Password = "my password"

var cOSVersion As String = GetOSVersioin()
var uuidCurrent as New UUIDMBS
var cUUID as String = StrTran(StrTran(uuidCurrent.ValueFormattedString,"{",""),"}","")
var messageID as String = "<"+cUUID+"@smtp1.dnsmadeeasy.com>"
mail.Headers.AppendHeader("Message-ID", messageID)
mail.fromAddress = """" + tfName.Text.Trim + """" + "<feedback@smtp1.dnsmadeeasy.com>"
mail.Headers.AddHeader("Reply-To",tfEmail.Text.Trim)
mail.Subject = tfSubject.Text
mail.FromAddress = tfEmail.Text.Trim

What was the result when you tried to run that code? Did you get any error codes or messages?

No error message using a try…catch…entry

mailsocket.Messages.Add(mail)

try 
  mailsocket.SendMail
  call MsgOK("EMAIL SENT!","Your password reset request email was successfully sent."+EOL(2)+_
  "An email reply from PRO-WARE, LLC will be sent to the primary email address associated with your account and will contain a password reset code.  You will need to "+_
  "enter the reset code as the password to access "+app.kAppName+".")
  self.Close
catch Error As RuntimeException
  call MsgOK("ERROR ENCOUNTERED!","An error was encountered when attempting to send your email request."+EOL(2)+_
  "Error number:  "+Error.ErrorNumber.ToString+"     Error message:  "+Error.Message+EOL(2)+_
  "You should contact PRO-WARE, LLC at 402.861.8800 to obtain a password reset code.")
  self.Close
end

Where in the code do you send the email? Are you sure that the port is okay? Have you tried different SSL versions?

I tried TSLv1 with no luck. Ports you can use is one of the ones listed on the DNSMadeEasy and the same one we used in AP1.

Try using CURL where you get more error information.

Do you have the socket in a window where you get an error event? The RuntimeException doesn’t sound right for the SMTP socket.

If you have the MBS plugins they have a CURL plugin and there are example projects in the MBS folder under Examples/CURL/Send Email. I have used code from the examples with great success. I use CURL in a thread:
image

SendEmailThread.Thread.Run

LogTransactions("Email Task Started")
DoUpload

SendEmailThread.DoUpload

Private Sub DoUpload()
  Var e as integer
  
  emailCURL = New CURLSMBS
  
  // this is the file content here:
  emailCURL.InputData = UploadData
  
  Var tos(-1) as string
  tos = Split(OptionMailRecipients,",")
  
  
  'emailCURL.OptionUseSSL = emailCURL.kFTPSSL_ALL // FTPSSL is the property name, but it's SSL for FTP, SMTP, POP3, IMAP etc.
  
  // as we don't use certificate here, we simply disable verification
  
  'emailCURL.OptionSSLVerifyPeer = 0
  'emailCURL.OptionSSLVerifyHost = 0
  'emailCURL.OptionSSLVersion = emailCURL.kSSLVersionDefault
  'emailCURL.OptionSSLSessionIDCache = false
  
  // Port 465 for SMTPS over SSL
  // Port 587 or 25 for SMTP without SSL
  
  emailCURL.OptionMailFrom = OptionMailFrom
  emailCURL.SetOptionMailRecipients tos
  emailCURL.OptionUsername = OptionUsername
  emailCURL.OptionPassword = OptionPassword
  emailCURL.OptionURL = OptionURL
  emailCURL.OptionUpload=true
  emailCURL.YieldTime = true
  emailCURL.OptionVerbose = true
  emailCURL.CollectOutputData = true
  emailCURL.CollectDebugData = true
  
  
  System.DebugLog "URL: " + emailCURL.OptionURL
  System.DebugLog "From: " + emailCURL.OptionMailFrom
  System.DebugLog "Username: " + emailCURL.OptionUsername
  System.DebugLog "Password: " + emailCURL.OptionPassword
  
  e=emailCURL.Perform
  
  LogTransactions("Last Email Error Message: " + emailCURL.LasterrorMessage + " - " + emailCURL.LasterrorText)
  LogTransactions("Email Thread Complete")
  
End Sub

I use the following Method with a SMTPSocket called EmailSocket:
Method: SendEmail

Public Sub SendEmail(msg As String, subj As String, msgtype As string, sendtoadd As String)
  // SendEmail(msg As String, subj As String, msgtype As string, sendtoadd As String)
  
  // sends an email to the specified address
  Var i As Integer
  Var currsec As Date
  Var timeout As Integer
  Var mail as EmailMessage
  Var msgbody,recipients(-1) as string
  Var emailSocket As EmailSocket
  
  If msgtype = "html" Then
    //compose email body
    msgbody = "<!DOCTYPE html PUBLIC ""-//W3C//DTD XHTML 1.0 Transitional//EN""  ""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"" >" + EndOfLine _
    + "<html xmlns=""http://www.w3.org/1999/xhtml"">" + EndOfLine _
    + "<head>" + EndOfLine _
    + "<meta http-equiv=""Content-Type"" content=""text/html; charset=utf-8"" />" + EndOfLine _
    + "<title>" + subj + "</title>" + EndOfLine _
    + "<style type=""text/css"">" + EndOfLine _
    + "body,td,th {" + EndOfLine _
    + "font-family: Calibri;" + EndOfLine _
    + "font-size: 12pt;" + EndOfLine _
    + "}" + EndOfLine _
    + "</style>" + EndOfLine _
    + "</head>" + EndOfLine _
    + "<body>" + EndOfLine
    
    msgbody = msgbody + "<p>" + msg + "</p>"
    
    msgbody = msgbody + "</body>" + EndOfLine _
    + "</html>"
  ElseIf msgtype = "text" Then
    msgbody = msg
  End If
  
  // make a new message
  mail = new EmailMessage
  
  recipients = Split(sendtoadd,",")
  For i = 0 To recipients.Lastindex
    mail.AddRecipient(recipients(i))
  Next
  mail.FromAddress = Email_FromAddress
  mail.Subject = subj
  
  If msgtype = "html" Then
    mail.BodyHTML = msgbody
  ElseIf msgtype = "text" Then
    mail.BodyPlainText = msgbody
  End If
  
  Select Case SMTP_UseAuthentication
  Case True
    LogTransactions("Sending Email using authentication to " + SMTP_ServerAddress)
    LogTransactions("Email Recipients: " + sendtoadd)
    App.EmailTask = New SendEmailThread
    App.EmailTask.UploadData = mail.source
    
    App.EmailTask.OptionMailFrom = Email_FromAddress
    App.EmailTask.OptionMailRecipients = sendtoadd
    App.EmailTask.OptionUsername = SMTP_LoginID
    App.EmailTask.OptionPassword = SMTP_Password
    App.EmailTask.OptionURL = "smtp://"+ SMTP_ServerAddress + ":587"
    
    App.EmailTask.Start
    
  Case False
    LogTransactions("Sending Email without authentication to " + SMTP_ServerAddress)
    LogTransactions("Email Recipients: " + sendtoadd)
    emailSocket = New EmailSocket
    emailSocket.port = 25
    emailSocket.address = SMTP_ServerAddress
    emailSocket.messages.Add mail
    emailSocket.SendMail
    
    currsec = New Date
    timeout = currsec.Second
    While emailSocket.MailSent = False
      emailSocket.Poll
      
      currsec = New Date
      If currsec.Second > (timeout + 60) Then
        LogExceptions("The Exception Notification Email timed out and was not sent")
        Exit
      End If
    Wend
    emailSocket = NIL
  End Select
  
  
End Sub

The SMTPSocket called EmailSocket:
image

Event Handlers

Error 
// we get error 102 for just a regular disconnect
// but handle others
If err.ErrorNumber = 102  Then
  If MailSent = False Then
    LogExceptions("Email Error: Mail not sent - " + Str(Self.LastErrorCode) + err.Message)
  End If
  MailSent = True
  Me.Disconnect
Else
  LogExceptions("Email Error: " + Str(Self.LastErrorCode) + err.Message)
  MailSent = True
  Me.Disconnect
End If

MailSent
LogTransactions("Email Send Event Complete")


ServerError
LogExceptions("Email Error ID: " + Str(ErrorID))
LogExceptions("Email Error Message: " + ErrorMessage)


MailSent = True
Me.Disconnect
Public Sub SendEmail(msg As String, subj As String, msgtype As string, sendtoadd As String)
  // SendEmail(msg As String, subj As String, msgtype As string, sendtoadd As String)
  
  // sends an email to the specified address
  Var i As Integer
  Var currsec As Date
  Var timeout As Integer
  Var mail as EmailMessage
  Var msgbody,recipients(-1) as string
  Var emailSocket As EmailSocket
  
  
  If msgtype = "html" Then
    //compose email body
    msgbody = "<!DOCTYPE html PUBLIC ""-//W3C//DTD XHTML 1.0 Transitional//EN""  ""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"" >" + EndOfLine _
    + "<html xmlns=""http://www.w3.org/1999/xhtml"">" + EndOfLine _
    + "<head>" + EndOfLine _
    + "<meta http-equiv=""Content-Type"" content=""text/html; charset=utf-8"" />" + EndOfLine _
    + "<title>" + subj + "</title>" + EndOfLine _
    + "<style type=""text/css"">" + EndOfLine _
    + "body,td,th {" + EndOfLine _
    + "font-family: Calibri;" + EndOfLine _
    + "font-size: 12pt;" + EndOfLine _
    + "}" + EndOfLine _
    + "</style>" + EndOfLine _
    + "</head>" + EndOfLine _
    + "<body>" + EndOfLine
    
    msgbody = msgbody + "<p>" + msg + "</p>"
    
    msgbody = msgbody + "</body>" + EndOfLine _
    + "</html>"
  ElseIf msgtype = "text" Then
    msgbody = msg
  End If
  
  // make a new message
  mail = new EmailMessage
  
  recipients = Split(sendtoadd,",")
  For i = 0 To recipients.Lastindex
    mail.AddRecipient(recipients(i))
  Next
  mail.FromAddress = Email_FromAddress
  mail.Subject = subj
  
  If msgtype = "html" Then
    mail.BodyHTML = msgbody
  ElseIf msgtype = "text" Then
    mail.BodyPlainText = msgbody
  End If
  
  Select Case SMTP_UseAuthentication
  Case True
    LogTransactions("Sending Email using authentication to " + SMTP_ServerAddress)
    LogTransactions("Email Recipients: " + sendtoadd)
    App.EmailTask = New SendEmailThread
    App.EmailTask.UploadData = mail.source
    
    App.EmailTask.OptionMailFrom = Email_FromAddress
    App.EmailTask.OptionMailRecipients = sendtoadd
    App.EmailTask.OptionUsername = SMTP_LoginID
    App.EmailTask.OptionPassword = SMTP_Password
    App.EmailTask.OptionURL = "smtp://"+ SMTP_ServerAddress + ":587"
    
    App.EmailTask.Start
    
  Case False
    LogTransactions("Sending Email without authentication to " + SMTP_ServerAddress)
    LogTransactions("Email Recipients: " + sendtoadd)
    emailSocket = New EmailSocket
    emailSocket.port = 25
    emailSocket.address = SMTP_ServerAddress
    emailSocket.messages.Add mail
    emailSocket.SendMail
    
    currsec = New Date
    timeout = currsec.Second
    While emailSocket.MailSent = False
      emailSocket.Poll
      
      currsec = New Date
      If currsec.Second > (timeout + 60) Then
        LogExceptions("The Exception Notification Email timed out and was not sent")
        Exit
      End If
    Wend
    emailSocket = NIL
  End Select

End Sub

have you any input at the error events?

I’ve made some progress. I am now getting an email to go out, but when I send it to GMAIL which is what we use for our support email, it’s getting routed to the Spam folder.

Here is the new code that works.

var mail As new EmailMessage
var file As EmailAttachment
var PreferencesFile As EmailAttachment
var DataFileSelected As EmailAttachment
var b_Ok2SendMail As Boolean = True

mailsocket = New SMTP
mailsocket.Address = "smtp1.dnsmadeeasy.com"
mailsocket.Port = 7725
MailSocket.SSLConnectionType = SSLSocket.SSLConnectionTypes.TLSv12
MailSocket.SMTPConnectionType = SMTPSecureSocket.SMTPConnectionTypes.SSLTLS
MailSocket.SSLEnabled = False
mailsocket.Username = "my username"
mailsocket.Password = "my password"

var cOSVersion As String = GetOSVersioin()
var uuidCurrent as New UUIDMBS
var cUUID as String = StrTran(StrTran(uuidCurrent.ValueFormattedString,"{",""),"}","")
var messageID as String = "<"+cUUID+"@smtp1.dnsmadeeasy.com>"
mail.Headers.AppendHeader("Message-ID", messageID)
mail.fromAddress = """" + tfName.Text.Trim + """" + "<feedback@smtp1.dnsmadeeasy.com>"
mail.Headers.AddHeader("Reply-To",tfEmail.Text.Trim)
mail.Subject = app.kAppName+" - "+tfSubject.Text
mail.FromAddress = tfEmail.Text.Trim

Briefly

The DNS Made Easy documentation indicates Port 7725 is the ‘explicit’ TLS port. The client connects with plain text and sends STARTTLS before negotiating the TLS connection. SMTPSecureSocket.SMTPConnectionType needs to be set to STARTTLS [1] or change to ‘implicit’ TLS by connecting to port 465 or 5555 and leaving SMTPConnectionTyoe as SSLTLS.

SMTPSecureSocket.Send triggers connection to the SMTP server. The call returns immediately and the outgoing mail queue is processed in the background. Wrapping .Send in a Try…Catch will (only) catch socket errors but it will not catch smtp protocol errors.

To catch protocol errors you need to populate the SMTPSecureSocket.ServerError event.

You can and should monitor the queue progress. The SMTPSocket.MessageSent event is raised for each message in the queue. When the mali queue has been processed completely the SMTPSecureSocket.MailSent event is raised.

Those are the obvious issues with your code. An incorrect .ConnectionType will definitely prevent the queue from being processed. There may be other issues.

[1] I seem to recall when using STARTTLS .SSLEnabled needs to be false during connection and set true in the .ConnectionEstablished event. Could be wrong. It’s some time since I used it.

This is easy to solve: don’t use Gmail.

I take it you mean gmail’s Spam mailbox in your account?

Can’t you set up any rules in gmail (I have a gmail account but haven’t explored it) to prevent this? Such as "all mails to this address mark as ‘not spam’ " ?

Your new code is working because you have set SSLEnabled to False. SMTP secure socket is not negotiating encryption of any sort and the password and username are being sent in the open. It makes sending the email easier but is poor practice and may increase the spam score on the way to the final destination.

If you are sending to GMail you need an SPF record aligned with a DKIM key and selector records in the sending domains DNS. That is the minimum requirement to avoid the GMail spam trap. Microsoft have similar requirements and Oath / Verizon / Yahoo recently followed suit.

1 Like