I need to generate a MIME attachment that contains a Base64 encoded file. However what I need to also allow for is encoding the attachment WITHOUT any new lines. The code I have is as followed:
msg_obj = MIMEMultipart()
msg_atch = MIMEBase(mime_type, mime_subtype)
msg_atch.set_payload(file_data)
Encoders.encode_base64(msg_atch)
msg_obj.attach(msg_atch)
What I have tried to perform to remove the new lines in the attach base64 message was this:
msg_obj = MIMEMultipart()
msg_atch = MIMEBase(mime_type, mime_subtype)
msg_atch.set_payload(file_data)
Encoders.encode_base64(msg_atch)
msg_atch.strip()
msg_obj.attach(msg_atch)
However this failed to change the results of the data. If anyone has any ideas on how to allow for this, it would be great.
I noticed in the penultimate line of your 2nd sample code, you call the msg_atch.strip() function. The problem with this is that there isn't any function strip() of MIMEBase.
What you probably want to do is something along the lines of this:
msg_obj = MIMEMultipart()
msg_atch = MIMEBase(mime_type, mime_subtype)
msg_atch.set_payload(file_data)
Encoders.encode_base64(msg_atch)
msg_atch._payload = msg_atch._payload.replace('\n','')
msg_obj.attach(msg_atch)
The MIMEBase._payload string is the actual (in this case, base64) content used by the attachment.
This code will take the inner content of the MIMEBase attachment and eliminate the extra newlines - including the ones inside to provide nice formatting of base64 text for "human readability" (my question is why they even bother). If you just want to get rid of the newlines at the end, just use msg_atch._payload = msg_atch._payload.rstrip('\n').
Keep in mind that the header of the attachment (Content-Type: application/octet-stream and MIME-Version: 1.0 are parts) require these newlines.
Also, try to remember that you shouldn't normally be editing internal variables in this fashion. However, one of the things I find nice about Python is that there are really no private members of a class, so you can modify whatever you want in a class. We can do whatever we want, especially if it's a special condition.
Happy Coding!
Related
I'm trying to build a python mailserver that rewrites certain URLs (given in a text file).
Plaintext messages work without problems, but multipart (HTML) E-Mails are a bit of concern to me. I can't find a proper way to rebuild the message after modifying the body and replace functions do not exist for email in python.
What I have currently (for plaintext messages, currently without replacement):
if(not msg.is_multipart()):
for part in msg.walk():
parttype = part.get_content_type()
partmaintype = parttype.split("/")[0]
partsubtype = parttype.split("/")[1]
parttype=partmaintype + "/" + partsubtype
bodypart = part.get_body(('plain','html'))
#bodypart = bodypart.get_content().encode()
bodypart = bodypart.get_content()
print(bodypart)
newmsg.set_content(bodypart)
#print(newmsg)
newdata = bytes(newmsg)
The _structure(msg) function is in knowledge to me, but I cannot find a way to preserve this message structure (to rebuild the email with a new message object). I also know about the msg.walk() function, but when used with multipart message contents, the subcontents occur twice.
Thanks a lot in advance!
An email comes in with the following header from an #outlook.com address
Message-ID:
<DM5PR1101MB2074A897F300D11D32C71B75EC049#DM5PR1101MB2074.namprd11.prod.outlook.com>
To respond with Python I put that message identifier in the In-Reply-To header
from email.message import EmailMessage
msg = EmailMessage()
msg['In-Reply-To'] = '<DM5PR1101MB2074A897F300D11D32C71B75EC049#DM5PR1101MB2074.namprd11.prod.outlook.com>'
However, when I print this message the header appears as follows:
In-Reply-To: =?utf-8?q?=3CDM5PR1101MB2074A897F300D11D32C71B75EC049=40DM5PR11?=
=?utf-8?q?01MB2074=2Enamprd11=2Eprod=2Eoutlook=2Ecom=3E?=
And when it arrives in Outlook it's not put in the same conversation thread as the original message.
Does anyone know why this may be happening and/or know of any work-around?
I suspect that this is happening because Python is trying to split the header across multiple lines since this header is above the RFC-specified line length and when it splits across multiple lines it starts each line with '?utf-8?q and converts the < character to ?=3C, etc... Whereas Outlook is expecting the header to be of the format
In-Reply-To':
<DM5PR1101MB2074A897F300D11D32C71B75EC049#DM5PR1101MB2074.namprd11.prod.outlook.com>'
Perhaps if someone knows how to force the header to be in this format that would suffice as a work-around.
Any help would be greatly appreciated :)
I am developing an app which prompts the user to upload a file which is then available for download.
Here is the download handler:
class ViewPrezentacje(blobstore_handlers.BlobstoreDownloadHandler, BaseHandler):
def get(self,blob_key):
blob_key = str(urllib.unquote(blob_key))
blob_info=blobstore.BlobInfo.get(blob_key)
self.send_blob(blob_info, save_as=urllib.quote(blob_info.filename.encode('utf-8')))
The file is downloaded with the correct file name (i.e. unicode literals are properly displayed) while using Chrome or IE, but in Firefox it is saved as a string of the form "%83%86%E3..."
Is there any way to make it work properly in Firefox?
Sending filenames with non-ASCII characters in attachments is fraught with difficulty, as the original specification was broken and browser behaviours have varied.
You shouldn't be %-encoding (urllib.quote) the filename; Firefox is right to offer it as literal % sequences as a result. IE's behaviour of %-decoding sequences in the filename is incorrect, even though Chrome eventually went on to copy it.
Ultimately the right way to send non-ASCII filenames is to use the mechanism specified in RFC6266, which ends up with a header that looks like this:
Content-Disposition: attachment; filename*=UTF-8''foo-%c3%a4-%e2%82%ac.html
However:
older browsers such as IE8 don't support it so if you care you should pass something as an ASCII-only filename= as well;
BlobstoreDownloadHandler doesn't know about this mechanism.
The bit of BlobstoreDownloadHandler that needs fixing is this inner function in send_blob:
def send_attachment(filename):
if isinstance(filename, unicode):
filename = filename.encode('utf-8')
self.response.headers['Content-Disposition'] = (
_CONTENT_DISPOSITION_FORMAT % filename)
which really wants to do:
rfc6266_filename = "UTF-8''" + urllib.quote(filename.encode('utf-8'))
fallback_filename = filename.encode('us-ascii', 'ignore')
self.response.headers['Content-Disposition'] = 'attachment; filename="%s"; filename*=%s' % (rfc6266_filename, fallback_filename)
but unfortunately being an inner function makes it annoying to try to fix in a subclass. You could:
override the whole of send_blob to replace the send_attachment inner function
or maybe you can write self.response.headers['Content-Disposition'] like this after calling send_blob? I'm not sure how GAE handles this
or, probably most practical of all, give up on having Unicode filenames for now until GAE fixes it
I want to make postfix send all emails to a python script that will scan the emails.
However, how do I pipe the output from postfix to python ?
What is the stdin for Python ?
Can you give a code example ?
Rather than calling sys.stdin.readlines() then looping and passing the lines to email.FeedParser.FeedParser().feed() as suggested by Michael, you should instead pass the file object directly to the email parser.
The standard library provides a conveinience function, email.message_from_file(fp), for this purpose. Thus your code becomes much simpler:
import email
msg = email.message_from_file(sys.stdin)
To push mail from postfix to a python script, add a line like this to your postfix alias file:
# send to emailname#example.com
emailname: "|/path/to/script.py"
The python email.FeedParser module can construct an object representing a MIME email message from stdin, by doing something like this:
# Read from STDIN into array of lines.
email_input = sys.stdin.readlines()
# email.FeedParser.feed() expects to receive lines one at a time
# msg holds the complete email Message object
parser = email.FeedParser.FeedParser()
msg = None
for msg_line in email_input:
parser.feed(msg_line)
msg = parser.close()
From here, you need to iterate over the MIME parts of msg and act on them accordingly. Refer to the documentation on email.Message objects for the methods you'll need. For example email.Message.get("Header") returns the header value of Header.
I need to generate a multipart/mime message to send as a response to a HTTP request but am hitting either a bug or limitation in the Python email.* package.
The problem is that using Python 2.6, the message.as_string() call below generates a string with \n rather that CRLF as the line endings:
message = MIMEMultipart()
for image in images:
f = image.open('rb')
img = MIMEImage(f.read(), _encoder=encode_7or8bit)
message.attach(img)
message.as_string()
There doesn't seem to be any way to persuade it to use the (MIME standard) CRLF. The Generator class that seems it should be able to do this, doesn't.
What have other people done to get round this?
This was a bug in Python that has now been fixed: http://hg.python.org/lookup/r85811
It should now be possible to use the MIME libraries over non-email transports and have sensible things happen.
What about a simple hack
message.as_string().replace('\n', '\r\n')
? Inelegant, but should work (and a bug report should be entered at the Python tracker).