<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>email — jd:/dev/blog</title><description>Posts tagged &quot;email&quot; on jd:/dev/blog.</description><link>https://julien.danjou.info/</link><item><title>Sending Emails in Python — Tutorial with Code Examples</title><link>https://julien.danjou.info/blog/sending-emails-in-python-tutorial-code-examples/</link><guid isPermaLink="true">https://julien.danjou.info/blog/sending-emails-in-python-tutorial-code-examples/</guid><description>What do you need to send an email with Python? Some basic programming and web knowledge along with the elementary Python skills. I assume you’ve already had a web app built with this language and now</description><pubDate>Tue, 15 Oct 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;What do you need to send an email with Python? Some basic programming and web knowledge along with the elementary Python skills. I assume you’ve already had a web app built with this language and now you need to extend its functionality with notifications or other emails sending. This tutorial will guide you through the most essential steps of sending emails via an SMTP server:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Configuring a server for testing (do you know why it’s important?)&lt;/li&gt;
&lt;li&gt;Local SMTP server&lt;/li&gt;
&lt;li&gt;Mailtrap test SMTP server&lt;/li&gt;
&lt;li&gt;Different types of emails: HTML, with images, and attachments&lt;/li&gt;
&lt;li&gt;Sending multiple personalized emails (Python is just invaluable for email automation)&lt;/li&gt;
&lt;li&gt;Some popular email sending options like Gmail and transactional email services&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;em&gt;Served with numerous code examples written and tested on Python 3.7!&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;Sending an email using an SMTP&lt;/h3&gt;
&lt;p&gt;The first good news about Python is that it has a built-in module for sending emails via SMTP in its standard library. No extra installations or tricks are required. You can import the module using the following statement:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import smtplib
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To make sure that the module has been imported properly and get the full description of its classes and arguments, type in an interactive Python session:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;help(smtplib)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At our next step, we will talk a bit about servers: choosing the right option and configuring it.&lt;/p&gt;
&lt;h4&gt;An SMTP server for testing emails in Python&lt;/h4&gt;
&lt;p&gt;When creating a new app or adding any functionality, especially when doing it for the first time, it’s essential to experiment on a test server. Here is a brief list of reasons:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;You won’t hit your friends’ and customers’ inboxes. This is vital when you test bulk email sending or work with an email database.&lt;/li&gt;
&lt;li&gt;You won’t flood your own inbox with testing emails.&lt;/li&gt;
&lt;li&gt;Your domain won’t be blacklisted for spam.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;Local SMTP server&lt;/h4&gt;
&lt;p&gt;If you prefer working in the local environment, the local SMTP debugging server might be an option. For this purpose, Python offers an &lt;em&gt;smtpd&lt;/em&gt; module. It has a &lt;code&gt;DebuggingServer&lt;/code&gt; feature, which will discard messages you are sending out and will print them to &lt;code&gt;stdout&lt;/code&gt;. It is compatible with all operations systems.&lt;/p&gt;
&lt;p&gt;Set your SMTP server to &lt;em&gt;localhost:1025&lt;/em&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;python -m smtpd -n -c DebuggingServer localhost:1025
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In order to run SMTP server on port 25, you’ll need root permissions:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo python -m smtpd -n -c DebuggingServer localhost:25
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It will help you verify whether your code is working and point out the possible problems if there are any. However, it won’t give you the opportunity to check how your HTML email template is rendered.&lt;/p&gt;
&lt;h4&gt;Fake SMTP server&lt;/h4&gt;
&lt;p&gt;Fake SMTP server imitates the work of a real 3rd party web server. In further examples in this post, we will use &lt;a href=&quot;https://mailtrap.io&quot;&gt;Mailtrap&lt;/a&gt;. Beyond testing email sending, it will let us check how the email will  be rendered and displayed, review the message raw data as well as will provide us with a spam report. Mailtrap is very easy to set up: you will need just copy the credentials generated by the app and paste them into your code.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://lh3.googleusercontent.com/xBVM7uyt4Q6mtpLLTiCBze9lNV-dpkO2rMBLSazZ9gb8LImFDgzZWVIOTCtke87LBixqrsJF-pii7usO3ezPbgjOWGRj7isa_ap2-EXK5GiHmSz4mtwenUIi-f_s05CfxQJoHGvl&quot; alt=&quot;Screenshot of Mailtrap fake SMTP server setup interface&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Here is how it looks in practice:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import smtplib

port = 2525
smtp_server = &quot;smtp.mailtrap.io&quot;
login = &quot;1a2b3c4d5e6f7g&quot; # your login generated by Mailtrap
password = &quot;1a2b3c4d5e6f7g&quot; # your password generated by Mailtrap
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Mailtrap makes things even easier. Go to the &lt;em&gt;Integrations&lt;/em&gt; section in the SMTP settings tab and get the ready-to-use template of the simple message, with your Mailtrap credentials in it. It is the most basic option of instructing your Python script on who sends what to who is the &lt;em&gt;sendmail()&lt;/em&gt; instance method:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://lh5.googleusercontent.com/eKUJ__R4SYnY5jdvPiPPucHnoaOMBUxHZIu0DT2NjnTMU2FhvObBzqVN-qgCOTeSIm7yc_ifUAe5a0RofkbNdxOqNrzAw1icea4c9WIyb6NGk8KMmIvctLgUPlblmzFMSeaRnbGQ&quot; alt=&quot;Screenshot of Mailtrap integration settings with Python SMTP credentials&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The code looks pretty straightforward, right? Let’s take a closer look at it and add some error handling (see the comments in between). To catch errors, we use the &lt;code&gt;try&lt;/code&gt; and &lt;code&gt;except&lt;/code&gt; blocks.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;## The first step is always the same: import all necessary components:
import smtplib
from socket import gaierror

## Now you can play with your code. Let’s define the SMTP server separately here:
port = 2525
smtp_server = &quot;smtp.mailtrap.io&quot;
login = &quot;1a2b3c4d5e6f7g&quot; # paste your login generated by Mailtrap
password = &quot;1a2b3c4d5e6f7g&quot; # paste your password generated by Mailtrap

## Specify the sender’s and receiver’s email addresses:
sender = &quot;from@example.com&quot;
receiver = &quot;mailtrap@example.com&quot;

## Type your message: use two newlines (\n) to separate the subject from the message body, and use &apos;f&apos; to  automatically insert variables in the text
message = f&quot;&quot;&quot;\
Subject: Hi Mailtrap
To: {receiver}
From: {sender}
This is my first message with Python.&quot;&quot;&quot;

try:
  # Send your message with credentials specified above
  with smtplib.SMTP(smtp_server, port) as server:
    server.login(login, password)
    server.sendmail(sender, receiver, message)
except (gaierror, ConnectionRefusedError):
  # tell the script to report if your message was sent or which errors need to be fixed
  print(&apos;Failed to connect to the server. Bad connection settings?&apos;)
except smtplib.SMTPServerDisconnected:
  print(&apos;Failed to connect to the server. Wrong user/password?&apos;)
except smtplib.SMTPException as e:
  print(&apos;SMTP error occurred: &apos; + str(e))
else:
  print(&apos;Sent&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once you get the &lt;em&gt;Sent&lt;/em&gt; result in Shell, you should see your message in your Mailtrap inbox:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://lh3.googleusercontent.com/xCCQOuWFyqmvbiOaLa7VgYyBdCu5c2q5oXzyn2aeFFE8tkfbUvDwi_H19fSNAempeUWIoDuHVn5ETqr34lO8WkT8vZh8iJVChjnCZgoAA3TsTJF2n32sGUl1GX89WcYUdChJZ2Ux&quot; alt=&quot;Screenshot of a test email received in the Mailtrap inbox&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Sending emails with HTML content&lt;/h3&gt;
&lt;p&gt;In most cases, you need to add some formatting, links, or images to your email notifications. We can simply put all of these with the HTML content. For this purpose, Python has an &lt;em&gt;email&lt;/em&gt; package.&lt;/p&gt;
&lt;p&gt;We will deal with the MIME message type, which is able to combine HTML and plain text. In Python, it is handled by the &lt;em&gt;email.mime&lt;/em&gt; module.&lt;/p&gt;
&lt;p&gt;It is better to write a text version and an HTML version separately, and then merge them with the &lt;code&gt;MIMEMultipart(&quot;alternative&quot;)&lt;/code&gt; instance. It means that such a message has two rendering options accordingly. In case an HTML isn’t be rendered successfully for some reason, a text version will still be available.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart

port = 2525
smtp_server = &quot;smtp.mailtrap.io&quot;
login = &quot;1a2b3c4d5e6f7g&quot; # paste your login generated by Mailtrap
password = &quot;1a2b3c4d5e6f7g&quot; # paste your password generated by Mailtrap

sender_email = &quot;mailtrap@example.com&quot;
receiver_email = &quot;new@example.com&quot;

message = MIMEMultipart(&quot;alternative&quot;)
message[&quot;Subject&quot;] = &quot;multipart test&quot;
message[&quot;From&quot;] = sender_email
message[&quot;To&quot;] = receiver_email
## Write the plain text part
text = &quot;&quot;&quot;\ Hi, Check out the new post on the Mailtrap blog: SMTP Server for Testing: Cloud-based or Local? https://blog.mailtrap.io/2018/09/27/cloud-or-local-smtp-server/ Feel free to let us know what content would be useful for you!&quot;&quot;&quot;

## write the HTML part
html = &quot;&quot;&quot;\ &amp;lt;html&amp;gt; &amp;lt;body&amp;gt; &amp;lt;p&amp;gt;Hi,&amp;lt;br&amp;gt; Check out the new post on the Mailtrap blog:&amp;lt;/p&amp;gt; &amp;lt;p&amp;gt;&amp;lt;a href=&quot;https://blog.mailtrap.io/2018/09/27/cloud-or-local-smtp-server&quot;&amp;gt;SMTP Server for Testing: Cloud-based or Local?&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt; &amp;lt;p&amp;gt; Feel free to &amp;lt;strong&amp;gt;let us&amp;lt;/strong&amp;gt; know what content would be useful for you!&amp;lt;/p&amp;gt; &amp;lt;/body&amp;gt; &amp;lt;/html&amp;gt; &quot;&quot;&quot;

## convert both parts to MIMEText objects and add them to the MIMEMultipart message
part1 = MIMEText(text, &quot;plain&quot;)
part2 = MIMEText(html, &quot;html&quot;)
message.attach(part1)
message.attach(part2)

## send your email
with smtplib.SMTP(&quot;smtp.mailtrap.io&quot;, 2525) as server:
  server.login(login, password)
  server.sendmail( sender_email, receiver_email, message.as_string() )

print(&apos;Sent&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://lh6.googleusercontent.com/jRh9dfieiWa1JAH1o5eb62Pv4wPUgIPGWpyz5RJkcFaflS-JnWJ7nQfdkr5hp87iOoDT-dx9WyvPwngJsvQnMoe9iKqa7jg6hDklFOxaLeftGqNp8MgtE8YDS13UmLLkBeee5cPT&quot; alt=&quot;The resulting output&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Sending Emails with Attachments in Python&lt;/h3&gt;
&lt;p&gt;The next step in mastering sending emails with Python is attaching files. Attachments are still the MIME objects but we need to encode them with the &lt;em&gt;base64&lt;/em&gt; module. A couple of important points about the attachments:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Python lets you attach text files, images, audio files, and even applications. You just need to use the appropriate email class like &lt;code&gt;email.mime.audio.MIMEAudio&lt;/code&gt; or &lt;code&gt;email.mime.image.MIMEImage&lt;/code&gt;&lt;em&gt;.&lt;/em&gt; For the full information, refer to &lt;a href=&quot;https://docs.python.org/3/library/email.mime.html&quot;&gt;this section&lt;/a&gt; of the Python documentation.&lt;/li&gt;
&lt;li&gt;Remember about the file size: sending files over 20MB is a bad practice.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In transactional emails, the PDF files are the most frequently used: we usually get receipts, tickets, boarding passes, order confirmations, etc. So let’s review how to send a boarding pass as a PDF file.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import smtplib
from email import encoders
from email.mime.base import MIMEBase
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

port = 2525
smtp_server = &quot;smtp.mailtrap.io&quot;
login = &quot;1a2b3c4d5e6f7g&quot; # paste your login generated by Mailtrap
password = &quot;1a2b3c4d5e6f7g&quot; # paste your password generated by Mailtrap

subject = &quot;An example of boarding pass&quot;
sender_email = &quot;mailtrap@example.com&quot;
receiver_email = &quot;new@example.com&quot;

message = MIMEMultipart()
message[&quot;From&quot;] = sender_email
message[&quot;To&quot;] = receiver_email
message[&quot;Subject&quot;] = subject

## Add body to email
body = &quot;This is an example of how you can send a boarding pass in attachment with Python&quot;
message.attach(MIMEText(body, &quot;plain&quot;))

filename = &quot;yourBP.pdf&quot;
## Open PDF file in binary mode
## We assume that the file is in the directory where you run your Python script from
with open(filename, &quot;rb&quot;) as attachment:
## The content type &quot;application/octet-stream&quot; means that a MIME attachment is a binary file
part = MIMEBase(&quot;application&quot;, &quot;octet-stream&quot;)
part.set_payload(attachment.read())
## Encode to base64
encoders.encode_base64(part)
## Add header
part.add_header(&quot;Content-Disposition&quot;, f&quot;attachment; filename= {filename}&quot;)
## Add attachment to your message and convert it to string
message.attach(part)

text = message.as_string()
## send your email
with smtplib.SMTP(&quot;smtp.mailtrap.io&quot;, 2525) as server:
  server.login(login, password)
  server.sendmail(sender_email, receiver_email, text)

print(&apos;Sent&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://lh5.googleusercontent.com/xxqg_Ro8uggpJxjCKMmukQ2jJmwDeXasadM5HA0LeOUktOPYc-0iXp2xQZHkILyfdFWroJEz-UqgTr_zBEKISuydHmoqCAPrvikrC23VgCDawHBVH-9-ufmmfF556nsU-1vPJ2Ng&quot; alt=&quot;The received email with your PDF&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;To attach several files&lt;/strong&gt;, you can call the &lt;code&gt;message.attach()&lt;/code&gt; method several times.&lt;/p&gt;
&lt;h4&gt;How to send an email with image attachment&lt;/h4&gt;
&lt;p&gt;Images, even if they are a part of the message body, are attachments as well. There are three types of them: CID attachments (embedded as a MIME object), &lt;em&gt;base64&lt;/em&gt; images (inline embedding), and linked images.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;For adding a CID attachment,&lt;/strong&gt; we will create a MIME multipart message with &lt;code&gt;MIMEImage&lt;/code&gt; component:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import smtplib
from email.mime.text import MIMEText
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart

port = 2525
smtp_server = &quot;smtp.mailtrap.io&quot;
login = &quot;1a2b3c4d5e6f7g&quot; # paste your login generated by Mailtrap
password = &quot;1a2b3c4d5e6f7g&quot; # paste your password generated by Mailtrap

sender_email = &quot;mailtrap@example.com&quot;
receiver_email = &quot;new@example.com&quot;

message = MIMEMultipart(&quot;alternative&quot;)
message[&quot;Subject&quot;] = &quot;CID image test&quot;
message[&quot;From&quot;] = sender_email
message[&quot;To&quot;] = receiver_email

## write the HTML part
html = &quot;&quot;&quot;\
&amp;lt;html&amp;gt;
&amp;lt;body&amp;gt;
&amp;lt;img src=&quot;cid:myimage&quot;&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&quot;&quot;&quot;
part = MIMEText(html, &quot;html&quot;)
message.attach(part)

## We assume that the image file is in the same directory that you run your Python script from
with open(&apos;mailtrap.jpg&apos;, &apos;rb&apos;) as img:
  image = MIMEImage(img.read())
## Specify the  ID according to the img src in the HTML part
image.add_header(&apos;Content-ID&apos;, &apos;&amp;lt;myimage&amp;gt;&apos;)
message.attach(image)

## send your email
with smtplib.SMTP(&quot;smtp.mailtrap.io&quot;, 2525) as server:
  server.login(login, password)
  server.sendmail(sender_email, receiver_email, message.as_string())

print(&apos;Sent&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://lh4.googleusercontent.com/VzdSmA1lJli_ZX_m6KmJW7VW-am20z5Vr_RUxJP5ZHxC72fRImhDuZxEXV0o2mDr09JTEMzPykskHKWh1DuMLF_yoKl5eIsMiKpebmILpvYioDGfzU70hFjfxFIu-fPVZqWF7vc8&quot; alt=&quot;The received email with CID image&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The CID image is shown both as a part of the HTML message and as an attachment. Messages with this image type are often considered spam: check the &lt;em&gt;Analytics&lt;/em&gt; tab in Mailtrap to see the spam rate and recommendations on its improvement. Many email clients — Gmail in particular — don’t display CID images in most cases. So let’s review &lt;strong&gt;how to embed a base64 encoded image instead.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Here we will use &lt;em&gt;base64&lt;/em&gt; module and experiment with the same image file:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
import base64

port = 2525
smtp_server = &quot;smtp.mailtrap.io&quot;
login = &quot;1a2b3c4d5e6f7g&quot; # paste your login generated by Mailtrap
password = &quot;1a2b3c4d5e6f7g&quot; # paste your password generated by Mailtrap
sender_email = &quot;mailtrap@example.com&quot;
receiver_email = &quot;new@example.com&quot;

message = MIMEMultipart(&quot;alternative&quot;)
message[&quot;Subject&quot;] = &quot;inline embedding&quot;
message[&quot;From&quot;] = sender_email
message[&quot;To&quot;] = receiver_email

## We assume that the image file is in the same directory that you run your Python script from
with open(&quot;image.jpg&quot;, &quot;rb&quot;) as image:
  encoded = base64.b64encode(image.read()).decode()

html = f&quot;&quot;&quot;\
&amp;lt;html&amp;gt;
&amp;lt;body&amp;gt;
&amp;lt;img src=&quot;data:image/jpg;base64,{encoded}&quot;&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&quot;&quot;&quot;
part = MIMEText(html, &quot;html&quot;)
message.attach(part)

## send your email
with smtplib.SMTP(&quot;smtp.mailtrap.io&quot;, 2525) as server:
  server.login(login, password)
  server.sendmail(sender_email, receiver_email, message.as_string())

print(&apos;Sent&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://lh5.googleusercontent.com/zMSMgzypDp3lL1o1M21RB1nr6Dcc5Tekq8ucJktZqzWHynM8-YR2I4Ze6Rp7TkHtDxmcfYMyZXe1F_5sQihWL7kwpEFmQhnCRrDhe9aPjlJ0E7FzmdNvvibUOIU2yGqqC3U3ULEl&quot; alt=&quot;A base64 encoded image&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Now the image is embedded into the HTML message and is not available as an attached file. Python has encoded our JPEG image, and if we go to the &lt;em&gt;HTML Source&lt;/em&gt; tab, we will see the long image data string in the &lt;code&gt;img src&lt;/code&gt; attribute.&lt;/p&gt;
&lt;h3&gt;How to Send Multiple Emails&lt;/h3&gt;
&lt;p&gt;Sending multiple emails to different recipients and making them personal is the special thing about emails in Python.&lt;/p&gt;
&lt;p&gt;To add several more recipients, you can just type their addresses in separated by a comma, add &lt;code&gt;Cc&lt;/code&gt; and &lt;code&gt;Bcc&lt;/code&gt;. But if you work with a bulk email sending, Python will save you with loops.&lt;/p&gt;
&lt;p&gt;One of the options is to create a database in a &lt;em&gt;CSV&lt;/em&gt; format (we assume it is saved to the same folder as your Python script).&lt;/p&gt;
&lt;p&gt;We often see our names in transactional or even promotional examples. Here is how we can make it with Python.&lt;/p&gt;
&lt;p&gt;Let’s organize the list in a simple table with just two columns: name and email address. It should look like the following example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#name,email
John Johnson,john@johnson.com
Peter Peterson,peter@peterson.com
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The code below will open the file and loop over its rows line by line, replacing the &lt;code&gt;{name}&lt;/code&gt; with the value from the “name” column.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import csv
import smtplib

port = 2525
smtp_server = &quot;smtp.mailtrap.io&quot;
login = &quot;1a2b3c4d5e6f7g&quot; # paste your login generated by Mailtrap
password = &quot;1a2b3c4d5e6f7g&quot; # paste your password generated by Mailtrap

message = &quot;&quot;&quot;Subject: Order confirmation
To: {recipient}
From: {sender}
Hi {name}, thanks for your order! We are processing it now and will contact you soon&quot;&quot;&quot;
sender = &quot;new@example.com&quot;
with smtplib.SMTP(&quot;smtp.mailtrap.io&quot;, 2525) as server:
  server.login(login, password)
  with open(&quot;contacts.csv&quot;) as file:
  reader = csv.reader(file)
  next(reader)  # it skips the header row
  for name, email in reader:
    server.sendmail(
      sender,
      email,
      message.format(name=name, recipient=email, sender=sender),
    )
    print(f&apos;Sent to {name}&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In our Mailtrap inbox, we see two messages: one for John Johnson and another for Peter Peterson, delivered simultaneously:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://lh3.googleusercontent.com/Q6fRy7tMexzLqSsfAwEDdkZVh7Onb4impsLJkqLs40HsuVo43JV0eAUjJiWvxf-L0t9vdoTgEfeiN3MYX0wBU0vUKVZCRbmstlHk2RqvQWnPqr9WJbMX7LciUO9ebj89B5UZLrLd&quot; alt=&quot;Screenshot of Mailtrap inbox showing two emails sent to different recipients&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;&lt;/h3&gt;
&lt;p&gt;Sending emails with Python via Gmail&lt;/p&gt;
&lt;p&gt;When you are ready for sending emails to real recipients, you can configure your production server. It also depends on your needs, goals, and preferences: your localhost or any external SMTP.&lt;/p&gt;
&lt;p&gt;One of the most popular options is Gmail so let’s take a closer look at it.&lt;/p&gt;
&lt;p&gt;We can often see titles like “How to set up a Gmail account for development”. In fact, it means that you will create a new Gmail account and will use it for a particular purpose.&lt;/p&gt;
&lt;p&gt;To be able to send emails via your Gmail account, you need to provide access to it for your application. You can &lt;a href=&quot;https://myaccount.google.com/lesssecureapps&quot;&gt;&lt;em&gt;Allow less secure apps&lt;/em&gt;&lt;/a&gt; or take advantage of the &lt;a href=&quot;https://developers.google.com/gmail/api/quickstart/python&quot;&gt;OAuth2 authorization protocol&lt;/a&gt;. It’s a way more difficult but recommended due to the security reasons.&lt;/p&gt;
&lt;p&gt;Further, to use a Gmail server, you need to know:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the server name = &lt;em&gt;smtp.gmail.com&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;port = &lt;em&gt;465 for SSL/TLS&lt;/em&gt; connection (preferred)&lt;/li&gt;
&lt;li&gt;or port = &lt;em&gt;587 for STARTTLS&lt;/em&gt; connection&lt;/li&gt;
&lt;li&gt;username = your Gmail email address&lt;/li&gt;
&lt;li&gt;password = your password&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;import smtplib
import ssl

port = 465
password = input(&quot;your password&quot;)
context = ssl.create_default_context()

with smtplib.SMTP_SSL(&quot;smtp.gmail.com&quot;, port, context=context) as server:
  server.login(&quot;my@gmail.com&quot;, password)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you tend to simplicity, then you can use &lt;a href=&quot;https://pypi.org/project/yagmail/&quot;&gt;Yagmail&lt;/a&gt;, the dedicated Gmail/SMTP. It makes email sending really easy. Just compare the above examples with these several lines of code:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import yagmail

yag = yagmail.SMTP()
contents = [
&quot;This is the body, and here is just text http://somedomain/image.png&quot;,
&quot;You can find an audio file attached.&quot;, &apos;/local/path/to/song.mp3&apos;
]
yag.send(&apos;to@someone.com&apos;, &apos;subject&apos;, contents)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Next steps with Python&lt;/h3&gt;
&lt;p&gt;Those are just basic options of sending emails with Python. To get great results, review the Python documentation and experiment with your own code!&lt;/p&gt;
&lt;p&gt;There are a bunch of various Python frameworks and libraries, which make creating apps more elegant and dedicated. In particular, some of them can help improve your experience with building emails sending functionality:&lt;/p&gt;
&lt;p&gt;The most popular frameworks are:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Flask, which offers a simple interface for email sending: Flask Mail.&lt;/li&gt;
&lt;li&gt;Django, which can be a great option for building HTML templates.&lt;/li&gt;
&lt;li&gt;Zope comes in handy for a website development.&lt;/li&gt;
&lt;li&gt;Marrow Mailer is a dedicated mail delivery framework adding various helpful configurations.&lt;/li&gt;
&lt;li&gt;Plotly and its Dash can help with mailing graphs and reports.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Also, here is a &lt;a href=&quot;https://awesome-python.com/&quot;&gt;handy list&lt;/a&gt; of Python resources sorted by their functionality.&lt;/p&gt;
&lt;p&gt;Good luck and don’t forget to stay on the safe side when sending your emails!&lt;/p&gt;
&lt;p&gt;&lt;em&gt;This article was originally published at Mailtrap’s blog: &lt;a href=&quot;https://blog.mailtrap.io/sending-emails-in-python-tutorial-with-code-examples/&quot;&gt;Sending emails with Python&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
</content:encoded><category>python</category><category>email</category></item><item><title>Handling multipart/form-data natively in Python</title><link>https://julien.danjou.info/blog/handling-multipart-form-data-python/</link><guid isPermaLink="true">https://julien.danjou.info/blog/handling-multipart-form-data-python/</guid><description>RFC7578 (who obsoletes RFC2388) defines the multipart/form-data type that is usually transported over HTTP when users submit forms on your Web page.</description><pubDate>Mon, 01 Jul 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://tools.ietf.org/html/rfc7578&quot;&gt;RFC7578&lt;/a&gt; (who obsoletes &lt;a href=&quot;https://tools.ietf.org/html/rfc2388&quot;&gt;RFC2388&lt;/a&gt;) defines the &lt;code&gt;multipart/form-data&lt;/code&gt; type that is usually transported over HTTP when users submit forms on your Web page. Nowadays, it tends to be replaced by JSON encoded payloads; nevertheless, it is still widely used.&lt;/p&gt;
&lt;p&gt;While you could decode an HTTP body request made with JSON natively with Python — thanks to the &lt;code&gt;json&lt;/code&gt; module — there is no such way to do that with &lt;code&gt;multipart/form-data&lt;/code&gt;. That&apos;s something barely understandable considering how old the format is.&lt;/p&gt;
&lt;p&gt;There is a wide variety of way available to encode and decode this format. Libraries such as &lt;em&gt;requests&lt;/em&gt; support this natively without making you notice, and the same goes for the majority of Web server frameworks such as &lt;em&gt;Django&lt;/em&gt; or &lt;em&gt;Flask&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;However, in certain circumstances, you might be on your own to encode or decode this format, and it might not be an option to pull (significant) dependencies.&lt;/p&gt;
&lt;h2&gt;Encoding&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;multipart/form-data&lt;/code&gt; format is quite simple to understand and can be summarised as an easy way to encode a list of keys and values, i.e., a portable way of serializing a dictionary.&lt;/p&gt;
&lt;p&gt;There&apos;s nothing in Python to generate such an encoding. The format is quite simple and consists of the key and value surrounded by a random boundary delimiter. This delimiter must be passed as part of the &lt;code&gt;Content-Type&lt;/code&gt;, so that the decoder can decode the form data.&lt;/p&gt;
&lt;p&gt;There&apos;s a simple implementation in &lt;em&gt;urllib3&lt;/em&gt; that does the job. It&apos;s possible to summarize it in this simple implementation:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import binascii
import os

def encode_multipart_formdata(fields):
    boundary = binascii.hexlify(os.urandom(16)).decode(&apos;ascii&apos;)

    body = (
        &quot;&quot;.join(&quot;--%s\r\n&quot;
                &quot;Content-Disposition: form-data; name=\&quot;%s\&quot;\r\n&quot;
                &quot;\r\n&quot;
                &quot;%s\r\n&quot; % (boundary, field, value)
                for field, value in fields.items()) +
        &quot;--%s--\r\n&quot; % boundary
    )

    content_type = &quot;multipart/form-data; boundary=%s&quot; % boundary

    return body, content_type
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can use by passing a dictionary where keys and values are bytes. For example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;encode_multipart_formdata({&quot;foo&quot;: &quot;bar&quot;, &quot;name&quot;: &quot;jd&quot;})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Which returns:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;--00252461d3ab8ff5c25834e0bffd6f70
Content-Disposition: form-data; name=&quot;foo&quot;

bar
--00252461d3ab8ff5c25834e0bffd6f70
Content-Disposition: form-data; name=&quot;name&quot;

jd
--00252461d3ab8ff5c25834e0bffd6f70--
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;multipart/form-data; boundary=00252461d3ab8ff5c25834e0bffd6f70
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can use the returned content type in your HTTP reply header &lt;code&gt;Content-Type&lt;/code&gt;. Note that this format is used for forms: it can also be used by emails.&lt;/p&gt;
&lt;p&gt;Emails did you say?&lt;/p&gt;
&lt;h2&gt;Encoding with &lt;code&gt;email&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Right, emails are usually encoded using MIME, which is defined by yet another RFC, &lt;a href=&quot;https://tools.ietf.org/html/rfc2046&quot;&gt;RFC2046&lt;/a&gt;. It turns out that &lt;code&gt;multipart/form-data&lt;/code&gt; is just a particular MIME format, and that if you have code that implements MIME handling, it&apos;s easy to use it to implement this format.&lt;/p&gt;
&lt;p&gt;Fortunately for us, Python standard library comes with a module that handles exactly that: &lt;code&gt;email.mime&lt;/code&gt;. I told you it was heavily used by email — I guess that&apos;s why they put that code in the &lt;code&gt;email&lt;/code&gt; subpackage.&lt;/p&gt;
&lt;p&gt;Here&apos;s a piece of code that handles &lt;code&gt;multipart/form-data&lt;/code&gt; in a few lines of code:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from email import message
from email.mime import multipart
from email.mime import nonmultipart
from email.mime import text

class MIMEFormdata(nonmultipart.MIMENonMultipart):
    def __init__(self, keyname, *args, **kwargs):
        super(MIMEFormdata, self).__init__(*args, **kwargs)
        self.add_header(
            &quot;Content-Disposition&quot;, &quot;form-data; name=\&quot;%s\&quot;&quot; % keyname)

def encode_multipart_formdata(fields):
    m = multipart.MIMEMultipart(&quot;form-data&quot;)

    for field, value in fields.items():
        data = MIMEFormdata(field, &quot;text&quot;, &quot;plain&quot;)
        data.set_payload(value)
        m.attach(data)

    return m
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Using this piece of code returns the following:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Content-Type: multipart/form-data; boundary=&quot;===============1107021068307284864==&quot;
MIME-Version: 1.0

--===============1107021068307284864==
Content-Type: text/plain
MIME-Version: 1.0
Content-Disposition: form-data; name=&quot;foo&quot;

bar
--===============1107021068307284864==
Content-Type: text/plain
MIME-Version: 1.0
Content-Disposition: form-data; name=&quot;name&quot;

jd
--===============1107021068307284864==--
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This method has several advantages over our first implementation:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It handles &lt;code&gt;Content-Type&lt;/code&gt; for each of the added MIME parts. We could add other data types than just &lt;code&gt;text/plain&lt;/code&gt; like it is implicitly done in the first version. We could also specify the charset (encoding) of the textual data.&lt;/li&gt;
&lt;li&gt;It&apos;s very likely more robust by leveraging the wildly tested Python standard library.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The main downside, in that case, is that the &lt;code&gt;Content-Type&lt;/code&gt; header is included with the content. In case of handling HTTP, it is problematic as this needs to be sent as part of the HTTP header and not as part of the payload.&lt;/p&gt;
&lt;p&gt;It should be possible to build a particular generator from &lt;code&gt;email.generator&lt;/code&gt; that does this. I&apos;ll leave that as an exercise to you, reader.&lt;/p&gt;
&lt;h2&gt;Decoding&lt;/h2&gt;
&lt;p&gt;We must be able to use that same &lt;code&gt;email&lt;/code&gt; package to decode our encoded data, right? It turns out that&apos;s the case, with a piece of code that looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import email.parser

msg = email.parser.BytesParser().parsebytes(my_multipart_data)

print({
    part.get_param(&apos;name&apos;, header=&apos;content-disposition&apos;): part.get_payload(decode=True)
    for part in msg.get_payload()
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With the example data above, this returns:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{&apos;foo&apos;: b&apos;bar&apos;, &apos;name&apos;: b&apos;jd&apos;}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Amazing, right?&lt;/p&gt;
&lt;p&gt;The moral of this story is that you should never underestimate the power of the standard library. While it&apos;s easy to add a single line in your list of dependencies, it&apos;s not always required if you dig a bit into what Python provides for you!&lt;/p&gt;
</content:encoded><category>python</category><category>email</category><category>web</category></item><item><title>mod_defensible 1.5 released</title><link>https://julien.danjou.info/blog/mod_defensible-1-5/</link><guid isPermaLink="true">https://julien.danjou.info/blog/mod_defensible-1-5/</guid><description>Apache 2.4 being out, I noticed that my good old mod defensible did not compile anymore.</description><pubDate>Tue, 03 Apr 2012 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Apache 2.4 being out, I noticed that my good old &lt;a href=&quot;http://github.com/jd/mod_defensible&quot;&gt;mod_defensible&lt;/a&gt; did not compile anymore.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;http://httpd.apache.org/docs/2.4/developer/new_api_2_4.html&quot;&gt;changes in the new Apache 2.4 API&lt;/a&gt; were small for its concern, so it was pretty easy to update this software to make it compile again.&lt;/p&gt;
&lt;p&gt;Honestly, I&apos;m not sure that this module is really used into the wild, but I still think that it can serve as a good prototype for doing other things so I like keeping it around. :-)&lt;/p&gt;
&lt;p&gt;All this has been triggered by the Apache 2.4 arrival into Debian experimental. Therefore I&apos;ve updated the mod_defensible package to use the new dh_apache2, and imported it into Git at the same time.&lt;/p&gt;
</content:encoded><category>web</category><category>email</category><category>debian</category></item><item><title>Using advanced filter with mod_authnz_ldap</title><link>https://julien.danjou.info/blog/using-advanced-filter-with-mod_authnz_ldap/</link><guid isPermaLink="true">https://julien.danjou.info/blog/using-advanced-filter-with-mod_authnz_ldap/</guid><description>How to work around mod_authnz_ldap&apos;s limited filtering by using a custom LDAP filter for Apache authentication.</description><pubDate>Mon, 04 Apr 2011 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;As you may know, Apache&apos;s &lt;a href=&quot;http://httpd.apache.org/docs/2.2/mod/mod_authnz_ldap.html&quot;&gt;mod_authzn_ldap&lt;/a&gt; allows to authenticate users in Apache HTTP server using an LDAP server. Unfortunately, it has a little implementation flaw.&lt;/p&gt;
&lt;p&gt;The filter used to authenticate the user is built by abusing the &lt;a href=&quot;http://www.ietf.org/rfc/rfc2255.txt&quot;&gt;RFC 2255&lt;/a&gt; which specifies the LDAP URL format. This format has an &quot;attribute&quot; field which is normally used to specify which attributes should be returned. But &lt;em&gt;mod_authzn_ldap&lt;/em&gt; uses this attribute to compare with the username given by the client. That means that you have to have an attribute in your LDAP entries which matches the username, and you have to use it in the &quot;attribute&quot; part of the URL to get things working.&lt;/p&gt;
&lt;p&gt;Therefore, I wrote a patch to add a format string in the LDAP URL in order to user the provided username in the filter, and ignore the attribute part of the URL, which has no use in such a context anyway.&lt;/p&gt;
&lt;p&gt;The bug has been opened in ASF Bugzilla and has number &lt;a href=&quot;https://issues.apache.org/bugzilla/show_bug.cgi?id=51005&quot;&gt;#51005&lt;/a&gt;, with the patch. The patch is backward compatible with the current configuration format, which is not the best choice in theory, but probably the more pragmatic.&lt;/p&gt;
&lt;p&gt;I&apos;ve no clue on the typical delay for patches inclusion in Apache HTTP&lt;br /&gt;
server, so let&apos;s just wait&apos;n see.&lt;/p&gt;
</content:encoded><category>web</category><category>email</category></item><item><title>Why notmuch is not much good</title><link>https://julien.danjou.info/blog/why-notmuch-is-not-much-good/</link><guid isPermaLink="true">https://julien.danjou.info/blog/why-notmuch-is-not-much-good/</guid><description>I&apos;ve recently got a mail from one of my faithful reader, asking why not considering notmuch.</description><pubDate>Thu, 07 Oct 2010 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I&apos;ve recently got a mail from one of my faithful reader, asking why not considering &lt;a href=&quot;http://notmuchmail.org/&quot;&gt;notmuch&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Actually, I think notmuch already exists in a better way, and that&apos;s called&lt;br /&gt;
&lt;a href=&quot;http://en.wikipedia.org/wiki/Internet_Message_Access_Protocol&quot;&gt;IMAP&lt;/a&gt; and &lt;a href=&quot;http://en.wikipedia.org/wiki/Sieve_(mail_filtering_language)&quot;&gt;Sieve&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;What &lt;em&gt;notmuch&lt;/em&gt; does, is tagging your mail with tags (obviously), based on filtering rules you write. The big downside is that you have to tag all your mails on your computer.&lt;/p&gt;
&lt;p&gt;And if you use several computers, you&apos;ll have to tag several times your mails. And you&apos;ll have to find a way to maintain your rules to be identical on all your computers. That does not scale.&lt;/p&gt;
&lt;p&gt;Using Sieve for mail filtering, one can do already that actually, and much&lt;br /&gt;
more.&lt;/p&gt;
&lt;p&gt;A &lt;em&gt;notmuch&lt;/em&gt; rule like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;notmuch tag +intel from:intel.com and not tag:intel
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Can be written as a Sieve rule like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;if address :all :contains &quot;From&quot; &quot;intel.com&quot; {
	addflag &quot;intel&quot;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The flags extension for Sieve is explained in &lt;a href=&quot;http://tools.ietf.org/html/rfc5232&quot;&gt;RFC5232&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The Sieve based solution has the advantage of being treated server side, and therefore not subject to multiple or different MUA usages. It&apos;s also fast, if you use a good IMAP server like &lt;a href=&quot;http://www.dovecot.org&quot;&gt;Dovecot&lt;/a&gt;, which has indexing, etc.&lt;/p&gt;
&lt;p&gt;Furthermore, Sieve can obviously do a lot more than tagging, like splitting into different mailboxes, filtering with regexp usage, vacation, etc.&lt;/p&gt;
&lt;p&gt;And if you want to fetch your mail locally, you can synchronize the IMAP box entirely with any software able to (like &lt;a href=&quot;http://github.com/jgoerzen/offlineimap/wiki&quot;&gt;OfflineIMAP&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Now, what&apos;s probably missing, is a correct support for IMAP flags on various MUA around. But that&apos;s not something &lt;em&gt;notmuch&lt;/em&gt; helps to solve either. :-)&lt;/p&gt;
</content:encoded><category>email</category></item><item><title>Emacs and OfflineIMAP</title><link>https://julien.danjou.info/blog/emacs-and-offlineimap/</link><guid isPermaLink="true">https://julien.danjou.info/blog/emacs-and-offlineimap/</guid><description>I recently decided to use OfflineIMAP to synchronize my mails on my laptop. It&apos;s a great piece of software, and allows me to read my mail while I&apos;m offline.</description><pubDate>Fri, 03 Sep 2010 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I recently decided to use &lt;a href=&quot;http://wiki.github.com/jgoerzen/offlineimap/&quot;&gt;OfflineIMAP&lt;/a&gt; to synchronize my mails on my laptop. It&apos;s a great piece of software, and allows me to read my mail while I&apos;m offline.&lt;/p&gt;
&lt;p&gt;I use it with &lt;a href=&quot;http://www.gnus.org&quot;&gt;Gnus&lt;/a&gt;, of course. But I lacked a proper way to integrate OfflineIMAP with it, so I decided to write a little Emacs extension to run and monitor OfflineIMAP directly from Emacs.&lt;/p&gt;
&lt;p&gt;Here comes &lt;a href=&quot;https://github.com/jd/offlineimap.el&quot;&gt;offlineimap.el&lt;/a&gt;, an Emacs extension to run OfflineIMAP directly within Emacs. It will display OfflineIMAP output in a buffer, and optionally shows the current OfflineIMAP operation in the mode line.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://julien.danjou.info/content/images/03/offlineimap-el-1.png&quot; alt=&quot;offlineimap-el-1&quot; /&gt;&lt;/p&gt;
&lt;p&gt;By default the status is in the mode line only if you are in the Gnus group buffer. But that&apos;s customizable, of course, since this is Emacs!&lt;/p&gt;
&lt;p&gt;If you are using &lt;a href=&quot;http://github.com/dimitri/el-get&quot;&gt;el-get&lt;/a&gt;, there&apos;s already a recipe to install it!&lt;/p&gt;
</content:encoded><category>emacs</category><category>email</category></item><item><title>Courier to Dovecot migration</title><link>https://julien.danjou.info/blog/courier-to-dovecot-migration/</link><guid isPermaLink="true">https://julien.danjou.info/blog/courier-to-dovecot-migration/</guid><description>This week, I&apos;ve managed to migrate from courier-imap to dovecot at work. I always had a good experience with dovecot, and I still have one.</description><pubDate>Fri, 02 Oct 2009 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This week, I&apos;ve managed to migrate from &lt;a href=&quot;http://www.courier-mta.org/imap/&quot;&gt;courier-imap&lt;/a&gt; to &lt;a href=&quot;http://www.dovecot.org&quot;&gt;dovecot&lt;/a&gt; at work. I always had a good experience with dovecot, and I still have one.&lt;/p&gt;
&lt;p&gt;Dovecot performances are very good in comparison with courier. With that switch, we dropped the CPU usage of the server from 25 % to 10 %, and it&apos;s damn faster now. I have no idea why, but I think that it&apos;s better written looking at the code, and also that its usage of index files helps a lot.&lt;/p&gt;
&lt;p&gt;We got no problem getting things work with public folders either, so the switch was almost painless.&lt;/p&gt;
&lt;p&gt;The only problem we had is that Dovecot is too smart for some MUA. Consequently, we hit an &lt;a href=&quot;http://dev.mutt.org/trac/ticket/969&quot;&gt;8 years old Mutt bug #969&lt;/a&gt;, which I also reported to the Debian BTS as &lt;a href=&quot;http://bugs.debian.org/549204&quot;&gt;#549204&lt;/a&gt; with a not-well-tested-but-seems-to-work patch.&lt;/p&gt;
&lt;p&gt;Thanks to &lt;a href=&quot;http://www.claws-mail.org/&quot;&gt;Claws mail&lt;/a&gt;, we also found a &lt;a href=&quot;http://dovecot.org/pipermail/dovecot/2009-October/043236.html&quot;&gt;bug in dovecot 1.2.5&lt;/a&gt;, which should be fixed soon. Dovecot upstream is very responsive and that&apos;s always something nice to know when you use a free software.&lt;/p&gt;
</content:encoded><category>email</category></item><item><title>Upgrading to dovecot 1.2: hello Sieve!</title><link>https://julien.danjou.info/blog/upgrading-to-dovecot-1-2-hello-sieve/</link><guid isPermaLink="true">https://julien.danjou.info/blog/upgrading-to-dovecot-1-2-hello-sieve/</guid><description>Last year, I told you I wanted to use Sieve) to filter my mail. I did not switch, because of the lacking implementation of some Sieve features inside Dovecot, my preferred IMAP server.</description><pubDate>Thu, 09 Jul 2009 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Last year, I told you I wanted to use &lt;a href=&quot;http://en.wikipedia.org/wiki/Sieve_(mail_filtering_language)&quot;&gt;Sieve&lt;/a&gt; to filter my mail. I did not switch, because of the lacking implementation of some Sieve features inside &lt;a href=&quot;http://www.dovecot.org&quot;&gt;Dovecot&lt;/a&gt;, my preferred IMAP server.&lt;/p&gt;
&lt;p&gt;After that disapointement, I kept my 8 years old mail setup, being &lt;em&gt;fetchmail&lt;/em&gt; running on my workstation and throwing the mails in &lt;em&gt;procmail&lt;/em&gt;, then using &lt;em&gt;mutt&lt;/em&gt; locally to read the maildirs. But that&apos;s over.&lt;/p&gt;
&lt;p&gt;I got a laptop to replace my workstation. It was not possible to continue using such a mail setup, since my laptop can be offline, and so would be my mails.&lt;/p&gt;
&lt;p&gt;So I decided to upgrade Dovecot to 1.2. I used the &lt;em&gt;dovecot-1.2-work&lt;/em&gt; Subversion branch of our lovely Debian maintainers, and built a Debian package for Lenny. The upgrade from 1.1 was almost painless, since the configuration file did not change heavily.&lt;/p&gt;
&lt;p&gt;Then I started to write my little Sieve script. Sieve is a very nice language. Almost user friendly. So in 20 lines I rewrote all my procmail stuff, matching things like &lt;em&gt;List-Id&lt;/em&gt; with regex to put the mails automagically in the right folder. I reconfigured &lt;em&gt;mutt&lt;/em&gt; to use IMAP, and it works fine. I even reimported my old Maildir via IMAP using &lt;em&gt;mutt&lt;/em&gt; too.&lt;/p&gt;
&lt;p&gt;I am now a happy IMAP user.&lt;/p&gt;
&lt;p&gt;For people wondering why I wanted to switch away from &lt;em&gt;procmail&lt;/em&gt; to &lt;em&gt;Sieve&lt;/em&gt;: the reason is that Sieve script can be uploaded remotely via &lt;em&gt;managesieve&lt;/em&gt;. This means you do not need FTP/SSH/whatever access to put your script. You can, for example, use &lt;em&gt;connect-sieve&lt;/em&gt; or the Sieve plugin for Thunderbird/Icedove.&lt;/p&gt;
</content:encoded><category>email</category></item><item><title>Kicking out Web spammers with DNSBL</title><link>https://julien.danjou.info/blog/kicking-out-web-spammers-with-dnsbl/</link><guid isPermaLink="true">https://julien.danjou.info/blog/kicking-out-web-spammers-with-dnsbl/</guid><description>Every project has its story. Every war has its winner, and its casualties. They were 20 millions men, fighting for their freedom.  And you&apos;ll never know their story.  Because during last week, I was l</description><pubDate>Mon, 15 Jan 2007 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Every project has its story. Every war has its winner, and its casualties. They were 20 millions men, fighting for their freedom.&lt;/p&gt;
&lt;p&gt;And you&apos;ll never know their story.&lt;/p&gt;
&lt;p&gt;Because during last week, I was looking why my Web server was so heavily loaded. And I discovered that my blog was attacked by spammers trying to post comments. They were stopped by a great plug-in named &lt;em&gt;spamplemousse&lt;/em&gt;, which use spam keywords and DNSBL to drop spam comments. However, this plug-in is written in PHP, like the rest of my blog, so it loads Apache and MySQL in a way that is no more acceptable: the page have still to be rendered for this !@#$ spammers.&lt;/p&gt;
&lt;p&gt;Consequently, I decided to write a Apache 2.x module which will just drop a &lt;em&gt;403 Forbidden&lt;/em&gt; error page in the spammers&apos; head using DNSBL servers. Here it is, and it is called &lt;a href=&quot;https://github.com/jd/mod_defensible&quot;&gt;mod_defensible&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I&apos;m using it since 3 days now, and I got some pretty interesting result and less load on my Web server, so &lt;em&gt;c&apos;est tout bon&lt;/em&gt;.&lt;/p&gt;
</content:encoded><category>web</category><category>email</category><category>security</category></item></channel></rss>