示例#1
0
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()
示例#2
0
    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!'})
示例#3
0
    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())
示例#4
0
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)
示例#5
0
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()
示例#6
0
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}>'
示例#7
0
文件: app.py 项目: AndrewQTUD/AQ_FYP
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')
示例#8
0
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)
示例#9
0
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()
示例#10
0
 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()
示例#11
0
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')
示例#13
0
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
示例#14
0
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
示例#17
0
    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
示例#18
0
 def get_datetime_received(msg: MIMEMultipart) -> datetime:
     return email.utils.parsedate_to_datetime(msg.get('date'))
示例#19
0
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()
示例#20
0
文件: email.py 项目: gisce/qreu
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()
示例#21
0
文件: mail.py 项目: RykerRus/pochta
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
示例#22
0
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()
示例#23
0
        # 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
示例#24
0
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()
示例#25
0
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
示例#26
0
文件: anet.py 项目: qmorgan/qsoft
    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)
示例#27
0
    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)
示例#28
0
    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')
示例#29
0
    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)
示例#30
0
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()