def mime_message(request): message = email.mime.multipart.MIMEMultipart() message["To"] = "*****@*****.**" message["From"] = "*****@*****.**" message["Subject"] = "A message" message.attach(email.mime.text.MIMEText("Hello World")) return message
def forward_msg(self, msg, from_, to, subject=None): message = email.mime.base.MIMEBase("multipart", "mixed") message["From"] = from_ message["To"] = to message["Subject"] = subject or "Fwd: {}".format(msg['subject']) message.attach(msg) conn = self._connect() conn.send_message(message) conn.quit()
def get_mail_messege(self, recipient): message = MIMEMultipart("alternative") message["Subject"] = self.subject message["From"] = self.EMAIL_HOST_USER message["To"] = recipient if self.subtype == 'html': part = MIMEText(self.body, 'html') else: part = MIMEText(self.body, 'plain') message.attach(part) return message
def send_mail(plan, EMAIL): message = MIMEMultipart() message["From"] = EMAIL message["To"] = EMAIL message["Subject"] = "AI_BOT" message.attach(MIMEText(plan, "plain")) text = message.as_string() context = ssl.create_default_context() with smtplib.SMTP_SSL("smtp.gmail.com", 465, context=context) as server: server.login("*****@*****.**", os.environ['EMAIL_PASSWORD']) server.sendmail("*****@*****.**", EMAIL, text)
def generate_message_from_payloads( payloads, message = None ): if message == None: message = email.mime.multipart.MIMEMultipart(payloads.get_content_subtype()) for payload in payloads.get_payload(): if( type( payload.get_payload() ) == list ): message.attach(generate_message_from_payloads(payload)) else: message.attach(payload) return message
def sendEmails(emails): fromaddr = "*****@*****.**"#'*****@*****.**' username = "******"#'*****@*****.**' password = "******"#'comoecampina2017' server = None try: server = smtplib.SMTP('smtp.gmail.com:587') server.ehlo() server.starttls() server.login(username,password) except Exception as e: print "failed to login" + str(e.args) + " " + str(e) for data in emails:#["[email protected],David"]: userData = data.split(",") toaddrs = userData[0].strip(' \t\n\r') #subject = "Nova Versão do Como é Campina?" subject = "Alumni Contact - Help in Research" # Prepare actual message #msg = 'Olá ' + userData[1].strip(' \t\n\r') + ', <br><br>Como está? Primeiramente queríamos te agradecer por sua contribuição em nossa versão anterior do projeto Como é Campina?, aquele mesmo que te pediu para comparar fotos da cidade de Campina Grande. A partir da sua ajuda conseguimos entender melhor a relação entre elementos presentes nas cidades (e.g., como árvores, carros e pessoas nas ruas) e as percepções de segurança e agradabilidade de nossos contribuidores. Com isso, pudemos contribuir para o aperfeiçoamento da maneira como pesquisas de percepção podem utilizar ferramentas de computação como o Como é Campina? <br><br>Em nossa nova versão do projeto, estamos tentando melhorar ainda mais essas ferramentas de modo que automaticamente possamos construir a percepção sobre ruas e, assim, agilizar ainda mais o trabalho de gestores e urbanistas na detecção de problemas e na melhoria das nossas cidades. Para isso <b>precisamos novamente da sua ajuda!</b> <br><br>Basta acessar a URL do projeto https://contribua.org/project/ruascampina/, fazer login com a sua conta (se você não lembrar da sua senha você pode resgatá-la, ou entrar com uma conta sua do Facebook ou Google) e em 5 minutos responder mais algumas comparações de imagens. Sua ajuda é muito importante para nós. Vamos contribuir? <br><br><br> Abraços, <br> Equipe do Como é Campina?' msg = 'Hi ' + userData[1].strip(' \t\n\r') + ', <br><br>How are you? My name is David, I\'ve participated in the Study of the United States Institutes for Student Leaders from Brazil in 2009, an US Department of State program, and currently I\'m a Computer Science PhD candidate. <br> <br> I\'ve found your contact through the US programs alumni website and I\'m here to ask you for a help with our research. We\'re studying how computer systems can help in urban planning by capturing and analyzing impressions about images of cities. We\'re asking people to give their impressions about some city images, pointing the scenes that look more pleasant to them. It\'s very important to us to gather opinions from different places and cultures, and your contribution is very important to help us achieve that. <br><br> In 5 minutes you can login in our site (you can use your Facebook or Google account) <br><br> https://contribua.org/project/ruascampina/ <br><br> And answer some images comparison. I would really appreciate your help. <br><br> Thanks in advance! <br><br> David Candeia M Maia - <br> http://www.lsd.ufcg.edu.br/~davidcmm/ <br> http://sites.google.com/site/davidcmmaia/ <br><br> Mestre em Ciência da Computação pela Universidade Federal de Campina Grande<br>Professor do Instituto de Educação, Ciência e Tecnologia do Estado da Paraíba - IFPB<br>Paraíba - Brasil<br><br>MS in Computer Science at Federal University of Campina Grande<br>Professor at Instituto de Educação, Ciência e Tecnologia do Estado da Paraíba - IFPB<br>Paraíba - Brazil' #message = """\From: %s\nTo: %s\nSubject: %s\n\n%s #""" % (fromaddr, toaddrs, "Nova Versão do Como é Campina?", msg) #message = email.message.Message() message = MIMEMultipart() message['Subject'] = subject message['From'] = fromaddr message['To'] = toaddrs message.add_header('Content-Type','text/html') #message.set_payload(msg) message.attach(MIMEText(msg, 'html')) fp = open("/local/david/pybossa_env/campinaPulse/imagens_divulgacao/avatar.jpg", 'rb')#"/home/davidcmm/workspace_doutorado/campinaPulse/imagens_divulgacao/avatar.jpg", "rb") img = MIMEImage(fp.read()) fp.close() img.add_header('Content-ID', '<{}>'.format("/local/david/pybossa_env/campinaPulse/imagens_divulgacao/avatar.jpg")) message.attach(img) try: server.sendmail(fromaddr, toaddrs, message.as_string()) print 'successfully sent the mail to ' + str(toaddrs) except Exception as e: print "failed to send mail" + str(e.args) + " " + str(e) try: server.quit() server.close() except Exception as e: print "failed to close " + str(e.args) + " " + str(e)
def handle_embedded(cid, filename, message): global edir global attachments_listed, attachments_found, attachments_missing, attachments_dirs global paths_found, paths_missing global missing_attachments, found_attachments if edir: realfilename = edir + os.sep + filename if not os.path.exists(realfilename): print "Couldn't find embedded file %s" % (realfilename, ) return else: return mimeinfo = mimetypes.guess_type(realfilename) if not mimeinfo[0]: (mimetype, mimesubtype) = ('application', 'octet-stream') else: (mimetype, mimesubtype) = mimeinfo[0].split('/') if os.path.isfile(realfilename): fp = open(realfilename, 'rb') try: if mimetype == 'application' or mimetype == 'video': msg = MIMEApplication(fp.read(), _subtype=mimesubtype) elif mimetype == 'image': msg = MIMEImage(fp.read(), _subtype=mimesubtype) elif mimetype == 'text': msg = MIMEText(fp.read(), _subtype=mimesubtype) elif mimetype == 'audio': msg = MIMEAudio(fp.read(), _subtype=mimesubtype) else: EudoraLog.log.error( "Unrecognized mime type '%s' while processing attachment '%s'" % (mimeinfo[0], filename)) return finally: fp.close() if cid: msg.add_header('Content-ID', cid) msg.add_header('Content-Disposition', 'inline', filename=filename) else: msg.add_header('Content-Disposition', 'attachment', filename=filename) message.attach(msg)
def test_mail_with_attachment(self): from email.mime.image import MIMEImage from email.mime.multipart import MIMEMultipart message = MIMEMultipart() message["From"] = self._from message["To"] = self._to message["Subject"] = "This is a test with attachment!" with open("test.png", "rb") as fp: att = MIMEImage(fp.read(), 'octet-stream') message.attach(att) self._server.send(message)
def test_mail_text(self): from email.mime.base import MIMEBase from email.mime.multipart import MIMEMultipart message = MIMEMultipart() message["From"] = self._from message["To"] = self._to message["Subject"] = "This is a test with text!" att = MIMEBase('text', 'plain') with open(__file__) as fp: att.set_payload(fp.read()) message.attach(att) self._server.send(message)
def handle_embedded( cid, filename, message ): global edir global attachments_listed, attachments_found, attachments_missing, attachments_dirs global paths_found, paths_missing global missing_attachments, found_attachments if edir: realfilename = edir + os.sep + filename if not os.path.exists(realfilename): print "Couldn't find embedded file %s" % (realfilename,) return else: return mimeinfo = mimetypes.guess_type(realfilename) if not mimeinfo[0]: (mimetype, mimesubtype) = ('application', 'octet-stream') else: (mimetype, mimesubtype) = mimeinfo[0].split('/') if os.path.isfile(realfilename): fp = open(realfilename, 'rb') try: if mimetype == 'application' or mimetype == 'video': msg = MIMEApplication(fp.read(), _subtype=mimesubtype) elif mimetype == 'image': msg = MIMEImage(fp.read(), _subtype=mimesubtype) elif mimetype == 'text': msg = MIMEText(fp.read(), _subtype=mimesubtype) elif mimetype == 'audio': msg = MIMEAudio(fp.read(), _subtype=mimesubtype) else: EudoraLog.log.error("Unrecognized mime type '%s' while processing attachment '%s'" % (mimeinfo[0], filename)) return finally: fp.close() if cid: msg.add_header('Content-ID', cid) msg.add_header('Content-Disposition', 'inline', filename=filename) else: msg.add_header('Content-Disposition', 'attachment', filename=filename) message.attach(msg)
def decrypt_inline_with_attachments( payloads, success, message = None ): if message is None: message = email.mime.multipart.MIMEMultipart(payloads.get_content_subtype()) for payload in payloads.get_payload(): if( type( payload.get_payload() ) == list ): # Take care of cascaded MIME messages submessage, subsuccess = decrypt_inline_with_attachments( payload, success ) message.attach(submessage) success = success or subsuccess else: msg_content = payload.get_payload() # Getting values for different implementations as PGP/INLINE is not implemented # the same on different clients pgp_inline_tags = "-----BEGIN PGP MESSAGE-----" in msg_content and "-----END PGP MESSAGE-----" in msg_content attachment_filename = payload.get_filename() if pgp_inline_tags or not (attachment_filename is None) and not (re.search('.\.pgp$', attachment_filename) is None): if pgp_inline_tags: start = msg_content.find("-----BEGIN PGP MESSAGE-----") end = msg_content.find("-----END PGP MESSAGE-----") decrypted_payload, decrypt_success = decrypt_payload(msg_content[start:end + 25]) # Some implementations like Enigmail have strange interpretations of PGP/INLINE # This tries to cope with it as good as possible. else: build_message = """ -----BEGIN PGP MESSAGE----- %s -----END PGP MESSAGE-----""" % msg_content decrypted_payload, decrypt_success = decrypt_payload(build_message) # Was at least one decryption successful? success = success or decrypt_success if decrypt_success: if not (attachment_filename is None): attachment_filename = re.sub('\.pgp$', '', attachment_filename) payload.set_param('filename', attachment_filename, 'Content-Disposition') payload.set_param('name', attachment_filename, 'Content-Type') # Need this nasty hack to avoid double blank lines at beginning of message payload.set_payload(decrypted_payload.as_string()[1:]) message.attach(payload) else: # Message could not be decrypted, so non-decrypted message is attached message.attach(payload) else: # There was no encrypted payload found, so the original payload is attached message.attach(payload) return message, success
def email(): global email_text, sender_email, receiver_email sender_email=os.getenv('EMAIL') receiver_email=input('Enter receiver\'s mail address: ') subject = input("\nSubject: ") lines = [] print("\nBody: \n") while True: line = input() if line: lines.append(line) else: break body = '\n'.join(lines) message = MIMEMultipart() message["From"] = sender_email message["To"] = receiver_email message["Subject"] = subject message.attach(MIMEText(body,"plain")) if input('\nDo you want to add an attachment:(y/n) ').lower() == 'y': filename = input("Enter filename: ") with open(filename, "rb") as attachment: part = MIMEBase("application","octet-stream") part.set_payload(attachment.read()) encoders.encode_base64(part) part.add_header("Content-Disposition", f"attachment; filename = {filename}",) message.attach(part) else: print("Sending mail without email-attachment") email_text = message.as_string()
def send_mail(to): message = MIMEMultipart() message["Subject"] = message_subject message["From"] = sender_email message["To"] = to app = Flask(__name__) code = random.randint(100000, 999999) htmlmessage = """ <div class="wrapper" style="text-align: center;"> <h4>Hey, you've requested for a password reset.</h4> <p> <h2>{{code}}</h2>is your code. </p> <p> If this was not you, simply ignore this message. </p> </div> """ with app.app_context(): MIME_message = str(render_template_string(htmlmessage, code=code)) # Turn these into plain/html MIMEText objects the_message = MIMEText(MIME_message, "html") # Add HTML/plain-text parts to MIMEMultipart message # The email client will try to render the last part first message.attach(the_message) # Create secure connection with server and send email context = ssl.create_default_context() with smtplib.SMTP_SSL("smtp.gmail.com", 465, context=context) as server: server.login(sender_email, password) server.sendmail(sender_email, to, message.as_string()) return code
def read_email(folder, output): with (folder / 'InternetHeaders.txt').open('rb') as f: raw_headers = f.read() message = email.message_from_bytes(raw_headers) del message['Content-Type'] message.set_payload(None) message['Content-Type'] = 'multipart/mixed' html_file = folder / 'Message.html' text_file = folder / 'Message.txt' if html_file.exists(): with html_file.open('rb') as f: html_src = f.read() utf8 = bool(re.search(br'charset\s*=["\']?utf[-]?8', html_src[:1024])) params = {'charset': 'utf-8'} if utf8 else {} html = MIMEBase('text', 'html', **params) html.set_payload(html_src) email.encoders.encode_base64(html) message.attach(html) elif text_file.exists(): text = MIMEBase('text', 'plain') with text_file.open('rb') as f: text.set_payload(f.read()) email.encoders.encode_base64(text) message.attach(text) attachments_dir = folder / 'Attachments' if attachments_dir.exists(): for p in attachments_dir.iterdir(): if p.is_dir(): for i in p.iterdir(): if i.name.startswith('Message'): message.attach(get_message_attachment(i)) else: raise RuntimeError('unknown attachment {!r}'.format(i)) else: message.attach(get_file_attachment(p)) output.write(message.as_bytes())
def handle_attachment(line, target, message): """ Mac versions put "Attachment converted", Windows (Lite) has "Attachment Converted". Next comes a system-dependent path to the attachment binary. On mac version, separated by colons, starts with volume, but omits path elements between: Eudora Folder:Attachments Folder. Windows versions have a full DOS path name to the binary (Lite version uses 8-char filenames) This replaces that filepath with a file URI to the file in the attachments_dirs directories. This has no direct effect in Kmail, but sometimes Pine can open the file (so long as there aren't any spaces in the filepath). At least it makes more sense than leaving the old filepath. """ global attachments_listed, attachments_found, attachments_missing, attachments_dirs global paths_found, paths_missing global missing_attachments, found_attachments global mac_mismatches attachments_listed = attachments_listed + 1 # Mac 1.3.1 has e.g. (Type: 'PDF ' Creator: 'CARO') # Mac 3.1 has e.g (PDF /CARO) (00000645) if re_quoted_attachment.match(line): attachment_desc = re_quoted_attachment.sub('\\1', line) elif re_attachment.match(line): attachment_desc = re_attachment.sub('\\1', line) else: # If we're dealing with attachments recorded by the # X-Attachments header, line will be a single, naked # attachment desc, with no Attachment Converted # surroundings attachment_desc = line if attachment_desc.find('"') != -1: print "**>>**", attachment_desc attachment_desc = strip_linesep(attachment_desc) # some of John's attachment names have an odd OutboundG4: # prefix which is not present in the filenames on disk.. if attachment_desc.find('OutboundG4:') != -1: attachment_desc = attachment_desc.replace('OutboundG4:', '') name = '' # if has :\, must be windows etc = '' if re_dos_path_beginning.match(attachment_desc): desc_list = attachment_desc.split("\\") # DOS backslashes name = desc_list.pop().strip() # pop off last portion of name orig_path = "/".join(desc_list) if name[-1] == '"': name = name[:-1] elif re_mac_info.match(line): name = re_mac_info.sub('\\1', line) etc = re_mac_info.sub('\\2', line).strip() dlist = name.split(":") # Mac path delim name = dlist.pop().strip() # pop off last portion of name orig_path = "/".join(dlist) else: # EudoraLog.log.warn( "FAILED to convert attachment: \'" # + attachment_desc + "\'" ) name = attachment_desc orig_path = attachment_desc if len(name) <= 0: return filename = None for adir in attachments_dirs: if not filename or not os.path.exists(filename): filename = os.path.join(target, adir, name) if not os.path.isabs(target): filename = os.path.join(os.environ['HOME'], filename) # Trim NULL bytes from filenames (found in old Eudora 2.x mailboxes) filename = filename.replace(b'\x00', '') if not os.path.exists(filename): if name.startswith('OutboundG4:'): name = name[11:] print "**** Hey, name is now %s" % (name, ) filename = os.path.join(target, attachments_dir, name) # our user has attachments that have / characters in # the file name, but when they got copied over to # unix, the / chars were taken out, if it would help. if not os.path.exists(filename): if name.find('/') != -1: name = name.replace('/', '') filename = os.path.join(target, adir, name) # our user also has attachments that have _ characters # in the file name where the file on disk has spaces. # translate that as well, if it would help. if not os.path.exists(filename): if name.find('_') != -1: name = name.replace('_', ' ') filename = os.path.join(target, adir, name) # our user actually also has attachments that have # space characters in the file name where the file on # disk has underscores. if we didn't find the match # after our last transform, try the rever if not os.path.exists(filename): if name.find(' ') != -1: name = name.replace(' ', '_') filename = os.path.join(target, adir, name) # in our user's attachments, we have some files named # akin to 'filename.ppt 1' and so forth. we're going # to trim anything after the first whitespace # character after the first . in the filename cleaner_match = re_filename_cleaner.match(filename) if cleaner_match: filename = cleaner_match.group(1) # Trim any NULL bytes we might have pulled in filename = filename.replace(b'\x00', '') mimeinfo = mimetypes.guess_type(filename) if not os.path.exists(filename): cleaner_match = re_filename_cleaner.match(filename.replace('_', ' ')) if cleaner_match and os.path.exists(cleaner_match.group(1)): filename = cleaner_match.group(1) if not mimeinfo[0]: (mimetype, mimesubtype) = ('application', 'octet-stream') else: (mimetype, mimesubtype) = mimeinfo[0].split('/') if os.path.isfile(filename): fp = open(filename, 'rb') try: if mimetype == 'application' or mimetype == 'video': msg = MIMEApplication(fp.read(), _subtype=mimesubtype) elif mimetype == 'image': msg = MIMEImage(fp.read(), _subtype=mimesubtype) elif mimetype == 'text': msg = MIMEText(fp.read(), _subtype=mimesubtype) elif mimetype == 'audio': msg = MIMEAudio(fp.read(), _subtype=mimesubtype) else: EudoraLog.log.error( "Unrecognized mime type '%s' while processing attachment '%s'" % (mimeinfo[0], filename)) return finally: fp.close() msg.add_header('Content-Disposition', 'attachment', filename=name) message.attach(msg) attachments_found = attachments_found + 1 # EudoraLog.log.warn(" SUCCEEDED finding attachment: \'" + attachment_desc + "\', name = \'" + name + "\'") if orig_path in paths_found: paths_found[orig_path] = paths_found[orig_path] + 1 else: paths_found[orig_path] = 1 if not EudoraLog.log.mbx_name() in found_attachments: found_attachments[EudoraLog.log.mbx_name()] = [] found_attachments[EudoraLog.log.mbx_name()].append( (attachment_desc, filename)) else: attachments_missing = attachments_missing + 1 if not EudoraLog.log.mbx_name() in missing_attachments: missing_attachments[EudoraLog.log.mbx_name()] = [] missing_attachments[EudoraLog.log.mbx_name()].append(attachment_desc) # EudoraLog.log.warn(" FAILED to find attachment: \'" + attachment_desc + "\'" ) if re_mangled_mac.search(filename): print "Mac pattern: %s" % (filename, ) mac_mismatches.append(filename) if orig_path in paths_missing: paths_missing[orig_path] = paths_missing[orig_path] + 1 else: paths_missing[orig_path] = 1
def craft_message(headers, body, attachments, embeddeds, mbx, is_html): """This function handles the creation of a Python email.message object from the headers and body lists created during the main loop.""" global edir attachments_ok = False embeddedcids = [] if body: msg_text = ''.join(body) else: msg_text = '' # there's no point honoring 'multipart' if we don't have any # attachments or embeddeds to attach, so we'll pay most # attention to whether we have any attachments or embeddeds # defined for this message if attachments or embeddeds: is_multipart = True else: is_multipart = False message = None contenttype = headers.getValue('Content-Type:') if contenttype and re_html.search(contenttype): is_html = True if not contenttype: msattach = headers.getValue('X-MS-Attachment:') if msattach: message = MIMEMultipart() attachments_ok = "Dunno" attachments_contenttype = "Still Dunno" else: if is_html: message = MIMENonMultipart('text', 'html') else: message = MIMENonMultipart('text', 'plain') attachments_ok = False attachments_contenttype = False print "T", elif re_rfc822.search(contenttype): print "[", message = MIMEMessage( craft_message(*extract_pieces(body, -1, mbx, True))) print "]", elif not is_multipart: mimetype = re_single_contenttype.search(contenttype) if mimetype: main = mimetype.group(1) sub = mimetype.group(2) if main != 'multipart': message = MIMENonMultipart(main, sub) attachments_ok = False attachments_contenttype = False print "X", else: # I've seen some messages in Eudora # mailboxes that label themselves as # multitype/related without having any # attachments ever declared in the # Eudora box. # # By passing here, we allow the next # clause to go ahead and create an # appropriate MIMENonMultipart pass if not message: if is_html: message = MIMENonMultipart('text', 'html') else: message = MIMENonMultipart('text', 'plain') attachments_ok = False attachments_contenttype = False else: subtype = re_multi_contenttype.search(contenttype) if subtype: message = MIMEMultipart(_subtype=subtype.group(1)) attachments_ok = subtype.group(1) attachments_contenttype = contenttype print "Y", else: message = MIMEMultipart() print "Z", attachments_ok = "Dunno" attachments_contenttype = "Still Dunno" # Need to add support here for processing embeddeds if embeddeds: if not isinstance(message, MIMEMultipart): print "\n\n==================================================\n" print "Found surprise multipart for embeddeds!\n" message = MIMEMultipart(_subtype='related') else: print "\n\n==================================================\n" print "Found embeddeds in multipart!\n" p = EudoraHTMLParser() try: p.feed(msg_text) cids = p.get_cids() except HTMLParseError: # okay, we've got unparseable HTML here. # Let's just use a quick regexp to see if we can make sense of this. cids = [] for match in re_cids_finder.finditer(msg_text): cids.append("cid:" + match.group(1)) if not len(cids) == len(embeddeds): print "cids / embeddeds mismatch!" print print mbx for piece in ['To:', 'From:', 'Subject:', 'Date:']: if headers.getValue(piece): print piece + " " + headers.getValue(piece)[:80] print print "\tcid\t\t\t\t\t\t\tembedded" i = 0 while i < len(cids) or i < len(embeddeds): if i < len(cids): print "%d.\t%s" % (i, cids[i]), print "\t" * (6 - (len(cids[i]) // 8)), else: print "%d.\t" % (i, ), print "\t\t\t\t\t\t", if i < len(embeddeds): print embeddeds[i], if edir and os.path.exists(edir + os.sep + embeddeds[i]): print " *" else: print " !" else: print i = i + 1 cidi = 0 embeddedi = 0 cidsmatched = set() while cidi < len(cids) or embeddedi < len(embeddeds): if cidi < len(cids) and embeddedi < len(embeddeds): if cids[cidi].startswith('cid:'): actualcid = cids[cidi][4:] else: actualcid = cids[cidi] # the document might have several img # references to the same cid.. we # don't want to try to mate up # multiple inline files in that case if actualcid in cidsmatched: cidi = cidi + 1 else: cidsmatched.add(actualcid) embeddedcids.append((actualcid, embeddeds[embeddedi])) embeddedi = embeddedi + 1 cidi = cidi + 1 elif embeddedi < len(embeddeds): embeddedcids.append((None, embeddeds[embeddedi])) embeddedi = embeddedi + 1 else: # we have more cids than # embeddeds, keep looping # through cidi = cidi + 1 print "\n\nAttaching inline components:" for c, f in embeddedcids: print "%s\t%s" % (c, f) print "\n==================================================\n" if attachments: if not isinstance(message, MIMEMultipart): #print "\n\n%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n" #print "Forcing surprise multipart!\n" #print "\n%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n" message = MIMEMultipart() # bind the headers into our message set_headers(message, headers) try: # rfc822 MIMEMessage objects handle payload management # on constructions, we must not try to do it here. if not isinstance(message, MIMEMessage): if not isinstance(message, MIMEMultipart): message.set_payload(msg_text) elif is_html: message.attach(MIMEText(msg_text, _subtype='html')) else: message.attach(MIMEText(msg_text)) except Exception, e: print "\nHEY HEY HEY message = " + str(msg_text) + "\n" print "Type of message's payload is " + str(type( message.get_payload())) + "\n" if isinstance(message.get_payload(), list): print "Size of message's payload list is " + str( len(message.get_payload())) + "\n" print ")))))))))))))))))))) First part" print str(message.get_payload()[0]) print ">>>>>>>>>>>>>>>>>>>> Second part" print str(message.get_payload()[1]) print "attachments_contenttype is (%s)" % (attachments_contenttype, ) print "attachments_ok is (%s)" % (attachments_ok, ) if attachments: print "Yeah, attachments were found: %d" % (len(attachments), ) print "EXCEPTION " + str(e) + "\n" traceback.print_exc(file=sys.stdout)
def store_and_build_forward_message(self, form, boundary=None, max_bytes_per_blob=None, max_bytes_total=None, bucket_name=None): """Reads form data, stores blobs data and builds the forward request. This finds all of the file uploads in a set of form fields, converting them into blobs and storing them in the blobstore. It also generates the HTTP request to forward to the user's application. Args: form: cgi.FieldStorage instance representing the whole form derived from original POST data. boundary: The optional boundary to use for the resulting form. If omitted, one is randomly generated. max_bytes_per_blob: The maximum size in bytes that any single blob in the form is allowed to be. max_bytes_total: The maximum size in bytes that the total of all blobs in the form is allowed to be. bucket_name: The name of the Google Storage bucket to store the uploaded files. Returns: A tuple (content_type, content_text), where content_type is the value of the Content-Type header, and content_text is a string containing the body of the HTTP request to forward to the application. Raises: webob.exc.HTTPException: The upload failed. """ message = multipart.MIMEMultipart('form-data', boundary) creation = self._now_func() total_bytes_uploaded = 0 created_blobs = [] mime_type_error = None too_many_conflicts = False upload_too_large = False filename_too_large = False content_type_too_large = False # Extract all of the individual form items out of the FieldStorage. form_items = [] # Sorting of forms is done merely to make testing a little easier since # it means blob-keys are generated in a predictable order. for key in sorted(form): form_item = form[key] if isinstance(form_item, list): form_items.extend(form_item) else: form_items.append(form_item) for form_item in form_items: disposition_parameters = {'name': form_item.name} variable = email.message.Message() if form_item.filename is None: # Copy as is variable.add_header('Content-Type', 'text/plain') variable.set_payload(form_item.value) else: # If there is no filename associated with this field it means that the # file form field was not filled in. This blob should not be created # and forwarded to success handler. if not form_item.filename: continue disposition_parameters['filename'] = form_item.filename try: main_type, sub_type = _split_mime_type(form_item.type) except _InvalidMIMETypeFormatError, ex: mime_type_error = str(ex) break # Seek to the end of file and use the pos as the length. form_item.file.seek(0, os.SEEK_END) content_length = form_item.file.tell() form_item.file.seek(0) total_bytes_uploaded += content_length if max_bytes_per_blob is not None: if content_length > max_bytes_per_blob: upload_too_large = True break if max_bytes_total is not None: if total_bytes_uploaded > max_bytes_total: upload_too_large = True break if form_item.filename is not None: if len(form_item.filename) > _MAX_STRING_NAME_LENGTH: filename_too_large = True break if form_item.type is not None: if len(form_item.type) > _MAX_STRING_NAME_LENGTH: content_type_too_large = True break # Compute the MD5 hash of the upload. digester = hashlib.md5() while True: block = form_item.file.read(1 << 20) if not block: break digester.update(block) form_item.file.seek(0) # Create the external body message containing meta-data about the blob. external = email.message.Message() external.add_header('Content-Type', '%s/%s' % (main_type, sub_type), **form_item.type_options) # NOTE: This is in violation of RFC 2616 (Content-MD5 should be the # base-64 encoding of the binary hash, not the hex digest), but it is # consistent with production. blob_key = base64.urlsafe_b64encode(digester.hexdigest()) # Create header MIME message headers = dict(form_item.headers) for name in _STRIPPED_FILE_HEADERS: if name in headers: del headers[name] headers['Content-Length'] = str(content_length) headers[blobstore.UPLOAD_INFO_CREATION_HEADER] = ( blobstore._format_creation(creation)) headers['Content-MD5'] = blob_key if bucket_name: headers[blobstore.CLOUD_STORAGE_OBJECT_HEADER] = ( '/gs/%s/fake-%s' % (bucket_name, blob_key)) for key, value in headers.iteritems(): external.add_header(key, value) # Add disposition parameters (a clone of the outer message's field). if not external.get('Content-Disposition'): external.add_header('Content-Disposition', 'form-data', **disposition_parameters) # Store the actual contents in the blobstore. base64_encoding = (form_item.headers.get('Content-Transfer-Encoding') == 'base64') try: blob_entity = self.store_blob(external['content-type'], form_item.filename, digester, form_item.file, creation, base64_encoding=base64_encoding) except _TooManyConflictsError: too_many_conflicts = True break # Track created blobs in case we need to roll them back. created_blobs.append(blob_entity) variable.add_header('Content-Type', 'message/external-body', access_type=blobstore.BLOB_KEY_HEADER, blob_key=blob_entity.key().name()) variable.set_payload([external]) # Set common information. variable.add_header('Content-Disposition', 'form-data', **disposition_parameters) message.attach(variable)
def handle_attachment( line, target, message ): """ Mac versions put "Attachment converted", Windows (Lite) has "Attachment Converted". Next comes a system-dependent path to the attachment binary. On mac version, separated by colons, starts with volume, but omits path elements between: Eudora Folder:Attachments Folder. Windows versions have a full DOS path name to the binary (Lite version uses 8-char filenames) This replaces that filepath with a file URI to the file in the attachments_dirs directories. This has no direct effect in Kmail, but sometimes Pine can open the file (so long as there aren't any spaces in the filepath). At least it makes more sense than leaving the old filepath. """ global attachments_listed, attachments_found, attachments_missing, attachments_dirs global paths_found, paths_missing global missing_attachments, found_attachments global mac_mismatches attachments_listed = attachments_listed + 1 # Mac 1.3.1 has e.g. (Type: 'PDF ' Creator: 'CARO') # Mac 3.1 has e.g (PDF /CARO) (00000645) if re_quoted_attachment.match(line): attachment_desc = re_quoted_attachment.sub( '\\1', line ) elif re_attachment.match(line): attachment_desc = re_attachment.sub( '\\1', line ) else: # If we're dealing with attachments recorded by the # X-Attachments header, line will be a single, naked # attachment desc, with no Attachment Converted # surroundings attachment_desc = line if attachment_desc.find('"') != -1: print "**>>**", attachment_desc attachment_desc = strip_linesep(attachment_desc) # some of John's attachment names have an odd OutboundG4: # prefix which is not present in the filenames on disk.. if attachment_desc.find('OutboundG4:') != -1: attachment_desc = attachment_desc.replace('OutboundG4:', '') name = '' # if has :\, must be windows etc = '' if re_dos_path_beginning.match( attachment_desc ): desc_list = attachment_desc.split( "\\" ) # DOS backslashes name = desc_list.pop().strip() # pop off last portion of name orig_path = "/".join(desc_list) if name[-1] == '"': name = name[:-1] elif re_mac_info.match( line ): name = re_mac_info.sub( '\\1', line ) etc = re_mac_info.sub( '\\2', line ).strip() dlist = name.split( ":" ) # Mac path delim name = dlist.pop().strip() # pop off last portion of name orig_path = "/".join(dlist) else: # EudoraLog.log.warn( "FAILED to convert attachment: \'" # + attachment_desc + "\'" ) name = attachment_desc orig_path = attachment_desc if len( name ) <= 0: return filename = None for adir in attachments_dirs: if not filename or not os.path.exists(filename): filename = os.path.join( target, adir, name ) if not os.path.isabs( target ): filename = os.path.join( os.environ['HOME'], filename ) if not os.path.exists(filename): if name.startswith('OutboundG4:'): name = name[11:] print "**** Hey, name is now %s" % (name, ) filename = os.path.join(target, attachments_dir, name) # our user has attachments that have / characters in # the file name, but when they got copied over to # unix, the / chars were taken out, if it would help. if not os.path.exists(filename): if name.find('/') != -1: name=name.replace('/','') filename = os.path.join(target, adir, name) # our user also has attachments that have _ characters # in the file name where the file on disk has spaces. # translate that as well, if it would help. if not os.path.exists(filename): if name.find('_') != -1: name = name.replace('_', ' ') filename = os.path.join(target, adir, name) # our user actually also has attachments that have # space characters in the file name where the file on # disk has underscores. if we didn't find the match # after our last transform, try the rever if not os.path.exists(filename): if name.find(' ') != -1: name = name.replace(' ', '_') filename = os.path.join(target, adir, name) # in our user's attachments, we have some files named # akin to 'filename.ppt 1' and so forth. we're going # to trim anything after the first whitespace # character after the first . in the filename cleaner_match = re_filename_cleaner.match( filename ) if cleaner_match: filename = cleaner_match.group(1) mimeinfo = mimetypes.guess_type(filename) if not os.path.exists(filename): cleaner_match = re_filename_cleaner.match(filename.replace('_', ' ')) if cleaner_match and os.path.exists(cleaner_match.group(1)): filename = cleaner_match.group(1) if not mimeinfo[0]: (mimetype, mimesubtype) = ('application', 'octet-stream') else: (mimetype, mimesubtype) = mimeinfo[0].split('/') if os.path.isfile(filename): fp = open(filename, 'rb') try: if mimetype == 'application' or mimetype == 'video': msg = MIMEApplication(fp.read(), _subtype=mimesubtype) elif mimetype == 'image': msg = MIMEImage(fp.read(), _subtype=mimesubtype) elif mimetype == 'text': msg = MIMEText(fp.read(), _subtype=mimesubtype) elif mimetype == 'audio': msg = MIMEAudio(fp.read(), _subtype=mimesubtype) else: EudoraLog.log.error("Unrecognized mime type '%s' while processing attachment '%s'" % (mimeinfo[0], filename)) return finally: fp.close() msg.add_header('Content-Disposition', 'attachment', filename=name) message.attach(msg) attachments_found = attachments_found + 1 # EudoraLog.log.warn(" SUCCEEDED finding attachment: \'" + attachment_desc + "\', name = \'" + name + "\'") if orig_path in paths_found: paths_found[orig_path] = paths_found[orig_path] + 1 else: paths_found[orig_path] = 1 if not EudoraLog.log.mbx_name() in found_attachments: found_attachments[EudoraLog.log.mbx_name()] = [] found_attachments[EudoraLog.log.mbx_name()].append((attachment_desc, filename)) else: attachments_missing = attachments_missing + 1 if not EudoraLog.log.mbx_name() in missing_attachments: missing_attachments[EudoraLog.log.mbx_name()] = [] missing_attachments[EudoraLog.log.mbx_name()].append(attachment_desc) # EudoraLog.log.warn(" FAILED to find attachment: \'" + attachment_desc + "\'" ) if re_mangled_mac.search(filename): print "Mac pattern: %s" % (filename, ) mac_mismatches.append(filename) if orig_path in paths_missing: paths_missing[orig_path] = paths_missing[orig_path] + 1 else: paths_missing[orig_path] = 1
def craft_message( headers, body, attachments, embeddeds, mbx, is_html): """This function handles the creation of a Python email.message object from the headers and body lists created during the main loop.""" global edir attachments_ok = False embeddedcids = [] if body: msg_text = ''.join(body) else: msg_text = '' # there's no point honoring 'multipart' if we don't have any # attachments or embeddeds to attach, so we'll pay most # attention to whether we have any attachments or embeddeds # defined for this message if attachments or embeddeds: is_multipart = True else: is_multipart = False message = None contenttype = headers.getValue('Content-Type:') if contenttype and re_html.search(contenttype): is_html = True if not contenttype: msattach = headers.getValue('X-MS-Attachment:') if msattach: message = MIMEMultipart() attachments_ok = "Dunno" attachments_contenttype = "Still Dunno" else: if is_html: message = MIMENonMultipart('text', 'html') else: message = MIMENonMultipart('text', 'plain') attachments_ok = False attachments_contenttype = False print "T", elif re_rfc822.search( contenttype ): print "[", message = MIMEMessage(craft_message(*extract_pieces(body, -1, mbx, True))) print "]", elif not is_multipart: mimetype = re_single_contenttype.search( contenttype ) if mimetype: main = mimetype.group(1) sub = mimetype.group(2) if main != 'multipart': message = MIMENonMultipart(main, sub) attachments_ok = False attachments_contenttype = False print "X", else: # I've seen some messages in Eudora # mailboxes that label themselves as # multitype/related without having any # attachments ever declared in the # Eudora box. # # By passing here, we allow the next # clause to go ahead and create an # appropriate MIMENonMultipart pass if not message: if is_html: message = MIMENonMultipart('text', 'html') else: message = MIMENonMultipart('text', 'plain') attachments_ok = False attachments_contenttype = False else: subtype = re_multi_contenttype.search( contenttype ) if subtype: message = MIMEMultipart(_subtype=subtype.group(1)) attachments_ok = subtype.group(1) attachments_contenttype = contenttype print "Y", else: message = MIMEMultipart() print "Z", attachments_ok = "Dunno" attachments_contenttype = "Still Dunno" # Need to add support here for processing embeddeds if embeddeds: if not isinstance( message, MIMEMultipart): print "\n\n==================================================\n" print "Found surprise multipart for embeddeds!\n" message = MIMEMultipart(_subtype='related') else: print "\n\n==================================================\n" print "Found embeddeds in multipart!\n" p = EudoraHTMLParser() try: p.feed(msg_text) cids = p.get_cids() except HTMLParseError: # okay, we've got unparseable HTML here. # Let's just use a quick regexp to see if we can make sense of this. cids = [] for match in re_cids_finder.finditer(msg_text): cids.append("cid:" + match.group(1)) if not len(cids) == len(embeddeds): print "cids / embeddeds mismatch!" print print mbx for piece in ['To:', 'From:' , 'Subject:', 'Date:']: if headers.getValue(piece): print piece + " " + headers.getValue(piece)[:80] print print "\tcid\t\t\t\t\t\t\tembedded" i = 0 while i < len(cids) or i < len(embeddeds): if i < len(cids): print "%d.\t%s" % (i, cids[i]), print "\t" * (6 - (len(cids[i]) // 8)), else: print "%d.\t" % (i, ), print "\t\t\t\t\t\t", if i < len(embeddeds): print embeddeds[i], if edir and os.path.exists(edir + os.sep + embeddeds[i]): print " *" else: print " !" else: print i = i + 1 cidi = 0 embeddedi = 0 cidsmatched = set() while cidi < len(cids) or embeddedi < len(embeddeds): if cidi < len(cids) and embeddedi < len(embeddeds): if cids[cidi].startswith('cid:'): actualcid = cids[cidi][4:] else: actualcid = cids[cidi] # the document might have several img # references to the same cid.. we # don't want to try to mate up # multiple inline files in that case if actualcid in cidsmatched: cidi = cidi + 1 else: cidsmatched.add(actualcid) embeddedcids.append( (actualcid, embeddeds[embeddedi]) ) embeddedi = embeddedi + 1 cidi = cidi + 1 elif embeddedi < len(embeddeds): embeddedcids.append( (None, embeddeds[embeddedi]) ) embeddedi = embeddedi + 1 else: # we have more cids than # embeddeds, keep looping # through cidi = cidi + 1 print "\n\nAttaching inline components:" for c, f in embeddedcids: print "%s\t%s" % (c, f) print "\n==================================================\n" if attachments: if not isinstance( message, MIMEMultipart): #print "\n\n%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n" #print "Forcing surprise multipart!\n" #print "\n%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n" message = MIMEMultipart() # bind the headers into our message set_headers( message, headers ) try: # rfc822 MIMEMessage objects handle payload management # on constructions, we must not try to do it here. if not isinstance( message, MIMEMessage ): if not isinstance( message, MIMEMultipart): message.set_payload(msg_text) elif is_html: message.attach(MIMEText(msg_text, _subtype='html')) else: message.attach(MIMEText(msg_text)) except Exception, e: print "\nHEY HEY HEY message = " + str(msg_text) + "\n" print "Type of message's payload is " + str(type(message.get_payload())) + "\n" if isinstance( message.get_payload(), list ): print "Size of message's payload list is " + str(len(message.get_payload())) + "\n" print ")))))))))))))))))))) First part" print str(message.get_payload()[0]) print ">>>>>>>>>>>>>>>>>>>> Second part" print str(message.get_payload()[1]) print "attachments_contenttype is (%s)" % (attachments_contenttype, ) print "attachments_ok is (%s)" % (attachments_ok, ) if attachments: print "Yeah, attachments were found: %d" % (len(attachments), ) print "EXCEPTION " + str(e) + "\n" traceback.print_exc(file=sys.stdout)
def mailwithAttachemnt(reciver, id, filelocation): subject = "QR Code" # body = "This is an email with attachment sent from Python" body = """ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <p>dear member,<br> your id number is {}. download this file</p> </body> </html> """.format(id) sender_email = "*****@*****.**" receiver_email = reciver password = "******" # Create a multipart message and set headers message = MIMEMultipart() message["From"] = sender_email message["To"] = receiver_email message["Subject"] = subject message["Bcc"] = receiver_email # Recommended for mass emails # Add body to email # message.attach(MIMEText(body, "plain")) message.attach(MIMEText(body, "html")) # filename = "../static/pdf/8SigneRichardson.pdf" # In same directory as script filename = filelocation # In same directory as script # Open PDF file in binary mode with open(filename, "rb") as attachment: # Add file as application/octet-stream # Email client can usually download this automatically as attachment part = MIMEBase("application", "octet-stream") part.set_payload(attachment.read()) # Encode file in ASCII characters to send by email encoders.encode_base64(part) # Add header as key/value pair to attachment part part.add_header( "Content-Disposition", f"attachment; filename= {filename}", ) # Add attachment to message and convert message to string message.attach(part) text = message.as_string() # Log in to server using secure context and send email context = ssl.create_default_context() with smtplib.SMTP_SSL("smtp.gmail.com", 465, context=context) as server: server.login(sender_email, password) server.sendmail(sender_email, receiver_email, text)
def store_and_build_forward_message(self, form, boundary=None, max_bytes_per_blob=None, max_bytes_total=None, bucket_name=None): """Reads form data, stores blobs data and builds the forward request. This finds all of the file uploads in a set of form fields, converting them into blobs and storing them in the blobstore. It also generates the HTTP request to forward to the user's application. Args: form: cgi.FieldStorage instance representing the whole form derived from original POST data. boundary: The optional boundary to use for the resulting form. If omitted, one is randomly generated. max_bytes_per_blob: The maximum size in bytes that any single blob in the form is allowed to be. max_bytes_total: The maximum size in bytes that the total of all blobs in the form is allowed to be. bucket_name: The name of the Google Storage bucket to store the uploaded files. Returns: A tuple (content_type, content_text), where content_type is the value of the Content-Type header, and content_text is a string containing the body of the HTTP request to forward to the application. Raises: webob.exc.HTTPException: The upload failed. """ message = multipart.MIMEMultipart('form-data', boundary) creation = self._now_func() total_bytes_uploaded = 0 created_blobs = [] mime_type_error = None too_many_conflicts = False upload_too_large = False filename_too_large = False content_type_too_large = False # Extract all of the individual form items out of the FieldStorage. form_items = [] # Sorting of forms is done merely to make testing a little easier since # it means blob-keys are generated in a predictable order. for key in sorted(form): form_item = form[key] if isinstance(form_item, list): form_items.extend(form_item) else: form_items.append(form_item) for form_item in form_items: disposition_parameters = {'name': form_item.name} variable = email.message.Message() if form_item.filename is None: # Copy as is variable.add_header('Content-Type', 'text/plain') variable.set_payload(form_item.value) else: # If there is no filename associated with this field it means that the # file form field was not filled in. This blob should not be created # and forwarded to success handler. if not form_item.filename: continue disposition_parameters['filename'] = form_item.filename try: main_type, sub_type = _split_mime_type(form_item.type) except _InvalidMIMETypeFormatError as ex: mime_type_error = str(ex) break # Seek to the end of file and use the pos as the length. form_item.file.seek(0, os.SEEK_END) content_length = form_item.file.tell() form_item.file.seek(0) total_bytes_uploaded += content_length if max_bytes_per_blob is not None: if content_length > max_bytes_per_blob: upload_too_large = True break if max_bytes_total is not None: if total_bytes_uploaded > max_bytes_total: upload_too_large = True break if form_item.filename is not None: if len(form_item.filename) > _MAX_STRING_NAME_LENGTH: filename_too_large = True break if form_item.type is not None: if len(form_item.type) > _MAX_STRING_NAME_LENGTH: content_type_too_large = True break # Compute the MD5 hash of the upload. digester = hashlib.md5() while True: block = form_item.file.read(1 << 20) if not block: break digester.update(block) form_item.file.seek(0) # Create the external body message containing meta-data about the blob. external = email.message.Message() external.add_header('Content-Type', '%s/%s' % (main_type, sub_type), **form_item.type_options) # NOTE: This is in violation of RFC 2616 (Content-MD5 should be the # base-64 encoding of the binary hash, not the hex digest), but it is # consistent with production. content_md5 = base64.urlsafe_b64encode(digester.hexdigest()) # Create header MIME message headers = dict(form_item.headers) for name in _STRIPPED_FILE_HEADERS: if name in headers: del headers[name] headers['Content-Length'] = str(content_length) headers[blobstore.UPLOAD_INFO_CREATION_HEADER] = ( blobstore._format_creation(creation)) headers['Content-MD5'] = content_md5 gs_filename = None if bucket_name: random_key = str(self._generate_blob_key()) gs_filename = '%s/fake-%s' % (bucket_name, random_key) headers[blobstore.CLOUD_STORAGE_OBJECT_HEADER] = ( blobstore.GS_PREFIX + gs_filename) for key, value in six.iteritems(headers): external.add_header(key, value) # Add disposition parameters (a clone of the outer message's field). if not external.get('Content-Disposition'): external.add_header('Content-Disposition', 'form-data', **disposition_parameters) base64_encoding = (form_item.headers.get( 'Content-Transfer-Encoding') == 'base64') content_type, blob_file, filename = self._preprocess_data( external['content-type'], form_item.file, form_item.filename, base64_encoding) # Store the actual contents to storage. if gs_filename: info_entity = self.store_gs_file(content_type, gs_filename, blob_file, filename) else: try: info_entity = self.store_blob(content_type, filename, digester, blob_file, creation) except _TooManyConflictsError: too_many_conflicts = True break # Track created blobs in case we need to roll them back. created_blobs.append(info_entity) variable.add_header('Content-Type', 'message/external-body', access_type=blobstore.BLOB_KEY_HEADER, blob_key=info_entity.key().name()) variable.set_payload([external]) # Set common information. variable.add_header('Content-Disposition', 'form-data', **disposition_parameters) message.attach(variable) if (mime_type_error or too_many_conflicts or upload_too_large or filename_too_large or content_type_too_large): for blob in created_blobs: datastore.Delete(blob) if mime_type_error: self.abort(400, detail=mime_type_error) elif too_many_conflicts: self.abort(500, detail='Could not generate a blob key.') elif upload_too_large: self.abort(413) else: if filename_too_large: invalid_field = 'filename' elif content_type_too_large: invalid_field = 'Content-Type' detail = 'The %s exceeds the maximum allowed length of %s.' % ( invalid_field, _MAX_STRING_NAME_LENGTH) self.abort(400, detail=detail) message_out = io.StringIO() gen = email.generator.Generator(message_out, maxheaderlen=0) gen.flatten(message, unixfrom=False) # Get the content text out of the message. message_text = message_out.getvalue() content_start = message_text.find('\n\n') + 2 content_text = message_text[content_start:] content_text = content_text.replace('\n', '\r\n') return message.get('Content-Type'), content_text