-
-
Notifications
You must be signed in to change notification settings - Fork 9.7k
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
Long filenames in "Content-Disposition: attachment; filename" breaks extension #1469
Comments
Does anyone have any thoughts on this? |
I can confirm that this bug is still present in <?php
use PHPMailer\PHPMailer\PHPMailer;
require_once 'vendor/autoload.php';
$mail = new PHPMailer();
$mail->setFrom('[email protected]');
$mail->addAddress('[email protected]');
$mail->Subject = 'Attachment with long filename';
$mail->Body = 'See attachment.';
$mail->addStringAttachment('Acme', '0________1_________2_________3_________4_________5_________6_________712.txt');
$mail->addStringAttachment('Acme', '0________1_________2_________3_________4_________5_________6_________7123.txt');
$mail->addStringAttachment('Acme', '0________1_________2_________3_________4_________5_________6_________71234.txt');
$mail->addStringAttachment('Acme', '0________1_________2_________3_________4_________5_________6_________712345.txt');
$mail->preSend();
$class = new ReflectionClass(PHPMailer::class);
$property = ($class)->getProperty('MIMEBody');
$property->setAccessible(true);
echo $property->getValue($mail); Output (click to expand)
Clients (e.g. Thunderbird) don't ignore the whitespace introduced by the forced line break. |
Here's how Thunderbird formats these long filenames: (click to expand)
Note that |
A fix should be possible in |
Dang, yet another header encoding scheme! That's from RFC2184, as suggested by RC2183. I suspect this is the same problem as in #1525, though the solution there will be to use RFC2047 encoding, which has the added advantage that it works with 8-bit charsets. I think that would probably work here too. That said, 2184 encodes individual fields within a header across multiple lines, whereas 2047 encodes the entire header value; I'm not sure which would be the better way, though I'm tempted more by 2047 because it does not require parsing the header value at all. |
The difference between the two approaches in your example is that the Content-type header is doing normal RFC822 folding, whereas the content-disposition header is doing RFC2184 followed by RFC822. I expect that the former would start doing 2047 encoding if you made your filename long enough. I'm not too keen on header-specific encoding schemes... |
@Synchro Actually Thunderbird never wraps the
|
Trying going over 998 chars in that header; I would expect it to start doing RFC2047 like Apple Mail does. |
I'm on a Windows machine right now, where filenames are limited to 255 characters, so I'm afraid I can't test that. |
Ah, you can probably provoke the same behaviour using a very long subject line - ultimately they're all headers. |
I just tried to send an email using Thunderbird with the following subject, but ironically this resulted in the mail not having any subject at all: |
Ha! It's nice to find bugs like that :) Have a look at #1525 for an example of how Apple mail does it. |
Have you already tried sending an attachment with a long filename using Apple Mail? I don't find it obvious that Apple Mail encodes regular headers and multipart headers the same way. PS: I just got the following long header line in Thunderbird with a 200 character subject:
|
There are two thresholds for line length in email: 76 and 998. Senders should limit line length to 76 and must limit line length to 998. You can see in your earlier examples that it attempts to do the former by folding the A 200 char subject is longer than 76, but won't cause problems because it's well below 998, so it will work as is with no additional encoding. |
The bug here could be that it's forcing folding at 76 chars instead of letting it expand up to 998. |
I find it a bit counterintuitive that the Lines 2942 to 2947 in 3c1c6e0
The responsible wrapping happens in line 3171 here btw: Lines 3166 to 3175 in 3c1c6e0
|
Replacing this: Lines 2941 to 2954 in 3c1c6e0
With the following resulted in a slightly better folding (at least for the if (!empty($name)) {
$value = sprintf(
'%s; name="%s"%s',
$type,
$this->secureHeader($name),
static::$LE
);
} else {
$value = $type;
}
$mime[] = sprintf(
'Content-Type: %s%s',
$this->encodeHeader($value),
static::$LE
); Result (click to expand)
|
This could also be done for Lines 2974 to 2996 in 3c1c6e0
Replace by: if (preg_match('/[ \(\)<>@,;:\\"\/\[\]\?=]/', $encoded_name)) {
$value = sprintf(
'%s; filename="%s"',
$disposition,
$encoded_name
);
} else {
if (!empty($encoded_name)) {
$value = sprintf(
'%s; filename=%s',
$disposition,
$encoded_name
);
} else {
$value = sprintf(
'%s%s',
$disposition
);
}
}
$mime[] = sprintf(
'Content-Disposition: %s%s',
$this->encodeHeader($value),
static::$LE . static::$LE
); Result (click to expand)
|
@Synchro Would you see any problem with this approach (as kind of suggested by yourself): https://github.com/PHPMailer/PHPMailer/pull/1840/files Result (click to expand)
|
* Always Q-encode headers exceeding maximum length Previously, headers exceeding the maximum line length without any special characters were only folded. This lead to problems with long filenames (#1469) and long headers in general (#1525). Now, long headers are always Q-encoded (and still folded). * Use ASCII as Q-encoding charset if applicable Previously, headers were Q-encoded using the message charset, e.g. UTF-8. This is excessive for ASCII values, as it requires a unicode engine. Now, we use ASCII if we only find 7-bit characters. * Separate header encoding from encoding selection * Use ASCII for B-encoding as well * Refactor max line length calculation Previously, we calculated the maximum line length for header encoding both for B- and Q-encoding, even though they share the same limits. Now, we calculate these once for both.
Changes from #1840 should have fixed this issue - please report if you still see this problem. |
I have just updated to version 6.1.4 and I still have this issue. I guess my code is nothing special:
|
@chrisSCM Would you be able to post the relevant lines of the email source code including the wrapped filename? |
Of course - thanks for your fast reply. (I had to anonymize a few fields first)
|
I don't see that wrapping anything? It's doing regular folding that looks correct, but none of it has any extra encoding or splitting of filenames? |
I'm not into the topic enough, I just noticed that for some emails the attachment file name is fine, and for some it's cut off. But just now I think I've found the problem: There's a '/' character in the filename! I guess that's not something the lib likes too much!? Any other special characters to be avoided? |
It's not so much the lib as the file system - given a path containing reserved characters, particularly those that are used as path delimiters ( I don't think there's anything that PHPMailer can or should do – escaping the filename is up to you, and I think any escaping should be carried through to the filenames that appear in the message. |
Thanks - I didn't see any connection with the filesystem, because it's only the attachment name, the actual content does come from the filesystem, yes, but under a different name. So that has to mean, somewhere between those two operations - between setting the attachment name and the actual email sending - there are filesystem operations included, regarding the attachment name!? In that case your explanation makes perfect sense. |
Well, of course - it has to load the file and encode it into the email, and it uses the filename to do so. For example, when you call Attachment encoding is something that could use refreshing in PHPMailer – it's currently very memory intensive. Ideally it would be rewritten using a generator that never needed to load the whole file at once. |
In my case, it doesn't load any file.
...which is $string_PDF, contains the PDF content! That's why I'm so puzzled, sorry. The $attachmentFileName contains the filename of the attached file, it doesn't exist in the filesystem - unless during processing it's written to the filesystem for whatever reason. You mean for encoding it has to write the attachment file to hard disk first? In the documentation it says "Add a string or binary attachment (non-filesystem)." (escapeshellarg didn't help, btw, but I guess I'll just remove such chars for now - and yes, that fixes it (as expected)) |
What was the value of |
Oh - your code example used I'm not quite sure what should happen in this case. I guess whether it is treated as a filename or a relative path is dependent on the receiver's OS, if PHPMailer passes it through untouched. |
- Problem description
I've been getting several complaints by customers about them receiving mail with attachments which they cannot open because of some malformed file extension.
It turns out the filenames are folded after 76 characters. This SEEMS RFC-compliant.
The extra spacing character is interpreted as a space and, of course, doesn't cause problems when not in the extension.
When sending the same, original, file through Outlook (client or web), GMail or any other hosted service, the file arrives with the complete and untouched extension.
- Code/Steps to reproduce
Send an email and include a file with a filename just over 76 characters long.
- Debug Output
- Possible solution
As said, the current implementation SEEMS RFC-compliant. But RFC 2184 Section 3 speaks about Parameter Value Continuation and ways to break too long parameter values into smaller chunks without folding and altering the original values.
Shortening filenames is not an option because:
- System-info
PHPMailer 6.0.5 (Composer)
PHP 7.1
CentOS 6.9/7.4
Can you look into this?
Thank you!
The text was updated successfully, but these errors were encountered: