Suneido

Integrated Application Platform

  • Home
  • Learning
    • Suneido Manual
    • Beginners
      • Inside Suneido
      • The Suneido Programming Language
      • The Suneido Database
      • Installing Suneido
      • Building Suneido
      • IDE Go To Tour
      • Upgrading To A New Release
    • Advanced
      • Canvas Control
      • DrawControl Part 1
      • DrawControl Part 2
      • DrawControl Part 3
      • SAX Like XML Processing
      • A Plug-In Architecture
      • A Simple Wizard Framework
      • An HTML Include Facility
      • An RSS 2 Feed Creator
      • MIME Generation
      • A New Add-on Facility
      • Workspace Improvement Hack
    • Mockito for Suneido
    • The Suneido Task Scheduler
    • Contributing To Suneido
    • Contributor Assignment of Copyright
    • Language Translation
    • Future Directions
    • Interview With Andrew Mckinlay
  • Forum
    • Announcements
    • Internals & Enhancements
    • Cookbook
    • General
  • FAQ
  • Screenshots
  • Downloads
  • Links

From the Couch 10 – MIME Generation

Multipurpose Internet Mail Extensions (MIME) is an Internet Standard that extends the format of e-mail to support non-text attachments and multi-part message bodies.

I added some MIME support to Suneido so I could send emails with attachments from our applications.

I based the design on the Python email module as described in Foundations of Python Network Programming, however I did not try to copy it exactly.

Here’s an example of generating a simple plain text message:

MimeText("hello world").
    To("joe@hotmail.com").
    From("sue@mail.com").
    Subject("html test").
    Date().
    Message_ID().
    ToString()

would produce:

Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
To: joe@hotmail.com
From: sue@mail.com
Date: Fri, 19 Oct 2007 14:41:02 -0600
Subject: html test
Message-ID: <2d91ef3e.5685.4d7c.b664.cdac914677f0@suneido.com>

hello world

And here’s an example of generating a message with an attachment:

MimeMultiPart().
    To("joe@hotmail.com").
    From("sue@mail.com").
    Subject("multipart test").
    Date().
    Message_ID().
    AttachFile("image.jpg").
    ToString()<pre>
 The code is in several classes. Here is the base class MimeBase
<pre class="prettyprint">
 class
	{
	New(maintype = 'text', subtype = 'plain')
		{
		.hdr = Object().Set_default(false)
		.extra = Object().Set_default('')
		.maintype = maintype
		.subtype = subtype
		.fields = .fields.Copy()
		}
	payload: ''
	SetPayload(s)
		{
		.payload = s
		return this
		}
	fields: ('Content-Transfer-Encoding', To, From, Date, Subject,
		'Message-ID')
	Default(@args)
		{
		field = args[0].Tr('_', '-')
		if .fields.Has?(field)
			{
			if args.Size() is 1
				{
				if field is 'Date'
					value = Date()
				else if field is 'Message-ID'
					value = .message_id()
				}
			else
				value = args[1]
			.AddHeader(field, value)
			}
		else
			throw "MimeText: method not found: " $ args[0]
		return this
		}
	AddHeader(@args)
		{
		name = args[0]
		value = args[1]
		.hdr[name] = Date?(value) ? .date(value) : value
		.fields.AddUnique(name)
		if args.Size() is 3
			{
			m = args.Members()[2]
			.extra[name] = '; ' $ m $ '=' $ Display(args[m])
			}
		return this
		}
	AddExtra(@args)
		{
		name = args.Members()[1]
		value = args[name]
		.extra[args[0]] = '; ' $ name $ '=' $ Display(value)
		return this
		}
	date(date)
		{
		return date.Format("ddd, d MMM yyyy HH:mm:ss") $
			' -' $ (Date.GetLocalGMTBias() / 60).Pad(2) $ '00'
		}
	message_id()
		{
		return '<' $ UuidString().Tr('-', '.') $ '@suneido.com' $ '>'
		}
	encode(s) { s }
	Base64()
		{
		.AddHeader('Content-Transfer-Encoding', 'base64')
		.encode = Base64.EncodeLines
		return this
		}
	ToString()
		{
		s = 'Content-Type: ' $ .maintype $ '/' $ .subtype $
				.extra['Content-Type'] $ '\r\n' $
			'MIME-Version: 1.0\r\n'
		for f in .fields
			if .hdr.Member?(f)
				s $= f $ ': ' $ .hdr[f] $
					.extra[f] $ '\r\n'
		s $= '\r\n'
		s $= (.encode)(.payload)
		if s.Substr(-2) isnt '\r\n'
			s $= '\r\n'
		return s
		}
	}

This is mostly straightforward. One "trick" is using the Default method to handle methods for the standard headers. This avoids having to define separate methods for each. Since method names don't allow dashes (-), undescores are converted to dashes. Date and Message-ID are handled specially to provide default values. Note: These are shortcut convenience methods - you could use AddHeader.

The AddExtra method is used to add "extra" information to an existing header field e.g. to add a charset value to Content-Type. Extra information is also handled by AddHeader e.g. AddHeader("Content-Disposition", "attachment", filename: "image.jpg")

Most of the methods return "this" to allow "chaining method calls as in the examples above.

This code requires a new EncodeLines method in Base64

	EncodeLines(src, eol = '\r\n', linelen = 70)
		{
		src = .Encode(src)
		for (dst = ""; src isnt ""; src = src.Substr(linelen))
			dst $= src.Substr(0, linelen) $ eol
		return dst
		}

Text messages do not require much more than MimeBase so MimeText is quite small:

MimeBase
	{
	New(text = "", subtype = "plain", charset = "us-ascii")
		{
		super('text', subtype)
		.AddExtra('Content-Type', charset: charset)
		.Content_Transfer_Encoding('7bit')
		.SetPayload(text.ChangeEol('\r\n'))
		}
	}
Multipart (and alternative) messages are also quite simple:

MimeBase
	{
	New(subtype = 'mixed')
		{
		super('multipart', subtype)
		.parts = Object()
		}
	Attach(mime)
		{
		.parts.Add(mime)
		return this
		}
	AttachFile(filename)
		{
		ext = filename.AfterLast('.')
		type = MimeTypes.GetDefault(ext, 'application/octet-stream').Split('/')
		if type[0] is 'text'
			m = MimeText(GetFile(filename), type[1])
		else
			{
			m = MimeBase(type[0], type[1])
			if false is s = GetFile(filename)
				throw "MimeMultiPart: AttachFile: can't get: " $ filename
			m.SetPayload(s).Base64()
			}
		m.AddHeader('Content-Disposition', 'attachment',
			filename: filename.Basename())
		.Attach(m)
		}
	ToString()
		{
		boundary = '='.Repeat(20) $ Random(100000) $ Random(100000)
		.AddExtra('Content-Type', boundary: boundary)
		boundary = '--' $ boundary $ '\r\n'
		s = super.ToString() $ boundary
		for p in .parts
			s $= p.ToString() $ boundary
		s = s.Substr(0, -2) $ '--\r\n'
		return s
		}
	}

There are other features that could be added, but this code provides the main functions you need to send MIME messages.

Contact Us | Legal Statement | Privacy Statement | SiteMap

Copyright © 2023 Axon® Development Corporation. All Rights Reserved. - Open Source Integrated Database and Programming Language