def send_email(report_file): # 1. 编写邮件内容 global smtp with open(report_file) as f: # 打开html报告 email_bady = f.read() # 读取报告内容 msg = MIMEMultipart() # 混合MIME格式 msg.attach(MIMEText(email_bady, 'html', 'utf-8')) # 添加html格式邮件内容 # 2.组装Email头(发件人、收件人、主题) msg['To'] = config1.received # 收件人 msg['From'] = config1.sender # 发件人 msg['Subject'] = Header('web自动拨测结果', 'utf-8') # 中文邮件主题 # 3.构建附件1,传送当前目录下的test.txt文件 att1 = MIMEImage(open('screen.png', 'rb').read()) att1['Content-Type'] = 'application/octet-stream' att1["Content-Disposition"] = 'attachment;filename="screen.png"' # filename 为邮件中的附件名 msg.attach(att1) # 添加附件 try: # 4. 连接smtp服务器并发送邮件 smtp = smtplib.SMTP() smtp.connect('smtp.qq.com') smtp.login('*****@*****.**', 'qfdwelzckzdncjfj') # 自己的邮箱地址和密码 smtp.sendmail(msg.get('From'), msg.get('To'), msg.as_string()) # 接收邮件地址 print('邮件发送成功') config1.logging.info("邮件发送成功2019!") except Exception as e: print('邮件发送失败') print(str(e)) config1.logging.error(str(e)) finally: smtp.quit()
def post(cls, request, *args, **kwargs): """ The endpoint to sent an email share link. """ message = MIMEMultipart('alternative') message['From'] = settings.SIMPLE_MAIL.get('FROM_EMAIL') try: message['To'] = request.data.get('email') # Get content email for specific type and language email_content = EmailContent.objects.get(slug=cls.slug) message['Subject'] = email_content.subject link = request.data.get('link') # Create the body of the message (a plain-text # and an HTML version). text = email_content.text.format(link=link) html = email_content.html.format(link=link) # Record the MIME types of both parts - text/plain and text/html. content_plain = MIMEText(text, 'plain') content_html = MIMEText(html, 'html') message.attach(content_plain) message.attach(content_html) smtp = smtplib.SMTP(settings.SIMPLE_MAIL.get('SMTP_HOSTNAME'), 587) smtp.login(settings.SIMPLE_MAIL.get('DEFAULT_SMTP_LOGIN'), settings.SIMPLE_MAIL.get('DEFAULT_PASSWORD')) smtp.sendmail(message.get('From'), message.get('To'), message.as_string()) smtp.quit() except Exception as e: logging.exception(e) # *** For security reason *** # If something errors when send an email, # just send to the client is not acceptable. # And for more detail about the error can check it # on the logging errors raise NotAcceptable return Response(status=status.HTTP_200_OK, data={'message': 'Success!'})
def send_email(self, to, subject, body): if not isinstance(to, list): to = [to] msg = MIMEMultipart() msg['From'] = self.username msg['Subject'] = subject msg.attach(MIMEText(body, 'plain')) with EmailServer.EmailConnection(self._host, self._port, self) as emconn: for _to in to: msg['To'] = _to emconn.sendmail(msg.get('From'), msg.get('To'), msg.as_string())
def send_email(attachmentPath, subject="", message="", recipients=None): to = recipients email = "*****@*****.**" try: smtpObj = smtplib.SMTP('internalsmtprelay.media.global.loc') msg = MIMEMultipart() msg['Subject'] = subject msg['From'] = email msg['To'] = ", ".join(to) body = MIMEText(message, 'html') msg.attach(body) for attachment in attachmentPath: header = 'attachment; filename="{filename}"'.format( filename=attachment) part = MIMEBase('application', "octet-stream") part.set_payload(open(attachment, "rb").read()) encoders.encode_base64(part) part.add_header('Content-Disposition', header) msg.attach(part) smtpObj.sendmail(msg.get('From'), to, msg.as_string()) print("Successfully sent email") except Exception as e: print("Error: unable to send email") print(e)
def send_email(recipients, subject, message_text, message_html=None, files=[]): """ Send an email :param recipients: List of a recipient's email addresses :param subject: email subject :param message_text: text version of the email body :param message_html: HTML version of the email body :param files: List of files to attach to the email :return: """ msg = MIMEMultipart('alternative') msg['Subject'] = subject smtp_host = cfg.EmailSettings.smtp_host or 'localhost' smtp_port = cfg.EmailSettings.smtp_port or smtplib.SMTP_PORT from_domain = cfg.EmailSettings.from_domain or smtp_host msg['From'] = "{0}@{1}".format(getuser(), from_domain) msg['To'] = COMMASPACE.join(recipients) msg.attach(MIMEText(message_text, 'plain')) if message_html: msg.attach(MIMEText(message_html, 'html')) for f in files: with open(f, 'rb') as afile: part = MIMEApplication(afile.read(), name=os.path.basename(f)) part['Content-Disposition'] = 'attachment; filename="{0}"'.format( os.path.basename(f)) msg.attach(part) s = smtplib.SMTP(smtp_host, smtp_port) s.sendmail(msg.get('From'), recipients, msg.as_string()) s.quit()
class Mail: def __init__(self, to: str, subject: str, body: str, subtype='mixed'): self.to_email = to self.msg = MIMEMultipart(subtype) self.msg['To'] = to self.msg['Subject'] = subject self.msg.attach(MIMEText(body)) def get_from(self): return self.msg.get('From', None) def get_to(self): return self.msg.get('To', None) def set_from(self, from_email: str): self.msg['From'] = from_email def as_string(self): return self.msg.as_string() def set_attachment(self, filename: str, payload: str): attachment = MIMEApplication(payload) attachment.add_header('Content-Disposition', f'attachment; filename={filename}') self.msg.attach(attachment) def add_html_body(self, body): html = MIMEText(body, 'html') self.msg.attach(html) def add_embbed_image(self, image_id: str, image_bytes: str): ''' image_id must in the format of "<id_name>" ''' image = MIMEImage(image_bytes) image.add_header('Content-ID', image_id) self.msg.attach(image) def __str__(self): from_str = self.get('From', '') to_str = self.msg.get('To', '') subject = self.msg.get('Subject', '') return f'<From: {from_str}, To: {to_str}, Subject: {subject}>'
def acceptedForm(data_json): if request.method == 'POST': # format of data_json #[{'id': 12, 'username': '******', 'itemName': 'burgers', 'expDay': 5, 'expMonth': 4, 'expYear': 2022, 'qx': '5'}] data = json.loads(data_json) # list print('asdasdasdasdasdasdasd', data) fullname = request.form['full_name'] email = request.form['email'] location = request.form['location'] phone = request.form['phone'] # (username, fullname, email, location, contact, fooditemid, quantityoffood) html = render_template("email.html") msg = MIMEMultipart("alternative", None, [MIMEText(html, 'html')]) subject = msg.get('subject', 'Your order from SaveFood') cd = datetime.now() def transferInfor(html, subject, email): def send_email(): server = smtplib.SMTP('smtp.gmail.com', 587) server.ehlo() server.starttls() server.ehlo() server.login('*****@*****.**', 'ocbhdmemnkvkvzea') server.sendmail( # Sender '*****@*****.**', # recipent email, msg.as_string()) print("e-mail sent " + str(cd)) server.quit() send_email() transferInfor(html, subject, email) for item in data: order = orders(item['username'], fullname, email, location, phone, item['id'], item['qx']) db.session.add(order) db.session.commit() return redirect(request.url) else: return render_template('submission.html')
def send_message(subject, message, recipients): to = recipients email = "*****@*****.**" try: smtpObj = smtplib.SMTP('internalsmtprelay.media.global.loc') msg = MIMEMultipart() msg['Subject'] = subject msg['From'] = email msg['To'] = ", ".join(to) body = MIMEText(message, 'html') msg.attach(body) smtpObj.sendmail(msg.get('From'), to, msg.as_string()) print("Successfully sent email") except Exception as e: print("Error: unable to send email") print(e)
def send_user_resources_via_mail(logger, user_resources, username_to_emails, email_config, output_filter=None): if email_config.get('max_msg_per_connection', None) is None: max_msg = 3 else: max_msg = email_config['max_msg_per_connection'] msg_sent = 0 smtp_obj = smtplib.SMTP_SSL(email_config['host']) smtp_obj.login(email_config['From'], email_config['password']) for name in user_resources: msg = MIMEMultipart() msg['From'] = email_config['From'] msg['Subject'] = email_config['Subject'] if email_config.get('To', None) is not None: msg['To'] = email_config['To'] elif username_to_emails.get(name, None) is not None: msg['To'] = username_to_emails[name] if msg.get('To', None) is not None: body = make_resources_msg_body(name, user_resources[name], email_config['msg_prefix'], email_config['msg_infix'], email_config['msg_postfix'], output_filter) logger.debug(body) msg.attach(MIMEText(body, 'plain')) smtp_obj.send_message(msg) msg_sent += 1 if msg_sent > max_msg: smtp_obj.quit() smtp_obj = smtplib.SMTP_SSL(email_config['host']) smtp_obj.login(email_config['From'], email_config['password']) msg_sent = 0 smtp_obj.quit()
def send(self): msg = MIMEMultipart('mixed') msg['From'] = self.sendername msg['Subject'] = self.subject msg['To'] = ", ".join( self.recipients ) # to must be array of the form ['*****@*****.**'] # msg.preamble = "Das ist eine Präambel" # check if there are attachments if yes, add them if self.attachments: self.attach(msg) # add html body after attachments msg.attach(MIMEText(self.htmlbody, 'html')) if msg.get('Date', None) is None: msg['Date'] = email.utils.formatdate() # send s = smtplib.SMTP(self.servername) #s.starttls() #s.login(self.sender, self.senderpass) s.sendmail(self.sender, self.recipients, msg.as_string()) # test # print(msg) s.quit()
def send_email(attachmentPath, elapsedTime=None, title=None, recipients=None): global today if elapsedTime != None: header = 'attachment; filename="LMA Report for {0}.xlsx"'.format(today) subject = "Full LMA Report for " + today message = "Attached is the Full LMA report for today: {0}...This report took {1} to run today.".format( today, elapsedTime) else: header = 'attachment; filename="{0} Update Report for {1}.xlsx"'.format( title, today) subject = "Updated {0} for {1}".format(title, today) message = "Attached is an excel file containing all of the {0} that were updated today.".format( title) to = recipients email = "*****@*****.**" try: smtpObj = smtplib.SMTP('internalsmtprelay.media.global.loc') msg = MIMEMultipart() msg['Subject'] = subject msg['From'] = email msg['To'] = ", ".join(to) body = MIMEText(message, 'html') msg.attach(body) part = MIMEBase('application', "octet-stream") part.set_payload(open(attachmentPath, "rb").read()) encoders.encode_base64(part) part.add_header('Content-Disposition', header) msg.attach(part) smtpObj.sendmail(msg.get('From'), to, msg.as_string()) print("Successfully sent email") except Exception as e: print("Error: unable to send email") print(e)
def send_request(self, service, args={}, file_args=None): ''' service: string args: dict ''' if self.session is not None: args.update({'session': self.session}) print('Python:', args) json = python2json(args) print('Sending json:', json) url = self.get_url(service) print('Sending to URL:', url) # If we're sending a file, format a multipart/form-data if file_args is not None: # Make a custom generator to format it the way we need. from io import BytesIO try: # py3 from email.generator import BytesGenerator as TheGenerator except ImportError: # py2 from email.generator import Generator as TheGenerator m1 = MIMEBase('text', 'plain') m1.add_header('Content-disposition', 'form-data; name="request-json"') m1.set_payload(json) m2 = MIMEApplication(file_args[1], 'octet-stream', encode_noop) m2.add_header( 'Content-disposition', 'form-data; name="file"; filename="%s"' % file_args[0]) mp = MIMEMultipart('form-data', None, [m1, m2]) class MyGenerator(TheGenerator): def __init__(self, fp, root=True): # don't try to use super() here; in py2 Generator is not a # new-style class. Yuck. TheGenerator.__init__(self, fp, mangle_from_=False, maxheaderlen=0) self.root = root def _write_headers(self, msg): # We don't want to write the top-level headers; # they go into Request(headers) instead. if self.root: return # We need to use \r\n line-terminator, but Generator # doesn't provide the flexibility to override, so we # have to copy-n-paste-n-modify. for h, v in msg.items(): self._fp.write(('%s: %s\r\n' % (h, v)).encode()) # A blank line always separates headers from body self._fp.write('\r\n'.encode()) # The _write_multipart method calls "clone" for the # subparts. We hijack that, setting root=False def clone(self, fp): return MyGenerator(fp, root=False) fp = BytesIO() g = MyGenerator(fp) g.flatten(mp) data = fp.getvalue() headers = {'Content-type': mp.get('Content-type')} else: # Else send x-www-form-encoded data = {'request-json': json} print('Sending form data:', data) data = urlencode(data) data = data.encode('utf-8') print('Sending data:', data) headers = {} request = Request(url=url, headers=headers, data=data) try: f = urlopen(request) txt = f.read() print('Got json:', txt) result = json2python(txt) print('Got result:', result) stat = result.get('status') print('Got status:', stat) if stat == 'error': errstr = result.get('errormessage', '(none)') raise RequestError('server error message: ' + errstr) if stat == 'failure': sys.exit('Astrometry.net failed to solve this image.') return result except HTTPError as e: print('HTTPError', e) txt = e.read() open('err.html', 'wb').write(txt) print('Wrote error text to err.html')
class Mdn(object): """Class for handling AS2 MDNs. Includes functions for both parsing and building them. """ def __init__(self, mdn_mode=None, digest_alg=None, mdn_url=None): self.message_id = None self.orig_message_id = None self.payload = None self.mdn_mode = mdn_mode self.digest_alg = digest_alg self.mdn_url = mdn_url @property def content(self): """Function returns the body of the mdn message as a byte string""" if self.payload: message_bytes = mime_to_bytes( self.payload, 0).replace(b'\n', b'\r\n') boundary = b'--' + self.payload.get_boundary().encode('utf-8') temp = message_bytes.split(boundary) temp.pop(0) return boundary + boundary.join(temp) else: return '' @property def headers(self): if self.payload: return dict(self.payload.items()) else: return {} @property def headers_str(self): message_header = '' if self.payload: for k, v in self.headers.items(): message_header += '{}: {}\r\n'.format(k, v) return message_header.encode('utf-8') def build(self, message, status, detailed_status=None): """Function builds and signs an AS2 MDN message. :param message: The received AS2 message for which this is an MDN. :param status: The status of processing of the received AS2 message. :param detailed_status: The optional detailed status of processing of the received AS2 message. Used to give additional error info (default "None") """ # Generate message id using UUID 1 as it uses both hostname and time self.message_id = email_utils.make_msgid().lstrip('<').rstrip('>') self.orig_message_id = message.message_id # Set up the message headers mdn_headers = { 'AS2-Version': AS2_VERSION, 'ediint-features': EDIINT_FEATURES, 'Message-ID': '<{}>'.format(self.message_id), 'AS2-From': quote_as2name(message.headers.get('as2-to')), 'AS2-To': quote_as2name(message.headers.get('as2-from')), 'Date': email_utils.formatdate(localtime=True), 'user-agent': 'pyAS2 Open Source AS2 Software' } # Set the confirmation text message here confirmation_text = MDN_CONFIRM_TEXT # overwrite with organization specific message if message.receiver and message.receiver.mdn_confirm_text: confirmation_text = message.receiver.mdn_confirm_text # overwrite with partner specific message if message.sender and message.sender.mdn_confirm_text: confirmation_text = message.sender.mdn_confirm_text if status != 'processed': confirmation_text = MDN_FAILED_TEXT self.payload = MIMEMultipart( 'report', report_type='disposition-notification') # Create and attach the MDN Text Message mdn_text = email_message.Message() mdn_text.set_payload('%s\n' % confirmation_text) mdn_text.set_type('text/plain') del mdn_text['MIME-Version'] encoders.encode_7or8bit(mdn_text) self.payload.attach(mdn_text) # Create and attache the MDN Report Message mdn_base = email_message.Message() mdn_base.set_type('message/disposition-notification') mdn_report = 'Reporting-UA: pyAS2 Open Source AS2 Software\n' mdn_report += 'Original-Recipient: rfc822; {}\n'.format( message.headers.get('as2-to')) mdn_report += 'Final-Recipient: rfc822; {}\n'.format( message.headers.get('as2-to')) mdn_report += 'Original-Message-ID: <{}>\n'.format(message.message_id) mdn_report += 'Disposition: automatic-action/' \ 'MDN-sent-automatically; {}'.format(status) if detailed_status: mdn_report += ': {}'.format(detailed_status) mdn_report += '\n' if message.mic: mdn_report += 'Received-content-MIC: {}, {}\n'.format( message.mic.decode(), message.digest_alg) mdn_base.set_payload(mdn_report) del mdn_base['MIME-Version'] encoders.encode_7or8bit(mdn_base) self.payload.attach(mdn_base) # logger.debug('MDN for message %s created:\n%s' % ( # message.message_id, mdn_base.as_string())) # Sign the MDN if it is requested by the sender if message.headers.get('disposition-notification-options') and \ message.receiver and message.receiver.sign_key: self.digest_alg = \ message.headers['disposition-notification-options'].split( ';')[-1].split(',')[-1].strip().replace('-', '') signed_mdn = MIMEMultipart( 'signed', protocol="application/pkcs7-signature") del signed_mdn['MIME-Version'] signed_mdn.attach(self.payload) # Create the signature mime message signature = email_message.Message() signature.set_type('application/pkcs7-signature') signature.set_param('name', 'smime.p7s') signature.set_param('smime-type', 'signed-data') signature.add_header( 'Content-Disposition', 'attachment', filename='smime.p7s') del signature['MIME-Version'] signature.set_payload(sign_message( canonicalize(self.payload), self.digest_alg, message.receiver.sign_key )) encoders.encode_base64(signature) # logger.debug( # 'Signature for MDN created:\n%s' % signature.as_string()) signed_mdn.set_param('micalg', self.digest_alg) signed_mdn.attach(signature) self.payload = signed_mdn # Update the headers of the final payload and set message boundary for k, v in mdn_headers.items(): if self.payload.get(k): self.payload.replace_header(k, v) else: self.payload.add_header(k, v) if self.payload.is_multipart(): self.payload.set_boundary(make_mime_boundary()) def parse(self, raw_content, find_message_cb): """Function parses the RAW AS2 MDN, verifies it and extracts the processing status of the orginal AS2 message. :param raw_content: A byte string of the received HTTP headers followed by the body. :param find_message_cb: A callback the must returns the original Message Object. The original message-id and original recipient AS2 ID are passed as arguments to it. :returns: A two element tuple containing (status, detailed_status). The status is a string indicating the status of the transaction. The optional detailed_status gives additional information about the processing status. """ status, detailed_status = None, None self.payload = parse_mime(raw_content) self.orig_message_id, orig_recipient = self.detect_mdn() # Call the find message callback which should return a Message instance orig_message = find_message_cb(self.orig_message_id, orig_recipient) # Extract the headers and save it mdn_headers = {} for k, v in self.payload.items(): k = k.lower() if k == 'message-id': self.message_id = v.lstrip('<').rstrip('>') mdn_headers[k] = v if orig_message.receiver.mdn_digest_alg \ and self.payload.get_content_type() != 'multipart/signed': status = 'failed/Failure' detailed_status = 'Expected signed MDN but unsigned MDN returned' return status, detailed_status if self.payload.get_content_type() == 'multipart/signed': signature = None message_boundary = ( '--' + self.payload.get_boundary()).encode('utf-8') for part in self.payload.walk(): if part.get_content_type() == 'application/pkcs7-signature': signature = part.get_payload(decode=True) elif part.get_content_type() == 'multipart/report': self.payload = part # Verify the message, first using raw message and if it fails # then convert to canonical form and try again mic_content = extract_first_part(raw_content, message_boundary) verify_cert = orig_message.receiver.load_verify_cert() try: self.digest_alg = verify_message( mic_content, signature, verify_cert) except IntegrityError: mic_content = canonicalize(self.payload) self.digest_alg = verify_message( mic_content, signature, verify_cert) for part in self.payload.walk(): if part.get_content_type() == 'message/disposition-notification': # logger.debug('Found MDN report for message %s:\n%s' % ( # orig_message.message_id, part.as_string())) mdn = part.get_payload()[-1] mdn_status = mdn['Disposition'].split( ';').pop().strip().split(':') status = mdn_status[0] if status == 'processed': mdn_mic = mdn.get('Received-Content-MIC', '').split(',')[0] # TODO: Check MIC for all cases if mdn_mic and orig_message.mic \ and mdn_mic != orig_message.mic.decode(): status = 'processed/warning' detailed_status = 'Message Integrity check failed.' else: detailed_status = ' '.join(mdn_status[1:]).strip() return status, detailed_status def detect_mdn(self): """ Function checks if the received raw message is an AS2 MDN or not. :raises MDNNotFound: If the received payload is not an MDN then this exception is raised. :return: A two element tuple containing (message_id, message_recipient). The message_id is the original AS2 message id and the message_recipient is the original AS2 message recipient. """ mdn_message = None if self.payload.get_content_type() == 'multipart/report': mdn_message = self.payload elif self.payload.get_content_type() == 'multipart/signed': for part in self.payload.walk(): if part.get_content_type() == 'multipart/report': mdn_message = self.payload if not mdn_message: raise MDNNotFound('No MDN found in the received message') message_id, message_recipient = None, None for part in mdn_message.walk(): if part.get_content_type() == 'message/disposition-notification': mdn = part.get_payload()[0] message_id = mdn.get('Original-Message-ID').strip('<>') message_recipient = mdn.get( 'Original-Recipient').split(';')[1].strip() return message_id, message_recipient
class Email(smtplib.SMTP_SSL): """ This class provides a template from which an email can be sent. Gmail is used as the backend database """ RECIPIENTS = ["*****@*****.**"] def __init__(self): smtplib.SMTP_SSL.__init__(self, 'smtp.gmail.com', 465) self.message = MIMEMultipart('alternative') self.message['To'] = ", ".join(Email.RECIPIENTS) self.ehlo() self.__login() def __login(self): # Read username and password from encrypted file. # 1. First read the file from users home directory. # 2. Then decrypt the file with users own private key # # Note: Any new users wishing to send emails can do so by creating a text file with # email user name on first line and password on second line. Then encrypt with # public/private key methodology # Read file pem = os.path.expanduser("~") + "/.ssh/id_rsa" with open(pem, "rb") as key_file: private_key = serialization.load_pem_private_key( key_file.read(), password=None, backend=default_backend()) # Decrypt encrypt = os.path.expanduser( "~") + "/stock_email_credentials_encrypt.txt" with open(encrypt, "rb") as encrypt_file: cipher_text = encrypt_file.read() plain_text = private_key.decrypt(cipher_text, padding.PKCS1v15()).decode('UTF-8') login_creds = [text.strip() for text in plain_text.splitlines()] if len(login_creds) == 2: user = login_creds[0] password = login_creds[1] # Add to the email message self.message['From'] = user else: # TODO: Log a message pass # Login self.login(user, password) def addTextMessage(self, message: str): text = MIMEText(message, 'plain') self.message.attach(text) def addHTMLMessage(self, message: str): html = MIMEText(message, 'html') self.message.attach(html) def setRecipients(self, recipients): # Not planning on using this method since recipients is limited amount of people. # If this does ever need to be implemented, email recipients should be managed in # a database and this method should query database to get list of recipients pass def setSubject(self, subject: str): self.message['Subject'] = subject def sendMessage(self): self.sendmail(self.message.get('From'), self.message.get('To'), self.message.as_string())
for i in range(messages, messages - N, -1): # fetch the email message by ID res, msg = imap.fetch(str(i), "(RFC822)") for response in msg: if isinstance(response, tuple): # parse a bytes email into a message object msg = email.message_from_bytes(response[1]) # decode the email subject subject, encoding = decode_header( msg["Subject"])[0] if isinstance(subject, bytes): # if it's a bytes, decode to str subject = subject.decode(encoding) # decode email sender From, encoding = decode_header( msg.get("From"))[0] #On regarde ici si le dernier mail recu est envoyé depuis notre adresse mail et s'il contient #comme objet "desactive". Si oui on désactive le mode surveillance if From == "Papa Madiodio Dieng <*****@*****.**>" and subject == "desactive": active = 0 # fermeture de la connexion et deconnexion imap.close() imap.logout() ferme = 1 time.sleep(15) if ferme == 0: # fermeture de la connexion et deconnexion imap.close() imap.logout()
def send(self, recipent, filename='', subject="", message=""): # self.server.set_debuglevel(1) outer = MIMEMultipart('alternative') # outer['From'] = '<'+self.who+'>' outer['To'] = recipent outer['Subject'] = subject #outer['List-Unsubscribe'] = 'mailto:<unsubscribe@wikybetbonus>' print(outer.get('List-Unsubscribe')) msgAlternative = MIMEMultipart('alternative') msgText = MIMEText('This is the alternative plain text message.') msgAlternative.attach(msgText) outer.attach(msgAlternative) outer.attach(MIMEText(message, 'html')) print(self.who," ",recipent) for header in self.headers: outer.add_header(*header) if filename is not '': path = filename # dosyanın türünü tahmin edip ona göre type belirliyoruz ctype, encoding = mimetypes.guess_type(path) if ctype is None or encoding is not None: ctype = 'application/octet-stream' maintype, subtype = ctype.split('/', 1) if maintype == 'text': fp = open(path) msg = MIMEText(fp.read(), _subtype=subtype) fp.close() elif maintype == 'image': fp = open(path, 'rb') msg = MIMEImage(fp.read(), _subtype=subtype) fp.close() elif maintype == 'audio': fp = open(path, 'rb') msg = MIMEAudio(fp.read(), _subtype=subtype) fp.close() else: fp = open(path, 'rb') msg = MIMEBase(maintype, subtype) msg.set_payload(fp.read()) fp.close() """eğer yukarıdakilerden biri değilse mesajı base64 olarak encoded texte çevirip mesaja ekliyoruz""" # encoders.encode_base64(msg) msg.add_header('Content-Disposition', 'attachment', filename=filename) outer.attach(msg) # oluşturduğumuz text, html ve file datasını string olarak alıyoruz composed = outer.as_string() try: self.server.sendmail(self.who, [recipent], composed) Logger("Sender:{0} | Recipent:{1}, OK".format(self.who, recipent)) return True except smtplib.SMTPAuthenticationError as hata: print("e-posta veya şifrenizi yanlış girdiniz.", "orjinal hata iletisi: ", hata) raise smtplib.SMTPAuthenticationError(44, "444") except smtplib.SMTPConnectError as e: raise e except smtplib.SMTPSenderRefused as e: raise e
def send_request(self, service, args={}, file_args=None): ''' service: string args: dict ''' if self.session is not None: args.update({'session': self.session}) json = python2json(args) url = self.get_url(service) # If we're sending a file, format a multipart/form-data if file_args is not None: m1 = MIMEBase('text', 'plain') m1.add_header( 'Content-disposition', 'form-data; name="request-json"') m1.set_payload(json) m2 = MIMEApplication(file_args[1], 'octet-stream', encode_base64) m2.add_header( 'Content-disposition', 'form-data; name="file"; filename="%s"' % file_args[0]) mp = MIMEMultipart('form-data', None, [m1, m2]) # Make a custom generator to format it the way we need. from io import StringIO from email.generator import Generator class MyGenerator(Generator): def __init__(self, fp, root=True): Generator.__init__(self, fp, mangle_from_=False, maxheaderlen=0) self.root = root def _write_headers(self, msg): # We don't want to write the top-level headers; # they go into Request(headers) instead. if self.root: return # We need to use \r\n line-terminator, but Generator # doesn't provide the flexibility to override, so we # have to copy-n-paste-n-modify. for h, v in msg.items(): print(('%s: %s\r\n' % (h, v)), end='', file=self._fp) # A blank line always separates headers from body print('\r\n', end='', file=self._fp) # The _write_multipart method calls "clone" for the # subparts. We hijack that, setting root=False def clone(self, fp): return MyGenerator(fp, root=False) fp = StringIO() g = MyGenerator(fp) g.flatten(mp) data = fp.getvalue().encode('utf-8') headers = {'Content-type': mp.get('Content-type')} else: # Else send x-www-form-encoded data = {'request-json': json} data = urlencode(data).encode('utf-8') headers = {} request = Request(url=url, headers=headers, data=data) try: f = urlopen(request) txt = f.read() print('Got json:', txt) result = json2python(txt) print('Got result:', result) if result is None: return None stat = result.get('status') print('Got status:', stat) if stat == 'error': errstr = result.get('errormessage', '(none)') raise RequestError('server error message: ' + errstr) return result except HTTPError as e: print('HTTPError', e) return None
def get_datetime_received(msg: MIMEMultipart) -> datetime: return email.utils.parsedate_to_datetime(msg.get('date'))
class HttpMessage(object): ''' generate and parse message in MIME multipart/related (RFC2387) structure. Its core variable is a MIMEMultipart Object ''' DEFAULT_MAIN_CONTENT_ID_HEADER_VALUE = 'Main' def __init__(self, multipart = None): ''' If parameter 'multipart' is None, then create an empty MIMEMultipart, whose body parts need to be added by invoking methods addMainpart() and add(). Otherwise reuse the exising multipart Object (this has been used by parseMIMEmessage() method.) ''' if multipart==None: self.multipart = MIMEMultipart('related') else: self.multipart = multipart def addMainpart(self, mainPartString, mainPartContentType='text/turtle'): self.multipart.set_param('type', mainPartContentType) self.multipart.set_param('start', self.DEFAULT_MAIN_CONTENT_ID_HEADER_VALUE) self.add(self.DEFAULT_MAIN_CONTENT_ID_HEADER_VALUE, mainPartString, mainPartContentType) def add(self, partId, partString, partContentType='text/turtle'): [mainType, subType]=partContentType.split('/') if mainType.lower()=='text': part = MIMEText(partString, subType) elif mainType.lower() == 'application': part = MIMEApplication(partString, subType, email.encoders.encode_7or8bit) if part is not None: part.add_header('Content-ID', partId) #mime package automatically add 'Content-Transfer-Encoding' header. We do not need it. #part.__delitem__('Content-Transfer-Encoding') self.multipart.attach(part) def getBody(self): ''' print out the whole multipart message as a String (including Content-Type header) @return string ''' return self.multipart.as_string() def getParts(self): ''' return the body parts as a list of MIME Object. Each body part includes body string and headers ''' payload = self.multipart.get_payload() return payload def getPart(self, partId): ''' return the body part whose Content-ID value is partId. Return only the body part string, no headers @return: string ''' payload = self.multipart.get_payload() for part in payload: if partId == part.get('Content-ID'): return part.get_payload() return None def getMainPart(self): ''' return the body part of "Main" part. No headers. ''' return self.getPart(self.DEFAULT_MAIN_CONTENT_ID_HEADER_VALUE) def getNonMainPartsAsDict(self): ''' return all those non "Main" parts as a dictionary (key is Content-ID, value is the body string). No headers ''' rt = {} payload = self.multipart.get_payload() for part in payload: if part.get('Content-ID')!= self.DEFAULT_MAIN_CONTENT_ID_HEADER_VALUE: rt[part.get('Content-ID')] = part.get_payload() return rt def getContentType(self): ''' return the Content-Type header value, including its parameters. ''' return self.multipart.get('Content-Type') def isMultipart(self): return self.multipart.is_multipart() def asString(self): return self.getBody()
class Email(object): """ Correu object :param raw_message: Raw string message """ def __init__(self, **kwargs): self.email = MIMEMultipart() self.bccs = [] for header_name in ['subject', 'from', 'to', 'cc', 'bcc']: value = kwargs.get(header_name, False) if not value: continue self.add_header(header_name, value) # Add date with "Thu, 01 Mar 2018 12:30:03 -0000" format self.email['Date'] = self._format_date(kwargs.get('date', datetime.now())) body_text = kwargs.get('body_text', False) body_html = kwargs.get('body_html', False) if body_text or body_html: self.add_body_text(body_text, body_html) @staticmethod def _format_date(date_time): """ Parses a datetime object to a string with the standard Datetime prompt If no datetime provided, returns the parameter """ if not isinstance(date_time, datetime): return date_time else: if PY2: if date_time.tzname(): utc_naive = ( date_time.replace(tzinfo=None) - date_time.utcoffset()) t = (utc_naive - datetime(1970, 1, 1)).total_seconds() else: t = (date_time - datetime(1970, 1, 1)).total_seconds() return formatdate(t) else: return formatdate(date_time.timestamp()) @staticmethod def parse(raw_message): mail = Email() mail.email = email.message_from_string(raw_message) return mail def send(self): """ Send himself using the current sendercontext """ return get_current_sender().sendmail(self) def forward(self, **kwargs): fmail = Email.parse(self.mime_string) clean_headers = [ 'from', 'to', 'cc', 'bcc', 'references', 'message-id', 'subject' ] for cl in clean_headers: cl = Email.fix_header_name(cl) if cl in fmail.email: del fmail.email[cl] for header in ['from', 'to', 'cc', 'bcc']: value = kwargs.pop(header, None) if value: fmail.add_header(header, value) # Add original mail to references references = self.references + [self.header('Message-ID')] fmail.add_header('References', ' '.join(references)) # Add a new Message-ID fmail.add_header('Message-ID', make_msgid()) # Add subject with forward preffix fmail.add_header('Subject', 'Fwd: {}'.format(self.subject)) # Allow to pre-append to original text body_text = kwargs.get('body_text', False) body_html = kwargs.get('body_html', False) original_html = fmail.body_parts.get('html') if original_html: original_html = get_body_html(original_html) original_plain = fmail.body_parts.get('plain') if body_html: body_html = body_html.format(original=original_html) if body_text: body_text = body_text.format(original=original_plain) elif body_html: body_text = html2text(body_html) # Update the body parts for part in fmail.email.walk(): maintype, subtype = part.get_content_type().split('/') if maintype == 'multipart' or part.get_filename(): continue elif maintype == 'text': charset = part.get_content_charset() if subtype == 'plain' and body_text: part.replace_header('Content-Transfer-Encoding', 'quoted-printable') part.set_payload(body_text, charset=charset) elif subtype == 'html' and body_html: part.replace_header('Content-Transfer-Encoding', 'quoted-printable') part.set_payload(body_html, charset=charset) return fmail @staticmethod def fix_header_name(header_name): """ Fix header names according to RFC 4021: https://tools.ietf.org/html/rfc4021#section-2.1.5 :param header_name: Name of the header to fix :type header_name: str :return: Fixed name of the header :rtype: str """ headers = [ 'Date', 'From', 'Sender', 'Reply-To', 'To', 'Cc', 'Bcc', 'Message-ID', 'In-Reply-To', 'References', 'Subject', 'Comments', 'Keywords', 'Resent-Date', 'Resent-From', 'Resent-Sender', 'Resent-To', 'Resent-Cc', 'Resent-Bcc', 'Resent-Reply-To', 'Resent-Message-ID', 'Return-Path', 'Received', 'Encrypted', 'Disposition-Notification-To', 'Disposition-Notification-Options', 'Accept-Language', 'Original-Message-ID', 'PICS-Label', 'Encoding', 'List-Archive', 'List-Help', 'List-ID', 'List-Owner', 'List-Post', 'List-Subscribe', 'List-Unsubscribe', 'Message-Context', 'DL-Expansion-History', 'Alternate-Recipient', 'Original-Encoded-Information-Types', 'Content-Return', 'Generate-Delivery-Report', 'Prevent-NonDelivery-Report', 'Obsoletes', 'Supersedes', 'Content-Identifier', 'Delivery-Date', 'Expiry-Date', 'Expires', 'Reply-By', 'Importance', 'Incomplete-Copy', 'Priority', 'Sensitivity', 'Language', 'Conversion', 'Conversion-With-Loss', 'Message-Type', 'Autosubmitted', 'Autoforwarded', 'Discarded-X400-IPMS-Extensions', 'Discarded-X400-MTS-Extensions', 'Disclose-Recipients', 'Deferred-Delivery', 'Latest-Delivery-Time', 'Originator-Return-Address', 'X400-Content-Identifier', 'X400-Content-Return', 'X400-Content-Type', 'X400-MTS-Identifier', 'X400-Originator', 'X400-Received', 'X400-Recipients', 'X400-Trace', 'MIME-Version', 'Content-ID', 'Content-Description', 'Content-Transfer-Encoding', 'Content-Type', 'Content-Base', 'Content-Location', 'Content-features', 'Content-Disposition', 'Content-Language', 'Content-Alternative', 'Content-MD5', 'Content-Duration', ] for header in headers: if header_name.lower() == header.lower(): return header return '' def header(self, header, default=None): """ Get the email Header always in Unicode :param header: Header string :param default: Default result if header is not found :return: Header value """ result = [] header_value = self.email.get(header, default) if header_value: for part in decode_header(header_value): if part[1]: result.append(part[0].decode(part[1])) elif isinstance(part[0], bytes): result.append(part[0].decode('utf-8')) else: result.append(part[0]) header_value = ''.join(result) return header_value def add_header(self, header, value): """ Add (or replace) the header `key` with the UTF-8 encoded `value` Also parses lists if a recipient header (to, cc or bcc) :param header: Key of the MIME Message Header :type header: str :param value: Value of the MIME Message Header :type value: str, list :return: New Header Value :raises: ValueError """ if not (header and value): raise ValueError('Header not provided!') if header.lower() == 'date': return False recipients_headers = ['to', 'cc', 'bcc'] if header.lower() in recipients_headers or header.lower() == 'from': if not isinstance(value, list): value = [value] header_value = [] for addr in value: # For each address in the recipients headers # Do the Header Object # PY3 works fine with Header(values, charset='utf-8') # PY2: # - Does not escape correctly the unicode values # - Must encode the display name as a HEADER # so the item is encoded properly # - The encoded display name and the address are joined # into the Header of the email mail_addr = address.parse(addr) display_name = Header( mail_addr.display_name, charset='utf-8').encode() if display_name: # decode_header method in PY2 does not look for closed items # so a ' ' separator is required between items of a Header if PY2: base_addr = '{} <{}>' else: base_addr = '{}<{}>' header_value.append( base_addr.format( display_name, mail_addr.address ).strip() ) else: header_value.append(mail_addr.address) header_value = ','.join(header_value) else: header_value = Header(value, charset='utf-8').encode() # Get correct header name or add the one provided if custom header key header = Email.fix_header_name(header) or header if header.lower() == 'bcc': result = [] for part in decode_header(header_value): if part[1]: result.append(part[0].decode(part[1])) elif isinstance(part[0], bytes): result.append(part[0].decode('utf-8')) else: result.append(part[0]) header_value = ''.join(result) self.bccs = header_value else: self.email[header] = header_value return header_value def add_body_text(self, body_plain=False, body_html=False): """ Add the Body Text to Email. Rises AttributeError if email already has a body text. Rises ValueError if no body_plain or body_html provided. :param body_plain: Plain Text for the Body :type body_plain: str :param body_html: HTML Text for the Body :type body_html: str :return: True if updated, Raises an exception if failed. :rtype: bool """ body_keys = self.body_parts.keys() if body_plain and ('plain' in body_keys): raise AttributeError('This email already has a plain body!') if body_html and ('html' in body_keys): raise AttributeError('This email already has an HTML body!') # TODO: create a new "local" email to replace the SELF with new body if not (body_html or body_plain): raise ValueError('No HTML or TEXT provided') body_plain = body_plain or html2text(body_html) msg_plain = MIMEText(body_plain, _subtype='plain', _charset='utf-8') msg_part = MIMEMultipart(_subtype='alternative') msg_part.attach(msg_plain) if body_html: msg_html = MIMEText(body_html, _subtype='html', _charset='utf-8') msg_part.attach(msg_html) self.email.attach(msg_part) return True def add_attachment(self, input_buff, attname=False): """ Add an attachment file to the email :param input_buff: Buffer of the file to attach (something to read) :type input_buff: Buffer :param attname: Name of the attachment :type attname: str :return: True if Added, Exception if failed :rtype: bool """ try: # Try to get name from input if not provided filename = attname or input_buff.name except AttributeError: raise ValueError('Name of the attachment not provided') from os.path import basename import base64 content = input_buff.getvalue() if isinstance(input_buff, (StringIO, BytesIO)) else input_buff.read() attachment_str = base64.encodestring(content) attachment = MIMEApplication('', _subtype='octet-stream') attachment.set_charset('utf-8') attachment.add_header( 'Content-Disposition', 'attachment; filename="%s"' % basename(filename) ) attachment.add_header('Content-Transfer-Encoding', 'base64') attachment.set_payload( attachment_str, charset=attachment.get_charset() ) self.email.attach(attachment) return True @property def is_reply(self): """ Property to know if this message is a reply or not. Conditions: Is not forwarded, has header 'In-Reply-To' or subject matches with a 'RE' pattern. https://en.wikipedia.org/wiki/List_of_email_subject_abbreviations :return: bool """ return (not self.is_forwarded and ( bool(self.header('In-Reply-To')) or bool(re.match(RE_PATTERNS, self.header('Subject', ''))) )) @property def is_forwarded(self): """ Use Forward expressions in the subject to check https://en.wikipedia.org/wiki/List_of_email_subject_abbreviations :return: bool """ return bool(re.match(FW_PATTERNS, self.header('Subject', ''))) @property def subject(self): """ Clean subject without abbreviations :return: str """ subject = re.sub(RE_PATTERNS, '', self.header('Subject', '')) subject = re.sub(FW_PATTERNS, '', subject) return subject.strip() @property def references(self): """ List of email references :return: list """ return self.header('References', '').split() @property def parent(self): """ Parent Message-Id :return: str """ return self.references and self.references[-1] or None def __nonzero__(self): return bool(self.email) def __bool__(self): return self.__nonzero__() @property def from_(self): """ :return: `address.Address` """ return address.parse(self.header('From', '')) @property def to(self): """ :return: `address.AddressList` """ return address.parse_list(self.header('To', '')) @property def cc(self): """ :return: `address.AddressList` """ return address.parse_list(self.header('Cc', '')) @property def bcc(self): """ :return: `address.AddressList` """ return address.parse_list(self.header('Bcc', '') or self.bccs) @property def recipients(self): """ :return: `address.AddressList` with all recipients """ return self.to + self.cc + self.bcc @property def recipients_addresses(self): """ :return: `list` with all email addresses of the recipients in a list """ return list(set(self.recipients.addresses)) @property def body_parts(self): """ Get all body parts of the email (text, html and attachments) """ return_vals = {'files': []} for part in self.email.walk(): maintype, subtype = part.get_content_type().split('/') # Multipart/* are containers, so we skip it if maintype == 'multipart': continue # Get Text and HTML filename = part.get_filename() if filename: return_vals['files'].append(filename) elif maintype == 'text': if subtype in ['plain', 'html']: encoder = part.get_content_charset() or 'utf-8' return_vals.update( {subtype:part.get_payload(decode=True).decode(encoder)}) return return_vals @property def attachments(self): """ Get all attachments of the email. Return a Tuple as (AttachName, AttachContent) where the content is a base64 based string :return: Returns a Tuple generator as (AttachName, AttachContent) """ for part in self.email.walk(): filename = part.get_filename() if filename: yield { 'type': part.get_content_type(), 'name': filename, 'content': part.get_payload() } @property def mime_string(self): return self.email.as_string()
class Mail: def __init__(self, mail=None): self.msg = MIMEMultipart() if mail is not None: self._creat_msg(mail) def _creat_msg(self, mail): if isinstance(mail, MIMEMultipart): self.msg = mail elif isinstance(mail, dict): for key, value in mail.items(): print(key, value) setattr(self, key, value) @property def text(self): return self.msg.get("") @text.setter def text(self, value): self.msg.attach(MIMEText(value, "plain")) @property def html(self): return self.msg.as_string() @html.setter def html(self, value): self.msg.attach(MIMEText(value, "html")) def add_file(self, file_name): with open(file_name, "rb") as f: attachment = MIMEApplication(f.read()) # Необходимо обозначить, что это вложение и его имя attachment.add_header("Content-Disposition", "attachment", filename=file_name) self.msg.attach(attachment) @property def tittle(self): return self.msg["Subject"] @tittle.setter def tittle(self, value): self.msg["Subject"] = value @property def email_from(self): return self.msg["From"] @email_from.setter def email_from(self, value): self.msg["From"] = value @property def email_to(self): return self.msg["To"] @email_to.setter def email_to(self, value): self.msg["To"] = value
import smtplib from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart from email.header import Header # 1. 编写邮件内容 with open('result.html') as f: #打开html报告 email_bady = f.read() #读取报告内容 msg = MIMEMultipart() #混合MIME格式 msg.attach(MIMEText(email_bady, 'html', 'utf-8')) # 添加html格式邮件内容 # 2.组装Email头(发件人、收件人、主题) msg['To'] = '*****@*****.**' #发件人 msg['From'] = '*****@*****.**' # 收件人 msg['Subject'] = Header('接口测试报告添加附件', 'utf-8') #中文邮件主题 # 3.构建附件1,传送当前目录下的test.txt文件 att1 = MIMEText(open('result.html', 'rb').read(), 'base64', 'utf-8') #二进制格式打开文件 att1['Content-Type'] = 'application/octet-stream' att1[ "Content-Disposition"] = 'attachment;filename="result.html"' #filename 为邮件中的附件名 msg.attach(att1) # 添加附件 # 4. 连接smtp服务器并发送邮件 smtp = smtplib.SMTP() smtp.connect('smtp.qq.com') smtp.login('*****@*****.**', 'evpadofhcsryegfb') #自己的邮箱地址和密码 smtp.sendmail(msg.get('From'), msg.get('To'), msg.as_string()) #接收邮件地址 smtp.quit()
# have to copy-n-paste-n-modify. for h, v in msg.items(): print >> self._fp, ('%s: %s\r\n' % (h,v)), # A blank line always separates headers from body print >> self._fp, '\r\n', # The _write_multipart method calls "clone" for the # subparts. We hijack that, setting root=False def clone(self, fp): return MyGenerator(fp, root=False) fp = StringIO() g = MyGenerator(fp) g.flatten(mp) upload_data = fp.getvalue() upload_headers = {'Content-type': mp.get('Content-type')} if False: print 'Sending headers:' print ' ', headers print 'Sending data:' print data[:1024].replace('\n', '\\n\n').replace('\r', '\\r') if len(data) > 1024: print '...' print data[-256:].replace('\n', '\\n\n').replace('\r', '\\r') print print 'url', upload_url print '\n\nheaders', upload_headers print '\n\ndata', upload_data
class Email(): def __init__(self, data=None, headers={}, attrs={}): if not data: self.__mail = MIMEMultipart('mixed') else: self.__mail = message_from_bytes(data) for key, value in headers.items(): self.__mail[key] = Header(value, 'utf-8') for key, value in attrs.items(): self.__dict__[key] = value def __getitem__(self, key): return self.__mail[key] def __setitem__(self, key, value): self.__mail[key] = Header(value, 'utf-8') def get(self, key): return self.__mail.get(key) def parse_header(self, header_bytes): headers = decode_header(header_bytes) data = '' for header in headers: if type(header[0]) is bytes: code = header[1] try: codecs.lookup(code) except (LookupError, TypeError) as e: logger.error('无法获取邮件编码或未知的编码格式:%s,使用默认的utf-8进行解码!', code) code = 'utf-8' data += header[0].decode(encoding=code, errors='ignore') else: data += header[0] return data def get_subject(self): return self.parse_header(self.__mail['subject']) def parse_address(self, header_bytes): addrs = [] if header_bytes: if type(header_bytes) is not str: header_bytes = str(header_bytes) header_bytes = header_bytes.replace(';', ',') for addr in header_bytes.split(','): name, mailbox = parseaddr(addr) name = self.parse_header(name) mailbox = self.parse_header(mailbox) if re.search( r'[a-z_0-9.-]{1,64}@(?:[a-z0-9-]{1,200}.){1,5}[a-z]{1,6}', mailbox, flags=re.M | re.S | re.I): addrs.append(( name, re.findall( r'[a-z_0-9.-]{1,64}@(?:[a-z0-9-]{1,200}.){1,5}[a-z]{1,6}', mailbox, flags=re.M | re.S | re.I)[0])) else: # 当mailbox地址不合法时,将mailbox合并至name中,mailbox返回'' addrs.append(((name + ' ' + mailbox).strip(), '')) return addrs def get_address(self, column='from'): # 获取邮件中的指定地址字段 data = self.__mail.get(column) return self.parse_address(data) def get_sender(self): # 获取邮件中的发件人 return self.get_address('from') def get_recipients(self): # 获取邮件中的收件人 addrs = [] addrs += self.get_address('to') addrs += self.get_address('cc') addrs += self.get_address('bcc') return addrs def get_all_addresses(self): # 获取邮件中所有的收、发件地址 addrs = [] addrs += self.get_address('from') addrs += self.get_address('to') addrs += self.get_address('cc') addrs += self.get_address('bcc') return addrs def parse_date(self, date_str=None): # 解析邮件发件时间 s = date_str.split() if re.search(r'Mon|Tue|Wed|Thu|Fri|Sat|Sun', s[0]): s = s[1:] if s[4].upper() in ("GMT", "UTC"): s[4] = '+0000' try: date = datetime.strptime(' '.join(s[:5]), '%d %b %Y %H:%M:%S %z') except Exception as e: logger.error('解析邮件时间出现错误:[%s] ,尝试将时区设为+0000重新解析', ' '.join(s)) s[4] = '+0000' date = datetime.strptime(' '.join(s[:5]), '%d %b %Y %H:%M:%S %z') return date def get_date(self, tzinfo=8): # 获取邮件中的发件时间,并转换为tzinfo指定的时区时间 date = self.parse_date(self.__mail.get('date')) return date.astimezone(tzinfo if type(tzinfo) is timezone else timezone(timedelta( hours=tzinfo))) if tzinfo else date def __guess_charset(self, msg): charset = msg.get_charset() if charset is None: content_type = msg.get('Content-Type', '') result = re.findall(r'(charset\s*=\s*)"?([a-z0-9\-]+)"?', content_type, re.I | re.M | re.S) if result: charset = result[0][1] try: codecs.lookup(charset) except (LookupError, TypeError) as e: logger.error('无法获取邮件编码或未知的编码格式:%s,使用默认的utf-8进行解码!', charset) charset = 'utf-8' return charset def get_html_message(self, msg=None): html_message = '' if not msg: msg = self.__mail for part in msg.walk(): if not part.is_multipart(): content_type = part.get_content_type() if content_type == 'text/html': content = part.get_payload(decode=True) charset = self.__guess_charset(part) content = content.decode(charset or 'utf-8', errors='ignore') html_message += content return html_message def get_text_message(self, msg=None): text = '' if not msg: msg = self.__mail for part in msg.walk(): if not part.is_multipart(): content_type = part.get_content_type() if content_type == 'text/plain': content = part.get_payload(decode=True) charset = self.__guess_charset(part) if charset == 'ansi': charset = 'utf8' try: content = content.decode(charset or 'utf8', errors='ignore') except Exception as e: print(charset, ':', repr(content)) text += content return text def attach_html(self, html_message): self.__mail.attach(MIMEText(html_message, 'html', 'utf-8')) def attach_text(self, text): self.__mail.attach(MIMEText(text, 'plain', 'utf-8')) def attach_file(self, file_path): file_name = basename(file_path) file = MIMEText(open(file_path, 'rb').read(), 'base64', 'utf-8') file["Content-Type"] = 'application/octet-stream' file[ "Content-Disposition"] = 'attachment; filename="' + file_name + '"' self.__mail.attach(file) def attach_image(self, file_path, cid): image = MIMEImage(open(file_path, 'rb').read()) image.add_header('Content-ID', '<' + cid + '>') self.__mail.attach(image) def get_mail(self): return self.__mail def as_string(self): return self.__mail.as_string()
class Mdn: """Class for handling AS2 MDNs. Includes functions for both parsing and building them. """ def __init__(self, mdn_mode=None, digest_alg=None, mdn_url=None): self.message_id = None self.orig_message_id = None self.payload = None self.mdn_mode = mdn_mode self.digest_alg = digest_alg self.mdn_url = mdn_url @property def content(self): """Function returns the body of the mdn message as a byte string""" if self.payload is not None: message_bytes = mime_to_bytes(self.payload) boundary = b"--" + self.payload.get_boundary().encode("utf-8") temp = message_bytes.split(boundary) temp.pop(0) return boundary + boundary.join(temp) return "" @property def headers(self): """Return the headers in the payload as a dictionary.""" if self.payload: return dict(self.payload.items()) return {} @property def headers_str(self): """Return the headers in the payload as a string.""" message_header = "" if self.payload: for k, v in self.headers.items(): message_header += f"{k}: {v}\r\n" return message_header.encode("utf-8") def build( self, message, status, detailed_status=None, confirmation_text=MDN_CONFIRM_TEXT, failed_text=MDN_FAILED_TEXT, ): """Function builds and signs an AS2 MDN message. :param message: The received AS2 message for which this is an MDN. :param status: The status of processing of the received AS2 message. :param detailed_status: The optional detailed status of processing of the received AS2 message. Used to give additional error info (default "None") :param confirmation_text: The confirmation message sent in the first part of the MDN. :param failed_text: The failure message sent in the first part of the failed MDN. """ # Generate message id using UUID 1 as it uses both hostname and time self.message_id = email_utils.make_msgid().lstrip("<").rstrip(">") self.orig_message_id = message.message_id # Set up the message headers mdn_headers = { "AS2-Version": AS2_VERSION, "ediint-features": EDIINT_FEATURES, "Message-ID": f"<{self.message_id}>", "AS2-From": quote_as2name(message.headers.get("as2-to")), "AS2-To": quote_as2name(message.headers.get("as2-from")), "Date": email_utils.formatdate(localtime=True), "user-agent": "pyAS2 Open Source AS2 Software", } # Set the confirmation text message here # overwrite with organization specific message if message.receiver and message.receiver.mdn_confirm_text: confirmation_text = message.receiver.mdn_confirm_text # overwrite with partner specific message if message.sender and message.sender.mdn_confirm_text: confirmation_text = message.sender.mdn_confirm_text if status != "processed": confirmation_text = failed_text self.payload = MIMEMultipart("report", report_type="disposition-notification") # Create and attach the MDN Text Message mdn_text = email_message.Message() mdn_text.set_payload(f"{confirmation_text}\r\n") mdn_text.set_type("text/plain") del mdn_text["MIME-Version"] encoders.encode_7or8bit(mdn_text) self.payload.attach(mdn_text) # Create and attache the MDN Report Message mdn_base = email_message.Message() mdn_base.set_type("message/disposition-notification") mdn_report = "Reporting-UA: pyAS2 Open Source AS2 Software\r\n" mdn_report += f'Original-Recipient: rfc822; {message.headers.get("as2-to")}\r\n' mdn_report += f'Final-Recipient: rfc822; {message.headers.get("as2-to")}\r\n' mdn_report += f"Original-Message-ID: <{message.message_id}>\r\n" mdn_report += f"Disposition: automatic-action/MDN-sent-automatically; {status}" if detailed_status: mdn_report += f": {detailed_status}" mdn_report += "\r\n" if message.mic: mdn_report += f"Received-content-MIC: {message.mic.decode()}, {message.digest_alg}\r\n" mdn_base.set_payload(mdn_report) del mdn_base["MIME-Version"] encoders.encode_7or8bit(mdn_base) self.payload.attach(mdn_base) logger.debug( f"MDN report for message {message.message_id} created:\n{mime_to_bytes(mdn_base)}" ) # Sign the MDN if it is requested by the sender if (message.headers.get("disposition-notification-options") and message.receiver and message.receiver.sign_key): self.digest_alg = ( message.headers["disposition-notification-options"].split( ";")[-1].split(",")[-1].strip().replace("-", "")) signed_mdn = MIMEMultipart("signed", protocol="application/pkcs7-signature") del signed_mdn["MIME-Version"] signed_mdn.attach(self.payload) # Create the signature mime message signature = email_message.Message() signature.set_type("application/pkcs7-signature") signature.set_param("name", "smime.p7s") signature.set_param("smime-type", "signed-data") signature.add_header("Content-Disposition", "attachment", filename="smime.p7s") del signature["MIME-Version"] signed_data = sign_message(canonicalize(self.payload), self.digest_alg, message.receiver.sign_key) signature.set_payload(signed_data) encoders.encode_base64(signature) signed_mdn.set_param("micalg", self.digest_alg) signed_mdn.attach(signature) self.payload = signed_mdn logger.debug(f"Signing the MDN for message {message.message_id}") # Update the headers of the final payload and set message boundary for k, v in mdn_headers.items(): if self.payload.get(k): self.payload.replace_header(k, v) else: self.payload.add_header(k, v) self.payload.set_boundary(make_mime_boundary()) logger.debug(f"MDN generated for message {message.message_id} with " f"content:\n {mime_to_bytes(self.payload)}") def parse(self, raw_content, find_message_cb): """Function parses the RAW AS2 MDN, verifies it and extracts the processing status of the orginal AS2 message. :param raw_content: A byte string of the received HTTP headers followed by the body. :param find_message_cb: A callback the must returns the original Message Object. The original message-id and original recipient AS2 ID are passed as arguments to it. :returns: A two element tuple containing (status, detailed_status). The status is a string indicating the status of the transaction. The optional detailed_status gives additional information about the processing status. """ status, detailed_status = None, None try: self.payload = parse_mime(raw_content) self.orig_message_id, orig_recipient = self.detect_mdn() # Call the find message callback which should return a Message instance orig_message = find_message_cb(self.orig_message_id, orig_recipient) # Extract the headers and save it mdn_headers = {} for k, v in self.payload.items(): k = k.lower() if k == "message-id": self.message_id = v.lstrip("<").rstrip(">") mdn_headers[k] = v if (orig_message.receiver.mdn_digest_alg and self.payload.get_content_type() != "multipart/signed"): status = "failed/Failure" detailed_status = "Expected signed MDN but unsigned MDN returned" return status, detailed_status if self.payload.get_content_type() == "multipart/signed": logger.debug( f"Verifying signed MDN: \n{mime_to_bytes(self.payload)}") message_boundary = ( "--" + self.payload.get_boundary()).encode("utf-8") # Extract the signature and the signed payload signature = None signature_types = [ "application/pkcs7-signature", "application/x-pkcs7-signature", ] for part in self.payload.walk(): if part.get_content_type() in signature_types: signature = part.get_payload(decode=True) elif part.get_content_type() == "multipart/report": self.payload = part # Verify the message, first using raw message and if it fails # then convert to canonical form and try again mic_content = extract_first_part(raw_content, message_boundary) verify_cert = orig_message.receiver.load_verify_cert() try: self.digest_alg = verify_message(mic_content, signature, verify_cert) except IntegrityError: mic_content = canonicalize(self.payload) self.digest_alg = verify_message(mic_content, signature, verify_cert) for part in self.payload.walk(): if part.get_content_type( ) == "message/disposition-notification": logger.debug( f"MDN report for message {orig_message.message_id}:\n{part.as_string()}" ) mdn = part.get_payload()[-1] mdn_status = mdn["Disposition"].split( ";").pop().strip().split(":") status = mdn_status[0] if status == "processed": # Compare the original mic with the received mic mdn_mic = mdn.get("Received-Content-MIC", "").split(",")[0] if (mdn_mic and orig_message.mic and mdn_mic != orig_message.mic.decode()): status = "processed/warning" detailed_status = "Message Integrity check failed." else: detailed_status = " ".join(mdn_status[1:]).strip() except MDNNotFound: status = "failed/Failure" detailed_status = "mdn-not-found" except Exception as e: # pylint: disable=W0703 status = "failed/Failure" detailed_status = f"Failed to parse received MDN. {e}" logger.error( f"Failed to parse AS2 MDN\n: {traceback.format_exc()}") return status, detailed_status def detect_mdn(self): """Function checks if the received raw message is an AS2 MDN or not. :raises MDNNotFound: If the received payload is not an MDN then this exception is raised. :return: A two element tuple containing (message_id, message_recipient). The message_id is the original AS2 message id and the message_recipient is the original AS2 message recipient. """ mdn_message = None if self.payload.get_content_type() == "multipart/report": mdn_message = self.payload elif self.payload.get_content_type() == "multipart/signed": for part in self.payload.walk(): if part.get_content_type() == "multipart/report": mdn_message = self.payload if not mdn_message: raise MDNNotFound("No MDN found in the received message") message_id, message_recipient = None, None for part in mdn_message.walk(): if part.get_content_type() == "message/disposition-notification": mdn = part.get_payload()[0] message_id = mdn.get("Original-Message-ID").strip("<>") message_recipient = None if "Original-Recipient" in mdn: message_recipient = mdn["Original-Recipient"].split( ";")[1].strip() elif "Final-Recipient" in mdn: message_recipient = mdn["Final-Recipient"].split( ";")[1].strip() return message_id, message_recipient
def get_wcs(self,tweak=False): # Note that the pixel scale is a tightly set search constraint for # astrometry.net. For coadd mosaic pairitel images this is about 1 arcsec/pixel, # but for triplestacks and reduced images this is about 2 arcsec/pixel. tweak = True if tweak == False: tweak_poly_order = 0 else: tweak_poly_order = 2 sexcat_file = self.image_name.replace(".fits", ".sexcat") anetcat_file = self.image_name.replace(".fits", ".anetcat") # Run source extractor os.system(sextractor_bin + " " + self.image_name + " -c pairitel_anet.sex " + " -CATALOG_NAME " + sexcat_file) # Read in and parse the source extractor catalog file. # Columns are X, Y, FWHM_IMAGE, FLUX_APER, FLAGS # Eliminate flagged sources, or sources with weird FWHM unflagged_sources = [] unflagged_fwhm_list = [] sexcat = file(sexcat_file, "r") for line in sexcat: if line.split()[4] == "0": unflagged_sources.append([ float(line.split()[3]), # FLUX_APER float(line.split()[0]), # X float(line.split()[1]), # Y float(line.split()[2]) ]) # FWHM_IMAGE unflagged_fwhm_list.append(float(line.split()[2])) sexcat.close() # Define lower and upper limits on acceptable FWHM (further weeds out bad sources) med_fwhm = median(unflagged_fwhm_list) std_fwhm = std(unflagged_fwhm_list) fwhm_ll = median(unflagged_fwhm_list) - 2*std(unflagged_fwhm_list) fwhm_ul = median(unflagged_fwhm_list) + 2*std(unflagged_fwhm_list) # Enforce FWHM limits final_source_list = [] for source in unflagged_sources: if source[3] > fwhm_ll and source[3] < fwhm_ul: final_source_list.append([ source[0], # FLUX_APER source[1], # X source[2]]) # Y # Sort the final source list in decreasing flux final_source_list.sort(reverse=True) # Crop to the brightest 50 sources if len(final_source_list) > 50: final_source_list = final_source_list[:50] anetcat = file(anetcat_file, "w") for source in final_source_list: anetcat.write(str(round(source[1], 3)) + "\t" + str(round(source[2], 3)) + "\n") anetcat.close() # Now we interface with nova.astrometry.net # First we retrieve the apikey from the environmental variables upload_filename = anetcat_file # Now we interface with nova.astrometry.net # First we retrieve the apikey from the environmental variables apikey = os.environ.get('AN_API_KEY', None) if apikey is None: print 'You must set AN_API_KEY in your environment variables. Exiting.' os.system("rm " + anetcat_file) os.system("rm " + sexcat_file) sys.exit() apiurl = 'http://nova.astrometry.net/api/' # Login to the service login_url = apiurl + "login" login_args = { 'apikey' : apikey} json = simplejson.dumps(login_args) login_data = {'request-json': json} login_data = urlencode(login_data) login_headers = {} request = Request(url=login_url, headers=login_headers, data=login_data) f = urlopen(request) txt = f.read() result = simplejson.loads(txt) stat = result.get('status') if stat == 'error': print "Login error, exiting." os.system("rm " + anetcat_file) os.system("rm " + sexcat_file) sys.exit() session_string = result["session"] # Upload the text file to request a WCS solution upload_url = apiurl + "upload" upload_args = { 'allow_commercial_use': 'd', 'allow_modifications': 'd', 'publicly_visible': 'y', 'scale_units': self.scale_units, 'scale_type': 'ul', 'scale_lower': self.scale_lower, # arcsec/pix 'scale_upper': self.scale_upper, # arcsec/pix 'parity': self.parity, 'tweak_order' : tweak_poly_order, # SIP tweak polynomial; set to 0 for no tweak 'session': session_string } upload_json = simplejson.dumps(upload_args) f = open(upload_filename, 'rb') file_args=(upload_filename, f.read()) m1 = MIMEBase('text', 'plain') m1.add_header('Content-disposition', 'form-data; name="request-json"') m1.set_payload(upload_json) m2 = MIMEApplication(file_args[1],'octet-stream',encode_noop) m2.add_header('Content-disposition', 'form-data; name="file"; filename="%s"' % file_args[0]) #msg.add_header('Content-Disposition', 'attachment', # filename='bud.gif') #msg.add_header('Content-Disposition', 'attachment', # filename=('iso-8859-1', '', 'FuSballer.ppt')) mp = MIMEMultipart('form-data', None, [m1, m2]) fp = StringIO() g = MyGenerator(fp) g.flatten(mp) upload_data = fp.getvalue() upload_headers = {'Content-type': mp.get('Content-type')} if False: print 'Sending headers:' print ' ', headers print 'Sending data:' print data[:1024].replace('\n', '\\n\n').replace('\r', '\\r') if len(data) > 1024: print '...' print data[-256:].replace('\n', '\\n\n').replace('\r', '\\r') print request = Request(url=upload_url, headers=upload_headers, data=upload_data) f = urlopen(request) txt = f.read() result = simplejson.loads(txt) stat = result.get('status') if stat == 'error': print "Upload error, exiting." os.system("rm " + anetcat_file) os.system("rm " + sexcat_file) sys.exit() submission_int = result["subid"] time.sleep(5) # Check submission status subcheck_url = apiurl + "submissions/" + str(submission_int) request = Request(url=subcheck_url) still_processing = True n_failed_attempts = 0 while still_processing and n_failed_attempts < 5: try: f = urlopen(request) txt = f.read() result = simplejson.loads(txt) # print result still_processing = False except: print "Submission doesn't exist yet, sleeping for 5s." time.sleep(5) n_failed_attempts += 1 if n_failed_attempts > 5: print "The submitted job has apparently timed out, exiting." os.system("rm " + anetcat_file) os.system("rm " + sexcat_file) sys.exit() job_id_list = result["jobs"] n_jobs = len(job_id_list) time.sleep(5) still_processing = True n_failed_attempts = 0 n_failed_jobs = 0 while still_processing and n_failed_attempts < 5 and n_failed_jobs < n_jobs: time.sleep(5) for job_id in job_id_list: jobcheck_url = apiurl + "jobs/" + str(job_id) request = Request(url=jobcheck_url) f = urlopen(request) txt = f.read() result = simplejson.loads(txt) print "Checking astrometry.net job ID", job_id, result if result["status"] == "failure": n_failed_jobs += 1 job_id_list.remove(job_id) if result["status"] == "success": solved_job_id = job_id still_processing = False print "SOLVED" n_failed_attempts += 1 if still_processing == True: print "Astrometry.net took too long to process, so we're exiting." os.system("rm " + anetcat_file) os.system("rm " + sexcat_file) sys.exit() if still_processing == False: os.system(wget_bin + " -q --output-document=wcs.fits http://nova.astrometry.net/wcs_file/" + str(solved_job_id)) # Finally, strip out the WCS header info from this solved fits file and write it # into the original fits file. string_header_keys_to_copy = [ "CTYPE1", "CTYPE2", "CUNIT1", "CUNIT2" ] float_header_keys_to_copy = [ "EQUINOX", "LONPOLE", "LATPOLE", "CRVAL1", "CRVAL2", "CRPIX1", "CRPIX2", "CD1_1", "CD1_2", "CD2_1", "CD2_2", "IMAGEW", "IMAGEH" ] SIP_Poly_keys = [ "A_ORDER", "A_0_2", "A_1_1", "A_2_0", "B_ORDER", "B_0_2", "B_1_1", "B_2_0", "AP_ORDER", "AP_0_1", "AP_0_2", "AP_1_0", "AP_1_1", "AP_2_0", "BP_ORDER", "BP_0_1", "BP_0_2", "BP_1_0", "BP_1_1", "BP_2_0" ] wcs_image = "wcs.fits" wcs_hdu = pyfits.open(wcs_image) wcs_header = wcs_hdu[0].header.copy() wcs_hdu.close() input_image = self.image_name input_hdu = pyfits.open(input_image) image_data = input_hdu[0].data updated_imagefile_header = input_hdu[0].header.copy() # remove SIP keywords if we're not doing tweaking if tweak == True: float_header_keys_to_copy += SIP_Poly_keys else: for key in SIP_Poly_keys: try: updated_imagefile_header.remove(key) except(ValueError): print "No instance of %s to remove." % (key) except(AttributeError): print "pyfits doesn't appear to have 'remove' attribute; update pyfits?" for hk in string_header_keys_to_copy: try: updated_imagefile_header.update(hk, wcs_header[hk]) except: print hk, "string header update failed" for hk in float_header_keys_to_copy: try: updated_imagefile_header.update(hk, float(wcs_header[hk])) except: print hk, "float header update failed" updated_imagefile_header.update("AN_JOBID", str(solved_job_id)) output_hdu = pyfits.PrimaryHDU(image_data) output_hdu.header = updated_imagefile_header output_hdu.verify("warn") output_hdulist = pyfits.HDUList([output_hdu]) output_hdulist.writeto(input_image.replace(".fits", ".wcs.fits"),output_verify="warn") input_hdu.close() os.system("rm " + input_image) os.system("rm wcs.fits") os.system("mv " + input_image.replace(".fits", ".wcs.fits") + " " + input_image) os.system("rm " + anetcat_file) os.system("rm " + sexcat_file)
def get_wcs(self, tweak=False): # Note that the pixel scale is a tightly set search constraint for # astrometry.net. For coadd mosaic pairitel images this is about 1 arcsec/pixel, # but for triplestacks and reduced images this is about 2 arcsec/pixel. tweak = True if tweak == False: tweak_poly_order = 0 else: tweak_poly_order = 2 sexcat_file = self.image_name.replace(".fits", ".sexcat") anetcat_file = self.image_name.replace(".fits", ".anetcat") # Run source extractor os.system(sextractor_bin + " " + self.image_name + " -c pairitel_anet.sex " + " -CATALOG_NAME " + sexcat_file) # Read in and parse the source extractor catalog file. # Columns are X, Y, FWHM_IMAGE, FLUX_APER, FLAGS # Eliminate flagged sources, or sources with weird FWHM unflagged_sources = [] unflagged_fwhm_list = [] sexcat = file(sexcat_file, "r") for line in sexcat: if line.split()[4] == "0": unflagged_sources.append([ float(line.split()[3]), # FLUX_APER float(line.split()[0]), # X float(line.split()[1]), # Y float(line.split()[2]) ]) # FWHM_IMAGE unflagged_fwhm_list.append(float(line.split()[2])) sexcat.close() # Define lower and upper limits on acceptable FWHM (further weeds out bad sources) med_fwhm = median(unflagged_fwhm_list) std_fwhm = std(unflagged_fwhm_list) fwhm_ll = median(unflagged_fwhm_list) - 2 * std(unflagged_fwhm_list) fwhm_ul = median(unflagged_fwhm_list) + 2 * std(unflagged_fwhm_list) # Enforce FWHM limits final_source_list = [] for source in unflagged_sources: if source[3] > fwhm_ll and source[3] < fwhm_ul: final_source_list.append([ source[0], # FLUX_APER source[1], # X source[2] ]) # Y # Sort the final source list in decreasing flux final_source_list.sort(reverse=True) # Crop to the brightest 50 sources if len(final_source_list) > 50: final_source_list = final_source_list[:50] anetcat = file(anetcat_file, "w") for source in final_source_list: anetcat.write( str(round(source[1], 3)) + "\t" + str(round(source[2], 3)) + "\n") anetcat.close() # Now we interface with nova.astrometry.net # First we retrieve the apikey from the environmental variables upload_filename = anetcat_file # Now we interface with nova.astrometry.net # First we retrieve the apikey from the environmental variables apikey = os.environ.get('AN_API_KEY', None) if apikey is None: print 'You must set AN_API_KEY in your environment variables. Exiting.' os.system("rm " + anetcat_file) os.system("rm " + sexcat_file) sys.exit() apiurl = 'http://nova.astrometry.net/api/' # Login to the service login_url = apiurl + "login" login_args = {'apikey': apikey} json = simplejson.dumps(login_args) login_data = {'request-json': json} login_data = urlencode(login_data) login_headers = {} request = Request(url=login_url, headers=login_headers, data=login_data) f = urlopen(request) txt = f.read() result = simplejson.loads(txt) stat = result.get('status') if stat == 'error': print "Login error, exiting." os.system("rm " + anetcat_file) os.system("rm " + sexcat_file) sys.exit() session_string = result["session"] # Upload the text file to request a WCS solution upload_url = apiurl + "upload" upload_args = { 'allow_commercial_use': 'd', 'allow_modifications': 'd', 'publicly_visible': 'y', 'scale_units': self.scale_units, 'scale_type': 'ul', 'scale_lower': self.scale_lower, # arcsec/pix 'scale_upper': self.scale_upper, # arcsec/pix 'parity': self.parity, 'tweak_order': tweak_poly_order, # SIP tweak polynomial; set to 0 for no tweak 'session': session_string } upload_json = simplejson.dumps(upload_args) f = open(upload_filename, 'rb') file_args = (upload_filename, f.read()) m1 = MIMEBase('text', 'plain') m1.add_header('Content-disposition', 'form-data; name="request-json"') m1.set_payload(upload_json) m2 = MIMEApplication(file_args[1], 'octet-stream', encode_noop) m2.add_header('Content-disposition', 'form-data; name="file"; filename="%s"' % file_args[0]) #msg.add_header('Content-Disposition', 'attachment', # filename='bud.gif') #msg.add_header('Content-Disposition', 'attachment', # filename=('iso-8859-1', '', 'FuSballer.ppt')) mp = MIMEMultipart('form-data', None, [m1, m2]) fp = StringIO() g = MyGenerator(fp) g.flatten(mp) upload_data = fp.getvalue() upload_headers = {'Content-type': mp.get('Content-type')} if False: print 'Sending headers:' print ' ', headers print 'Sending data:' print data[:1024].replace('\n', '\\n\n').replace('\r', '\\r') if len(data) > 1024: print '...' print data[-256:].replace('\n', '\\n\n').replace('\r', '\\r') print request = Request(url=upload_url, headers=upload_headers, data=upload_data) f = urlopen(request) txt = f.read() result = simplejson.loads(txt) stat = result.get('status') if stat == 'error': print "Upload error, exiting." os.system("rm " + anetcat_file) os.system("rm " + sexcat_file) sys.exit() submission_int = result["subid"] time.sleep(5) # Check submission status subcheck_url = apiurl + "submissions/" + str(submission_int) request = Request(url=subcheck_url) still_processing = True n_failed_attempts = 0 while still_processing and n_failed_attempts < 5: try: f = urlopen(request) txt = f.read() result = simplejson.loads(txt) # print result still_processing = False except: print "Submission doesn't exist yet, sleeping for 5s." time.sleep(5) n_failed_attempts += 1 if n_failed_attempts > 5: print "The submitted job has apparently timed out, exiting." os.system("rm " + anetcat_file) os.system("rm " + sexcat_file) sys.exit() job_id_list = result["jobs"] n_jobs = len(job_id_list) time.sleep(5) still_processing = True n_failed_attempts = 0 n_failed_jobs = 0 while still_processing and n_failed_attempts < 5 and n_failed_jobs < n_jobs: time.sleep(5) for job_id in job_id_list: jobcheck_url = apiurl + "jobs/" + str(job_id) request = Request(url=jobcheck_url) f = urlopen(request) txt = f.read() result = simplejson.loads(txt) print "Checking astrometry.net job ID", job_id, result if result["status"] == "failure": n_failed_jobs += 1 job_id_list.remove(job_id) if result["status"] == "success": solved_job_id = job_id still_processing = False print "SOLVED" n_failed_attempts += 1 if still_processing == True: print "Astrometry.net took too long to process, so we're exiting." os.system("rm " + anetcat_file) os.system("rm " + sexcat_file) sys.exit() if still_processing == False: os.system( wget_bin + " -q --output-document=wcs.fits http://nova.astrometry.net/wcs_file/" + str(solved_job_id)) # Finally, strip out the WCS header info from this solved fits file and write it # into the original fits file. string_header_keys_to_copy = ["CTYPE1", "CTYPE2", "CUNIT1", "CUNIT2"] float_header_keys_to_copy = [ "EQUINOX", "LONPOLE", "LATPOLE", "CRVAL1", "CRVAL2", "CRPIX1", "CRPIX2", "CD1_1", "CD1_2", "CD2_1", "CD2_2", "IMAGEW", "IMAGEH" ] SIP_Poly_keys = [ "A_ORDER", "A_0_2", "A_1_1", "A_2_0", "B_ORDER", "B_0_2", "B_1_1", "B_2_0", "AP_ORDER", "AP_0_1", "AP_0_2", "AP_1_0", "AP_1_1", "AP_2_0", "BP_ORDER", "BP_0_1", "BP_0_2", "BP_1_0", "BP_1_1", "BP_2_0" ] wcs_image = "wcs.fits" wcs_hdu = pyfits.open(wcs_image) wcs_header = wcs_hdu[0].header.copy() wcs_hdu.close() input_image = self.image_name input_hdu = pyfits.open(input_image) image_data = input_hdu[0].data updated_imagefile_header = input_hdu[0].header.copy() # remove SIP keywords if we're not doing tweaking if tweak == True: float_header_keys_to_copy += SIP_Poly_keys else: for key in SIP_Poly_keys: try: updated_imagefile_header.remove(key) except (ValueError): print "No instance of %s to remove." % (key) except (AttributeError): print "pyfits doesn't appear to have 'remove' attribute; update pyfits?" for hk in string_header_keys_to_copy: try: updated_imagefile_header.update(hk, wcs_header[hk]) except: print hk, "string header update failed" for hk in float_header_keys_to_copy: try: updated_imagefile_header.update(hk, float(wcs_header[hk])) except: print hk, "float header update failed" updated_imagefile_header.update("AN_JOBID", str(solved_job_id)) output_hdu = pyfits.PrimaryHDU(image_data) output_hdu.header = updated_imagefile_header output_hdu.verify("warn") output_hdulist = pyfits.HDUList([output_hdu]) output_hdulist.writeto(input_image.replace(".fits", ".wcs.fits"), output_verify="warn") input_hdu.close() os.system("rm " + input_image) os.system("rm wcs.fits") os.system("mv " + input_image.replace(".fits", ".wcs.fits") + " " + input_image) os.system("rm " + anetcat_file) os.system("rm " + sexcat_file)
def send_request(self, service, args={}, file_args=None): ''' service: string args: dict ''' if self.session is not None: args.update({'session': self.session}) print('Python:', args) json = python2json(args) print('Sending json:', json) url = self.get_url(service) print('Sending to URL:', url) # If we're sending a file, format a multipart/form-data if file_args is not None: m1 = MIMEBase('text', 'plain') m1.add_header('Content-disposition', 'form-data; name="request-json"') m1.set_payload(json) m2 = MIMEApplication(file_args[1], 'octet-stream', encode_noop) m2.add_header( 'Content-disposition', 'form-data; name="file"; filename="%s"' % file_args[0]) #msg.add_header('Content-Disposition', 'attachment', # filename='bud.gif') #msg.add_header('Content-Disposition', 'attachment', # filename=('iso-8859-1', '', 'FuSballer.ppt')) mp = MIMEMultipart('form-data', None, [m1, m2]) # Makie a custom generator to format it the way we need. from cStringIO import StringIO from email.generator import Generator class MyGenerator(Generator): def __init__(self, fp, root=True): Generator.__init__(self, fp, mangle_from_=False, maxheaderlen=0) self.root = root def _write_headers(self, msg): # We don't want to write the top-level headers; # they go into Request(headers) instead. if self.root: return # We need to use \r\n line-terminator, but Generator # doesn't provide the flexibility to override, so we # have to copy-n-paste-n-modify. for h, v in msg.items(): print(self._fp, ('%s: %s\r\n' % (h, v))) # A blank line always separates headers from body print(self._fp, '\r\n') # The _write_multipart method calls "clone" for the # subparts. We hijack that, setting root=False def clone(self, fp): return MyGenerator(fp, root=False) fp = StringIO() g = MyGenerator(fp) g.flatten(mp) data = fp.getvalue() headers = {'Content-type': mp.get('Content-type')} if False: print('Sending headers:') print(' ', headers) print('Sending data:') print(data[:1024].replace('\n', '\\n\n').replace('\r', '\\r')) if len(data) > 1024: print('...') print(data[-256:].replace('\n', '\\n\n').replace('\r', '\\r')) print('') else: # Else send x-www-form-encoded data = {'request-json': json} print('Sending form data:', data) data = urlencode(data) print('Sending data:', data) headers = {} request = Request(url=url, headers=headers, data=data) try: f = urlopen(request) txt = f.read() print('Got json:', txt) result = json2python(txt) print('Got result:', result) stat = result.get('status') print('Got status:', stat) if stat == 'error': errstr = result.get('errormessage', '(none)') raise RequestError('server error message: ' + errstr) return result except HTTPError as e: print('HTTPError', e) txt = e.read() open('err.html', 'wb').write(txt) print('Wrote error text to err.html')
def send_request(self, service, args={}, file_args=None): ''' service: string args: dict ''' if self.session is not None: args.update({ 'session' : self.session }) #print 'Python:', args json = python2json(args) #print 'Sending json:', json url = self.get_url(service) #print 'Sending to URL:', url # If we're sending a file, format a multipart/form-data if file_args is not None: m1 = MIMEBase('text', 'plain') m1.add_header('Content-disposition', 'form-data; name="request-json"') m1.set_payload(json) m2 = MIMEApplication(file_args[1],'octet-stream',encode_noop) m2.add_header('Content-disposition', 'form-data; name="file"; filename="%s"' % file_args[0]) mp = MIMEMultipart('form-data', None, [m1, m2]) # Make a custom generator to format it the way we need. from cStringIO import StringIO from email.generator import Generator class MyGenerator(Generator): def __init__(self, fp, root=True): Generator.__init__(self, fp, mangle_from_=False, maxheaderlen=0) self.root = root def _write_headers(self, msg): # We don't want to write the top-level headers; # they go into Request(headers) instead. if self.root: return # We need to use \r\n line-terminator, but Generator # doesn't provide the flexibility to override, so we # have to copy-n-paste-n-modify. for h, v in msg.items(): print >> self._fp, ('%s: %s\r\n' % (h,v)), # A blank line always separates headers from body print >> self._fp, '\r\n', # The _write_multipart method calls "clone" for the # subparts. We hijack that, setting root=False def clone(self, fp): return MyGenerator(fp, root=False) fp = StringIO() g = MyGenerator(fp) g.flatten(mp) data = fp.getvalue() headers = {'Content-type': mp.get('Content-type')} else: # Else send x-www-form-encoded data = {'request-json': json} #print 'Sending form data:', data data = urlencode(data) #print 'Sending data:', data headers = {} request = Request(url=url, headers=headers, data=data) try: f = urlopen(request) txt = f.read() #print 'Got json:', txt result = json2python(txt) #print 'Got result:', result stat = result.get('status') #print 'Got status:', stat if stat == 'error': errstr = result.get('errormessage', '(none)') raise RequestError('server error message: ' + errstr) return result except HTTPError, e: #print 'HTTPError', e txt = e.read() open('err.html', 'wb').write(txt)
def file_to_mail(filenames, subject, owner, to, cc=None, bcc=None, body_prepend='', customized_styles='', fake_cc=None, mail_user=MAIL_USER, mail_passwd=MAIL_PASSWD, supervised=None, caption='', report_type='report', sender_display=None, fake_to=None, smtp_protocol=SMTP_PROTOCOL): if smtp_protocol.lower() == 'ssl': s = smtplib.SMTP_SSL(SMTP_HOST, port=SMTP_PORT) s.ehlo() else: s = smtplib.SMTP(SMTP_HOST, port=SMTP_PORT) s.ehlo() if (smtp_protocol == 'tls'): s.starttls() s.login(mail_user, mail_passwd) me = mail_user msg = MIMEMultipart() msg['Subject'] = subject if sender_display is None: msg['From'] = me else: msg['From'] = "%s<%s>" % (sender_display, me) print(msg['From']) receiver_list = to.split(',') msg_to_list = to.split(',') msg_cc_list = [] if cc is not None: msg_cc_list += cc.split(',') receiver_list += cc.split(',') if supervised is None: os_supervised = os.environ.get('supervised', 'false') if os_supervised.lower().startswith('t'): supervised = True else: supervised = False print("supervised:", supervised) if supervised and MAIL_MONITOR not in receiver_list: msg_cc_list.append(MAIL_MONITOR) receiver_list.append(MAIL_MONITOR) if fake_cc is not None: msg_cc_list += fake_cc.split(',') if fake_to is not None: msg_to_list += fake_to.split(',') msg['to'] = ','.join(msg_to_list) msg['cc'] = ','.join(msg_cc_list) msg['bcc'] = bcc if bcc is not None: receiver_list += bcc.split(',') if type(filenames) == str: filenames = [filenames] if filenames is not None: for filename in filenames: with open(filename, 'rb') as f: file_part = MIMEApplication(f.read(), Name=os.path.basename(filename)) file_part.add_header('Content-Disposition', 'attachment', filename=os.path.basename(filename)) msg.attach(file_part) mail_body = ''' <style>\n{styles}\n{customized_styles}\n</style> <br/><br/> {body_prepend} {caption} <br/><br/> -------------------------------- <br/><br/> 如对{report_type_name}有任何疑问,请联系{owner} <br/><br/> [BI自建报表系统] <br/><br/> '''.format(styles=STYLES, customized_styles=customized_styles, body_prepend=body_prepend, owner=owner, caption=caption, report_type_name=REPORT_TYPE_MAP[report_type]) mail_body = premailer.transform(mail_body) mail_body_html = MIMEText(mail_body, 'html', 'utf-8') msg.attach(mail_body_html) print("\nowner:", owner) print("receiver_list:", receiver_list) print("subject:", msg.get('subject')) print("to:", msg.get('to')) print("cc:", msg.get('cc')) print("bcc:", msg.get('bcc')) s.sendmail(me, receiver_list, msg.as_string()) print("mail sent!\n") s.quit()