simple example of mimelib? and embedding (not attaching) images in email sent with python.

Oleg Broytmann phd at
Tue Aug 14 16:08:31 CEST 2001

On 14 Aug 2001, Stephen wrote:
> in Microsoft Outlook, it's possible to embed image files into
> the actual message as opposed to having them attached.  This way, they
> display when the recipient views the message.  Just how does one compose
> a message like that with Python may I ask?

   Though it looks like "embedding", it is really attachment. It is just a
special attachment, with IDs that referenced in main body, so viewr can
later find corresponding images. Not only M$ Outloose (how else do you call
this buggy virus-prone thingy), but any mailer that can display/send HTML
can embed images this way.
   Attached is a program that sends HTML with embeded images. It takes 4
mail parameters from command line, plus email addresses and reads HTML from
stdin. It parses HTML, download images from the Web (using urllib, of
course), and generates MIME message with main body (HTML) and attached
images. Attachments marked exactly the way you need: images in the HTML
marked with "cid", and attachments marked with Content-ID headers.
   Last time I tested it (about 3 years ago) all browsers displayed
generated HTML with embedded images: Netscape, M$ Mail, Outloose.

     Oleg Broytmann              phd at
           Programmers don't die, they just GOSUB without RETURN.
-------------- next part --------------
#! /usr/local/bin/python -O

# mail-html: send MIME message with attached HTML (come from stdin)
# and GIFs (GIFs are retrieved by HTTP/FTP/etc)
# to addresses listed on command line
# Initial Author: Oleg Broytmann <phd at>
# $Id: mail-html,v 1.9 2000/01/17 11:16:17 ran Exp $

import sys, os, string
from sgmllib import SGMLParser

from mimetools import choose_boundary
from MimeWriter import MimeWriter

from StringIO import StringIO
import base64

import urllib
urllib._urlopener = urllib.URLopener()
from urllib import urlretrieve

def join_attrs(attrs):
   attr_list = []
   for attrname, value in attrs:
      attr_list.append('%s="%s"' % (attrname, string.strip(value)))

   if attr_list:
      s = " " + string.join(attr_list, " ")
      s = ""

   return s

images = {}

def get_image(name):
   if images.has_key(name):
      return images[name][0] # Image already there - return its cid

      global base
         fname, headers = urlretrieve("%s/%s" % (base, name))
         exc_type, exc_value, exc_tb = sys.exc_info()
         return "Error=%s/%s" % (exc_type, exc_value)

      datafile = open(fname, 'r')
      data =

      cid = choose_boundary() # I need a random string as a Content-ID; MIME boundary seems appropriate
      images[name] = (cid, data)
      return cid

class IMG_Parser(SGMLParser):
   def __init__(self):
      self.saved_data = ""

   def handle_data(self, data):
      if data:
         self.saved_data = self.saved_data + data

   def handle_comment(self, data):
      if data:
         self.saved_data = self.saved_data + "<!--%s-->" % data

   def do_img(self, attrs):
      new_attr = []
      for attr in attrs:
         a = [attr[0]]
         if attr[0] == 'src':
            cid = get_image(attr[1])
            a.append("cid:%s" % cid)

      s = join_attrs(new_attr)
      self.saved_data = self.saved_data + "<IMG%s>" % s

   def unknown_starttag(self, tag, attrs):
      self.saved_data = self.saved_data + "<%s%s>" % (string.upper(tag), join_attrs(attrs))

   def unknown_endtag(self, tag):
      self.saved_data = self.saved_data + "</%s>" % string.upper(tag)

def parse_html():
   parser = IMG_Parser()

   infile = sys.stdin
   while 1:
      line = infile.readline()
      if not line: break


   return parser.saved_data

def gen_mime(html, subj, charset, email_from, email_to):
   #sendmail = open("sendmail", 'w')
   sendmail = os.popen("/usr/sbin/sendmail -f '%s' %s" % (email_from, email_to), 'w')
   sendmail.write("""From: %s
To: %s
Subject: %s
Precedence: bulk
MIME-Version: 1.0
""" % (email_from, email_to, subj))

   mime = MimeWriter(sendmail)

   firstpart = mime.startmultipartbody("related")
   Your mailer does not support MIME encoding. Please upgarde to MIME-enabled
mailer (almost every modern mailer is MIME-capable).

   subwriter = mime.nextpart()
   subwriter.addheader("Content-Transfer-Encoding", "base64")
   subwriter.startbody("text/html; charset=%s" % charset)
   base64.encode(StringIO(html), sendmail)

   for name in images.keys():
      image = images[name]

      subwriter = mime.nextpart()
      subwriter.addheader("Content-Transfer-Encoding", "base64")
      subwriter.addheader("Content-Disposition", 'inline; filename="%s"' % name)
      subwriter.addheader("Content-ID", "<%s>" % image[0])

      base64.encode(StringIO(image[1]), sendmail)


def run():
   global base
   base, subj, charset, email_from = sys.argv[1:5]
   addrs = string.join(sys.argv[5:], " ")

   html = parse_html()
   gen_mime(html, subj, charset, email_from, addrs)

if __name__ == '__main__':

More information about the Python-list mailing list