I'm sending emails using python. Currently my technique is this:
msg = email.mime.Multipart.MIMEMultipart()
msg["From"] = username
msg["To"] = recipient
msg["Subject"] = subject
mimeText = email.mime.Text.MIMEText(body, "html")
msg.attach(mimeText)
stringMsg = msg.as_string()
I would like to also add some metadata to the message - specifically, a unique identifier of the task the email is achieving, so that when it is checked later (possibly by a different service), I can avoid sending a duplicate email.
This metadata doesn't need to be completely secret or secure, just something that standard email clients don't render. Obviously there are the options of including a bogus BCC email address containing the id, or adding a hidden html node to the body.
<div style="display:none;">123456789</div>
But both of those seem quite "hacky". Is there anything like this that will get persisted and sent as part of the email, that I can check using imaplib later?
msg["secretMetadata"] = "123456789"
User defined fields are permitted and explained in RFC822. Basically you can prefix your custom field with X- and this will not conflict with any existing fields nor extension fields.
So, something like msg["X-secretMetadata"] = "123456789" should suffice.
It sounds like you might want to use X-headers. Information in the X-headers of an email message is usually used for application-specific purposes, and this information is usually not displayed by mail clients.
Related
The recipient address and the distro lists do not immediately resolve. Outlook is smart enough to recognize the email addresses, but it doesn't recognize my distro list. At least not immediately.
I'm using Office 365 if that matters.
from win32com.client import Dispatch
outlook = Dispatch('Outlook.Application')
Mail_Item = outlook.CreateItem(0)
# This sends no problem
# Mail_Item.To = 'first.last#company.com'
# This does not send
Mail_Item.To = 'Contact_Group_Test'
Mail_Item.Subject = "Subject_text"
Mail_Item.Body = "Body_text"
Mail_Item.Recipients.ResolveAll()
Mail_Item.Send()
While troubleshooting I used Mail_Item.Display() to see the message. After a few seconds it resolves all my addresses, including the contact group. HOWEVER, the contact group itself still doesn't work despite this.
If you couldn't solve, you can try smtplib.
import smtplib
sender = 'from#fromdomain.com'
receivers = ['to#todomain.com']
message = """From: From Person <from#fromdomain.com>
To: To Person <to#todomain.com>
Subject: SMTP e-mail test
This is a test e-mail message.
"""
try:
smtpObj = smtplib.SMTP('localhost')
smtpObj.sendmail(sender, receivers, message)
print "Successfully sent email"
except SMTPException:
print "Error: unable to send email"
If you will send mail from outlook account, you have to write smtp-mail.outlook.com instead of localhost
mtbenj's answer may work for others. I'm just choosing to go the win32com route because of my organization.
For whatever reason using 'Test', and I'm assuming, any one-word contact list takes ~5 seconds to resolve. I counted after the .Display() popup came up. Naming it 'Test_' didn't do it for me either.
I decided to try one named 'Test_Test'. After calling .Recipients.ResolveAll(), it works perfect.
So use an underscore and multiple words.
I'm trying to send emails with smtplib and they seem to be delivering fine. The only problem is that DKIM fails and the mails usually go straight to the spam folder.
DKIM is enabled on my shared hosting (host is a2hosting, if that helps) and the process works fine when sending individual emails with Thunderbird, and DKIM passes, suggesting that the problem lies on my end.
I even tried using dkimpy to explicitly sign the emails using the private key but I still get dkim=fail under ARC-Authentication-Results.
Some posts and answers I referred to suggested "logging in" as the solution but I am already logging in using SMTP.login() and as I mentioned earlier, the emails are being sent.
An answer I referred to mentioned that it is the server's job to sign the email and it's worth mentioning that the raw email output includes the DKIM signature, even without explicitly signing it with dkimpy, indicating that the server is signing as expected.
But the problem remains that DKIM fails affecting the email deliverability, and the raw output does not provide any details as to why DKIM failed for the domain.
I use the following code snippet to send an email
msg = MIMEMultipart()
msg['From'] = 'myemail#mydomain.tld'
msg['To'] = 'someemail#gmail.com'
msg['Subject'] = "Subject"
msg.attach(MIMEText("SomeText", "plain"))
s = smtplib.SMTP_SSL("mydomain.tld:465")
s.login("myemail#mydomain.tld", "mypassword")
s.sendmail("myemail#mydomain.tld", 'someemail#gmail.com',msg.as_string())
I tried signing the message as follows
headers = ["To", "From", "Subject"]
with open("cert.pem") as fh:
dkim_private = fh.read()
sig = dkim.sign(
message=msg.as_string().encode("ascii"),
selector=str(dkim_selector).encode("ascii"),
domain="robogyan.tech".encode("ascii"),
privkey=dkim_private.encode("ascii"),
include_headers=headers,)
msg["DKIM-Signature"] = sig.decode("ascii").lstrip("DKIM-Signature: ")
The raw output did reflect the signature with the above code but DKIM still failed.
There seems to be no problem with the authentication whatsoever since the server replies with "Authentication succeeded"
Edit:
DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed;
d=mydomain.tld; s=default; h=Subject:To:From:MIME-Version:Content-Type:
Sender:Reply-To:Date:Message-ID:Cc:Content-Transfer-Encoding:Content-ID:
Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc
:Resent-Message-ID:In-Reply-To:References:List-Id:List-Help:List-Unsubscribe:
List-Subscribe:List-Post:List-Owner:List-Archive;
bh=giCDGo/0duFr1Ex65l7Ixc3N45EAULK+gw5cHV8pO0k=; b=DR08Q+CjgOLqo8WkLJs/XROfTw
Z7+ph+qnzi5p49cT3+UwQolcL1CKIVPk7XRkL8WZ3FFa9hZuc6TumquRSiYd5uR0AC5Z3lopEfnQe
fdbOOTRnks2ZzoOnQusy/gmydUttypu8wTthFhy7vTWXMFcdI29X/HkrokCtiGKCoD2u2kWBtn2sm
3/aP83lBbMpcWsNbvo3HTsL71o8QPd6bVKpqRGyAy89cAwMLwP4dnJ9WcCxxNzowlJNPQja3o5W16
t3rG/KizcRehjaDUXhPPRF/4RdYUSIi/SGNwmIPwvkZNc17k3wQpszKeG6/Ujgax/i7Li7V7dLJBT
Fu/x6xDA==;
Signed-by: myemail#mydomain.tld
Expected-Body-Hash: giCDGo/0duFr1Ex65l7Ixc3N45EAULK+gw5cHV8pO0k=
Here's the DKIM of the failing email if that helps. The expected body hash and the received body hash match too. I am not sure what the problem is then.
After a lot of research and brute force approaches, I finally found the solution to my problem.
I needed to include the Message-ID and the date in the headers as well.
Adding the following lines to the code helped me pass the verification.
msg['Date'] = email.utils.formatdate()
msg['Message-ID'] = email.utils.make_msgid(domain='mydomain.tld')
Important note: you need to add your smtp client's machine IP address to InternalHosts list, because OpenDKIM will check client's permission with these rules.
The you need to add this line to your /etc/opendkim.conf:
InternalHosts file:/etc/opendkim/TrustedHosts # or any location you want
Content of /etc/opendkim/TrustedHosts could look like:
127.0.0.1
::1
localhost
<server_ip>
hostname.example1.com
example1.com
hostname.example2.com
example2.com
...
It's just for example. You need to put here your python smtplib-client machine's address (ip/host).
Then just restart your opendkim:
$ sudo service opendkim restart
I use send_raw_email to send emails with HTML content and file attachments. How do I insert an ical/ics invite to the email?
I use icalendar to generate ics content.
This is what I came up with so far, but it shows in Gmail as a file attachment.
if calendar_reminder_date:
cal = Calendar()
cal.add('prodid', '-//My calendar product//mxm.dk//')
cal.add('version', '2.0')
cal.add('calscale', 'GREGORIAN')
cal.add('method', 'REQUEST')
event = Event()
event['dtstart'] = calendar_reminder_date.strftime("%Y%m%dT%H%M%SZ")
event['dtstamp'] = calendar_reminder_date.strftime("%Y%m%dT%H%M%SZ")
event['summary'] = 'Python meeting about calendaring'
cal.add_component(event)
attachment_part = MIMEText(cal.to_ical())
print repr(cal.to_ical())
del attachment_part['Content-Type']
attachment_part.add_header('Content-Type', 'text/calendar', name='invite.ics')
attachment_part.add_header('Content-Disposition', 'attachment', filename='invite.ics')
msg.attach(attachment_part)
Hard to tell without seeing the actual full MIME message as received on the Google side but there are definitely several things missing here:
a calendar REQUEST must have an ORGANIZER property, as well as at least one ATTENDEE property with a value corresponding to the email address of the gmail user (prefixed with a mailto:).
your content-type is missing a METHOD=REQUEST parameter and you probably do not want to set any content-disposition.
See also Multipart email with text and calendar: Outlook doesn't recognize ics
I'm trying really hard to check why this is happening but not really sure if its on gmail's server side or in a part of the script I'm using.
The problem is that I'm trying to send an email to a mailing list (I have found many posts explaining how to include various emails but none explaining if there is a limitation or workaround to send it to a single email which contains multiple addresses).
In this case I want to send the email to many different people which make part of the BI team of our company (bi#company.com), normally sending an email to this address will result in everyone from the team getting the email, but I can't manage to make i work, and I don't want to include all emails in the list because there are too much and it will have to be manually changed every time.
When I try this with another single person email it works perfectly
import smtplib
sender = 'server#company.com'
receivers = ['bi#company.com']
q = ""
message = """From: Error alert <Server-company>
To: BI <bi#company.com>
Subject: Error e-mail
%s
""" % q
try:
smtpObj = smtplib.SMTP('localhost')
smtpObj.sendmail(sender, receivers, message)
print "Successfully sent email"
except SMTPException:
print "Error: unable to send email"
Alright. I found out why this was not working in my organization. Hopefully this can help someone else out. The outlook groups were setup to only work from an internal email address.
Sending emails programmatically, may use a relay method, which has no authentication. As such the email will never go through. The fix is to have the Network ops people (or exchange group) at your company turn on receiving external emails. The emails will then go through.
make recivers a list of emails you want to send:
example:
recivers = ['a#gmail.com','b#gmail.com,...]
try like this:
import smtplib
from email.mime.text import MIMEText
sender = 'server#company.com'
receivers = ['a#company.com','b]#company.com'....]
server = smtplib.SMTP('smtp#serv.com',port)
server.starttls()
msg = MIMEText('your_message')
msg['Subject'] = "subject line"
msg['From'] = sender
msg['To'] = ", ".join(receivers)
server.login('username','password')
server.sendmail(sender, recipients, msg.as_string())
server.quit()
I finally decided to stick to writing the full list of email addresses inside the list. Apparently if mailing lists are managed locally the re distribution of the emails cant be done.
I am developing an application with Django. In forms.py, where the classes for my forms are stored, I have written a clean function to verify that all the emails typed into a textbox adhere to the proper format (person#site.com).
In this clean function, I build the email message with an EmailMessage object:
def clean_recipients(self):
rec = self.data['recipients'].split(",")
recList = []
for recipient in rec:
reci = str.strip(str(recipient))
recList.append(reci)
message = (self.data['subject'], self.data['message'], 'hi#world.com', recList)
mail = EmailMessage(self.data['subject'], self.data['message'], 'from#somebody.com', ['email_list#mysite.org'], recList)
try:
mail.send(fail_silently=False)
except Exception:
raise forms.ValidationError('Please check inputted emails for validity.')
return self.data['recipients']
However, the exception 'Please check inputted emails for validity.' is never raised on the form regardless of what I input into the textbox. If I input random characters into the textbox, simply no message is sent.
What is the proper way to catch if the email was not sent properly?
Thank you.
Try to clean recipients emails format only in the clean_recipients. There is snippet how to check email. http://djangosnippets.org/snippets/1093/ . Raise validation error if email format does not match.
Create email and send it from the form's clean method (if you need show sending errors. You will need check for form errors before send.) or from the view.
PS. get your data this way - self.cleaned_data instead of self.data.