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)
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
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 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
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)
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')
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()
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
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
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
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!
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)
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
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()