MIME-Mini

 view release on metacpan or  search on metacpan

lib/MIME/Mini.pm  view on Meta::CPAN

	body message parts
	newparam newmail
);
our %EXPORT_TAGS = (all => [@EXPORT_OK]);

sub formail # rfc2822 + mboxrd format (see http://www.qmail.org/man/man5/mbox.html)
{
	sub mime # rfc2045, rfc2046
	{
		my ($mail, $parent) = @_;
		return $mail unless exists $mail->{header} && exists $mail->{header}->{'content-type'} || defined $parent && exists $parent->{mime_type} && $parent->{mime_type} =~ /^multipart\/digest$/i;
		my ($content_type) = (exists $mail->{header} && exists $mail->{header}->{'content-type'}) ? @{$mail->{header}->{'content-type'}} : "Content-Type: message/rfc822\n";
		my ($type) = $content_type =~ /^content-type:\s*([\w\/.-]+)/i;
		my $boundary = param($mail, 'content-type', 'boundary') if $type =~ /^multipart\//i;
		return $mail unless defined $type && ($type =~ /^multipart\//i && $boundary || $type =~ /^message\/rfc822$/i);
		($mail->{mime_boundary}) = $boundary =~ /^(.*\S)/ if $boundary;
		$mail->{mime_type} = $type;
		$mail->{mime_message} = mimepart(delete $mail->{body} || '', $mail), return $mail if $type =~ /^message\/(?:rfc822|external-body)$/i;
		return tnef2mime(mimeparts($mail, $parent));
	}

	sub mimeparts
	{
		my ($mail, $parent) = @_;

lib/MIME/Mini.pm  view on Meta::CPAN

	}

	return join '', map { $_->[1] } sort { my ($ad) = $a->[0] =~ /(\d+)/; my ($bd) = $b->[0] =~ /(\d+)/; $ad <=> $bd } @p;
}

sub mimetype # rfc2045, rfc2046
{
	my ($m, $p) = @_;
	my ($e) = header($m, 'content-transfer-encoding');
	return 'application/octet-stream' if defined $e && $e !~ /^(?:[78]bit|binary|quoted-printable|base64)$/i;
	my ($type) = header($m, 'content-type');
	return lc $1 if defined $type && $type =~ /^((?:text|image|audio|video|application|message|multipart)\/[^\s;]+)/i;
	return 'message/rfc822' if !defined $type && defined $p && exists $p->{mime_type} && $p->{mime_type} =~ /^multipart\/digest/i;
	return 'text/plain';
}

sub encoding # rfc2045
{
	my $m = shift;
	my ($e) = header($m, 'content-transfer-encoding');
	return (defined $e && $e =~ /^([78]bit|binary|quoted-printable|base64)$/i) ? lc $1 : (exists $m->{body} && $m->{body} =~ tr/\x80-\xff//) ? '8bit' : '7bit';
}

my $filename_counter;
sub filename # rfc2183, rfc2045?
{
	my $p = shift;
	my $fn = param($p, 'content-disposition', 'filename') || param($p, 'content-type', 'name') || 'attachment' . ++$filename_counter;
	$fn =~ s/^.*[\\\/]//, $fn =~ tr/\x00-\x1f !"#\$%&'()*\/:;<=>?@[\\]^`{|}~\x7f/_/s;
	return $fn;
}

sub body
{
	my $m = shift;
	return exists $m->{body} ? decode($m->{body}, encoding($m)) : undef;
}

t/01_MIME_Mini.latin1.t  view on Meta::CPAN

};

# Test parsing of nasty header parameters

subtest param => sub
{
	plan tests => 1;

	my $m = {};
	append_header($m, (my $h = "Content-Type: multipart/mixed; boundary*0*=\"iso-8859-1'en'%61a%61\" boundary*2=ccc boundary*1*=%62b%62"));
	is param($m, 'content-type', 'boundary'), 'aaabbbccc', 'param: split into out-of-order pieces with encoding';
};

# Test mimetype identification and defaults

subtest mimetype => sub
{
	plan tests => 5;

	my $m = {};
	append_header($m, "Content-Transfer-Encoding: wierd");

t/01_MIME_Mini.latin1.t  view on Meta::CPAN

 name*=iso-8859-1'en'h%E9%20h%E9;
 long1*0=1234567890123456789012345678901234567890;
 long1*1=1234567890123456789012345678901234567890; long1*2=1234567890;
 long2*0=\"123456789 123456789 123456789 123456789 \";
 long2*1=\"123456789 123456789 123456789 123456789 \"; long2*2=\"123456789 \";
 french*0*=iso-8859-1'fr'Ceci%20est%20un%20exemple%20tr%E8s%20s;
 french*1*=\"imple mais un peu long et avec un accent\";
 french*2*=%20grave%20sur%20deux%20%E8

", 'newparam';
	is param($m, 'content-type', 'charset'), 'us-ascii', 'newparam: charset';
	is param($m, 'content-type', 'filename'), 'he he he', 'newparam: filename';
	is param($m, 'content-type', 'name'), 'hé hé', 'newparam: name';
	is param($m, 'content-type', 'long1'), '123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890', 'newparam: long1'; 
	is param($m, 'content-type', 'long2'), '123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 ', 'newparam: long2'; 
	is param($m, 'content-type', 'french'), 'Ceci est un exemple très simple mais un peu long et avec un accent grave sur deux è', 'newparam: french'; 
};

# Test creating mail objects

subtest newmail => sub
{
	plan tests => 10;

	like mail2str(mail2mbox(newmail(To => 'to@to.org', From => 'from@from.org', Subject => 'plain', body => "hello mail\n"))),
"/^From from\@from\.org  $mboxdate_re

t/01_MIME_Mini.latin1.t  view on Meta::CPAN

	return $m;
}

# Test the boundary2test helper function

subtest boundary2test => sub
{
	plan tests => 7;

	is boundary2test("Content-Type: multipart/mixed; boundary=asdfasdf\n"), "Content-Type: multipart/mixed; boundary=test-boundary\n", 'boundary2test: Content-Type noquotes';
	is boundary2test("content-type: multipart/mixed; boundary=asdfasdf\n"), "content-type: multipart/mixed; boundary=test-boundary\n", 'boundary2test: content-type noquotes';
	is boundary2test("Content-Type: multipart/mixed; boundary=\"asdfasdf\"\n"), "Content-Type: multipart/mixed; boundary=\"test-boundary\"\n", 'boundary2test: Content-Type noquotes';
	is boundary2test("content-type: multipart/mixed; boundary=\"asdfasdf\"\n"), "content-type: multipart/mixed; boundary=\"test-boundary\"\n", 'boundary2test: content-type noquotes';
	is boundary2test("--asdfadf\n"), "--test-boundary\n", 'boundary2test: --asdfasdf\n';
	is boundary2test("--asdfadf-\n"), "--test-boundary\n", 'boundary2test: --asdfasdf-\n';
	is boundary2test("--asdfadf--\n"), "--test-boundary--\n", 'boundary2test: --asdfasdf--\n';
};

# Test parser: mbox filter with mail2multipart

subtest parse2multi => sub
{
	my @fname = glob 't/in.m/*' or return;

t/02_MIME_Mini.utf8.t  view on Meta::CPAN

};

# Test parsing of nasty header parameters

subtest param => sub
{
	plan tests => 1;

	my $m = {};
	append_header($m, (my $h = "Content-Type: multipart/mixed; boundary*0*=\"iso-8859-1'en'%61a%61\" boundary*2=ccc boundary*1*=%62b%62"));
	is param($m, 'content-type', 'boundary'), 'aaabbbccc', 'param: split into out-of-order pieces with encoding';
};

# Test mimetype identification and defaults

subtest mimetype => sub
{
	plan tests => 5;

	my $m = {};
	append_header($m, "Content-Transfer-Encoding: wierd");

t/02_MIME_Mini.utf8.t  view on Meta::CPAN

 name*=iso-8859-1'en'h%E9%20h%E9;
 long1*0=1234567890123456789012345678901234567890;
 long1*1=1234567890123456789012345678901234567890; long1*2=1234567890;
 long2*0=\"123456789 123456789 123456789 123456789 \";
 long2*1=\"123456789 123456789 123456789 123456789 \"; long2*2=\"123456789 \";
 french*0*=iso-8859-1'fr'Ceci%20est%20un%20exemple%20tr%E8s%20s;
 french*1*=\"imple mais un peu long et avec un accent\";
 french*2*=%20grave%20sur%20deux%20%E8

", 'newparam';
	is param($m, 'content-type', 'charset'), 'us-ascii', 'newparam: charset';
	is param($m, 'content-type', 'filename'), 'he he he', 'newparam: filename';
	is param($m, 'content-type', 'name'), 'hé hé', 'newparam: name';
	is param($m, 'content-type', 'long1'), '123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890', 'newparam: long1'; 
	is param($m, 'content-type', 'long2'), '123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 ', 'newparam: long2'; 
	is param($m, 'content-type', 'french'), 'Ceci est un exemple très simple mais un peu long et avec un accent grave sur deux è', 'newparam: french'; 
};

# Test creating mail objects

subtest newmail => sub
{
	plan tests => 10;

	like mail2str(mail2mbox(newmail(To => 'to@to.org', From => 'from@from.org', Subject => 'plain', body => "hello mail\n"))),
"/^From from\@from\.org  $mboxdate_re

t/02_MIME_Mini.utf8.t  view on Meta::CPAN

	return $m;
}

# Test the boundary2test helper function

subtest boundary2test => sub
{
	plan tests => 7;

	is boundary2test("Content-Type: multipart/mixed; boundary=asdfasdf\n"), "Content-Type: multipart/mixed; boundary=test-boundary\n", 'boundary2test: Content-Type noquotes';
	is boundary2test("content-type: multipart/mixed; boundary=asdfasdf\n"), "content-type: multipart/mixed; boundary=test-boundary\n", 'boundary2test: content-type noquotes';
	is boundary2test("Content-Type: multipart/mixed; boundary=\"asdfasdf\"\n"), "Content-Type: multipart/mixed; boundary=\"test-boundary\"\n", 'boundary2test: Content-Type noquotes';
	is boundary2test("content-type: multipart/mixed; boundary=\"asdfasdf\"\n"), "content-type: multipart/mixed; boundary=\"test-boundary\"\n", 'boundary2test: content-type noquotes';
	is boundary2test("--asdfadf\n"), "--test-boundary\n", 'boundary2test: --asdfasdf\n';
	is boundary2test("--asdfadf-\n"), "--test-boundary\n", 'boundary2test: --asdfasdf-\n';
	is boundary2test("--asdfadf--\n"), "--test-boundary--\n", 'boundary2test: --asdfasdf--\n';
};

# Test parser: mbox filter with mail2multipart

subtest parse2multi => sub
{
	my @fname = glob 't/in.m/*' or return;

t/good/malformed-multipart-truncated  view on Meta::CPAN

 [127.0.0.1]) (amavisd-new, port 10024)	with ESMTP id rHGmEA9yZU-R for
 <meego-dev@meego.com>;	Wed, 13 Oct 2010 09:48:27 -0700 (PDT)
Received: from mail-ww0-f48.google.com (mail-ww0-f48.google.com
 [74.125.82.48])	by mail.meego.com (Postfix) with ESMTP	for
 <meego-dev@meego.com>; Wed, 13 Oct 2010 09:48:26 -0700 (PDT)
Received: by wwi14 with SMTP id 14so92446wwi.5	for <meego-dev@meego.com>; Wed,
 13 Oct 2010 09:48:25 -0700 (PDT)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma;
	h=domainkey-signature:received:received:sender:message-id:date:from
	:reply-to:organization:user-agent:mime-version:to:subject
	:x-enigmail-version:content-type;
	bh=8mOSD8KfW+FGSmGIumHAs9auaL6zBlPdozt0rbw14IE=;
	b=HCWFXbpQqAk8Tebb5dAk+Loj29ek3gg/KIeGgVqi/0bmW9KU3rgPZHZ4UGosFFdOti
	T4kaLbACBBq0RfsKQz8JWzNa9cKrn5QexEm0iuxylxzC4qc2LxRKcnQCPK1G2WeuieCr
	2HIRgWPm6wAQr9KjWMKq/SquYSACIpJzkkC18=
DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=gamma;
	h=sender:message-id:date:from:reply-to:organization:user-agent
	:mime-version:to:subject:x-enigmail-version:content-type;
	b=Yno3wge63XPt2MOW80/tIXtQz9tQyzgy1Sy8m4wCeP8H4rCoi4/ZKUOum9UlHqKCbx
	NR0UaMzS6gZ86AFLyArS9mnFRSFOg8hBomojUhspQXJrxr/l2+wN2Ld5IRnFwn1LykW/
	jYG+Dz9x79EFEqlD0dj9+5EkFC7ZK9ZLENLT0=
Received: by 10.227.152.131 with SMTP id g3mr8838258wbw.108.1286988504131;
	Wed, 13 Oct 2010 09:48:24 -0700 (PDT)
Received: from [192.168.1.10] (ALyon-152-1-99-113.w86-202.abo.wanadoo.fr
	[86.202.50.113])	by mx.google.com with ESMTPS id
 g9sm8456296wbh.19.2010.10.13.09.48.22	(version=SSLv3 cipher=RC4-MD5); Wed, 13
 Oct 2010 09:48:23 -0700 (PDT)
Message-ID: <4CB5E2D5.2040509@maemo.org>

t/in/malformed-multipart-truncated  view on Meta::CPAN

 [127.0.0.1]) (amavisd-new, port 10024)	with ESMTP id rHGmEA9yZU-R for
 <meego-dev@meego.com>;	Wed, 13 Oct 2010 09:48:27 -0700 (PDT)
Received: from mail-ww0-f48.google.com (mail-ww0-f48.google.com
 [74.125.82.48])	by mail.meego.com (Postfix) with ESMTP	for
 <meego-dev@meego.com>; Wed, 13 Oct 2010 09:48:26 -0700 (PDT)
Received: by wwi14 with SMTP id 14so92446wwi.5	for <meego-dev@meego.com>; Wed,
 13 Oct 2010 09:48:25 -0700 (PDT)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma;
	h=domainkey-signature:received:received:sender:message-id:date:from
	:reply-to:organization:user-agent:mime-version:to:subject
	:x-enigmail-version:content-type;
	bh=8mOSD8KfW+FGSmGIumHAs9auaL6zBlPdozt0rbw14IE=;
	b=HCWFXbpQqAk8Tebb5dAk+Loj29ek3gg/KIeGgVqi/0bmW9KU3rgPZHZ4UGosFFdOti
	T4kaLbACBBq0RfsKQz8JWzNa9cKrn5QexEm0iuxylxzC4qc2LxRKcnQCPK1G2WeuieCr
	2HIRgWPm6wAQr9KjWMKq/SquYSACIpJzkkC18=
DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=gamma;
	h=sender:message-id:date:from:reply-to:organization:user-agent
	:mime-version:to:subject:x-enigmail-version:content-type;
	b=Yno3wge63XPt2MOW80/tIXtQz9tQyzgy1Sy8m4wCeP8H4rCoi4/ZKUOum9UlHqKCbx
	NR0UaMzS6gZ86AFLyArS9mnFRSFOg8hBomojUhspQXJrxr/l2+wN2Ld5IRnFwn1LykW/
	jYG+Dz9x79EFEqlD0dj9+5EkFC7ZK9ZLENLT0=
Received: by 10.227.152.131 with SMTP id g3mr8838258wbw.108.1286988504131;
	Wed, 13 Oct 2010 09:48:24 -0700 (PDT)
Received: from [192.168.1.10] (ALyon-152-1-99-113.w86-202.abo.wanadoo.fr
	[86.202.50.113])	by mx.google.com with ESMTPS id
 g9sm8456296wbh.19.2010.10.13.09.48.22	(version=SSLv3 cipher=RC4-MD5); Wed, 13
 Oct 2010 09:48:23 -0700 (PDT)
Message-ID: <4CB5E2D5.2040509@maemo.org>



( run in 3.169 seconds using v1.01-cache-2.11-cpan-524268b4103 )