addAttachment v Nette/Mail – název přílohy?

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
Martin Mates
Člen | 179
+
0
-

Pokud přidám do mailu PDF přílohu $mail->addAttachment(WWW_DIR .'test.pdf', null, 'application/pdf'); přijde v mailu příloha sice v pořádku, ale s názvem „Mail Attachment.pdf“.

Můžu to nějak ovlivnit, jaký bude mít příloha název? Druhý parametr metody addAttachment moc nechápu.

Díky!

Patrik Votoček
Člen | 2221
+
0
-

Zajímavé příloha by totiž měla přijít s názvem test.pdf viz https://api.nette.org/…ail.php.html#300

Martin Mates
Člen | 179
+
0
-

Včera jsem to ještě zkoušel a problém bude v nějakém limitu názvu přílohy. Když je název moc dlouhý, tak to přijde jako „Mail Attachment“. Nevím přesně jaký ten limit je. Každopádně to nebude záležitost Nette. Díky!

kravčo
Člen | 721
+
0
-

Nepomôže tu dĺžku skresať lomítko?

WWW_DIR .'/test.pdf'
westrem
Člen | 398
+
0
-

kravčo napsal(a):

Nepomôže tu dĺžku skresať lomítko?

WWW_DIR .'/test.pdf'

+1

Podla mna tam chyba lomitko, a potom basepath vrati asi nieco ine co cakas ..

Patrik Votoček
Člen | 2221
+
0
-

Ale pokud by mu tam chybělo lomítko tak mu to ten soubor ani nenačte ne?

MzK
Člen | 127
+
0
-

Škoda že nejde jako to nefunguje takto:

<?php
$mail->addAttachment('http://www.server.cz/test.pdf');
?>

je s tím někde problém? jakmile zadám soubor s přílohou, email se odešle, ale nikdy ho nepřijmu.. Žádná chyba se při odesílání nevypisuje, může být na server zakázáno odesílání emialů s přílohou?

ptacek.pavel
Člen | 27
+
0
-

Narazil jsem na tento problém také při odesíláni .pdf souborů – soubor mailem dorazil jako „Unnamed attachment“ bez přípony. Při snaze to vyřešit jsem narazil na zajímavý efekt odesílání příloh, který se děje pouze pokud je název souboru vč. přípony delší než cca 40 znaků. Během toho co jsem debuggoval, narazil jsem na efekt použití classy Nette\Mail\MailMimePart, který chybně escapuje hlavičku Content-Disposition v případě, že se jedná o delší název souboru.

Pokud nahodím dlouhý soubor, pak mi v mailu dorazila hlavička takováto:

----------51beff7ff9ea7f871ceccb336e59173c
Content-Type: application/pdf
Content-Transfer-Encoding: base64
Content-Disposition: attachment

(všimněte si, že tam není jméno souboru)

Což je způsobeno tím, že samotný MailMimePart definuje hlavičku takto:

MIME-Version: 1.0
X-Mailer: Nette Framework
Date: Tue, 12 Apr 2011 15:18:17 +0200
From: test
Message-ID: <5bc872177f38759b94033ffcae41912e@manghi.loc>
Content-Type: multipart/mixed;
	boundary="--------f814f502c0f8e76c7f4c72054c3611cd"

Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 7bit

Content-Type: application/octet-stream
Content-Transfer-Encoding: base64
Content-Disposition: =?UTF-8?B?YXR0YWNobWVudDsgZmlsZW5hbWU9InRvaGw=?=
	=?UTF-8?B?ZS1qZS1zdXBlci1kbG91aGVqLXV0ZjgtbmF6ZXYtcGRma2Eta3RlcmU=?=
	=?UTF-8?B?ai1ieS1tZWwtZGVsYXQtcHJvYmxlbXktYS10YWstdG8tdGFraGxlLXA=?=
	=?UTF-8?B?b2ptZW5vdmF2YW0tZXNjcnp5YWllZHRuLXBkZi5wZGYi?=

Zkoumal jsem i stránku http://www.velocityreviews.com/…lenames.html kde řeší, jak „správně“ zalamovat hlavičku s dlouhými názvy souborů tak, aby to mail server & klient sežral v pohodě.

U mě escapování nebylo potřeba, neboť jsem všechny názvy souborů nejdříve prohnal skrz Nette\Utils\String::webalize a zachoval příponu (jinak vznikal pro změnu bordel ve filesystemu → soubory jsou přílohy z kontaktního formuláře). Nakonec jsem si doplnil hotfix, který zabraňuje escapování (prostě proto, že u mě to není potřeba) – ale není to elegantní řešení a proto postuju zde, s prosbou o nějaké lepší :-)

Nette\Mail\MailMimePart 269:

<?php
/**
 * Returns encoded message.
 * @return string
 */
public function generateMessage()
{
        $output = '';
        $boundary = '--------' . md5(uniqid('', TRUE));

        foreach ($this->headers as $name => $value) {
                // Hotfix by Pavel Ptacek -> I don't care about wrapping up,
                // somehow (elsewhere in the file?) it gets wrapped automatically
                // The key is to prevent utf escape of the filename itself
                if($name === 'Content-Disposition') {
                    $output .= $name . ': ' . $value;
                    $output .= self::EOL;
                    continue;
                }

                $output .= $name . ': ' . $this->getEncodedHeader($name);
                if ($this->parts && $name === 'Content-Type') {
                        $output .= ';' . self::EOL . "\tboundary=\"$boundary\"";
                }
                $output .= self::EOL;
        }
        $output .= self::EOL;

        $body = (string) $this->body;
        if ($body !== '') {
                switch ($this->getEncoding()) {
                case self::ENCODING_QUOTED_PRINTABLE:
                        $output .= function_exists('quoted_printable_encode') ? quoted_printable_encode($body) : self::encodeQuotedPrintable($body);
                        break;

                case self::ENCODING_BASE64:
                        $output .= rtrim(chunk_split(base64_encode($body), self::LINE_LENGTH, self::EOL));
                        break;

                case self::ENCODING_7BIT:
                        $body = preg_replace('#[\x80-\xFF]+#', '', $body);
                        // break intentionally omitted

                case self::ENCODING_8BIT:
                        $body = str_replace(array("\x00", "\r"), '', $body);
                        $body = str_replace("\n", self::EOL, $body);
                        $output .= $body;
                        break;

                default:
                        throw new \InvalidStateException('Unknown encoding');
                }
        }

        if ($this->parts) {
                if (substr($output, -strlen(self::EOL)) !== self::EOL) $output .= self::EOL;
                foreach ($this->parts as $part) {
                        $output .= '--' . $boundary . self::EOL . $part->generateMessage() . self::EOL;
                }
                $output .= '--' . $boundary.'--';
        }

        return $output;
}
?>

edit: Ještě doplním, že problém není v samotném escapování názvu souboru, problém je v tom, že hlavička Content-disposition (resp. její parametr filename="") musí být na jednom řádku. Následující hlavičky by tedy měli být OK:

Content-Type: application/octet-stream
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="test.pdf"

Content-Type: application/octet-stream
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
	filename="tohle je hrozne dlouhe jmeno attachmentu ktere nema zalomeni a tim padem projde.pdf"

Oproti tomu problém způsobuje tento formát:

Content-Type: application/octet-stream
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
	filename="tohle je hrozne dlouhe jmeno
	attachmentu ktere ma zalomeni a tim
	padem to dojde jako nepojmenovana priloha BEZ PRIPONY.pdf"

Můj hotfix nahoře funguje tedy jenom a pouze proto, že jméno souboru nemá mezeru → spíše celý parametr filename=„…“ nemá mezeru. A tím pádem nedojde k zalomení, na základě čehož to všechny mail servery & klienti vezmou bez problému.

Editoval ptacek.pavel (12. 4. 2011 16:03)

22
Člen | 1478
+
0
-

já se vrátím k původní otázce..jak můžu přejmenovat jméno přílohy? Nerad bych posílal file s ID usera…víte někdo, jak to změnit on the fly?

kravčo
Člen | 721
+
0
-
<?php

$file = 'pick-your-name.zip';
$content = file_get_contents(__DIR__ . '/files/'
	. 'very long named file that needs to be attached to an email.zip');
$contentType = 'application/zip';

$mail->addAttachment($file, $content, $contentType);
22
Člen | 1478
+
0
-

chápu to dobře viz.: https://api.nette.org/…age.php.html#302
ve $file je cesta k souboru, to se využije pro získání $content, pokud je NULL, ze kterého se získá $contentType a s $file se vezme konec, jak název souboru.
Takže asi jsi se akorát upsal $filename ⇒ $file ?

Editoval 22 (27. 4. 2011 0:43)

kravčo
Člen | 721
+
0
-

Áno, bol to preklep. Presnejšie, basename($file) je názov prílohy, $content jej binárny obsah a $contentType jej MIME typ (štvrtý parameter nás teraz netrápi). Akonáhle je $content a/alebo $contentType NULLový, prirodzeným spôsobom sa dopočíta:

  • do $content sa pokúsi načítať obsah súboru $file
  • do $contentType detekuje typ z obsahu $content (nie nutne z obsahu súboru $file)
22
Člen | 1478
+
0
-

díky za osvětu! Kdyby 4. parametr byl vlastní název přílohy, tak by to bylo geniální :-)

kravčo
Člen | 721
+
0
-

Prvý parameter je vlastný názov prílohy, v prípade, že naplníš $content!

22
Člen | 1478
+
0
-

jo, to je mi jasné, ale to jsou 3 řádky navíc, kdyby to byl parametr 4, tak by to zvladla celé sam funkce createAttachement, až budu velkej, tak bych to napsal do pool requestu, ale tak dobrej ještě nejsem :-)

Editoval 22 (27. 4. 2011 10:51)

kravčo
Člen | 721
+
0
-

Nerozumiem…

22
Člen | 1478
+
0
-

tvoje řešení:

<?php

$file = 'pick-your-name.zip';
$content = file_get_contents(__DIR__ . '/files/'
        . 'very long named file that needs to be attached to an email.zip');
$contentType = 'application/zip';

$mail->addAttachment($file, $content, $contentType);

moje vize:

$mail->addAttachment($path, $content = NULL, $contentType = NULL, $publicName = NULL);

pokud je cesta a zbytek NULL, tak všechno obslouží Nette, pokud zadám $publicName, tak se nevolá basename(), ale dosadí se 4. parametr

Editoval 22 (27. 4. 2011 15:18)

pjoter
Člen | 118
+
0
-

+1 pro vlastni nazev prilohy, generuju pdfko na odkazu jako ?do=invoice&id=35 a pak se tak i jmenuje priloha, takze si musim ohnout Nette ale tohle by tam mohlo urcite byt od zakladu

petr.pavel
Člen | 535
+
0
-

@pjoter: Tohle vlákno je asi už mimo. Koukám do zdrojáku a vidím, že když dodáš vlastní $content, tak $file může být cokoliv – ten tvůj vlastní název přílohy.