Commons Email
  1. Commons Email
  2. EMAIL-65

MIME layout generated by HtmlEmail causes trouble

    Details

    • Type: Bug Bug
    • Status: Closed
    • Priority: Major Major
    • Resolution: Fixed
    • Affects Version/s: 1.1
    • Fix Version/s: 1.2
    • Labels:
      None

      Description

      Previous bugs (e.g. http://issues.apache.org/jira/browse/EMAIL-50 ) raised against commons-email version 1.0 have complained about Outlook not being able to properly display multipart emails generated by HtmlEmail. While this issue is resolved as of rev 510708, I believe there a several issues regarding MIME layout, that still cause trouble to email clients.

      In the current codebase, a multipart email containing: plaintext part, html part, inline images and attachments, is constructed (to the best of my knowledge) with a MIME layout like this:

      Generated by HtmlEmail. Contains parts: plaintext, html, embedded inline img, attach
      PART#, MIME_TYPE, (COMMENTS)
      1 multipart/related
      1.1 multipart/alternative
      1.1.1 text/plain (the plaintext content)
      1.1.2 text/html (the html content with cid: references to embedded images)
      1.2+ / (attachment)
      1.3+ image/* (embedded image, referenced by 1.1.2)

      ("+" above indicates that multiple (1..n) parts may be included)

      The above structure may cause email clients to display embedded images as attachments, even though they are semantically part of the text/html content.

      Furthermore, the current codebase (as documented in the HtmlEmail.setMsg() JavaDoc) synthesizes a html part by <pre>...</pre> wrapping the plaintext part, thus generating a bloated (double size) message, for no apparent reason. In my opinion, HtmlEmail should degrade to the mime layout of Email, if no html part is available.

      Proposed MIME layout
      ------------------------------

      To the best of my knowledge, a multipart email containing: plaintext part, html part, inline images and attachments, should be constructed like so:

      PART#, MIME_TYPE, (COMMENTS)
      1. multipart/mixed
      1.1 multipart/related
      1.1.1 multipart/alternative
      1.1.1.1 text/plain (the plaintext content)
      1.1.1.2 text/html (the HTML content with cid: references to embedded images)
      1.1.2+ image/* (embedded image, referenced by 1.1.1.2)
      1.2+ / (attachment)

      The following simplifications of the above structure may be applied:
      a. If no embedded images are included, items 1.1.2+ and 1.1 are removed.
      b. if no text/html part is included, items 1.1.1.2 and 1.1.1 are removed
      c. if no attachments are included, items 1.2+ and 1 are removed

      Incidentially, this MIME layout is used by GMail, which is an indication that it is the "proper" way.

      I seriously believe that this issue should be investigated and resolved, if at all possible, as part of version 1.1.

      I may be able to supply a patch to HtmlEmail.java in the April/May 2007 timeframe, but I am not prepared to put any body parts on the block on that one

      I welcome any comments!

      Morten Hattesen

      References:
      See http://en.wikipedia.org/wiki/MIME for additional information and references

      1. HtmlEmail.java
        24 kB
        Kenneth Gendron
      2. CustomHtmlEmail.java
        14 kB
        Martin Lau

        Activity

        Hide
        Ben Speakmon added a comment -

        I like the MIME structure you propose.

        I'm not aware of any popular email client that handles the current structure incorrectly, and without a clear breakage the amount of testing required to prove this change seems onerous. If you can show me a test case for one, I'd be happy to work with you to figure out what's going on; otherwise, in the interest of getting the important 1.1 fixes out the door ASAP, I view this as more of an enhancement request. (Disclaimer: I'm not a committer and only a committer's opinion is binding on release issues.)

        Creating an HTML part (when none is explicitly specified) is expected behavior. If you want plaintext only or plaintext with attachments, I'd say you should use SimpleEmail or MultiPartEmail directly. Changing this would mean changing the API contract, and I don't think that's something we should do for this release. (See previous disclaimer.)

        Show
        Ben Speakmon added a comment - I like the MIME structure you propose. I'm not aware of any popular email client that handles the current structure incorrectly, and without a clear breakage the amount of testing required to prove this change seems onerous. If you can show me a test case for one, I'd be happy to work with you to figure out what's going on; otherwise, in the interest of getting the important 1.1 fixes out the door ASAP, I view this as more of an enhancement request. (Disclaimer: I'm not a committer and only a committer's opinion is binding on release issues.) Creating an HTML part (when none is explicitly specified) is expected behavior. If you want plaintext only or plaintext with attachments, I'd say you should use SimpleEmail or MultiPartEmail directly. Changing this would mean changing the API contract, and I don't think that's something we should do for this release. (See previous disclaimer.)
        Hide
        Morten Hattesen added a comment -

        After a careful review of the current codebase (HtmlEmail.java rev 511673, MultiPartEmail.java rev 517714), I have discovered, that the MIME layout generated is not quite as described in my previous posting.

        In many circumstances both HtmlEmail and MultiPartEmail generates MIME structures that do not conform to MIME recommendations.

        Below, I have listed the MIME structures generated, as well as how I believe they should have been.

        Example 1:
        Generated by HtmlEmail. Contains parts: plaintext
        PART#, MIME_TYPE, (COMMENTS)
        1 multipart/related (WRONG!)
        1.1 text/plain (the plaintext content)

        Should have been:
        PART#, MIME_TYPE, (COMMENTS)
        1 text/plain (the plaintext content)

        Example 2:
        Generated by HtmlEmail. Contains parts: plaintext, html
        PART#, MIME_TYPE, (COMMENTS)
        1 multipart/related (WRONG!)
        1.1 text/plain (the plaintext content)
        1.2 text/html (the html content)

        Should have been:
        PART#, MIME_TYPE, (COMMENTS)
        1 multipart/alternative
        1.1 text/plain (the plaintext content)
        1.2 text/html (the html content)

        Example 3:
        Generated by HtmlEmail. Contains parts: plaintext, html, embedded inline img, attach
        PART#, MIME_TYPE, (COMMENTS)
        1 multipart/related
        1.1 multipart/alternative
        1.1.1 text/plain (the plaintext content)
        1.1.2 text/html (the html content with cid: references to embedded images)
        1.2+ / (attachment)
        1.3+ image/* (embedded image, referenced by 1.1.2)

        should have been:
        PART#, MIME_TYPE, (COMMENTS)
        1. multipart/mixed
        1.1 multipart/related
        1.1.1 multipart/alternative
        1.1.1.1 text/plain (the plaintext content)
        1.1.1.2 text/html (the HTML content with cid: references to embedded images)
        1.1.2+ image/* (embedded image, referenced by 1.1.1.2)
        1.2+ / (attachment)

        Example 4:
        Generated by MultiPartEmail. Contains parts: plaintext
        MultiPartEmail: plaintext
        PART#, MIME_TYPE, (COMMENTS)
        1 multipart/mixed (WRONG!)
        1.1 text/plain (the plaintext content)

        should have been:
        PART#, MIME_TYPE, (COMMENTS)
        1 text/plain (the plaintext content)

        Example 5:
        Generated by MultiPartEmail. Contains parts: plaintext, attach
        PART#, MIME_TYPE, (COMMENTS)
        1 multipart/mixed
        1.1 text/plain (the plaintext content)
        1.2+ / (attachment)

        This one is well-structured!

        Conclusion:

        With regards to the automatically generated text/html part, based on the text/plain part, I concede that this is part of the API contract (specified in JavaDoc of HtmlEmail.setMsg(String) ), although I still believe that generating redundant data is silly.

        It is fairly easy to show email clients choking on the above MIME structures, one example being embedded images being displayed as attachments by virtually any clients.

        I agree that the current codebase is an improvement on version 1.0, but I think making the 1.1 release at this stage with MIME structures that violates even the most basic MIME recommendations (see examples above) would be wrong.

        One alternative possibility would be planning a version 1.2 with a (thoroughly tested) restructuring of the MIME generation.

        Show
        Morten Hattesen added a comment - After a careful review of the current codebase (HtmlEmail.java rev 511673, MultiPartEmail.java rev 517714), I have discovered, that the MIME layout generated is not quite as described in my previous posting. In many circumstances both HtmlEmail and MultiPartEmail generates MIME structures that do not conform to MIME recommendations. Below, I have listed the MIME structures generated, as well as how I believe they should have been. Example 1: Generated by HtmlEmail. Contains parts: plaintext PART#, MIME_TYPE, (COMMENTS) 1 multipart/related (WRONG!) 1.1 text/plain (the plaintext content) Should have been: PART#, MIME_TYPE, (COMMENTS) 1 text/plain (the plaintext content) Example 2: Generated by HtmlEmail. Contains parts: plaintext, html PART#, MIME_TYPE, (COMMENTS) 1 multipart/related (WRONG!) 1.1 text/plain (the plaintext content) 1.2 text/html (the html content) Should have been: PART#, MIME_TYPE, (COMMENTS) 1 multipart/alternative 1.1 text/plain (the plaintext content) 1.2 text/html (the html content) Example 3: Generated by HtmlEmail. Contains parts: plaintext, html, embedded inline img, attach PART#, MIME_TYPE, (COMMENTS) 1 multipart/related 1.1 multipart/alternative 1.1.1 text/plain (the plaintext content) 1.1.2 text/html (the html content with cid: references to embedded images) 1.2+ / (attachment) 1.3+ image/* (embedded image, referenced by 1.1.2) should have been: PART#, MIME_TYPE, (COMMENTS) 1. multipart/mixed 1.1 multipart/related 1.1.1 multipart/alternative 1.1.1.1 text/plain (the plaintext content) 1.1.1.2 text/html (the HTML content with cid: references to embedded images) 1.1.2+ image/* (embedded image, referenced by 1.1.1.2) 1.2+ / (attachment) Example 4: Generated by MultiPartEmail. Contains parts: plaintext MultiPartEmail: plaintext PART#, MIME_TYPE, (COMMENTS) 1 multipart/mixed (WRONG!) 1.1 text/plain (the plaintext content) should have been: PART#, MIME_TYPE, (COMMENTS) 1 text/plain (the plaintext content) Example 5: Generated by MultiPartEmail. Contains parts: plaintext, attach PART#, MIME_TYPE, (COMMENTS) 1 multipart/mixed 1.1 text/plain (the plaintext content) 1.2+ / (attachment) This one is well-structured! Conclusion: With regards to the automatically generated text/html part, based on the text/plain part, I concede that this is part of the API contract (specified in JavaDoc of HtmlEmail.setMsg(String) ), although I still believe that generating redundant data is silly. It is fairly easy to show email clients choking on the above MIME structures, one example being embedded images being displayed as attachments by virtually any clients. I agree that the current codebase is an improvement on version 1.0, but I think making the 1.1 release at this stage with MIME structures that violates even the most basic MIME recommendations (see examples above) would be wrong. One alternative possibility would be planning a version 1.2 with a (thoroughly tested) restructuring of the MIME generation.
        Hide
        Ben Speakmon added a comment -

        What clients, specifically, do you see choking? I'll take a closer look. Your MIME structures also look correct in the examples you give.

        Regarding a 1.1 release: when I started working on this, I noticed quickly that there were some design choices that I didn't agree with. For example, I'm not particularly happy with the inheritance hierarchy; I see no reason for HtmlEmail to extend MultiPartEmail, since the former does not have an is-a relationship with the latter. However, I figured that it could be made to work in the current incarnation, and since there are already people using this library, maintaining the API contract and avoiding breaking changes wherever possible was the most important consideration. Unfortunately, this makes it tricky to address situations like this where the objectively best solution is to refactor.

        I hope it's easier to see where I'm coming from now...

        In Examples 1 and 4, if you just want to send a plaintext email, then use SimpleEmail; that's what it's for. While you could certainly argue that HtmlEmail should know how to reshape its MIME structure to handle a plaintext email, you could also make the same argument that MultiPartEmail should know how to handle HTML text without attachments in the same way. And also for SimpleEmail to handle HTML text, and so on. Suddenly there are three classes that have the exact same functionality, so naturally you refactor them into one class or start abstracting out interfaces and concrete classes... but doing that breaks the API contract, and you're back to square one.

        The least evil solution I can see is getting the existing classes to work as well as possible while maintaining the contracts and then helping users to understand that they need to use the right class for the right task. To that end, it's probably a good idea to try to fix examples 2 and 3. I'll take a look this week and see what I can do.

        Show
        Ben Speakmon added a comment - What clients, specifically, do you see choking? I'll take a closer look. Your MIME structures also look correct in the examples you give. Regarding a 1.1 release: when I started working on this, I noticed quickly that there were some design choices that I didn't agree with. For example, I'm not particularly happy with the inheritance hierarchy; I see no reason for HtmlEmail to extend MultiPartEmail, since the former does not have an is-a relationship with the latter. However, I figured that it could be made to work in the current incarnation, and since there are already people using this library, maintaining the API contract and avoiding breaking changes wherever possible was the most important consideration. Unfortunately, this makes it tricky to address situations like this where the objectively best solution is to refactor. I hope it's easier to see where I'm coming from now... In Examples 1 and 4, if you just want to send a plaintext email, then use SimpleEmail; that's what it's for. While you could certainly argue that HtmlEmail should know how to reshape its MIME structure to handle a plaintext email, you could also make the same argument that MultiPartEmail should know how to handle HTML text without attachments in the same way. And also for SimpleEmail to handle HTML text, and so on. Suddenly there are three classes that have the exact same functionality, so naturally you refactor them into one class or start abstracting out interfaces and concrete classes... but doing that breaks the API contract, and you're back to square one. The least evil solution I can see is getting the existing classes to work as well as possible while maintaining the contracts and then helping users to understand that they need to use the right class for the right task. To that end, it's probably a good idea to try to fix examples 2 and 3. I'll take a look this week and see what I can do.
        Hide
        Morten Hattesen added a comment -

        I have a couple of comments...

        The API contract for HtmlEmail http://jakarta.apache.org/commons/email/api-release/org/apache/commons/mail/HtmlEmail.html states that ...
        "Either the text or HTML can be omitted, in which case the "main" part of the multipart becomes whichever is supplied rather than a multipart/alternative."
        ... which is in violation with the currently generated MIME structure (see example 1), which builds a multipart/related container, even though no "related" (i.e. embedded inline) MIME parts are available.

        If HtmlEmail does not support generating messages with a single text/plain MIME part, by gracefully degrading to content resembling that generated by SimpleEmail/MultiPartEmail, then it should throw an IllegalStateException (possibly wrapped in an EmailException), rather than generating a fundamentally flawed MIME structure.

        With regards to my example 3, the argument is that the currently generated MIME structure, with attachments inside a multipart/related container does not follow MIME recommendations nor does it resemble the structure generated by other popular smtp clients. Attachments should be contained in a multipart/mixed container.
        References:
        + multipart/mixed - http://tools.ietf.org/html/rfc2046#section-5.1.3 "The "mixed" subtype of "multipart" is intended for use when the body parts are independent and need to be bundled in a particular order."
        + multipart/related - http://tools.ietf.org/html/rfc2387 "The Multipart/Related content-type provides a common mechanism for representing objects that are aggregates of related MIME body parts."
        My interpretation of the above standards is that multipart/related should be used for mail body with inline parts, which should be treated as a whole (compound), whereas multipart/mixed is used for parts which may be used in isolation (mail body and attachments). If we are forced to use only one MIME type for both situations, it should be multipart/mixed.

        I may not be able to provide concrete proof of email clients that will choke on the current MIME structure (although I will do my best to do so), but that should not be used as an excuse to continue to generate flawed MIME structure. I do, however recognise the need for thorough testing prior to any major change being made, as well as the "if it ain't broke, don't fix it" paradigm.

        My final argument is, that since the principal role of commons-email is to provide a simple API facade on top of the complex JavaMail API, it should always generate MIME structures that adapts to the actual contents. In other words, the client-programmer need not concern himself with having to choose a "lesser" Email subclass just because a certain email instance does not contain one of the allowed content types. In other words, I feel that the only reason not to always choose HtmlEmail to generate emails, is when the client programmer never requires facilities offered by HtmlEmail, and wishes a simpler API (MultiPartEmail or SimpleEmail).
        If this structure should be reflected by the type hierarch of Email, the hierarchy should look like so:
        Email (might as well be made abstract)
        +-- SimpleEmail
        +-- MultiPartEmail
        +-- HtmlEmail

        • mind you, to maintain API backwards compatability, I am in no way suggesting such a type hierarchy change for version 1.x

        I will be happy to contribute code, and/or testing effort on this issue, please let me know if I can be of any assistance.

        Show
        Morten Hattesen added a comment - I have a couple of comments... The API contract for HtmlEmail http://jakarta.apache.org/commons/email/api-release/org/apache/commons/mail/HtmlEmail.html states that ... "Either the text or HTML can be omitted, in which case the "main" part of the multipart becomes whichever is supplied rather than a multipart/alternative." ... which is in violation with the currently generated MIME structure (see example 1), which builds a multipart/related container, even though no "related" (i.e. embedded inline) MIME parts are available. If HtmlEmail does not support generating messages with a single text/plain MIME part, by gracefully degrading to content resembling that generated by SimpleEmail/MultiPartEmail, then it should throw an IllegalStateException (possibly wrapped in an EmailException), rather than generating a fundamentally flawed MIME structure. With regards to my example 3, the argument is that the currently generated MIME structure, with attachments inside a multipart/related container does not follow MIME recommendations nor does it resemble the structure generated by other popular smtp clients. Attachments should be contained in a multipart/mixed container. References: + multipart/mixed - http://tools.ietf.org/html/rfc2046#section-5.1.3 "The "mixed" subtype of "multipart" is intended for use when the body parts are independent and need to be bundled in a particular order." + multipart/related - http://tools.ietf.org/html/rfc2387 "The Multipart/Related content-type provides a common mechanism for representing objects that are aggregates of related MIME body parts." My interpretation of the above standards is that multipart/related should be used for mail body with inline parts, which should be treated as a whole (compound), whereas multipart/mixed is used for parts which may be used in isolation (mail body and attachments). If we are forced to use only one MIME type for both situations, it should be multipart/mixed. I may not be able to provide concrete proof of email clients that will choke on the current MIME structure (although I will do my best to do so), but that should not be used as an excuse to continue to generate flawed MIME structure. I do, however recognise the need for thorough testing prior to any major change being made, as well as the "if it ain't broke, don't fix it" paradigm. My final argument is, that since the principal role of commons-email is to provide a simple API facade on top of the complex JavaMail API, it should always generate MIME structures that adapts to the actual contents. In other words, the client-programmer need not concern himself with having to choose a "lesser" Email subclass just because a certain email instance does not contain one of the allowed content types. In other words, I feel that the only reason not to always choose HtmlEmail to generate emails, is when the client programmer never requires facilities offered by HtmlEmail, and wishes a simpler API (MultiPartEmail or SimpleEmail). If this structure should be reflected by the type hierarch of Email, the hierarchy should look like so: Email (might as well be made abstract) +-- SimpleEmail +-- MultiPartEmail +-- HtmlEmail mind you, to maintain API backwards compatability, I am in no way suggesting such a type hierarchy change for version 1.x I will be happy to contribute code, and/or testing effort on this issue, please let me know if I can be of any assistance.
        Hide
        Morten Hattesen added a comment -

        For a thorough (although non-authoritative) explanation of MIME multipart subtypes, see http://en.wikipedia.org/wiki/Multipurpose_Internet_Mail_Extensions#Multipart_Subtypes

        Show
        Morten Hattesen added a comment - For a thorough (although non-authoritative) explanation of MIME multipart subtypes, see http://en.wikipedia.org/wiki/Multipurpose_Internet_Mail_Extensions#Multipart_Subtypes
        Hide
        Ben Speakmon added a comment -

        The current plan is to try to implement and test cases 2 and 3 above. Hopefully I'll get to it soon – this is the last thing blocking a 1.1 release.

        Show
        Ben Speakmon added a comment - The current plan is to try to implement and test cases 2 and 3 above. Hopefully I'll get to it soon – this is the last thing blocking a 1.1 release.
        Hide
        Ben Speakmon added a comment -

        I can't really do any of these cases without rewriting HtmlEmail and I really don't see the need to get this into 1.1. Pushing back to 2.0 for revisiting then.

        Show
        Ben Speakmon added a comment - I can't really do any of these cases without rewriting HtmlEmail and I really don't see the need to get this into 1.1. Pushing back to 2.0 for revisiting then.
        Hide
        Martin Lau added a comment - - edited

        I'm pretty sure I'm jumping the gun here and I shouldn't be posting this here, but after a morning with Google searches I've been unable to find any real solution for the messed up mime types.

        So, my afternoon has been spent writing something that at least fixes the issues I've been facing with the MIME types mentioned above. As far as I can tell, my abuse of the standard HtmlEmail class should work for variations of:

        text + html + attachment + inline Example #3
        text + html + attachment
        text + html + inline
        text + html Example #2

        The only significant changes are in the build() method, but I'm still pretty sure I would've broken something else (time limitations mean I can't really give it a good going over - it works for my use cases), but this might help someone else who has the same problem...

        Show
        Martin Lau added a comment - - edited I'm pretty sure I'm jumping the gun here and I shouldn't be posting this here, but after a morning with Google searches I've been unable to find any real solution for the messed up mime types. So, my afternoon has been spent writing something that at least fixes the issues I've been facing with the MIME types mentioned above. As far as I can tell, my abuse of the standard HtmlEmail class should work for variations of: text + html + attachment + inline Example #3 text + html + attachment text + html + inline text + html Example #2 The only significant changes are in the build() method, but I'm still pretty sure I would've broken something else (time limitations mean I can't really give it a good going over - it works for my use cases), but this might help someone else who has the same problem...
        Hide
        Kenneth Gendron added a comment - - edited

        Since this has not been solved, I was wondering if I could put forth some code for evaluation and feedback. Martin is right, only the build() method in HtmlEmail must be fixed to properly handle text/html, inline images, and attachments. The quick solution would be to rewrite it so that the multiparts look something like this:

        Content-Type: multipart/mixed; boundary="attachment"

        --attachment
        Content-Type: multipart/related; boundary="html-inline"

        --html-inline
        Content-Type: multipart/alternative; boundary="text-andor-html"

        --text-andor-html
        Content-Type: text/plain;
        [Textual representation of message]

        --text-andor-html
        Content-Type: text/html;
        [HTML representation of message with inline images]

        -text-andor-html-

        --html-inline
        Content-Type: image/jpeg;
        Content-ID: image1

        --html-inline
        Content-Type: image/jpeg;
        Content-ID: imageN

        -html-inline-

        --attachment

        Content-Type: [some type]; name=file1.zip

        --attachment

        Content-Type: [some type]; name=fileN.zip

        -attachment-

        Ken

        See the HtmlEmail.java attachment

        Show
        Kenneth Gendron added a comment - - edited Since this has not been solved, I was wondering if I could put forth some code for evaluation and feedback. Martin is right, only the build() method in HtmlEmail must be fixed to properly handle text/html, inline images, and attachments. The quick solution would be to rewrite it so that the multiparts look something like this: Content-Type: multipart/mixed; boundary="attachment" --attachment Content-Type: multipart/related; boundary="html-inline" --html-inline Content-Type: multipart/alternative; boundary="text-andor-html" --text-andor-html Content-Type: text/plain; [Textual representation of message] --text-andor-html Content-Type: text/html; [HTML representation of message with inline images] - text-andor-html - --html-inline Content-Type: image/jpeg; Content-ID: image1 --html-inline Content-Type: image/jpeg; Content-ID: imageN - html-inline - --attachment Content-Type: [some type] ; name=file1.zip --attachment Content-Type: [some type] ; name=fileN.zip - attachment - Ken See the HtmlEmail.java attachment
        Hide
        Siegfried Goeschl added a comment -

        Kenneth, I'm currently integrating your patch .... do you have some more regression tests by accident?

        Show
        Siegfried Goeschl added a comment - Kenneth, I'm currently integrating your patch .... do you have some more regression tests by accident?
        Hide
        Siegfried Goeschl added a comment -

        Moved to 1.2 release

        Show
        Siegfried Goeschl added a comment - Moved to 1.2 release
        Hide
        Siegfried Goeschl added a comment -

        Used Kenneth's implementation of HtmlEmail.buildMimeMessage() and did some field testing (Outlook, Outlook WebAccess, GMX WebMail, Apple Mail, Thunderbird) - it looks good. The other guys are much more knowledgeable regarding mail spec ... ... so I hope this solves the issue.

        Show
        Siegfried Goeschl added a comment - Used Kenneth's implementation of HtmlEmail.buildMimeMessage() and did some field testing (Outlook, Outlook WebAccess, GMX WebMail, Apple Mail, Thunderbird) - it looks good. The other guys are much more knowledgeable regarding mail spec ... ... so I hope this solves the issue.

          People

          • Assignee:
            Siegfried Goeschl
            Reporter:
            Morten Hattesen
          • Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved:

              Development