Auto-decoding TNEF attachments

Date: Thu, 28 Feb 2008
Author: Daniel Vérité
Applies to: Manitou-Mail, all versions

TNEF stands for Transport Neutral Encapsulation Format; it's a relatively complex format that may be produced by Outlook in attachments with application/ms-tnef MIME types, generally as files named as 'winmail.dat'. Some basic reasons about why and how it's used are explained in this Microsoft Knowledge Base article.

The trouble with this format is that it is opaque to non-Outlook users, who need to use an external conversion program to get at the actual contents.

The Microsoft article says:

If you are sending e-mail with file attachments to a recipient who does not use Outlook or the Exchange Client, you should manually choose to use a mail format that does not require TNEF (such as plain text). By not sending TNEF messages, the recipient will be able to view and save the attachments as expected.
It assumes that senders know what email program the recipients may use, which is obviously generally impossible.

In a Manitou-Mail database, what makes most sense is to have a plugin that decodes the TNEF-formatted contents and stores attached files as if they hadn't been encapsulated in the first place, to spare the end-user any decoding effort. The original winmail.dat can then be discarded.

Fortunately, code libraries are available to try and extract the real contents from TNEF files. One of these is CPAN's Convert::TNEF module that we're going to use in a manitou-mdx mimeprocess plugin. Precisely, we do an incoming_mimeprocess plugin since we want to read and write the MIME structure of the mail message at import time.

Code

(download: tnef_decoder.pm)
# TNEF attachments decoder plugin for Manitou-Mail
# Copyright (C) 2008 Daniel Verite

# This file is part of Manitou-Mail (see http://www.manitou-mail.org)

# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.

package Manitou::Plugins::tnef_decoder;

use Convert::TNEF;
use MIME::Types;
use Manitou::Config qw(getconf);


sub init {
  my $self={};
  bless $self;
  $self->{mime_types} = MIME::Types->new;
  return $self;
}

sub finish {
 1;
}

sub process {
  my ($self,$ctxt)=@_;
  my $obj=$ctxt->{'mimeobj'};
  if ($obj->is_multipart) {
    foreach my $subobj ($obj->parts) {
      if ($subobj->effective_type =~/ms-tnef/i) {
	my $tnef = Convert::TNEF->read_ent($subobj,
					   {output_dir=>getconf("tmpdir")})
	  or die $Convert::TNEF::errstr;

	for my $a ($tnef->attachments) {
	  my $type;
	  if ($a->longname=~/\.([A-Za-z]{2,4})$/) {
	    $type = $self->{mime_types}->mimeTypeOf($1);
	  }
	  if (!$type) {
	    $type="application/octet-stream";
	  }

	  $obj->attach(Filename => $a->longname,
		       Type => $type,
		       Data => $a->data,
		       Disposition => "attachment");
	}
	$tnef->purge;
      }
    }
    # Remove the ms-tnef parts
    $obj->parts([ grep { !($_->effective_type =~/ms-tnef/i) } $obj->parts ]);
  }
}

1;

That code will be available in the next released version (v0.9.10) but it can also be installed on any previous one. To be activated, the plugin needs to be declared in the manitou-mdx configuration file, as documented.

Also, see the