HTML body is lost when MapiMEssage.SaveAsTnef is used (C#)

Hi, Aspose team.

Scenario: My app processes EML messages. Some of them have TNEF format (with “application/ms-tnef” content type and winmail.dat attachment). App reads winmail.dat attachment using MapiMessage.LoadFromTnef message.

If TNEF message contains embedded message, app saves it to file (MSG format is used), processes MSG file in some way, then replaces embedded message content with the content of the processed MSG file.

Then app saves MapiMessage back to TNEF and replaces content of winmail.dat attachment.

Issue: HTML body of the embedded message is lost. Outlook show plain text body. see issue.png (59.7 KB)

Sample EML: sample.zip (876.4 KB)

using (var mailMessage = MailMessage.Load("sample for Aspose.eml",
    new EmlLoadOptions
    {
        PreserveTnefAttachments = true,
    }))
{
    using (var newTnefStream = new MemoryStream())
    {
        using (var mapiFromTnef = MapiMessage.LoadFromTnef(mailMessage.Attachments[0].ContentStream))
        {
            // for the sake of simplicity I reassign 
            // content of the embedded message
            mapiFromTnef.Attachments[0]
                .SetProperty(new MapiProperty(MapiPropertyTag.PR_ATTACH_DATA_OBJ,
                      mapiFromTnef.Attachments[0].ObjectData.Data));

            // Save MAPI message back to TNEF attachment
            mapiFromTnef.SaveAsTnef(newTnefStream);
        }

        // update content of the winmail.dat attachment
        newTnefStream.Position = 0;
        mailMessage.Attachments[0].ContentStream = newTnefStream;
        mailMessage.Save("resaved.eml", new EmlSaveOptions(MailMessageSaveType.EmlFormat));
    }
}

Thanks.

@licenses,

I have worked with source file and sample code shared by you using Aspose.Email for .NET 19.3 and unable to observe HTML body issue. I have also shared my generated result with you for your kind reference. Please check attachment.resaved123.zip (875.1 KB)

I opened EML file that you attached in Outlook 2016 (MS Office Professional Plus 2016) and Outlook 2013 and was able to observe the issue.

See screenshot (37.1 KB)

Thanks.

@licenses,

I have worked with the issue shared by you and it seems to be an issue in API. An issue with ID EMAILNET-39440 has been created in our issue tracking system to further investigate and resolve the issue. This thread has been linked with the issue so that you may be notified once the issue will be fixed.

Hi,

I found another issue that may be connected with the one reported above.

Scenario: User sends an email with RTF body in Exchange on-premises environment. There is custom transport agent on Exchange server. Transport agent uses Aspose.Email to do some manipulation with email. Emails have TNEF format (EML with winmail.dat).

Issue. Recipients receive corrupted email (see screenshots). It seems that RTF body is lost and plain text version is shown:

  • no test formatting in the message body;
  • attachments are listed in the attachment panel (not in the body);
  • ATTxxxxx attachment appears.

The thing is that you can replicate it only in Exchange on-premises environment when code runs in transport agent. If you simply run the code provided below in this post on the sample file (RTF sample.zip), then open resulting file in the Outlook, email will be shown OK.

It seems that changes made by Aspose.Email are critical for Exchange server.

Do you have Exchange environment? We can arrange meeting to help you install transport agent so that you can replicate the issue on your side.

Code snippet

using (var originalEmailStream = args.MailItem.GetMimeReadStream())
{
    using (var m = MailMessage.Load(originalEmailStream, new EmlLoadOptions
    {
        PreserveTnefAttachments = true,
    }))
    {
        using (var msForNewTnef = new MemoryStream())
        using (var mapi = MapiMessage.LoadFromTnef(m.Attachments[0].ContentStream))
        {

            mapi.SaveAsTnef(msForNewTnef);
            msForNewTnef.Position = 0;
            m.Attachments[0].ContentStream = msForNewTnef;

            originalEmailStream.Dispose();
            using (var ms = new MemoryStream())
            {
                m.Save(ms, new EmlSaveOptions(MailMessageSaveType.EmlFormat));
                ms.Position = 0;
                CopyStreamTo(ms, args.MailItem.GetMimeWriteStream(), true, true);
            }
        }
    }
}

Thanks.

@licenses,

I have worked with the issue shared by you and it seems to be an issue in API. An issue with ID EMAILNET-39445 has been created in our issue tracking system to further investigate and resolve the issue. This thread has been linked with the issue so that you may be notified once the issue will be fixed.

Hi Adnan.Ahmad

Did management team have a chance to look at EMAILNET-39440 and EMAILNET-39445 issues and scheduler work on them? Do you have an estimate on when fixes will be available?

Thanks.

@licenses,

I like tho inform that these issue has been resolved in Aspose.Email 19.4. Can you please try to use Aspose.Email 19.4 and share feedback with us if there is still an issue.

Hi.

Tested scenarios described above with Aspose.Email 19.4. Was not able to replicate the issues.

But discovered other issue similar to the previous ones. This time it affects task requests.

Scenario: User sends task request to other internal user in Exchange on-premises environment. Task request does not have attachments or text in the body. There is custom transport agent on Exchange server. Transport agent uses Aspose.Email to do some manipulation with email. Emails have TNEF format (EML with winmail.dat). See code snippet later in this post.

See screenshot on how to create task request for user Task request - Create.png (51.0 KB)

Issue: Task request comes corrupted to the recipient. Outlook cannot open it:

As with the previous issue, you can replicate it only in Exchange on-premises environment when code runs in transport agent. If you simply run the code provided below in this post on the sample file (sample task request.zip), then open resulting file in the Outlook, email will be shown OK.

It seems that changes made by Aspose.Email are critical for Exchange server.

We can arrange meeting to help you install transport agent so that you can replicate the issue on your side.

Code snippet

using (var originalEmailStream = args.MailItem.GetMimeReadStream())
{
    using (var mainEml = MailMessage.Load(originalEmailStream, new EmlLoadOptions
    {
        PreserveTnefAttachments = true,
    }))
    {
        using (var msForNewTnef = new MemoryStream())
        using (var mapiFromTnef = MapiMessage.LoadFromTnef(mainEml.Attachments[0].ContentStream))
        {
            // Read MAPI message from TNEF attachment.

            using (var taskMs = new MemoryStream())
            {
                var taskRequestAttachment = mapiFromTnef.Attachments[0];
                taskRequestAttachment.Save(taskMs);
                // Saved task
                taskMs.Position = 0;

                // Re-assigned task object data.
                mapiFromTnef.Attachments[0].SetProperty(
                    new MapiProperty(MapiPropertyTag.PR_ATTACH_DATA_OBJ, taskMs.ToArray()));
            }


            mapiFromTnef.SaveAsTnef(msForNewTnef);
            // Re-saved MAPI message
            msForNewTnef.Position = 0;
            mainEml.Attachments[0].ContentStream = msForNewTnef;
            // Re-assigned content of TNEF attachment.

            originalEmailStream.Dispose();
            using (var ms = new MemoryStream())
            {
                mainEml.Save(ms, new EmlSaveOptions(MailMessageSaveType.EmlFormat));
                ms.Position = 0;
                CopyStreamTo(ms, args.MailItem.GetMimeWriteStream(), true, true);
            }
        }
    }
}

private void CopyStreamTo(Stream inputStream, Stream destStream, 
                bool closeInputStream, bool closeDestStream)
{
    var buffer = new byte[8 * 1024];
    while (true)
    {
        var read = inputStream.Read(buffer, 0, buffer.Length);
        if (read <= 0)
            break;
        destStream.Write(buffer, 0, read);
    }
    //close streams
    if (closeInputStream) inputStream.Close();
    else if (inputStream.CanSeek)
    {
        inputStream.Seek(0, SeekOrigin.Begin);
    }

    if (closeDestStream)
    {
        destStream.Close();
    }
    else if (destStream.CanSeek)
    {
        destStream.Seek(0, SeekOrigin.Begin);
    }
}

@licenses,

We have internally verified the scenarios shared by you and its not getting reproduced. Perhaps, can you please try following modification in your code while saving and share your feedback with us.

EmlSaveOptions saveOptions = new EmlSaveOptions(MailMessageSaveType.EmlFormat); 
   saveOptions.FileCompatibilityMode = FileCompatibilityMode.PreserveTnefAttachments;
  mailMessage.Save(......);

Hi.

Thanks for a quick reply.

I tried to use PreserveTnefAttachments while saving EML file. It did not help (I still can replicate the issue). Theoretically, it should not help, because original EML message was read using EmlLoadOptions with PreserveTnefAttachments = true.

Did you run my code snippet in the context of the transport agent on the Exchange server?

Issue can be replicated only in the scenario that I described. Running code in a desktop application (on the sample email I provided) does not reveal the bug. Outlook can open resulting email w/o issues.

Additional information: in my case code runs in on-premises Exchange 2013 SP1 environment.

@licenses,

I have observed your comments and it’s unfortunate that suggested code sample didn’t work on your end. For your following comments:

It will be better if you may please send an email or message before meeting with detailed description for how to setup transport agent, sources of test transport agent, and sample of test with exchange server. We will try to set up it on our end first and test that . Regardless of the result, we will gain some experience with transport agent and meeting well be more simple. I appreciate your cooperation in this regard.

Hi,

I attached sample project that builds transport agent ExchnageAgent2013SP1 - Task requests.zip (793.1 KB).

Project details:

  • Contains PowerShell scripts to register/unregister agent on Exchange Mailbox server;
  • Contains code snippet to reproduce the issue.
  • Agent skips all emails except IPM.Task.*
  • Agent creates log file in the “Log” sub-folder of the agent installation folder (AGENT_FOLDER).
  • Do not forget to restore NuGet packages.
  • Contains libraries required for Exchange 2013 SP1.
  • I removed content of the ExchangeConnector\Aspose.Total.lic because did not know if it is safe to post it in the ticket.

Installation instructions

  1. Create the following folder “c:\Program Files\Microsoft\Exchange Server\V15\TransportRoles\agents\RoutingAgents\TestRoutingAgent” on the Exchange server. Will refer to this folder as AGENT_FOLDER. V15 folder name depends on the version of Exchange server.
  2. Copy all files from the project output folder to the AGENT_FOLDER.
  3. Open Exchange Management Shell, go to AGENT_FOLDER and run install.ps1 script.

@licenses,

Thank you for sharing the details with us. We will share feedback with you as soon as possible.

Hi, is there any update on the task request issue?

@licenses,

I like to inform that this issue has been resolved in Aspose.Email 19.4.