3

I have a Delphi 6 application that generates E-mails that I send to my Evernote E-mail address, a special E-mail address for sending documents via E-mail so that they are stored automatically into my Evernote account.

I have successfully created HTML documents and sent them to my Evernote E-mail address using the Indy 9.x TIdSMTP component. I set the Content-Type to 'text/html'. It works fine as long as I don't add any attachments to the E-mail. As soon as I add an attachment, something about the generated E-mail makes the Evernote web interface interpret the E-mail as raw HTML. In other words, I see the raw HTML in the document display area as if I had done "view-source" in a browser, instead of seeing a rendered web page. The E-mail attachments I am adding are an AVI file and a WAV file if that matters. When I do add attachments both of them show up properly at the bottom of the E-mail in the Evernote web display area.

To repeat, as soon as I don't add attachments the document shows up as a pretty web page in the Evernote web interface. If I add attachments, I see raw HTML. Can anyone suggest something I can try to fix this problem? I have enclosed the code I use to send the generated document to my Evernote E-mail address below. The variable named body contains a fully formatted HTML document.

UPDATE: I sent the E-mail to a non-Evernote E-mail address so I could see the raw E-mail message. I turns out that adding attachments makes TIdSMTP change the Content-Type of the first part of the multi-part E-mail it generates back to 'text/plain' despite the fact I set it to 'text/html' in my code when I create the message. I'm going to have a look at the Indy source and see if I can figure out what is going wrong.

function easySendEmail(
                theIdSmtp               : TIdSmtp;
                destEMailAddress        : string;
                subject                 : string;
                body                    : string;
                emailServerSettings     : TEmailServerSettingsRecord;
                aryAttachmentFilenames  : TDynamicStringArray;
                connectTimeOut_ms       : integer;
                bUseEHLO                : boolean;
                authLoginType           : TAuthenticationType): boolean;
var
    IdMsg: TIdMessage;
    aryAttachments: TDynamicIdAttachmentArray;
    i: integer;
begin
    aryAttachments := nil;
    IdMsg := nil;

    destEMailAddress := Trim(destEMailAddress);

    if destEMailAddress = '' then
       raise Exception.Create('(TframeEmailServerSettings.easySendEmail) The destination E-mail address is empty.');

    subject := Trim(subject);

    if subject = '' then
        raise Exception.Create('(TframeEmailServerSettings.easySendEmail) The subject line is empty.');

    body := Trim(body);

    if body = '' then
        raise Exception.Create('(TframeEmailServerSettings.easySendEmail) The message body is empty.');

    try
        with emailServerSettings do
        begin
            // Build a test message and send it.
            IdMsg := TIdMessage.Create(nil);
            IdMsg.Recipients.EMailAddresses := destEMailAddress;
            {
                Most SMTP servers require the sending E-mail address as the
                user name for the authentication.  However, if we
                encounter one that doesn't work this way then re-using
                the authentication user name as the From address
                will not work.
            }
            IdMsg.From.Name    := APPLICATION_NAME_EVERMAIL;
            IdMsg.From.Address := user_name;
            IdMsg.Subject := subject;
            IdMsg.Body.Text := body;

            IdMsg.ContentType :=  'text/html';
            // IdMsg.ContentType :=  'text/plain';

            theIdSmtp.Host := host;
            theIdSmtp.Username := user_name;
            theIdSmtp.Password := password;
            theIdSmtp.Port := port_number;
            // Use EHLO method.
            theIdSmtp.UseEhlo := true;
            // Login method of authentication.
            theIdSmtp.AuthenticationType := atLogin;

            // Add the attachments.

            // >>> If I comment out the code below the document shows
            //  up as a rendered web page in the Evernote web interface.
            //  If I uncomment it and therefore add attachments, the
            //  document shows up as raw HTML.
            {
            if Length(aryAttachmentFilenames) > 0 then
            begin
                SetLength(aryAttachments, Length(aryAttachmentFilenames));

                for i := Low(aryAttachmentFilenames) to High(aryAttachmentFilenames) do
                    // Add each attachment.
                    aryAttachments[i] := TIdAttachment.Create(IdMsg.MessageParts, aryAttachmentFilenames[i]);
            end; // if Length(aryAttachmentFilenames) > 0 then
            }

            // Connect to the desired SMTP server.  N second time-out.
            theIdSmtp.Connect(connectTimeOut_ms);

            // Send it.
            theIdSmtp.Send(IdMsg);

            // If we got here than the test succeeded.  Set the flag
            //  indicating the current settings are valid.
            Result := true;
        end; // with mergeEditsWithOriginal do

    finally
        theIdSmtp.Disconnect;

        if Assigned(IdMsg) then
            IdMsg.Free;
    end; // try
end;
Robert Oschler
  • 14,153
  • 18
  • 94
  • 227
  • No idea what's going on, just a thought: have you tried adding the attachments before setting the content type of the body and setting its contents? – Marjan Venema Jun 05 '12 at 07:46
  • @MarjanVenema - Interesting idea. However I just tried it and it didn't work. – Robert Oschler Jun 05 '12 at 11:59
  • I don't have Indy 9, but have you tried setting the content-type to multipart-mixed or multipart-alternative? – John Easley Jun 05 '12 at 12:16
  • @JohnEasley - I'll try that now. Please see the UPDATE I just added to the main post just now. – Robert Oschler Jun 05 '12 at 12:18
  • @JohnEasley - I tried multipart/mixed and TIdSMTP just ignored it. – Robert Oschler Jun 05 '12 at 12:26
  • @RobertOschler Try multi-part/alternative and multipart/related as well. – John Easley Jun 05 '12 at 12:51
  • 2
    @RobertOschler read this from the Indy blog http://www.indyproject.org/sockets/Blogs/RLebeau/2005_08_17_A.en.html – John Easley Jun 05 '12 at 12:53
  • @JohnEasley - I was on a similar path with TIdMessageParts when you sent me that Indy blog link. Unfortunately when I try that approach the HTML part does get included in the E-mail message but Evernote (and Gmail) are completely ignoring it. That is, I can see the HTML part with the correct Content-Type of 'text/html' when I view the raw message, but the Evernote (and G-mail) display areas do not show any of the HTML. – Robert Oschler Jun 05 '12 at 13:29
  • 1
    The `TIdMessage.ContentType` property MUST be set to either `multipart/mixed` or `multipart/related; type='text/html'`, depending on whether the attachments are referenced by the HTML or not. If you set the `TIdMessage.ContentType` to `text/html` and attachments are present, email readers are not going to parse the email correctly. Also, when using the `TIdMessage.MessageParts` collection, parts MUST be ordered from least complex to most complex, and attachment parts MUST follow after the HTML part. – Remy Lebeau Jun 05 '12 at 18:35

1 Answers1

6

Your code is not setting up TIdMessage correctly when attachments are present. Try this instead:

function easySendEmail( 
                theIdSmtp               : TIdSmtp; 
                destEMailAddress        : string; 
                theSubject              : string; 
                theBody                 : string; 
                emailServerSettings     : TEmailServerSettingsRecord; 
                aryAttachmentFilenames  : TDynamicStringArray; 
                connectTimeOut_ms       : integer; 
                bUseEHLO                : boolean; 
                authLoginType           : TAuthenticationType): boolean; 
var 
  IdMsg: TIdMessage; 
  i: integer; 
begin 
  destEMailAddress := Trim(destEMailAddress); 
  if destEMailAddress = '' then 
    raise Exception.Create('(TframeEmailServerSettings.easySendEmail) The destination E-mail address is empty.'); 

  theSubject := Trim(theSubject); 
  if theSubject = '' then 
    raise Exception.Create('(TframeEmailServerSettings.easySendEmail) The subject line is empty.'); 

  theBody := Trim(theBody); 
  if theBody = '' then 
    raise Exception.Create('(TframeEmailServerSettings.easySendEmail) The message body is empty.'); 

  IdMsg := TIdMessage.Create(nil); 
  try 
    with emailServerSettings do 
    begin 
      // Build a test message and send it. 
      IdMsg.Recipients.EMailAddresses := destEMailAddress; 
      { 
        Most SMTP servers require the sending E-mail address as the 
        user name for the authentication.  However, if we 
        encounter one that doesn't work this way then re-using 
        the authentication user name as the From address 
        will not work. 
      } 
      IdMsg.From.Name    := APPLICATION_NAME_EVERMAIL; 
      IdMsg.From.Address := user_name; 
      IdMsg.Subject := theSubject; 

      // Add the attachments. 
      if Length(aryAttachmentFilenames) > 0 then 
      begin 
        with TIdText.Create(IdMsg.MessageParts, nil) do
        begin
          Body.Text := 'An HTML viewer is required to see this message'; 
          ContentType := 'text/plain';
        end; 

        with TIdText.Create(IdMsg.MessageParts, nil) do
        begin
          Body.Text := theBody; 
          ContentType := 'text/html';
        end; 

        // Add each attachment. 
        for i := Low(aryAttachmentFilenames) to High(aryAttachmentFilenames) do 
          TIdAttachment.Create(IdMsg.MessageParts, aryAttachmentFilenames[i]); 

        IdMsg.ContentType := 'multipart/mixed'; 
      end else
      begin
        IdMsg.Body.Text := theBody; 
        IdMsg.ContentType := 'text/html'; 
      end; // if Length(aryAttachmentFilenames) > 0 then 

      theIdSmtp.Host := host; 
      theIdSmtp.Username := user_name; 
      theIdSmtp.Password := password; 
      theIdSmtp.Port := port_number; 
      // Use EHLO method. 
      theIdSmtp.UseEhlo := true; 
      // Login method of authentication. 
      theIdSmtp.AuthenticationType := atLogin; 

      // Connect to the desired SMTP server.  N second time-out. 
      theIdSmtp.Connect(connectTimeOut_ms); 
      try
        // Send it. 
        theIdSmtp.Send(IdMsg); 

        // If we got here than the test succeeded.  Set the flag 
        //  indicating the current settings are valid. 
        Result := true; 

      finally
        theIdSmtp.Disconnect; 
      end;
    end; // with emailServerSettings do 
  finally 
    IdMsg.Free; 
  end; // try 
end; 
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • Thanks, that did it. It's amazing how such a subtle thing as sorting the parts by complexity kept my messages from displaying correctly. Just a note to anyone who uses the code above. Do a case sensitive search and replace to change the "body" method parameter to the "theBody", or you will get a compiler error in the second Body.Text property assignment due to the "with" statement surrounding it, because of the name ambiguity between the property and the method parameter since they only vary by case. – Robert Oschler Jun 05 '12 at 21:59
  • 2
    The order of the `MessageParts` items is important, because that is how MIME works. MIME-enabled readers start at the last part and work backwards until they find a part they know how to handle, where the plain text on front is the last item tried as a final fallback. This way, MIME-formatted emails can contain multiple representations of the data (in this case, plain text and HTML, but emails are not limited to just that) and let email readers pick which representation they want to process/display. – Remy Lebeau Jun 06 '12 at 00:07