TeamMsgExtractor / msg-extractor

Extracts emails and attachments saved in Microsoft Outlook's .msg files
GNU General Public License v3.0
736 stars 173 forks source link

preparedHTML: Incorrect output of two-byte utf-8 characters in header data #432

Closed digidigital closed 1 week ago

digidigital commented 2 weeks ago

Bug Metadata

Describe the bug If you have a two-byte character (like the German umlaut "ä") in the header data that is transmitted with =UTF8?Q? … ?= the result is two separate characters when using prepared-html. Output for text or "regular" html is fine

What code did you use or can we use to reproduce this error? Just try the attached text-email with --html --prepared-html

Is there a message.msg file you want to share to help us reproduce this?

Additional context I tried to track the issue and i assume it is caused by passing the html as UTF-8-encoded bytes to beautiful soup (message_base.py, line 385) -> I assume the two-byte character is interpreted as two separate characters by bs

def getSaveHtmlBody(self, preparedHtml: bool = False, charset: str = 'utf-8', **_) -> bytes:
    """
    Returns the HTML body that will be used in saving based on the
    arguments.

    :param preparedHtml: Whether or not the HTML should be prepared for
        standalone use (add tags, inject images, etc.).
    :param charset: If the html is being prepared, the charset to use for
        the Content-Type meta tag to insert. This exists to ensure that
        something parsing the html can properly determine the encoding (as
        not having this tag can cause errors in some programs). Set this to
        ``None`` or an empty string to not insert the tag. (Default:
        'utf-8')
    :param _: Used to allow kwargs expansion in the save function.
        Arguments absorbed by this are simply ignored.
    """
    if self.htmlBody:
        # Inject the header into the data.
        data = self.injectHtmlHeader(prepared = preparedHtml)

        # If we are preparing the HTML, then we should
        if preparedHtml and charset:
            bs = bs4.BeautifulSoup(data, features = 'html.parser')

Potential fix Decode the value passed to beatifulsoup in getSaveHtmlBody with .decode('utf-8') -> pass data as regular utf-8 string to bs

  if self.htmlBody:
      # Inject the header into the data.
      data = self.injectHtmlHeader(prepared = preparedHtml).decode('utf-8')
TheElementalOfDestruction commented 2 weeks ago

I've gone and modified the correct section of the data so it should not escape any non-ascii characters in the header. I would test this myself to ensure it is working correctly, but I don't have any examples with this issue that I can find, and the uploaded file is a .eml file rather than a .msg file. If you could either upload the correct file or download the version on the #next-release branch and test it yourself, that would be great.

I did try to manually do a conversion using outlook, but the file is causing RTFDE to throw mysterious errors, so now I have something else to look into as well 😅

digidigital commented 2 weeks ago

Thx for the quick fix. I tested it for html, prepared html, text, and it works as expected. 😻

Here is the msg -> test.zip

I did not notice that a .msg you send as an attachment from Outlook/Windows is saved as .eml when you get the mail in Thunderbird/Ubuntu and save the attachment 😇

TheElementalOfDestruction commented 2 weeks ago

Yeah, the reason it saves that way is that often attaching the msg file will actually mangle it when the email gets sent.

I'll probably look a little bit harder at some of the other things that can influence the HTML body to ensure that this issue won't come up anywhere else and then I'll publish the release.

TheElementalOfDestruction commented 1 week ago

I was right, there was still one more place that had that as a potential issue, but it's fixed too now in the 0.50.0 release.