-
-
Notifications
You must be signed in to change notification settings - Fork 29.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Wrong attachement filename when mail mime header was too long #83221
Comments
I'm working on a mailfilter in python and used the method "get_filename" of the "EmailMessage" class. In some cases a wrong filename was returned. The reason was, that the Content-Disposition Header had a line break and the following intention was interpreted as part of the filename. After fixing this bug, I was able to get the right filename. I had to change "linesep_splitter" in "email.policy" to match the intention. Old Value: linesep_splitter = re.compile(r'\n|\r') New Value: linesep_splitter = re.compile(r'\n\s+|\r\s+') |
Thanks for the report. Can you provide an example that reproduces the problem? Per the RFC, lines may be broken before whitespace in certain places in certain headers, but that does not make the whitespace go away. Only the crlf sequence is removed when unfolding the header, per the RFC, so your proposed fix is incorrect. I suspect your example header is invalid, and the question will then become is there some sort of Postel-style error recovery we can and want to do in the function that parses the content-disposition header. |
The original filename is "Schulbesuchsbestättigung.pdf", but when I use the method "get_filename" I got "Schulbesuchsbestättigung. pdf" I removed some headers from the mail for privacy reasons |
The mail was sent from the GMail web interface |
That header is *completely* non-RFC compliant. If gmail generated that header there is something very wrong in google-land :( The RFC compliant formatting for that header looks like this: Content-Disposition: attachment; You will note that this is nothing like encoded word format. Encoded words are not valid inside quoted strings, and quoted strings can't be used in mime header attributes if there are non-ascii characters involved. Nor can encoded words. Now, all that said, there is an obvious rule that can be followed to understand what that header is trying to convey, and the current parser already implements most of it (you will find comments about it in the parser, as well as defects being registered). So, a patch to _header_value_parser to fix the error recovery will be accepted. I've looked at the code to remind myself, but not deeply enough to be *sure* where the changes need to be made. There are two possibilities I see off the bat (and both may need fixing): get_bare_quoted_string and get_parameter. Either one or both of those may be forgetting that whitespace between encoded words should be dropped. |
thanks for your response. I have found the RFC https://tools.ietf.org/html/rfc2184 Gmail creates wrong Headers, which are not rfc-compliant. How can we solve this problem? It is not a Python problem. We can create workarrounds. But in my opinion Google has to fix the bug. |
RFC-2184 was obsoleted by RFC-2231 (https://www.rfc-editor.org/rfc/rfc2231.html) There are also no quoted strings, like google uses. |
as you mentioned, rfc-2047 forbidds encoded words in quoted strings. Source: https://tools.ietf.org/html/rfc2047 - Chapter 5/3 I have tested a few web mail clients and they have the same issue. According to the RFCs, this is not allowed, but I think it is widely used. Should we fix this problem? |
Yes, google should fix their bug. However, the python email package tries very hard to interpret even RFC-non-compliant emails when there is a way to do so. As I said, the package already tries to interpret headers such as google is generating, it's just that there is a bug in that interpretation: it is keeping the blank between then encoded words when it should not be. That bug can be fixed, in get_raw_encoded_word and/or get_parameter, in email._header_value_parser. |
And you are right that this is a very common bug in email programs. So common that I suspect the RFC folks will eventually have to accept it as a de-facto standard. So we do need to support it in the python email library. |
I tried to take a look at the code to see where the fix needs to be and I probably need some help. I looked at the parse tree for the header and it looks something like this: ContentDisposition([Token([ValueTerminal('attachment')]), ValueTerminal(';'), MimeParameters([Parameter([Attribute([CFWSList([WhiteSpaceTerminal(' ')]), ValueTerminal('filename')]), ValueTerminal('='), Value([QuotedString([BareQuotedString([EncodedWord([ValueTerminal('Schulbesuchsbestättigung.')]), WhiteSpaceTerminal(' '), EncodedWord([ValueTerminal('pdf')])])])])])])]) The offending piece of code, which seems to be working as designed is get_bare_quoted_string() in email/_header_value_parser.py. while value and value[0] != '"':
if value[0] in WSP:
token, value = get_fws(value)
elif value[:2] == '=?':
try:
token, value = get_encoded_word(value)
bare_quoted_string.defects.append(errors.InvalidHeaderDefect(
"encoded word inside quoted string"))
except errors.HeaderParseError:
token, value = get_qcontent(value)
else:
token, value = get_qcontent(value)
bare_quoted_string.append(token) It just loops and parses the values. We cannot ignore the FWS until we know that the atom before and after the FWS are encoded words. I can't seem to find a clean way to look-ahead (which can perhaps be used in get_parameters()) or look-back (which can be used after parsing the entire bare_quoted_string?) in the parse tree to delete the offending whitespace. Any example of such kind of parse-tree manipulation in the code base would be awesome! |
The example you want to look at is get_unstructured. That shows both lookback and modification of the parse tree to handle the whitespace between encoded words. |
Thanks for the pointer, David! I created a PR for the fix, would you be able to review it please? |
In general your solution looks good, just a few naming comments and an additional test request. |
Thanks David! I applied the fixes as per your comments, can you please take another look? |
One more tweak to the test and we'll be good to go. |
Sure, fixed as per your comments in the PR. |
I don't see the change to the test in the PR. Did you miss a push or is github doing something wonky with the review? (I haven't used github review in a while and I had forgetten how hard it is to use...) |
I double checked, there should be 4 commits in the PR and last 2 have the changes that you asked for in the test case and NEWS entry. Your previous comment will point at the old diff, you might have to look at the full diff here: https://github.com/python/cpython/pull/17620/files or if you want, this is the diff for the 2 commits with the changes you requested: https://github.com/python/cpython/pull/17620/files/bf2cb76009d72869d9df6550b473b5818ceab311..016ceb3ef00b3b940993d35d539ce63d68437d4f |
Strange that this hasn't been fixed when a fix was submitted literally years ago. |
It looks like it was and the issue just wasn't closed. |
@bitdancer It does not appear to have been fixed. I just ran into it with python 3.10.4:
|
That's a different bug, and it is in the legacy API. The new API handles it correctly:
I don't think this is worth trying to fix in the legacy api; the reason it happens there is probably a result of some deeply embedded assumptions in that code and may not be easy to fix. (What would be worth fixing is making the new API truly be the default policy, but I don't currently have time to shepherd that process). |
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
The text was updated successfully, but these errors were encountered: