Пример #1
0
class MHTML(object):
    def __init__(self):
        self._msg = EmailMessage()
        self._msg['MIME-Version'] = '1.0'
        self._msg.add_header('Content-Type', 'multipart/related', type='text/html')

    def add(self, location: str, content_type: str, payload: str, encoding: str = 'quoted-printable') -> None:
        resource = EmailMessage()
        if content_type == 'text/html':
            resource.add_header('Content-Type', 'text/html', charset='utf-8')
        else:
            resource['Content-Type'] = content_type
        if encoding == 'quoted-printable':
            resource['Content-Transfer-Encoding'] = encoding
            resource.set_payload(quopri.encodestring(payload.encode()))
        elif encoding == 'base64':
            resource['Content-Transfer-Encoding'] = encoding
            resource.set_payload(base64.b64encode(payload))
        elif encoding == 'base64-encoded':  # Already base64 encoded
            resource['Content-Transfer-Encoding'] = 'base64'
            resource.set_payload(payload)
        else:
            raise ValueError('invalid encoding')
        resource['Content-Location'] = location
        self._msg.attach(resource)

    def __str__(self) -> str:
        return str(self._msg)

    def __bytes__(self) -> bytes:
        return bytes(self._msg)
Пример #2
0
def create_mail(sender, recipient, subject, body, attachments, gpgme_ctx):
    """Create an email either as single or multi-part with attachments.
    """
    msg = EmailMessage(policy=mailgen_policy)
    msg.set_content(body)
    attachment_parent = msg
    if gpgme_ctx is not None:
        msg.make_mixed()
        attachment_parent = next(msg.iter_parts())

    if attachments:
        for args, kw in attachments:
            attachment_parent.add_attachment(*args, **kw)

    if gpgme_ctx is not None:
        signed_bytes = attachment_parent.as_bytes()
        hash_algo, signature = detached_signature(gpgme_ctx, signed_bytes)

        msg.add_attachment(signature, "application", "pgp-signature",
                           cte="8bit")
        # the signature part should now be the last of two parts in the
        # message, the first one being the signed part.
        signature_part = list(msg.iter_parts())[1]
        if "Content-Disposition" in signature_part:
            del signature_part["Content-Disposition"]

        msg.replace_header("Content-Type", "multipart/signed")

        micalg = hash_algorithms.get(hash_algo)
        if micalg is None:
            raise RuntimeError("Unexpected hash algorithm %r from gpgme"
                               % (signature[0].hash_algo,))

        msg.set_param("protocol", "application/pgp-signature")
        msg.set_param("micalg", micalg)

    msg.add_header("From", sender)
    msg.add_header("To", recipient)
    msg.add_header("Subject", subject)
    msg.add_header("Date", formatdate(timeval=None, localtime=True))

    # take the domain part of sender as the domain part of the message
    # ID. We assume that sender has the form local@domain, so we can
    # just the part of sender after the '@'.
    sender_domain = sender.partition("@")[-1]
    if not sender_domain:
        raise RuntimeError("Could not extract the domain from the sender (%r)"
                           " for the Message-ID" % (sender,))
    msg.add_header("Message-Id", make_msgid(domain=sender_domain))

    return msg
Пример #3
0
 def add(self, location: str, content_type: str, payload: str, encoding: str = 'quoted-printable') -> None:
     resource = EmailMessage()
     if content_type == 'text/html':
         resource.add_header('Content-Type', 'text/html', charset='utf-8')
     else:
         resource['Content-Type'] = content_type
     if encoding == 'quoted-printable':
         resource['Content-Transfer-Encoding'] = encoding
         resource.set_payload(quopri.encodestring(payload.encode()))
     elif encoding == 'base64':
         resource['Content-Transfer-Encoding'] = encoding
         resource.set_payload(base64.b64encode(payload))
     elif encoding == 'base64-encoded':  # Already base64 encoded
         resource['Content-Transfer-Encoding'] = 'base64'
         resource.set_payload(payload)
     else:
         raise ValueError('invalid encoding')
     resource['Content-Location'] = location
     self._msg.attach(resource)
Пример #4
0
def encrypt(message, certs, algorithm='aes256_cbc'):
    """
    Takes the contents of the message parameter, formatted as in RFC 2822 (type str or message), and encrypts them,
    so that they can only be read by the intended recipient specified by pubkey.
    :return: the new encrypted message (type str or message, as per input).
    """
    # Get the chosen block cipher
    block_cipher = get_cipher(algorithm)
    if block_cipher == None:
        raise ValueError('Unknown block algorithm')

    # Get the message content. This could be a string, or a message object
    passed_as_str = isinstance(message, str)
    if passed_as_str:
        msg = message_from_string(message)
    else:
        msg = message
    # Extract the message payload without conversion, & the outermost MIME header / Content headers. This allows
    # the MIME content to be rendered for any outermost MIME type incl. multipart
    pl = EmailMessage()
    for i in msg.items():
        hname = i[0].lower()
        if hname == 'mime-version' or hname.startswith('content-'):
            pl.add_header(i[0], i[1])
    pl._payload = msg._payload
    content = pl.as_string()

    recipient_infos = []
    for recipient_info in __iterate_recipient_infos(certs, block_cipher.session_key):
        if recipient_info == None:
            raise ValueError('Unknown public-key algorithm')
        recipient_infos.append(recipient_info)

    # Encode the content
    encrypted_content_info = block_cipher.encrypt(content)

    # Build the enveloped data and encode in base64
    enveloped_data = cms.ContentInfo({
        'content_type': 'enveloped_data',
        'content': {
            'version': 'v0',
            'recipient_infos': recipient_infos,
            'encrypted_content_info': encrypted_content_info
        }
    })
    encoded_content = '\n'.join(wrap_lines(b64encode(enveloped_data.dump()), 64))

    # Create the resulting message
    result_msg = MIMEText(encoded_content)
    overrides = (
        ('MIME-Version', '1.0'),
        ('Content-Type', 'application/pkcs7-mime; smime-type=enveloped-data; name=smime.p7m'),
        ('Content-Transfer-Encoding', 'base64'),
        ('Content-Disposition', 'attachment; filename=smime.p7m')
    )

    for name, value in list(msg.items()):
        if name in [x for x, _ in  overrides]:
            continue
        result_msg.add_header(name, value)

    for name, value in overrides:
        if name in result_msg:
            del result_msg[name]
        result_msg[name] = value

    # return the same type as was passed in
    if passed_as_str:
        return result_msg.as_string()
    else:
        return result_msg
Пример #5
0
def send_email(email_settings: dict,
               subject: str,
               body: str = None,
               template_args: dict = None) -> None:
    """
    This function offers many customized options when sending an email.

    Email can be sent with port 25 or using TLS.

    Email sections can be sent encrypted or unencrypted.

    Emails can be sent using one of two options:
    \tNon-HTML:
    \t\t\\- These messages are basic non HTML emails. Multiple lines are supported.
    \tHTML:
    \t\t\\- These messages use HTML templates that can have variables in the HTML template get populated with data.\\
    \t\t\\- HTML will be preferred if both are configured.\\
    \t\t\\- Templates and variables require structure from HTMLs://jinja.palletsprojects.com.\\
    \t\t\\- When using HTML, the jinja module is used in conjunction with some added features and simplified use from this function.

    Email Encryption:
    \t String encryption can be added to any string in an email with the email_settings variables setup and\\
    \t the string section identified as needing to be encrypted.

    \t Any string section starting with @START-ENCRYPT@ and ending with @END-ENCRYPT@ will have that code section\\
    \t get encrypted and supported for the body or **template_args parameters.

    \t Encryption Requirements:
    \t\t\\- The encrypt_info function is required when enabling message encryption.\\
    \t\t\\- The decrypt_info function is required when decrypting the message.\\
    \t\t\t\\- This function can be used outside the main program.\\
    \t\t\t\\- The decrypt_info can be a small separate program or a website using flask.\\
    \t\t\\- To create a random "salt" use this command "print("urandom16 Key:", os.urandom(16))"\\

    \t Format Example:
    \t\t\\- The encrypted message will be in bytes format. Formatting needs in the template or body of the message.\\
    \t\t\t\\- Example1:
    \t\t\t\t\\- <p>     Decryption Code:@START-ENCRYPT@This is my original string@END-ENCRYPT@</p>\\
    \t\t\t\\- Example2:
    \t\t\t\t\\- @START-ENCRYPT@This is my original string@END-ENCRYPT@\\

    Args:
        email_settings (dict):
        \t\\- Email settings constructed within a dictionary\\
        subject (str):
        \t\\- Email subject information\\
        body (str, optional):
        \t\\- The email body is for raw non-HTML email messages.\\
        \t\\- Adding a message to this body will override any template options and use\\
        \t   a basic non-HTML email message.\\
        \t\\- Defaults to None.\\
        template_args(dict, optional):
        \t\\- The template arguments are used to populate the HTML template variables.\\
        \t\\- Defaults to None.\\
        \t\t\\- Example (url is the passing parameter):\\
        \t\t\t\\- <p><a href="{{ url }}">Decrypt</a></p>\\

    Arg Keys:
        email_settings Keys:\\
        \t\\- smtp (str):\\
        \t\t\\- SMTP server.\\
        \t\\- authentication_required (bool):\\
        \t\t\\- Enables authentication.\\
        \t\\- use_tls (str):\\
        \t\t\\- Enables TLS.
        \t\\- username (str):\\
        \t\t\\- Username for email authentication.\\
        \t\\- password (str):\\
        \t\t\\- Password for email authentication.\\
        \t\\- from_email (str):\\
        \t\t\\- From email address.\\
        \t\\- to_email (str):\\
        \t\t\\- To email address.\\
        \t\\- attachment_path (str, optional):\\
        \t\t\\- Allows sending an email attachment.\\
        \t\t\\- Defaults to None.\\
        \t\\- send_email_template (bool, optional):\\
        \t\t\\- Allows sending an email with an HTML template.\\
        \t\t\\- Defaults to False.\\
        \t\\- email_template_name (str, optional):\\
        \t\t\\- The template name.\\
        \t\t\\- Defaults to None.\\
        \t\\- email_template_path (str, optional):\\
        \t\t\\- The template folder path.\\
        \t\t\\- This directory will hold all HTML templates.\\
        \t\t\\- This path is commonly the directory of the main program.\\
        \t\t\\- Defaults to None.\\
        \t\\- message_encryption_password (str, optional):\\
        \t\t\\- Encryption password if encryption is enabled.\\
        \t\t\\- Set to None if no encryption.\\
        \t\\- message_encryption_random_salt (bytes, optional):\\
        \t\t\\- Random salt in bytes format.\\
        \t\t\\- Set to None if no encryption.

    Raises:
        FTypeError (fexception):
        \t\\- The value '{email_settings}' is not in <class 'dict'> format.
        FTypeError (fexception):
        \t\\- The value '{subject}' is not in <class 'str'> format.
        FTypeError (fexception):
        \t\\- The value '{body}' is not in <class 'str'> format.
        FTypeError (fexception):
        \t\\- The value '{template_args}' is not in <class 'dict'> format.
        EmailSendFailure:
        \t\\- An error occurred while sending the email.
        CreateTemplateFailure:
        \t\\- The email HTML template path does not exist.
        EncryptionFailure:
        \t\\- A failure occurred while encrypting the message.
        FGeneralError (fexception):
        \t\\- A general exception occurred while creating the email message body.
        EmailSendFailure:
        \t\\- The attachment path for the email message does not exist.
        FGeneralError (fexception):
        \t\\- A general exception occurred while preparing the email message structure.
        EmailSendFailure:
        \t\\- Failed to initialize SMTP connection using TLS.
        EmailSendFailure:
        \t\\- Failed to send the email message. Connection to SMTP server failed.
        EmailSendFailure:
        \t\\- Failed to reach the SMTP server.
        FGeneralError (fexception):
        \t\\- A general exception occurred while sending the email message.
        EmailSendFailure:
        \t\\- SMTP authentication is set to required but it is not supported by the server.
        EmailSendFailure:
        \t\\- The SMTP server rejected the connection.
        EmailSendFailure:
        \t\\- SMTP server authentication failed.
        EmailSendFailure:
        \t\\- Incorrect username and/or password or authentication_required is not enabled\\
        \t  or Less Secure Apps needs enabled in your gmail settings.
        EmailSendFailure:
        \t\\- Incorrect username and/or password or the authentication_required setting is not enabled.
        FGeneralError (fexception):
        \t\\- A general exception occurred while sending the email.
        EmailSendFailure:
        \t\\- Failed to send message. SMTP terminatation error occurred.
        FGeneralError (fexception):
        \t\\- A general exception occurred while terminating the SMTP object.
    """
    logger = logging.getLogger(__name__)
    logger.debug(f'=' * 20 + get_function_name() + '=' * 20)
    # Custom flowchart tracking. This is ideal for large projects that move a lot.
    # For any third-party modules, set the flow before making the function call.
    logger_flowchart = logging.getLogger('flowchart')
    logger_flowchart.debug(f'Flowchart --> Function: {get_function_name()}')

    try:
        type_check(email_settings, dict)
        type_check(subject, str)
        if body:
            type_check(body, str)
        if template_args:
            type_check(template_args, dict)
    except FTypeError:
        raise

    formatted_email_settings = (
        '  - email_settings (dict):\n        - ' +
        '\n        - '.join(': '.join((key, str(val)))
                            for (key, val) in email_settings.items()))
    if body:
        formatted_body = f'- email_template_name (str):\n        - {body}'
    else:
        formatted_body = f'- email_template_name (str):\n        - None'
    if template_args:
        formatted_template_args = (
            '  - template_args (dict):\n        - ' +
            '\n        - '.join(': '.join((key, str(val)))
                                for (key, val) in template_args.items()))
    else:
        formatted_template_args = '  - template_args (dict):\n        - None'

    logger.debug('Passing parameters:\n'
                 f'{formatted_email_settings}\n'
                 f'  - subject (str):\n        - {subject}\n'
                 f'{formatted_body}\n'
                 f'{formatted_template_args}\n')

    logger.debug(f'Starting to send an email message')

    try:
        logger.debug(f'Checking if the email is sending non-HTML or HTML')
        # Gets send_email_template option.
        send_email_template = email_settings.get('send_email_template')
        # Checks if the email is template or non-HTML.
        if (send_email_template is True and template_args):
            logger.debug(f'Sending HTML templated email')
            email_template_name = email_settings.get('email_template_name')
            email_template_path = email_settings.get('email_template_path')

            # Creates the email template.
            email_body = create_template_email(email_template_name,
                                               email_template_path,
                                               **template_args)
        elif body:
            logger.debug(f'Sending non-HTML email')
            email_body = body
        else:
            exc_args = {
                'main_message':
                'An error occurred while sending the email.',
                'custom_type':
                EmailSendFailure,
                'expected_result':
                'Body or HTML Template',
                'returned_result':
                'No body or template was sent.',
                'suggested_resolution':
                'Ensure the body or template is being passed to the email_director module functions.',
            }
            raise EmailSendFailure(FCustomException(exc_args))
        # Holds the updating email body lines.
        updating_body = []
        # Checks the email for any encryption identifiers.
        # Splits the email into individual lines that can be looped through.
        adjusted_body = email_body.split('\n')
        logger.debug(
            f'Looping through each line of the email to check for any encryption identifiers'
        )
        for email_line in adjusted_body:
            # Checks if the body contains any encryption identifiers.
            if ('@START-ENCRYPT@' in email_line
                    and '@END-ENCRYPT@' in email_line):
                logger.debug(
                    f'Encryption identifiers found. Encrypting this section of the output.'
                )
                # Original String: <p>     Decryption Code: @START-ENCRYPT@This is my original string@END-ENCRYPT@</p>
                # Matched String: This is my original string
                unencrypted_string = (re.search(
                    '@START-ENCRYPT@(.*)@END-ENCRYPT@', email_line)).group(1)
                logger.debug(
                    'Converting unencrypted message string into bytes')
                password = email_settings.get('message_encryption_password')
                salt = email_settings.get('message_encryption_random_salt')
                # Calls function to sends unencrypted message for encryption.
                # Return Example: <encrypted message>
                encrypted_info = encrypt_info(unencrypted_string, password,
                                              salt)
                # Removes the encryption string identifiers and sets encryption on the string.
                updating_body.append(
                    email_line.replace('@START-ENCRYPT@', '').replace(
                        '@END-ENCRYPT@', '').replace(unencrypted_string,
                                                     str(encrypted_info)))
            else:
                # Adds the non-encrypted line to the list.
                updating_body.append(email_line)

        logger.debug(f'Setting the updated email body')
        # Converts the list back into a string with new lines for each entry.
        updated_body = "\n".join(updating_body)
    except CreateTemplateFailure:
        raise
    except EmailSendFailure:
        raise
    except EncryptionFailure:
        raise
    except Exception as exc:
        exc_args = {
            'main_message':
            'A general exception occurred while creating the email message body.',
            'original_exception': exc,
        }
        raise FGeneralError(exc_args)

    try:
        logger.debug('Preparing the email message structure')
        # Preparing email message structure.
        message = EmailMessage()
        message['Subject'] = subject
        message['From'] = email_settings.get('from_email')
        message['To'] = [email_settings.get('to_email')]
        # Sets header based on HTML or text be passed to the function.
        if (send_email_template is True and template_args):
            # Sets header for text and html. Required to allow html template.
            message.add_header('Content-Type', 'text/html')
        elif body:
            # Sets header for text only. Required to allow new lines.
            message.add_header('Content-Type', 'text')
        logger.debug('Setting payload to HTML')
        # Setting email body payload.
        message.set_payload(updated_body)
        # Attaches file if a path is sent.
        if email_settings.get('attachment_path'):
            # Checks that the attachment file exists.
            attachment_path = os.path.abspath(
                email_settings.get('attachment_path'))
            if not os.path.exists(attachment_path):
                exc_args = {
                    'main_message':
                    'The attachment path for the email message does not exist.',
                    'custom_type':
                    EmailSendFailure,
                    'expected_result':
                    'A valid email attachment path.',
                    'returned_result':
                    attachment_path,
                    'suggested_resolution':
                    'Please verify you have set the correct path and try again.',
                }
                raise EmailSendFailure(FCustomException(exc_args))
            # Gets the mime type to determine the type of message being sent.
            mime_type, _ = mimetypes.guess_type(attachment_path)
            # Gets the MIME type and subtype.
            mime_type, mime_subtype = mime_type.split('/', 1)
            # Attaches the attachment to the message.
            with open(attachment_path, 'rb') as ap:
                message.add_attachment(
                    ap.read(),
                    maintype=mime_type,
                    subtype=mime_subtype,
                    filename=os.path.basename(attachment_path))
    except EmailSendFailure:
        raise
    except Exception as exc:
        exc_args = {
            'main_message':
            'A general exception occurred while preparing the email message structure.',
            'original_exception': exc,
        }
        raise FGeneralError(exc_args)

    try:
        logger.debug('Setting up SMTP object')
        # Setting up SMTP object.
        if email_settings.get('use_tls') is True:
            logger.debug(
                'Opening connection to SMTP server on port 587 for TLS')
            smtp_Object = smtplib.SMTP(email_settings.get('smtp'), 587)

            try:
                smtp_Object.ehlo()
                logger.debug('Sending StartTLS message')
                smtp_Object.starttls()
            except Exception as exc:
                exc_args = {
                    'main_message':
                    'Failed to initialize SMTP connection using TLS.',
                    'custom_type': EmailSendFailure,
                    'original_exception': exc
                }
                raise EmailSendFailure(FCustomException(exc_args))
        else:
            logger.debug('Opening connection to SMTP server on port 25')
            smtp_Object = smtplib.SMTP(email_settings.get('smtp'), 25)
    except EmailSendFailure:
        raise
    except Exception as exc:
        if ("target machine actively refused it" in str(
                exc
        ) or "connected party did not properly respond after a period of time"
                in str(exc) or "getaddrinfo failed" in str(exc)):
            exc_args = {
                'main_message':
                'Failed to send the email message. Connection to SMTP server failed.',
                'custom_type': EmailSendFailure,
                'suggested_resolution':
                'Ensure the server address and TLS options are set correctly.',
                'original_exception': exc
            }
            raise EmailSendFailure(FCustomException(exc_args))
        elif 'Connection unexpectedly closed' in str(exc):
            exc_args = {
                'main_message': 'Failed to reach the SMTP server.',
                'custom_type': EmailSendFailure,
                'suggested_resolution': 'Ensure SMTP is reachable.',
                'original_exception': exc
            }
            raise EmailSendFailure(FCustomException(exc_args))
        else:
            exc_args = {
                'main_message':
                'A general exception occurred while sending the email message.',
                'original_exception': exc,
            }
            raise FGeneralError(exc_args)

    # Sends email.
    try:
        # If authentication required, log in to mail server with credentials.
        if email_settings.get('authentication_required') is True:
            logger.debug(
                'SMTP server authentication required, logging into server')
            smtp_Object.login(email_settings.get('username'),
                              email_settings.get('password'))
            logger.debug('Sending the email')

        smtp_Object.sendmail(email_settings.get('from_email'),
                             email_settings.get('to_email'),
                             str(message).encode('utf-8').strip())
    except Exception as exc:
        if "SMTP AUTH extension not supported" in str(exc):
            exc_args = {
                'main_message':
                'SMTP authentication is set to required but it is not supported by the server.',
                'custom_type': EmailSendFailure,
                'suggested_resolution':
                'Try changing the INI [email] AuthenticationRequired value to False',
                'original_exception': exc
            }
        elif "Client host rejected: Access denied" in str(exc):
            exc_args = {
                'main_message': 'The SMTP server rejected the connection.',
                'custom_type': EmailSendFailure,
                'suggested_resolution':
                'Authentication may be required, ensure the INI [email] AuthenticationRequired is set correctly.',
                'original_exception': exc
            }
        elif "authentication failed" in str(exc):
            exc_args = {
                'main_message': 'SMTP server authentication failed.',
                'custom_type': EmailSendFailure,
                'suggested_resolution':
                'Ensure the INI [email] Username and Password are set correctly.',
                'original_exception': exc
            }
        elif " Authentication Required. Learn more at\n5.7.0  HTMLs://support.google.com" in str(
                exc):
            exc_args = {
                'main_message':
                'Incorrect username and/or password or authentication_required is not enabled or Less Secure Apps needs enabled in your gmail settings.',
                'custom_type': EmailSendFailure,
                'original_exception': exc
            }
        elif "Authentication Required" in str(exc):
            exc_args = {
                'main_message':
                'Incorrect username and/or password or the authentication_required setting is not enabled.',
                'custom_type': EmailSendFailure,
                'original_exception': exc
            }
        else:
            exc_args = {
                'main_message':
                'A general exception occurred while sending the email.',
                'original_exception': exc,
            }
            raise FGeneralError(exc_args)

        raise EmailSendFailure(FCustomException(exc_args))

    finally:

        try:
            logger.debug(f'Terminating SMTP object')
            # Terminating SMTP object.
            smtp_Object.quit()
        except Exception as exc:
            if 'Failed to send message' in str(exc):
                exc_args = {
                    'main_message':
                    'Failed to send message. SMTP terminatation error occurred.',
                    'custom_type': EmailSendFailure,
                    'original_exception': exc
                }
                raise EmailSendFailure(FCustomException(exc_args))
            else:
                exc_args = {
                    'main_message':
                    'A general exception occurred while terminating the SMTP object.',
                    'original_exception': exc,
                }
                raise FGeneralError(exc_args)
Пример #6
0
class ExportPstEml(base.job.BaseModule):
    """ Exports to html items from pst/ost.

    """

    def run(self, path):
        """
        Exports to eml items from pst/ost

        Parameters:
            path (str): path to item to export (Message, Task, Appointment, Activity, Meeting, Note, Contact)
        """
        self.logger().info('Running on %s', path)
        self.path = path
        self.msg = EmailMessage()
        self.path = path

        self.export_eml(self.path)
        return []

    def export_eml(self, item, write=True):
        """
        Exports to eml an item parsed with pff-export

        Args:
            item (str): path to item
            write (bool): writes eml file or returns eml content
        """
        content = ""
        bodyfile = ""
        for i in os.listdir(item):
            if i.startswith("Message"):
                bodyfile = os.path.join(item, i)
                break

        if not os.path.isfile(os.path.join(item, "InternetHeaders.txt")):
            self._setIntHeaders(item)
        else:
            content, boundary = self._getIntHeaders(item)
        self._getBody(bodyfile)
        self._addAttach(item)

        if content == "":
            email = self.msg.as_string()
        else:
            email = "{}\r\n--{}\r\n{}\r\n--{}--".format(content, boundary, self.msg.as_string(), boundary)
        if write:
            export_dir = os.path.dirname(item)
            base.utils.check_folder(export_dir)
            output_filename = "{}.eml".format(os.path.join(export_dir, os.path.basename(item)))
            with open(output_filename, 'w') as of:
                of.write(email)
        else:
            return email

    def _addAttach(self, item):

        if not os.path.isdir(os.path.join(item, "Attachments")):
            return

        for i in os.listdir(os.path.join(item, "Attachments")):
            fname = os.path.join(item, "Attachments", i)
            if os.path.isdir(fname):  # attachment is a message
                for l in os.listdir(fname):
                    msg2 = self.msg
                    self.msg = EmailMessage()
                    a = self.export_eml(os.path.join(fname, l), False)
                    with tempfile.NamedTemporaryFile() as fp:
                        fp.write(a.encode())
                        self.msg = msg2
                        # ctype, encoding = mimetypes.guess_type(fp.name)
                        # if ctype is None or encoding is not None:
                        #     # No guess could be made, or the file is encoded (compressed), so
                        #     # use a generic bag-of-bits type.
                        #     ctype = 'text/rfc822'
                        # maintype, subtype = ctype.split('/', 1)
                        with open(fp.name, 'rb') as fp2:
                            self.msg.add_attachment(fp2.read(),
                                                    maintype='application',
                                                    subtype='rfc822',
                                                    filename="%s.eml" % os.path.basename(fname))
            else:
                ctype, encoding = mimetypes.guess_type(fname)
                if ctype is None or encoding is not None:
                    # No guess could be made, or the file is encoded (compressed), so
                    # use a generic bag-of-bits type.
                    ctype = 'application/octet-stream'
                maintype, subtype = ctype.split('/', 1)
                with open(fname, 'rb') as fp:
                    self.msg.add_attachment(fp.read(),
                                            maintype=maintype,
                                            subtype=subtype,
                                            filename=i)

    def _getIntHeaders(self, item):
        int_headers = os.path.join(item, "InternetHeaders.txt")
        content = ''
        addcontent = True
        boundary = "--=11111111111"

        with open(int_headers, 'r') as f:
            for line in f:
                if line.find("%s--" % boundary) > -1:
                    return content, boundary
                if line.strip() == '':
                    addcontent = False
                    continue
                if addcontent:
                    content += line

                bound = re.search('boundary="([^"]+)"', line)
                if bound:
                    boundary = bound.group(1)
        return content, boundary

    def _setIntHeaders(self, item):

        d = self._getDataFromFile(os.path.join(item, "OutlookHeaders.txt"), ['Client submit time', 'Subject', 'Sender name', 'Sender email address'])
        self.msg.add_header('Date', d['Client submit time'][:21])
        self.msg.add_header('Subject', d['Subject'])
        self.msg.add_header('From', "{} <{}>".format(d['Sender name'], d['Sender email address']))

        messageID = self._getGUID(item)
        self.msg.add_header('MessageID', messageID)

        self._setRecipients(item)

    def _setRecipients(self, item):
        r = {'To': '', 'CC': '', 'BCC': ''}
        recipients = os.path.join(item, 'Recipients.txt')
        if not os.path.isfile(recipients):
            return r
        rec = {'To': [], 'CC': [], 'BCC': []}
        with open(recipients, 'r') as f:
            temp = {}
            for line in f:
                if line == "\n":
                    rec[temp['Recipient type']].append("{} <{}>".format(temp['Display name'], temp['Email address']))  # Solves problems with multiples To, CC and BCC fields
                    # self.msg.add_header(temp['Recipient type'], "{} <{}>".format(temp['Display name'], temp['Email address']))
                    temp = {}
                else:
                    aux = line.split(":")
                    temp[aux[0]] = aux[1].strip()
        for i in ['To', 'CC', 'BCC']:
            if len(rec[i]) > 0:
                self.msg.add_header(i, ", ".join(rec[i]))

    def _getDataFromFile(self, filename, items):
        """ Gets item information from a file

        File structure is 'item:        content'

        Args:
            filename (str): file to get information
            items (list): list of items to extract

        """
        res = {}
        for i in items:
            res[i] = ''

        if not os.path.isfile(filename):
            return res

        regex = r'({}):\s+(.*)'.format("|".join(items))
        with open(filename, 'r') as f:
            for line in f:
                item = re.search(regex, line)
                if item:
                    res[item.group(1)] = item.group(2)
        return res

    def _getGUID(self, item):
        conv_ind = os.path.join(item, "ConversationIndex.txt")
        if not os.path.isfile(conv_ind):
            return "[email protected]"
        with open(conv_ind, 'r') as f:
            for line in f:
                guid = re.search(r'GUID:\s+([^\s]+)', line)
                if guid:
                    return "--=%s" % guid.group(1)
        return "[email protected]"

    def _getBody(self, bodyfile):
        if not os.path.isfile(bodyfile):
            return ""

        if bodyfile.endswith('.txt'):
            with open(bodyfile) as bf:
                self.msg.set_content(bf.read())

        if bodyfile.endswith('.rtf'):
            tika_parser = base.job.load_module(self.config, 'indexer.tikaparser.TikaParser')
            body = tika_parser.run(bodyfile)[0]['content']

            self.msg.set_content(body)

        if bodyfile.endswith('.html'):
            with open(bodyfile, 'rb') as bf:
                data = bf.read()
                enc = chardet.detect(data)
                # self.msg.add_header('Content-Type', 'text/html')
                # self.msg.set_payload(bf.read())
                # self.msg.set_content(bf.read(), subtype='html')
                self.msg.set_content(data.decode(enc['encoding']), subtype='html')
Пример #7
0
def _mail_recipient(recipient_name,
                    recipient_email,
                    sender_name,
                    sender_url,
                    subject,
                    body,
                    body_html=None,
                    headers=None,
                    attachments=None):

    if not headers:
        headers = {}

    if not attachments:
        attachments = []

    mail_from = config.get_value('smtp.mail_from')
    reply_to = config.get_value('smtp.reply_to')

    msg = EmailMessage()

    msg.set_content(body, cte='base64')

    if body_html:
        msg.add_alternative(body_html, subtype='html', cte='base64')

    for k, v in headers.items():
        if k in msg.keys():
            msg.replace_header(k, v)
        else:
            msg.add_header(k, v)

    msg['Subject'] = subject
    msg['From'] = _("%s <%s>") % (sender_name, mail_from)
    msg['To'] = u"%s <%s>" % (recipient_name, recipient_email)
    msg['Date'] = utils.formatdate(time())
    msg['X-Mailer'] = "CKAN %s" % ckan.__version__
    if reply_to and reply_to != '':
        msg['Reply-to'] = reply_to

    for attachment in attachments:
        if len(attachment) == 3:
            name, _file, media_type = attachment
        else:
            name, _file = attachment
            media_type = None

        if not media_type:
            media_type, encoding = mimetypes.guess_type(name)
        if media_type:
            main_type, sub_type = media_type.split('/')
        else:
            main_type = sub_type = None

        msg.add_attachment(_file.read(),
                           filename=name,
                           maintype=main_type,
                           subtype=sub_type)

    # Send the email using Python's smtplib.
    smtp_server = config.get_value('smtp.server')
    smtp_starttls = config.get_value('smtp.starttls')
    smtp_user = config.get_value('smtp.user')
    smtp_password = config.get_value('smtp.password')

    try:
        smtp_connection = smtplib.SMTP(smtp_server)
    except (socket.error, smtplib.SMTPConnectError) as e:
        log.exception(e)
        raise MailerException(
            'SMTP server could not be connected to: "%s" %s' %
            (smtp_server, e))

    try:
        # Identify ourselves and prompt the server for supported features.
        smtp_connection.ehlo()

        # If 'smtp.starttls' is on in CKAN config, try to put the SMTP
        # connection into TLS mode.
        if smtp_starttls:
            if smtp_connection.has_extn('STARTTLS'):
                smtp_connection.starttls()
                # Re-identify ourselves over TLS connection.
                smtp_connection.ehlo()
            else:
                raise MailerException("SMTP server does not support STARTTLS")

        # If 'smtp.user' is in CKAN config, try to login to SMTP server.
        if smtp_user:
            assert smtp_password, ("If smtp.user is configured then "
                                   "smtp.password must be configured as well.")
            smtp_connection.login(smtp_user, smtp_password)

        smtp_connection.sendmail(mail_from, [recipient_email], msg.as_string())
        log.info("Sent email to {0}".format(recipient_email))

    except smtplib.SMTPException as e:
        msg = '%r' % e
        log.exception(msg)
        raise MailerException(msg)
    finally:
        smtp_connection.quit()
Пример #8
0
    def _send_http2_request_to_server(self, request_headers, req_body,
                                      client_stream_id):
        if not self.is_h2_to_server:
            raise RuntimeError(
                "Unexpected received non is_h2_to_server in _send_http2_request_to_server"
            )

        request_headers_message = HttpHeaders()
        for name, value in request_headers:
            request_headers_message.add_header(name.decode("utf-8"),
                                               value.decode("utf-8"))
        request_headers = request_headers_message
        request_headers = ProxyRequestHandler.filter_headers(request_headers)

        scheme = request_headers[':scheme']
        replay_server = f"127.0.0.1:{self.server_port}"
        path = request_headers[':path']
        url = f'{scheme}://{replay_server}{path}'
        method = request_headers[':method']

        original_request_headers = request_headers
        request_headers = self._remove_pseudo_headers(request_headers)

        try:
            origin = (scheme, replay_server, self.client_sni)
            if origin not in self.tls.http_conns:
                ssl_context = httpx.create_ssl_context(cert=self.cert_file,
                                                       verify=False)
                if self.client_sni:
                    setattr(ssl_context, "old_wrap_socket",
                            ssl_context.wrap_socket)

                    def new_wrap_socket(sock, *args, **kwargs):
                        kwargs['server_hostname'] = self.client_sni
                        return ssl_context.old_wrap_socket(
                            sock, *args, **kwargs)

                    setattr(ssl_context, "wrap_socket", new_wrap_socket)

                http2_connection = httpx.Client(verify=ssl_context, http2=True)

                self.tls.http_conns[origin] = http2_connection

            client = self.tls.http_conns[origin]
            response_from_server = client.request(
                method=method,
                url=url,
                headers=request_headers.items(),
                content=req_body)
            response_body = response_from_server.content
        except Exception as e:
            if origin in self.tls.http_conns:
                del self.tls.http_conns[origin]
            self.listening_conn.send_headers(client_stream_id,
                                             ((':status', '502')),
                                             end_stream=True)
            authority = request_headers.get(':authority', '')
            print(f"Connection to '{replay_server}' initiated with request to "
                  f"'{scheme}://{authority}{path}' failed: {e}")
            traceback.print_exc(file=sys.stdout)
            return

        setattr(
            response_from_server, 'headers',
            ProxyRequestHandler.filter_headers(response_from_server.headers))
        response_headers = ((':status',
                             str(response_from_server.status_code)), )
        previous_k = b''
        previous_v = b''
        for k, v in response_from_server.headers.raw:
            if k == b'date' and k == previous_k:
                # This happens with date, which HTTPHeaderMap annoyingly splits
                # on the comma:
                # "Sat, 16 Mar 2019 01:13:21 GMT"
                #
                # This yields the following two tuples:
                # (b'date', b'Sat')
                # (b'date', b'16 Mar 2019 01:13:21 GMT')
                v = previous_v + b', ' + v
                response_headers = response_headers[0:-1]
            response_headers += ((k, v), )
            previous_k, previous_v = k, v

        # httpx will insert a reason phrase for HTTP/2, but there technically
        # isn't one, so don't confuse the output with it.
        empty_reason_phrase = ''
        self.print_info(original_request_headers, req_body, response_headers,
                        response_body, response_from_server.status_code,
                        empty_reason_phrase)
        return response_headers, response_body
Пример #9
0
     pass
 else:
     img_saved = Image.open(BytesIO(img_req.content))
     img2 = img_saved.convert('RGB')
     file_name_forecast = r'forecast_img.pdf'
     img2.save(file_name_forecast)
     try:
         #sending e-mail
         server = smtplib.SMTP('smtp.gmail.com:587')
         body_message = 'There is the new updated forecast for the region.<br> Snow Amount Potential Update'
         msg = EmailMessage()
         msg['From'] = sender_email
         msg['To'] = target_email
         msg['Subject'] = 'Snow Amount Potential wheater forecast'
         password = google_app_password
         msg.add_header('Content-Type', 'text/html')
         msg.set_payload(body_message)
         #msg.preamble = body_message
         #loading pdf to attach and add to email body
         path = file_name_forecast
         # Guess the content type based on the file's extension.
         ctype, encoding = mimetypes.guess_type(path)
         if ctype is None or encoding is not None:
             ctype = 'application/octet-stream'
         maintype, subtype = ctype.split('/', 1)
         with open(path, 'rb') as fp:
             msg.add_attachment(fp.read(), maintype=maintype, subtype=subtype, 
                                filename=file_name_forecast)
         s = smtplib.SMTP('smtp.gmail.com: 587')
         s.starttls()
         # Login Credentials for sending the mail
Пример #10
0
    def _send_http1_request_to_server(self, request_headers, req_body,
                                      stream_id):
        if not isinstance(request_headers, HttpHeaders):
            request_headers_message = HttpHeaders()
            for name, value in request_headers:
                request_headers_message.add_header(name.decode("utf-8"),
                                                   value.decode("utf-8"))
            request_headers = request_headers_message
        request_headers = ProxyRequestHandler.filter_headers(request_headers)

        scheme = request_headers[':scheme']
        replay_server = f"127.0.0.1:{self.server_port}"
        method = request_headers[':method']
        path = request_headers[':path']

        try:
            origin = (scheme, replay_server)
            if origin not in self.tls.http_conns:
                if scheme == 'https':
                    if self.client_sni:
                        gcontext = WrapSSSLContext(self.client_sni)
                    else:
                        gcontext = ssl.SSLContext()
                    self.tls.http_conns[origin] = http.client.HTTPSConnection(
                        replay_server,
                        timeout=self.timeout,
                        context=gcontext,
                        cert_file=self.cert_file)
                else:
                    self.tls.http_conns[origin] = http.client.HTTPConnection(
                        replay_server, timeout=self.timeout)
            connection_to_server = self.tls.http_conns[origin]
            http1_headers = self.convert_headers_to_http1(request_headers)
            connection_to_server.request(method, path, req_body, http1_headers)
            res = connection_to_server.getresponse()

            version_table = {10: 'HTTP/1.0', 11: 'HTTP/1.1'}
            setattr(res, 'headers', res.msg)
            setattr(res, 'response_version', version_table[res.version])

            response_body = res.read()
        except Exception as e:
            if origin in self.tls.http_conns:
                del self.tls.http_conns[origin]
            self.listening_conn.send_headers(stream_id, ((':status', '502')),
                                             end_stream=True)
            authority = request_headers.get(':authority', '')
            print(f"Connection to '{replay_server}' initiated with request to "
                  f"'{scheme}://{authority}{path}' failed: {e}")
            traceback.print_exc(file=sys.stdout)
            return

        setattr(res, 'headers',
                ProxyRequestHandler.filter_headers(res.headers))

        response_headers = ((':status', str(res.status)), )
        for k, v in res.headers.items():
            response_headers += ((k, v), )
        self.print_info(request_headers, req_body, response_headers,
                        response_body, res.status, res.reason)
        return response_headers, response_body
Пример #11
0
SMTP_SERVER = 'insert smtp server FQDN or IP'

IMG = 'jon_snow.jpg'
TO_ADDR = '*****@*****.**'
FROM_ADDR = 'JON SNOW <*****@*****.**>'
BCC_ADDR = '*****@*****.**'
REPLY_TO_ADDRESS = 'Jon Snow <*****@*****.**>'

# Create the base text message.
msg = EmailMessage()
msg['Subject'] = "Brace yourself"
msg['From'] = FROM_ADDR
msg['To'] = TO_ADDR
msg['Bcc'] = BCC_ADDR
msg.add_header('reply-to', REPLY_TO_ADDRESS)
msg.set_content("""\
See my message in attach""")

# Add the html version.  This converts the message into a multipart/alternative
# container, with the original text message as the first part and the new html
# message as the second part.
meme_cid = make_msgid()
msg.add_alternative("""\
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>

    <meta http-equiv="content-type" content="text/html; charset=ISO-8859-15">
  </head>
  Keep the strong momentum!
Пример #12
0
    def _send_encrypted_email(
        self,
        from_email: str,
        from_name: str,
        subject: str,
        message: str,
        public_key: Optional[str],
        pgp_public_key: PGPKey,
    ) -> None:
        # Sources:
        # - [ProtonMail](https://protonmail.com/support/knowledge-base/pgp-mime-pgp-inline/)
        # - [StackOverflow](https://stackoverflow.com/questions/54486279/how-to-send-gpg-encrypted-email-with-attachment-using-python?answertab=active#tab-top)

        msg = EmailMessage()
        msg.add_header(_name="Content-Type", _value="multipart/mixed")

        msg_text = MIMEText(message, _subtype="plain", _charset="utf-8")
        msg.attach(msg_text)

        if public_key:
            msg_attachment = EmailMessage()
            msg_attachment.add_header(
                _name="Content-Type",
                _value="application/pgp-keys",
                name="publickey.asc",
            )
            msg_attachment.add_header(
                _name="Content-Disposition",
                _value="attachment",
                filename="publickey.asc",
            )
            msg_attachment.set_payload(public_key)
            encoders.encode_base64(msg_attachment)
            msg.attach(msg_attachment)

        try:
            encrypted_message = pgp_public_key.encrypt(
                PGPMessage.new(msg.as_string()))
        except PGPError:
            raise RuntimeError

        pgp_msg = MIMEBase(
            _maintype="multipart",
            _subtype="encrypted",
            protocol="application/pgp-encrypted",
            charset="UTF-8",
        )

        pgp_msg["Date"] = formatdate()
        pgp_msg["From"] = formataddr((from_name, self.sender_email))
        pgp_msg["To"] = formataddr((self.to_name, self.to_email))
        pgp_msg["Reply-To"] = formataddr((from_name, from_email))
        pgp_msg["Subject"] = Header(subject, "utf-8")

        pgp_msg_part1 = EmailMessage()
        pgp_msg_part1.add_header(_name="Content-Type",
                                 _value="application/pgp-encrypted")
        pgp_msg_part1.add_header(_name="Content-Description",
                                 _value="PGP/MIME version identification")
        pgp_msg_part1.set_payload("Version: 1\n")
        pgp_msg.attach(pgp_msg_part1)

        pgp_msg_part2 = EmailMessage()
        pgp_msg_part2.add_header(
            _name="Content-Type",
            _value="application/octet-stream",
            name="encrypted.asc",
        )
        pgp_msg_part2.add_header(_name="Content-Description",
                                 _value="OpenPGP encrypted message")
        pgp_msg_part2.add_header(_name="Content-Disposition",
                                 _value="inline",
                                 filename="encrypted.asc")
        pgp_msg_part2.set_payload(str(encrypted_message))
        pgp_msg.attach(pgp_msg_part2)

        return self._send_smtp(pgp_msg)
Пример #13
0
async def send_email(subject,
                     content,
                     to,
                     username,
                     password,
                     display_name=None,
                     cc_recipients=None,
                     bcc_recipients=None,
                     important=False,
                     attach_img=None,
                     content_type='text/plain',
                     server='smtp.gmail.com',
                     port=465):

    if not to or not username or not password or not subject or not content:
        return False

    def email_name(addr):
        return addr.split('@')[0]

    def email_domain(addr):
        return addr.split('@')[1]

    def generate_header(addresses):
        if isinstance(addresses, Address):
            return str(addresses)
        return ', '.join([str(addr) for addr in addresses])

    display_name = display_name or email_name(username)

    from_addr = Address(display_name, display_name.lower(),
                        email_domain(username))
    to_addr = [
        Address(email_name(recipient),
                email_name(recipient).lower(), email_domain(recipient))
        for recipient in (to if isinstance(to, list) else [to])
    ]
    cc_addr = [
        Address(email_name(recipient),
                email_name(recipient).lower(), email_domain(recipient))
        for recipient in (cc_recipients or [])
    ]
    bcc_addr = [
        Address(email_name(recipient),
                email_name(recipient).lower(), email_domain(recipient))
        for recipient in (bcc_recipients or [])
    ]

    # Build the list of recipients (To + Bcc):
    recipients = [addr.addr_spec for addr in (to_addr + cc_addr + bcc_addr)]

    # Build the EmailMessage object:
    message = EmailMessage()
    message.add_header("From", generate_header(from_addr))
    message.add_header("To", generate_header(to_addr))
    if cc_addr:
        message.add_header("Cc", generate_header(cc_addr))
    if bcc_addr:
        message.add_header("Bcc", generate_header(bcc_addr))
    message.add_header("Subject", subject)
    message.add_header("Content-type", content_type, charset="utf-8")
    if attach_img:
        message.add_attachment(attach_img,
                               main_type='image',
                               subtype=imghdr.what(None, attach_img))
    if important:
        message.add_header("X-Priority", "1 (Highest)")
        message.add_header("X-Priority", "1 (Highest)")
        message.add_header("X-MSMail-Priority", "High")
        message.add_header("Importance", "High")

    message.set_content(content)

    async with SMTP(hostname=server,
                    port=port,
                    use_tls=True,
                    username=username,
                    password=password) as client:
        await client.sendmail(from_addr.addr_spec, recipients,
                              message.as_string())

    return True
Пример #14
0
host = 'localhost'
port = int(os.getenv('PORT') or 1143)
user = os.getenv('IMAP_USER') or 'alice'
password = os.getenv('IMAP_PASS') or 'alice'

mailbox = 'Notes'
subject = 'Hello 📧 IMAP!'
keywords = 'test, hello'
sender = '*****@*****.**'
body = 'The quick brown fox 🦊 jumps over the lazy dog 🐶 .'
date = datetime.now(timezone.utc)

message_id = str(uuid.uuid4()) + '@example.com'

message = EmailMessage()
message.add_header('MIME-Version', '1.0')
message.add_header('Date', email.utils.format_datetime(date))
message.add_header('From', sender)
message.add_header('Subject', subject)
message.add_header('Keywords', keywords)
message.add_header('Message-ID', message_id)
message.set_content(body)

with IMAP4(host, port) as imap:
    imap.debug = 4

    imap.noop()
    imap.login(user, password)
    imap.enable('UTF8=ACCEPT')
    imap.list()