Mail-Attachements mit Umlauten werden umbenannt (3.8)

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Erich1959
User
Beiträge: 6
Registriert: Samstag 28. März 2020, 14:52

Ich will in Windows eine Datei aus einem Ordner per e-mail an mich schicken und das funktioniert grundsätzlich auch, aber wenn im Dateinamen ein Umlaut (oder ein anderes Nicht-ASCII-Zeichen) enthalten ist dann wird das Attachment zwar gesendet aber umbenannt und zwar zB von "KÖST.txt" in "attych3r.txt".

Nachdem der Umlaut im "Subject" kein Problem ist müsste ich wohl irgendwo (im email-header? - wie geht das) festhalten, dass der Dateiname auch Umlaute enthalten kann - weiß vielleicht jemand wie das geht?

Mein Code

import smtplib
import os # damit os.path.join funktioniert
from email.message import EmailMessage

# vgl. https://stackoverflow.com/questions/954 ... on-smtplib

# Variablen zum Testen definieren (das File muss im angegebenen Ordner stehen)

ordner = r'G:\Posteingang\Ordner_Test'
file = 'KÖST.txt' # - Attachement wird umbenannt???
# file = 'KOEST.txt' # - Attachement wird NICHT umbenannt

path = os.path.join(ordner, file) # stellt Dateinamen + Pfad in die Variable

# me == the sender's email address
me = " EINGEBEN " # Mail-Adresse des Absenders!
# you == the recipient's email address
you = " EINGEBEN " # Mail-Adresse des Empfängers!

msg = EmailMessage() # muss VOR Verwendung von msg stehen

msg["From"] = me
msg["Subject"] = file # Umlaute im Subject sind kein Problem?!
msg["To"] = you
msg.set_content("This is the message body")
# msg.add_attachment(open(filename, "r").read(), filename=path)
msg.add_attachment(open(path, "r").read(), filename=file) # Attachement mit Umlaut wird umbenannt!

# s = smtplib.SMTP('smtp.sendgrid.net', 587)
s = smtplib.SMTP( EINGEBEN ) # (" Mail-Server", Port)
# s.login(USERNAME, PASSWORD)
s.send_message(msg)

Für einen Lösungsvorschlag wäre ich sehr dankbar :-)
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ich wuerde mal mit dem marrow-mailer Paket experimentieren. Das kannst du mit pip installieren, und das hat all die ganzen Schweinereien die Email mit Umlauten und Anhaengen so mit sich bringt eigentlich eingebaut.
Benutzeravatar
__blackjack__
User
Beiträge: 14052
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Erich1959: Der Umlaut im Subject ist sehr wohl ein Problem. Das scheint zufällig zu funktionieren, garantiert ist das aber nicht. Und die Umbenennung des Anhangs passiert auch nicht in Deinem Python-Code sondern entweder bei einem der beteiligten Mailserver oder in Deinem Mail-Client. So sieht nämlich die Nachricht aus die Du zusammenbaust (From/To habe ich nicht gesetzt):

Code: Alles auswählen

In [41]: print(msg)                                                             
Subject: KÖST.txt
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="===============5528799123829735311=="

--===============5528799123829735311==
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: 7bit

This is the message body

--===============5528799123829735311==
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment; filename="KÖST.txt"
MIME-Version: 1.0

foo

--===============5528799123829735311==--
Die Headerdaten müssen aber auf ASCII beschränkt sein. Der Betreff könnte dann beispielsweise so kodiert werden ``Subject: =?utf-8?b?S8OWU1QudHh0?=`` und die Dateiname für den Anhang so ``Content-Disposition: attachment; filename*=utf-8''K%C3%96ST.txt``. Das ist das was `marrow.mailer` macht.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Erich1959
User
Beiträge: 6
Registriert: Samstag 28. März 2020, 14:52

Danke - aber am Mail-Server bzw Client kann es nicht liegen (weil ich solche Nachrichten über den gleichen Server an den selben Client auch aus unserem Scanner verschicke und dabei wird sowohl Subject als auch Name des Attachements korrekt mit Umlauten beim Empfänger angezeigt).

Das Problem dürfte mE in der Codierung 'Content-Type: text/plain; charset="utf-8"" beim Senden der Nachricht liegen, wobei intessanterweise Umlaute im Subject und im Body richtig dargestellt werden (da hat scheinbar Version 3 von Python deutliche Verbesserungen gebracht) und nur das Attachement umbenannt wird.

Es ist scheinbar so dass in Version 3.8 mit "from email.message import EmailMessage" bzw. Verwendung von "msg = EmailMessage()" die Sonderzeichnen sowieso schon richtig umgesetzt werden und nur der Name des Attachements aus unerfindlichen Gründen nicht.

Ich habe schon versucht eine neue Variable "fileatt" einzubauen und die Code-Zeile auf

msg.add_attachment(open(path, "r").read(), filename=fileatt) # dh ich weise der Variable filename den utf-8 codierten Wert zu

zu ändern (dh ich müsste die Variable "file" so umcodieren, dass "KÖST.txt" zu "''K%C3%96ST.txt" wird), das funktioniert aber auch nicht - interessanterweise wird dabei das Attachment als ''K%C3%96ST.txt" ausgegeben und wenn ich diese Variable auf "?b?S8OWU1QudHh0?" setze wird das Attachement als "?b?S8OWU1QudHh0?.txt" ausgegeben.

ME ist das Problem die Zeile "Content-Transfer-Encoding: 7bit" in den übermittelten Mail-Nachrichten und die Frage wäre ob es möglich wäre meinen Python-Code so abzuändern dass diese Zeile geändert wird (ich nehme an zB auf

Content-Type: text/plain; charset=ISO-8859-1
Content-transfer-encoding: base64

- vgl. https://www.w3.org/Protocols/rfc1341/5_ ... oding.html); annehmen würde ich dass das zB in

https://docs.python.org/2/library/quopri.html

beschrieben wird, aber das ist mir ehrlich gesagt zu hoch.
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Niemand hat bestritten, dass der Server oder ein anderer Client das kann. Und auch Python kann das. Es ist einfach nur komplizierter, als man das so denkt, und weil es so kompliziert ist, haben sich Leute was einfallen lassen, und das Problem *einmal* fuer alle geloest. Das ist das Paket "marrow mailer".
Benutzeravatar
__blackjack__
User
Beiträge: 14052
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Erich1959: Der Mail-Server oder der Client benennen das um. Ich habe Dir doch gezeigt wie das Python verlässt: Kaputt/falsch, denn die ö's dürfen nicht in den Headerdaten stehen, denn die sind auf ASCII beschränkt. Das heisst entweder einer der beteiligeten Server oder der Mailclient bemerkt, dass das kaputt ist und ersetzt es durch einen Wert der nicht kaputt ist.

Das hat auch nichts mit dem Scanner zu tun. *Der* wird halt die Nachricht korrekt zusammenbauen. Dann kommt sie natürlich auch korrekt an.

``Content-Type: text/plain; charset="utf-8"`` bezieht sich auf die Nutzlast des Nachrichtenteils zu dem dieser Header gehört. Das hat nichts mit dem Betreff oder dem Dateinamen der Anlage zu tun.

Und noch mal: das der Betreff richtig angezeigt wird ist Zufall. Der ist auch kaputt, denn wie schon mal gesagt müssen die Header reines ASCII sein, Sonderzeichen entsprechend kodiert werden, *und* es muss auch angegeben sein in welcher Zeichenkodierung sie vorliegen.

Der Body hingegen ist korrekt kodiert. Das ``Content-Transfer-Encoding: 7bit`` für den Nachrichtenteil ist korrekt, denn da kommt nur ASCII drin vor. Was soll das also für ein Problem machen? Und wenn man da Umlaute im Body-Text hat, dann ändert sich das ``Content-Transfer-Encoding`` auch zu `quoted-printable`:

Code: Alles auswählen

Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: quoted-printable
MIME-Version: 1.0

This is a t=C3=A4st.
Wenn Du den Dateinamen von der Anlage auf "K%C3%96ST.txt" setzt, dann ”funktioniert” das natürlich in dem Sinne, dass das dann halt der Name ist, und der wird auch nicht von einem der beteiligeten Webserver oder dem Mail-Client ersetzt, weil der formal korrekt ist, da er nur aus ASCII-Zeichen besteht, also aus den erlaubten Zeichen in Headern.

Das gilt auch für "?b?S8OWU1QudHh0?". Das ist aber nun auch sehr wild geraten nun genau diesen Teil von einer (möglichen) Kodierung des Betreffs für den Dateinamen zu verwenden.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Erich1959
User
Beiträge: 6
Registriert: Samstag 28. März 2020, 14:52

Heureka - gefunden und soeben ausprobiert (für unseren Server modifiziert)

http://code.activestate.com/recipes/5781...-python-3/

funktioniert - das attachment "KÖST.txt" wird verschickt und landet in der InBox des Empfängers als "KÖST.txt".

:) :) :)
Benutzeravatar
__blackjack__
User
Beiträge: 14052
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Der Link funktioniert nicht.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Erich1959
User
Beiträge: 6
Registriert: Samstag 28. März 2020, 14:52

Sorry, nochmals der Link

http://code.activestate.com/recipes/578 ... -python-3/

so sollte es passen
Benutzeravatar
__blackjack__
User
Beiträge: 14052
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Wieder mal ein Fall von Typannotationen die anscheinend keiner geprüft hat. ☹️ Die Funktionssignatur behauptet `port` wäre eine Zeichenkette, `smtplib.SMTP()` erwartet aber eine ganze Zahl. Scheint ja auch mit einer Zeichenkette zu funktionieren, aber nichts in der Dokumentation von `smtplib.SMTP.__init__()` deutet darauf hin, dass das garantiert ist. Bei `formatdate()` wird eine 1 statt `True` übergeben. Und bei den beiden `MIMEText`-Objekten wird der Text als Bytes statt als Zeichenkette übergeben, was auch beides falsch ist, und wohl auch nur durch Zufall funktioniert. Das hätte alles bei einer statischen Typprüfung auffallen sollen.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Erich1959
User
Beiträge: 6
Registriert: Samstag 28. März 2020, 14:52

Die von mir aus obigem Beispiel abgeleitete - einfachere - Lösung (funktioniert auch):

Code: Alles auswählen


import os
import smtplib
from email.utils import formataddr # unnötig?

from email.header import Header
from email import encoders

from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email.mime.text import MIMEText
from email.mime.image import MIMEImage

# vgl. http://code.activestate.com/recipes/578150-sending-non-ascii-emails-from-python-3/
# von https://docs.python.org/3/library/email.examples.html

ordner = r'C:\PythonTest\Ordner_Test'
file = 'KÖST.txt' # - Attachement wird umbenannt???

def sendMessage (file, ordner): 

    me = "Absender-Mailadresse"
    you = "Empfänger-Mailadresse"
    
    attachment = os.path.join(ordner, file)

    msg = MIMEMultipart('mixed')
    msg['Subject'] = (file_short)
    msg['From'] = me
    msg['To'] = you
    msg.preamble = 'This is a multi-part message in MIME format.'

    fname = os.path.basename(attachment) # Attachement samt Pfad in Variable als bytes und string

    with open(attachment, 'rb') as f:
        msg_attach = MIMEBase('application', 'octet-stream')
        msg_attach.set_payload(f.read())
        encoders.encode_base64(msg_attach) # encoded attachment in base64
        msg_attach.add_header('Content-Disposition', 'attachment',
                                filename=(Header(fname, 'utf-8').encode())) # codiert filename als utf-8
        msg.attach(msg_attach)
           
    # Send the message via our own SMTP server.
    s = smtplib.SMTP("IP-Adresse", Port)
    s.send_message(msg)
    s.quit()

sendMessage (file, file_short, ordner) # Funktion ausführen zum Testen

Erich1959
User
Beiträge: 6
Registriert: Samstag 28. März 2020, 14:52

letzte Zeile muss richtig heissen

Code: Alles auswählen

sendMessage (file, ordner) # Funktion ausführen zum Testen
Benutzeravatar
__blackjack__
User
Beiträge: 14052
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Erich1959: Auch mit der Änderung funktioniert das nicht. `Port` und `file_short` werden nirgends definiert.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Antworten