Email-MIME-CreateHTML-1.030/0000755016522100007720000000000011335311643016430 5ustar jamielengineers00000000000000Email-MIME-CreateHTML-1.030/t/0000755016522100007720000000000011335311643016673 5ustar jamielengineers00000000000000Email-MIME-CreateHTML-1.030/t/pod_coverage.t0000644016522100007720000000033011335311615021510 0ustar jamielengineers00000000000000use Test::More; eval "use Test::Pod::Coverage 1.00"; plan skip_all => "Test::Pod::Coverage 1.00 required for testing POD Coverage" if $@; all_pod_coverage_ok({ also_private => [ qr/^[A-Z_]+$/ ], }); #Ignore all caps Email-MIME-CreateHTML-1.030/t/data/0000755016522100007720000000000011335311643017604 5ustar jamielengineers00000000000000Email-MIME-CreateHTML-1.030/t/data/CreateHTML_04a.html0000644016522100007720000000031711335311613023024 0ustar jamielengineers00000000000000

Hello HTML World

Look! a nice landscape

This is the end image again.

Email-MIME-CreateHTML-1.030/t/data/CreateHTML_04b.html0000644016522100007720000000027411335311613023027 0ustar jamielengineers00000000000000

Hello HTML World

Look! a nice landscape

This is the end image again.

Email-MIME-CreateHTML-1.030/t/data/CreateHTML_03b.html0000644016522100007720000000031611335311613023023 0ustar jamielengineers00000000000000

Hello HTML World with an image landscape using a Content-ID

and some ISO-8859-1 extended chars: a cote les yeux de la tte

Email-MIME-CreateHTML-1.030/t/data/CreateHTML_03a.html0000644016522100007720000000031611335311613023022 0ustar jamielengineers00000000000000

Hello HTML World with an image landscape using a Content-ID

and some ISO-8859-1 extended chars: a cote les yeux de la tte

Email-MIME-CreateHTML-1.030/t/data/CreateHTML_02b.html0000644016522100007720000000101511335311613023017 0ustar jamielengineers00000000000000

Hello HTML World

Look! a nice landscape

Look! the same landscape, I hope it was included only once in the mime object!

This link (the w3c homepage) did not get rewritten as it is fully qualified.

This is the end image again. Bye.

Email-MIME-CreateHTML-1.030/t/data/landscape.jpg0000644016522100007720000000341211335311613022235 0ustar jamielengineers00000000000000JFIFHHC!)E-)&&)T<@2EdXigbX`_n|nuw_`kΞC)$)Q--Qr`r`"5!1AQaq"2#BCRbc!1qAa ?H`q `71ы Qss%Rx [ bgK<ƫ-s"c(e fv][M_3F\pc?\Ѩ)yPoj]gX}"zbaUZTÁxĠlX]s0:39ּ\ժF4Njckc*ew4p' ^W#(&eLYq_rѐ~#X€rZ<}e1KЂ16jQ5IjHcIK:MykZ^#,Rӈ̛`$ $0vCOkc#$jg(Kj@fr-nX_F^(5P5 h^̎ pOBV4A+$]V`̓_H,7I^W$K"q'r\7A,3WPD jH\>rJW-# ;g 3p*\LvɂPqF2ՎhINm]=w oߨ cۛ`|Zj>WSPܓ,7]0[9KdP|U#rR{>@PZ{nL:_?8Q5ոemtA '!>;ʀ[L[]cK'#;E^ 'V\9?oC9I%~$ǢAAg'v9?HA^ߐ +!Bg:lk\("7DQq9iܽW$ )luňbWl'!Նid{/aFqu!&s qy2/R냋#.h%1scQga{1( NAAu N#6l@_n%j麾1m`/]ÊWUPzC]MKQQtw/KlCˬީ*>F9X~"2NLQu#8S )ܧXe25]*@q)\_8+jH||T #JKء4xvܵgqfGqd郧LGHCBz ~"~d H Wh٩jh # J. IU  O4(Hi#0~sZ!}2 LmWI`PZ˥Ⱥ[El+'_q"j"iq]lTy"6 vݴ0k 9}Z^*[#w H#>Cs5j &0F yqY-U/6|ahufh<_3[0,gZfb [5GI7fYz^ 71.97&tɪ\8ƖH=x1gLEmail-MIME-CreateHTML-1.030/t/data/CreateHTML_02a.html0000644016522100007720000000103511335311613023020 0ustar jamielengineers00000000000000

Hello HTML World

Look! a nice landscape

Look! the same landscape, I hope it was included only once in the mime object!

This link (the w3c homepage) did not get rewritten as it is fully qualified.

This is the end image again. Bye.

Email-MIME-CreateHTML-1.030/t/data/CreateHTML_01.html0000644016522100007720000000056011335311613022660 0ustar jamielengineers00000000000000 Hello HTML World with an image the end and another one landscape Email-MIME-CreateHTML-1.030/t/data/end.png0000644016522100007720000000150311335311613021054 0ustar jamielengineers00000000000000PNG  IHDRdd̈gPLTE|{^CG\eRժ2`IDATxڕn @d?FՕjZ_[ т0 6HNl>Yft_FL1F 4+n/`;PUJ,JR\<$UX^nq ci$a $.a^-.$co /a X(,2ڸ$%`HXB%ZtHR HŢL$-&$%/9BЖ"HBcik~W$-۞~%9IyI$T&oNMt%C<]NERə!t{^47fx&pFy6e%{-mT @:d+v20$4ZxɋCDRTȾ%@a^vItG/ e%B$1"y YDۋbDĈ%12b^sH/ IDD$K2)BrX Nȥڢw.B;!{9 K bX1Q[CX84 ɒ?cA[-ҳVKUo)x\LM`r]Hczg?kR0W6wKU[.C>mWT8IENDB`Email-MIME-CreateHTML-1.030/t/data/test_style.css0000644016522100007720000000006411335311613022512 0ustar jamielengineers00000000000000body { font-family: Verdana, Arial, sans-serif; } Email-MIME-CreateHTML-1.030/t/Email-MIME-CreateHTML-Resolver.t0000644016522100007720000000620311335311613024317 0ustar jamielengineers00000000000000#!/usr/local/bin/perl # # Unit test for Email::MIME::CreateHTML::Resolver # # -t Trace # -T Deep trace # use strict; use Test::Assertions::TestScript; #Compilation require Email::MIME::CreateHTML::Resolver; ASSERT($INC{'Email/MIME/CreateHTML/Resolver.pm'}, "Compiled Email::MIME::CreateHTML::Resolver version $Email::MIME::CreateHTML::Resolver::VERSION"); #Try some different URLs my $obj = Email::MIME::CreateHTML::Resolver->new()->_select_resolver('http://www.bbc.co.uk/'); ASSERT(ref $obj eq 'Email::MIME::CreateHTML::Resolver::LWP', 'HTTP URL'); $obj = Email::MIME::CreateHTML::Resolver->new()->_select_resolver('https://ssl.bbc.co.uk/'); ASSERT(ref $obj eq 'Email::MIME::CreateHTML::Resolver::LWP', 'HTTPS URL'); $obj = Email::MIME::CreateHTML::Resolver->new()->_select_resolver('ftp://www.bbc.co.uk/'); ASSERT(ref $obj eq 'Email::MIME::CreateHTML::Resolver::LWP', 'FTP URL'); $obj = Email::MIME::CreateHTML::Resolver->new()->_select_resolver('/absolute/filepath/file.extension'); ASSERT(ref $obj eq 'Email::MIME::CreateHTML::Resolver::Filesystem', 'absolute filepath'); $obj = Email::MIME::CreateHTML::Resolver->new()->_select_resolver('some/filepath/file.extension'); ASSERT(ref $obj eq 'Email::MIME::CreateHTML::Resolver::Filesystem', 'relative filepath'); $obj = Email::MIME::CreateHTML::Resolver->new()->_select_resolver('\\server\filepath\file.extension'); ASSERT(ref $obj eq 'Email::MIME::CreateHTML::Resolver::Filesystem', 'UNC filepath'); $obj = Email::MIME::CreateHTML::Resolver->new()->_select_resolver('c:\some\filepath\file.extension'); ASSERT(ref $obj eq 'Email::MIME::CreateHTML::Resolver::Filesystem', 'windows-style filepath'); $obj = Email::MIME::CreateHTML::Resolver->new()->_select_resolver('file://some/filepath/file.extension'); ASSERT(ref $obj eq 'Email::MIME::CreateHTML::Resolver::Filesystem', 'file URL'); #Try custom resolver my $resolver = new UnitTestResolver; $obj = Email::MIME::CreateHTML::Resolver->new({resolver => $resolver})->_select_resolver('http://www.bbc.co.uk/'); ASSERT(ref $obj eq 'UnitTestResolver', 'Custom resolver'); #Error checking ASSERT(DIED(sub { Email::MIME::CreateHTML::Resolver->new()->get_resource('')}) && $@ =~ /get_resource without a URI/, "No URI"); ASSERT(DIED(sub { Email::MIME::CreateHTML::Resolver->new({resolver => 1})->_select_resolver('abc')}), "Resolver not an object"); ASSERT(DIED(sub { Email::MIME::CreateHTML::Resolver->new({resolver => new Dummy()})->_select_resolver('abc')}) && $@ =~ /resolver does not seem to use the expected interface/, "Resolver doesn't have get_resource method"); ASSERT(DIED(sub { Email::MIME::CreateHTML::Resolver->new({object_cache => new Dummy()})->_select_resolver('abc')}) && $@ =~ /object_cache does not seem to use the expected cache interface/, "Dodgy object cache"); # Dummy object for error checking package Dummy; sub new { return bless({}, shift); } ####################################################### # # Trivial resource resolver for testing # ####################################################### package UnitTestResolver; sub new { return bless({}, shift()); } sub get_resource { return ("invariant value","invariant-name","text/plain","iso8859-1"); } 1; Email-MIME-CreateHTML-1.030/t/pod.t0000644016522100007720000000020111335311615017632 0ustar jamielengineers00000000000000use Test::More; eval "use Test::Pod 1.00"; plan skip_all => "Test::Pod 1.00 required for testing POD" if $@; all_pod_files_ok(); Email-MIME-CreateHTML-1.030/t/Email-MIME-CreateHTML.t0000644016522100007720000002312211335311613022517 0ustar jamielengineers00000000000000#!/usr/local/bin/perl ################################################################################## # -t : Trace # -T : Deep Trace # -m
: send the emails that we create for each test (set $SMTP_HOST) ################################################################################## use strict; use vars qw/$opt_m/; use Test::Assertions::TestScript(tests => 49, options => {'m=s' => \$opt_m}); use File::Slurp; use File::Copy; my $mailto = $opt_m || 'somebody@example.com'; # SetUp my $text_body = "Hello World"; my $html_body = "Hello HTML World"; my ($html_in, $html_out); ####################################################### # # The tests # ####################################################### use Email::MIME; use Email::MIME::CreateHTML; ASSERT(1,"compiled version $Email::MIME::CreateHTML::VERSION"); # # Test Mail Construction # # HTML, no embedded objects, no text alternative # ---------------------------------------------- my $mime = Email::MIME->create_html( header => [ From => 'unittest_a@example.co.uk', To => $mailto, Subject => 'HTML, no embedded objects, no text alternative', ], body => $html_body, ); ASSERT(ref $mime eq 'Email::MIME', "------ HTML, no embedded objects, no text alternative - Email::MIME object returned"); test_mime( $mime, qr'text/html', $html_body ); send_mail( $mime ) if($opt_m); # HTML, no embedded objects, with text alternative # ------------------------------------------------ $mime = Email::MIME->create_html( header => [ From => 'unittest_b@example.co.uk', To => $mailto, Subject => 'HTML, no embedded objects, with text alternative', ], body => $html_body, text_body => $text_body, ); ASSERT(ref $mime eq 'Email::MIME', "------ HTML, no embedded objects, with text alternative - Email::MIME object returned"); test_mime( $mime, qr'multipart/alternative', undef ); my @parts = $mime->parts; ASSERT( scalar(@parts) == 2, "number of parts"); test_mime( $parts[0], qr'text/plain', $text_body ); test_mime( $parts[1], qr'text/html', $html_body ); send_mail( $mime ) if($opt_m); # HTML with embedded objects, no text alternative # using objects hash # ----------------------------------------------- # inline_css is false, no base or base_rewrite # ----------------------------------------------- $html_in = read_file( './data/CreateHTML_01.html' ); $html_out = $html_in; $mime = Email::MIME->create_html( header => [ From => 'unittest_c@example.co.uk', To => $mailto, Subject => 'HTML with embedded objects, no text alternative', ], body => $html_in, objects => { '123@bbc.co.uk' => './data/end.png', 'landscapeview' => './data/landscape.jpg', }, inline_css => 0, ); ASSERT(ref $mime eq 'Email::MIME', "------ HTML with embedded objects, no text alternative - Email::MIME object returned"); test_mime( $mime, qr'multipart/related', undef ); @parts = $mime->parts; ASSERT( scalar(@parts) == 3, "number of parts"); test_mime( $parts[0], qr'text/html', $html_out ); my $p = join '', map defined $_ ? $_->content_type : '', @parts[1..2]; ASSERT($p =~ m|image/png|i && $p =~ m|image/jpeg|i, "Mime types image/png and image/jpeg"); send_mail( $mime ) if($opt_m); # HTML with embedded objects, with text alternative # using embedded images # ----------------------------------------------- # inline_css default on, base with base_rewrite, embed default on, # multiple reference to same object do not cause multiple attached mime parts, # can use objects and embed together, fully qualified links are not rewritten # ----------------------------------------------- $html_in = read_file( './data/CreateHTML_02a.html' ); $html_out = read_file( './data/CreateHTML_02b.html' ); $mime = Email::MIME->create_html( header => [ From => 'unittest_d@example.co.uk', To => $mailto, Subject => 'HTML with embedded objects, with text alternative', ], body => $html_in, text_body => $text_body, base => './data', objects => { '123@bbc.co.uk' => 'end.png', }, inline_javascript => 1, ); ASSERT(ref $mime eq 'Email::MIME', "------ HTML with embedded objects, with text alternative - Email::MIME object returned"); test_mime( $mime, qr'multipart/alternative', undef ); @parts = $mime->parts; ASSERT( scalar(@parts) == 2, "number of parts"); test_mime( $parts[0], qr'text/plain', $text_body ); test_mime( $parts[1], qr'multipart/related', undef ); my @sub_parts = defined $parts[1] ? $parts[1]->parts : (); ASSERT( scalar(@sub_parts) == 3, "number of parts"); test_mime( $sub_parts[0], qr'text/html', $html_out ); my $sp = [map { defined($_) ? $_->content_type : () } @sub_parts[1..2]]; DUMP("Sub parts",$sp); ASSERT((grep { m!image/png!i } @$sp), "MIME type image/png present"); ASSERT((grep { m!image/jpeg!i } @$sp), "MIME type image/jpeg present"); send_mail( $mime ) if($opt_m); # HTML with embedded objects, no text alternative # use a different char set # ----------------------------------------------- # no base but have base_rewrite, embed is false # ----------------------------------------------- $html_in = read_file( './data/CreateHTML_03a.html' ); $html_out = read_file( './data/CreateHTML_03b.html' ); $mime = Email::MIME->create_html( header => [ From => 'unittest_e@example.co.uk', To => $mailto, Subject => 'HTML with embedded objects, no text alternative, uses ISO-8859-1', ], body => $html_in, body_attributes => { charset => 'ISO-8859-1' }, objects => { 'landscapeview' => './data/landscape.jpg', }, embed => 0, ); ASSERT(ref $mime eq 'Email::MIME', "------ HTML with embedded objects, no text alternative - Email::MIME object returned"); test_mime( $mime, qr'multipart/related', undef ); @parts = $mime->parts; ASSERT( scalar(@parts) == 2, "number of parts"); test_mime( $parts[0], qr'text/html', $html_out ); test_mime( $parts[1], qr'image/jpeg', undef ); send_mail( $mime ) if($opt_m); # Caching # ---------------------------------------------- my $cache = "this is not a cache object"; ASSERT( copy( './data/landscape.jpg','./data/cache_test_landscape.jpg' ) && copy( './data/end.png','./data/cache_test_end.png' ), "------ Caching : Image files in place" ); $html_in = read_file( './data/CreateHTML_04a.html' ); $html_out = read_file( './data/CreateHTML_04b.html' ); # bad cache object eval { $mime = Email::MIME->create_html( header => [ From => 'unittest_f@example.co.uk', To => $mailto, Subject => 'Test of caching', ], body => $html_in, base => './data', objects => { 'abcdefghi@bbc.co.uk' => 'cache_test_end.png', }, object_cache => $cache, ); }; ASSERT( scalar( $@ =~ /object_cache must be an object/ ), "Bad object_cache caught"); # good cache object $cache = new UnitTestCache(); $mime = Email::MIME->create_html( header => [ From => 'unittest_f@example.co.uk', To => $mailto, Subject => 'Test of caching', ], body => $html_in, base => './data', objects => { 'abcdefghi@bbc.co.uk' => 'cache_test_end.png', }, object_cache => $cache, ); ASSERT( ref $mime eq 'Email::MIME', "mime object created"); @parts = $mime->parts; ASSERT( scalar(@parts) == 3, "number of parts"); test_mime( $parts[0], qr'text/html', $html_out ); test_mime( $parts[1], qr'image/png', undef ); test_mime( $parts[2], qr'image/jpeg', undef ); ASSERT( unlink('./data/cache_test_landscape.jpg', './data/cache_test_end.png') == 2, "Image files removed" ); $mime = Email::MIME->create_html( header => [ From => 'unittest_f@example.co.uk', To => $mailto, Subject => 'Test of caching', ], body => $html_in, base => './data', objects => { 'abcdefghi@bbc.co.uk' => 'cache_test_end.png', }, object_cache => $cache, ); ASSERT( ref $mime eq 'Email::MIME', "mime object created (second mail)"); @parts = $mime->parts; ASSERT( scalar(@parts) == 3, "number of parts"); test_mime( $parts[0], qr'text/html', $html_out ); test_mime( $parts[1], qr'image/png', undef ); test_mime( $parts[2], qr'image/jpeg', undef ); send_mail( $mime ) if($opt_m); # End of tests ####################################################### # # Subroutines # ####################################################### sub test_mime { my ($mime, $exp_content_type, $exp_body) = @_; my $got_content_type = defined $mime ? $mime->content_type : undef; ASSERT( defined $got_content_type && $got_content_type =~ /^$exp_content_type/i, "content-type: $got_content_type"); if ( defined $exp_body ) { my $got_body; $exp_body =~ s/\s+$//g; if(defined $mime) { $got_body = $mime->body; # we don't care about trailing white space $got_body =~ s/\s+$//g; # This is a quick fix to allow us to test against randomly generated cids # note that the 10 is because the existing tests had some short all numeric cids $got_body =~ s/cid:\d{10}\d+/cid:/g; } DUMP("test_mime", { expected => $exp_body, got => $got_body }); ASSERT(defined $got_body && $got_body eq $exp_body, "body"); } } # Actually send the mail sub send_mail { my $email = shift; my $smtp_host = $ENV{SMTP_HOST} || 'localhost'; warn "SMTP_HOST env var not set in environment using 'localhost'\n" unless ($ENV{SMTP_HOST}); require Email::Send; warn "Sending email to '$mailto'...\n"; if ( $Email::Send::VERSION < 2.0 ) { my $rv = Email::Send::send('SMTP',$email, $smtp_host); die $rv if ! $rv; } else { my $sender = Email::Send->new({mailer => 'SMTP'}); $sender->mailer_args([Host => $smtp_host]); my $rv = $sender->send($email); die $rv if ! $rv; } } ####################################################### # # Simple in-memory cache for testing # ####################################################### package UnitTestCache; sub new { return bless({}, shift()); } sub set { my ($self, $key, $value) = @_; $self->{$key} = $value; } sub get { my ($self, $key) = @_; return $self->{$key}; } 1; Email-MIME-CreateHTML-1.030/lib/0000755016522100007720000000000011335311643017176 5ustar jamielengineers00000000000000Email-MIME-CreateHTML-1.030/lib/Email/0000755016522100007720000000000011335311643020225 5ustar jamielengineers00000000000000Email-MIME-CreateHTML-1.030/lib/Email/MIME/0000755016522100007720000000000011335311643020754 5ustar jamielengineers00000000000000Email-MIME-CreateHTML-1.030/lib/Email/MIME/CreateHTML/0000755016522100007720000000000011335311643022644 5ustar jamielengineers00000000000000Email-MIME-CreateHTML-1.030/lib/Email/MIME/CreateHTML/Resolver/0000755016522100007720000000000011335311643024445 5ustar jamielengineers00000000000000Email-MIME-CreateHTML-1.030/lib/Email/MIME/CreateHTML/Resolver/Cached.pm0000644016522100007720000000470611335311613026156 0ustar jamielengineers00000000000000############################################################################### # Purpose : Apply caching to another resolver # Author : John Alden # Created : Aug 2006 # CVS : $Header: /home/cvs/software/cvsroot/email/lib/Email/MIME/CreateHTML/Resolver/Cached.pm,v 1.4 2006/08/24 21:41:38 johna Exp $ ############################################################################### package Email::MIME::CreateHTML::Resolver::Cached; use strict; use Data::Serializer; use URI::Escape; use vars qw($VERSION); $VERSION = sprintf "%d.%03d", q$Revision: 1.4 $ =~ /: (\d+)\.(\d+)/; sub new { my ($class, $args) = @_; my $self = { 'Resolver' => $args->{resolver}, 'Cache' => $args->{object_cache}, 'base' => $args->{base}, }; return bless($self, $class); } sub get_resource { my ($self, $uri) = @_; my $args = {'uri' => $uri, 'base' => $self->{base}, 'resolver' => ref $self->{Resolver}}; my $key = join('&', map {$_ . '=' . URI::Escape::uri_escape($args->{$_})} grep {defined $args->{$_}} sort(keys %$args)); my $cache = $self->{Cache}; my $serialized = $cache->get( $key ); my $ds = Data::Serializer->new(); my @rv; if ( defined $serialized ) { my $deserialized = $ds->deserialize( $serialized ); @rv = @$deserialized; } else { @rv = $self->{Resolver}->get_resource( $uri ); my $serialized = $ds->serialize( \@rv ); $cache->set( $key,$serialized ); } return @rv; } 1; =head1 NAME Email::MIME::CreateHTML::Resolver::Cached - wraps caching around a resource resolver =head1 SYNOPSIS my $o = new Email::MIME::CreateHTML::Resolver::Cached(\%args) my ($content,$filename,$mimetype,$xfer_encoding) = $o->get_resource($uri) =head1 DESCRIPTION This is used by Email::MIME::CreateHTML to load resources. =head1 METHODS =over 4 =item $o = new Email::MIME::CreateHTML::Resolver::Cached(\%args) %args can contain: =over 4 =item base Base URI to resolve URIs passed to get_resource. =item object_cache (mandatory) A cache object =item resolver (mandatory) Another resolver to apply caching to =back =item ($content,$filename,$mimetype,$xfer_encoding) = $o->get_resource($uri) =back =head1 VERSION $Revision: 1.4 $ on $Date: 2006/08/24 21:41:38 $ by $Author: johna $ =head1 AUTHOR Tony Hennessy, Simon Flack and John Alden =head1 COPYRIGHT (c) BBC 2005,2006. This program is free software; you can redistribute it and/or modify it under the GNU GPL. See the file COPYING in this distribution, or http://www.gnu.org/licenses/gpl.txt =cut Email-MIME-CreateHTML-1.030/lib/Email/MIME/CreateHTML/Resolver/LWP.pm0000644016522100007720000000660711335311613025453 0ustar jamielengineers00000000000000############################################################################### # Purpose : Load resources using LWP # Author : John Alden # Created : Aug 2006 # CVS : $Header: /home/cvs/software/cvsroot/email/lib/Email/MIME/CreateHTML/Resolver/LWP.pm,v 1.7 2006/08/24 21:41:38 johna Exp $ ############################################################################### package Email::MIME::CreateHTML::Resolver::LWP; use strict; use Carp; use MIME::Types; use LWP::UserAgent; use vars qw($VERSION); $VERSION = sprintf "%d.%03d", q$Revision: 1.7 $ =~ /: (\d+)\.(\d+)/; sub new { my ($class, $options) = @_; $options ||= {}; my $ua = LWP::UserAgent->new(agent => __PACKAGE__); $ua->env_proxy; # Stop us getting cached resources when they have been updated on the server $ua->default_header( 'Cache-Control' => 'no-cache' ); $ua->default_header( 'Pragma' => 'no-cache' ); my $self = { %$options, 'UA' => $ua, }; return bless($self, $class); } #Resource loader using LWP sub get_resource { my ($self, $src) = @_; my $base = $self->{base}; #Resolve URIs relative to optional base URI my $uri; if(defined $base) { require URI::WithBase; $uri = URI::WithBase->new_abs( $src, $base ); } else { $uri = new URI($src); } #Fetch resource from URI using LWP my $response = $self->{UA}->get($uri->as_string); croak( "Could not fetch ".$uri->as_string." : ".$response->status_line ) unless ($response->is_success); my $content = $response->content; DUMP("HTTP response", $response); #Filename my $path = $uri->path; my ($volume,$directories,$filename) = File::Spec->splitpath( $path ); #Deduce MIME type and transfer encoding my ($mimetype, $encoding); if(defined $filename && length($filename)) { TRACE("Using file extension to deduce MIME type and transfer encoding"); ($mimetype, $encoding) = MIME::Types::by_suffix($filename); } else { $filename = 'index'; } #If we have a content-type header we can make a more informed guess at MIME type if ($response->header('content-type')) { $mimetype = $response->header('content-type'); TRACE("Content Type header: $mimetype"); $mimetype = $1 if($mimetype =~ /(\S+);\s*charset=(.*)$/); #strip down to just a MIME type } #If all else fails then some conservative and general-purpose defaults are: $mimetype ||= 'application/octet-stream'; $encoding ||= 'base64'; #Return values expected from a resource callback return ($content, $filename, $mimetype, $encoding); } sub TRACE {} sub DUMP {} 1; =head1 NAME Email::MIME::CreateHTML::Resolver::LWP - uses LWP as a resource resolver =head1 SYNOPSIS my $o = new Email::MIME::CreateHTML::Resolver::LWP(\%args) my ($content,$filename,$mimetype,$xfer_encoding) = $o->get_resource($uri) =head1 DESCRIPTION This is used by Email::MIME::CreateHTML to load resources. =head1 METHODS =over 4 =item $o = new Email::MIME::CreateHTML::Resolver::LWP(\%args) %args can contain: =over 4 =item base Base URI to resolve URIs passed to get_resource. =back =item ($content,$filename,$mimetype,$xfer_encoding) = $o->get_resource($uri) =back =head1 VERSION $Revision: 1.7 $ on $Date: 2006/08/24 21:41:38 $ by $Author: johna $ =head1 AUTHOR Tony Hennessy, Simon Flack and John Alden =head1 COPYRIGHT (c) BBC 2005,2006. This program is free software; you can redistribute it and/or modify it under the GNU GPL. See the file COPYING in this distribution, or http://www.gnu.org/licenses/gpl.txt =cutEmail-MIME-CreateHTML-1.030/lib/Email/MIME/CreateHTML/Resolver/Filesystem.pm0000644016522100007720000000522711335311613027132 0ustar jamielengineers00000000000000############################################################################### # Purpose : Load resources from the filesystem # Author : John Alden # Created : Aug 2006 # CVS : $Header: /home/cvs/software/cvsroot/email/lib/Email/MIME/CreateHTML/Resolver/Filesystem.pm,v 1.6 2006/08/24 21:41:38 johna Exp $ ############################################################################### package Email::MIME::CreateHTML::Resolver::Filesystem; use strict; use URI::file; use File::Slurp::WithinPolicy 'read_file'; use MIME::Types; use File::Spec; use vars qw($VERSION); $VERSION = sprintf "%d.%03d", q$Revision: 1.6 $ =~ /: (\d+)\.(\d+)/; sub new { my ($class, $options) = @_; $options ||= {}; my $self = {%$options}; return bless($self, $class); } #Simple/secure resource loader from local filesystem sub get_resource { my ($self, $uri) = @_; my $base = $self->{base}; #Handle file:// URIs if necessary my ($path, $base_dir) = map {defined && m|^file://|? URI::file->new($_)->file() : $_} ($uri, $base); #Allow for base dir my $fullpath = defined($base_dir) ? File::Spec->catfile($base_dir,$path) : $path; #Read in the file my $content = read_file($fullpath); my ($volume,$directories,$filename) = File::Spec->splitpath( $path ); #Deduce MIME type/transfer encoding (currently using extension) #We may want to improve the sophistication of this (e.g. making use of $content) my ($mimetype,$encoding) = MIME::Types::by_suffix($filename); return ($content,$filename,$mimetype,$encoding); } 1; =head1 NAME Email::MIME::CreateHTML::Resolver::Filesystem - finds resources via the filesystem =head1 SYNOPSIS my $o = new Email::MIME::CreateHTML::Resolver::Filesystem(\%args) my ($content,$filename,$mimetype,$xfer_encoding) = $o->get_resource($uri) =head1 DESCRIPTION This is used by Email::MIME::CreateHTML to load resources. =head1 METHODS =over 4 =item $o = new Email::MIME::CreateHTML::Resolver::Filesystem(\%args) %args can contain: =over 4 =item base Base directory used to resolve relative filepaths passed to get_resource. =back =item ($content,$filename,$mimetype,$xfer_encoding) = $o->get_resource($uri) =back =head1 TODO - Currently the MIME type is deduced from the file extension via MIME::Types; given we have the content available, more sophisticated strategies are probably possible =head1 VERSION $Revision: 1.6 $ on $Date: 2006/08/24 21:41:38 $ by $Author: johna $ =head1 AUTHOR Tony Hennessy, Simon Flack and John Alden =head1 COPYRIGHT (c) BBC 2005,2006. This program is free software; you can redistribute it and/or modify it under the GNU GPL. See the file COPYING in this distribution, or http://www.gnu.org/licenses/gpl.txt =cutEmail-MIME-CreateHTML-1.030/lib/Email/MIME/CreateHTML/Resolver.pm0000644016522100007720000001024311335311613025000 0ustar jamielengineers00000000000000############################################################################### # Purpose : Pick the most appropriate resolver # Author : John Alden # Created : Aug 2006 # CVS : $Header: /home/cvs/software/cvsroot/email/lib/Email/MIME/CreateHTML/Resolver.pm,v 1.5 2006/08/24 21:41:38 johna Exp $ ############################################################################### package Email::MIME::CreateHTML::Resolver; use strict; use Carp; use vars qw($VERSION $HaveCache $HaveLWP $HaveFilesystem); $VERSION = sprintf "%d.%03d", q$Revision: 1.5 $ =~ /: (\d+)\.(\d+)/; $HaveCache = 0; eval { require Email::MIME::CreateHTML::Resolver::Cached; $HaveCache = 1; }; $HaveLWP = 0; eval { require Email::MIME::CreateHTML::Resolver::LWP; $HaveLWP = 1; }; $HaveFilesystem = 0; eval { require Email::MIME::CreateHTML::Resolver::Filesystem; $HaveFilesystem = 1; }; # # API # sub new { my ($class, $args) = @_; $args ||= {}; #Do some sanity checking of inputs my $resolver = $args->{resolver}; if(defined $resolver) { confess "resolver must be an object" unless ( UNIVERSAL::isa($resolver,'UNIVERSAL') ); confess "resolver does not seem to use the expected interface (get_resource)" unless ($resolver->can('get_resource')); } my $object_cache = $args->{'object_cache'}; if(defined $object_cache ) { confess "object_cache must be an object" unless ( UNIVERSAL::isa($object_cache,'UNIVERSAL') ); confess "object_cache does not seem to use the expected cache interface (get and set methods)" unless ($object_cache->can('get') && $object_cache->can('set')); warn("Caching support is not available - object_cache will not be used") unless($HaveCache); } #Construct object my $self = bless ({ %$args }, $class); return $self; } sub get_resource { my ($self, $uri) = @_; croak("get_resource without a URI") unless(defined $uri && length($uri)); my $resolver = $self->_select_resolver($uri); return $resolver->get_resource($uri); } # # Private methods # sub _select_resolver { my ($self, $uri) = @_; #Look at the start of the URI my $start = (defined $self->{base} && length($self->{base}))? $self->{base} : $uri; #Pick an appropriate resolver... my $resolver; if($self->{resolver}) { #If we've been told to use a specific resolver we'll respect that $resolver = $self->{resolver}; } else { #Decide on the best resolver to use - does URL start with protocol:// TRACE("Start is $start"); if($HaveFilesystem && $start =~ /^file:\/\//){ #Push file URLs through filesystem resolver if available (so File::Policy gets applied) $resolver = new Email::MIME::CreateHTML::Resolver::Filesystem($self); } elsif($start =~ /^\w+:\/\//) { die("External URLs in emails cannot be resolved without the LWP resolver (which is currently not installed)\n") unless($HaveLWP); $resolver = new Email::MIME::CreateHTML::Resolver::LWP($self); } else { die("Local URLs in emails cannot be resolved without the Filesystem resolver (which is currently not installed)\n") unless($HaveFilesystem); $resolver = new Email::MIME::CreateHTML::Resolver::Filesystem($self); } } #Optionally wrap it with caching if($HaveCache && defined $self->{'object_cache'} ) { $resolver = new Email::MIME::CreateHTML::Resolver::Cached({resolver => $resolver, object_cache => $self->{'object_cache'}}); } return $resolver; } sub TRACE {} sub DUMP {} 1; =head1 NAME Email::MIME::CreateHTML::Resolver - provides the appropriate resource resolver =head1 SYNOPSIS my $o = new Email::MIME::CreateHTML::Resolver(\%args) my ($content,$filename,$mimetype,$xfer_encoding) = $o->get_resource($uri) =head1 DESCRIPTION This is used by Email::MIME::CreateHTML to load resources. =head1 METHODS =over 4 =item $o = new Email::MIME::CreateHTML::Resolver(\%args) =item ($content,$filename,$mimetype,$xfer_encoding) = $o->get_resource($uri) =back =head1 VERSION $Revision: 1.5 $ on $Date: 2006/08/24 21:41:38 $ by $Author: johna $ =head1 AUTHOR Tony Hennessy, Simon Flack and John Alden =head1 COPYRIGHT (c) BBC 2005,2006. This program is free software; you can redistribute it and/or modify it under the GNU GPL. See the file COPYING in this distribution, or http://www.gnu.org/licenses/gpl.txt =cut Email-MIME-CreateHTML-1.030/lib/Email/MIME/CreateHTML.pm0000644016522100007720000005026011335311613023202 0ustar jamielengineers00000000000000############################################################################### # Purpose : Build HTML emails # Author : Tony Hennessy # Created : Aug 2006 # CVS : $Header: /home/cvs/software/cvsroot/email/lib/Email/MIME/CreateHTML.pm,v 1.30 2010/02/12 17:44:26 jamiel Exp $ ############################################################################### package Email::MIME::CreateHTML; use strict; use Carp; use Exporter; use Email::MIME; use HTML::TokeParser::Simple; use HTML::Tagset; use Email::MIME::CreateHTML::Resolver; #Globals use vars qw($VERSION %EMBED @EXPORT_OK @ISA); $VERSION = sprintf "%d.%03d", q$Revision: 1.30 $ =~ /: (\d+)\.(\d+)/; %EMBED = ( 'bgsound' => {'src'=>1}, 'body' => {'background'=>1}, 'img' => {'src'=>1}, 'input' => {'src'=>1}, 'table' => {'background'=>1}, 'td' => {'background'=>1}, 'th' => {'background'=>1}, 'tr' => {'background'=>1}, ); @EXPORT_OK = qw(embed_objects parts_for_objects build_html_email); @ISA = qw(Exporter); # # Public routines used by create_html and also exportable # sub embed_objects { my ($html, $args) = @_; my $embed = ( defined $args->{embed} && $args->{embed} eq '0' ) ? 0 : 1; my $inline_css = ( defined $args->{inline_css} && $args->{inline_css} eq '0' ) ? 0 : 1; my $resolver = new Email::MIME::CreateHTML::Resolver($args); my $embed_tags = $args->{'embed_elements'} || \%EMBED; return ($html, {}) unless ( $embed || $inline_css ); #No-op unless one of these is set my ($html_modified, %embedded_cids); my $parser = HTML::TokeParser::Simple->new( \$html ); my $regex = '^(' . join('|',keys %HTML::Tagset::linkElements) . ')'; $regex = qr/$regex/; while ( my $token = $parser->get_token ) { unless ( $token->is_start_tag( $regex ) ) { $html_modified .= $token->as_is; next; } my $token_tag = $token->get_tag(); my $token_attrs = $token->get_attr(); # inline_css if ( $token_tag eq 'link' && $token_attrs->{type} eq 'text/css' ) { unless ( $inline_css ) { $html_modified .= $token->as_is; next; } my $link = $token_attrs->{'href'}; my ($content,$filename,$mimetype,$encoding) = $resolver->get_resource( $link ); $html_modified .= "\n".'\n"; next; } # rewrite and embed for my $attr ( @{ $HTML::Tagset::linkElements{$token_tag} } ) { if ( defined $token_attrs->{$attr} ) { my $link = $token_attrs->{$attr}; next if ($link =~ m/^cid:/i); # embed if ( $embed && $embed_tags->{$token_tag}->{$attr} ) { unless ( defined $embedded_cids{$link} ) { # make a unique cid my $newcid = time().$$.int(rand(1e6)); $embedded_cids{$link} = $newcid; } my $link_rewrite = "cid:".$embedded_cids{$link}; $token->set_attr( $attr => $link_rewrite ); } } } $html_modified .= $token->as_is; } my %objects = reverse %embedded_cids; #invert mapping return ($html_modified, \%objects); } sub parts_for_objects { my ($objects, $args) = @_; my $resolver = new Email::MIME::CreateHTML::Resolver($args); my @html_mime_parts; foreach my $cid (keys %$objects) { croak "Content-Id '$cid' contains bad characters" unless ($cid =~ m/^[\w\-\@\.]+$/); croak "Content-Id must be given" unless length($cid); my $path = $objects->{$cid}; my ($content,$filename,$mimetype,$encoding) = $resolver->get_resource( $path ); $mimetype ||= 'application/octet-stream'; my $newpart = Email::MIME->create( attributes => { content_type => $mimetype, encoding => $encoding, disposition => 'inline', # maybe useful rfc2387 charset => undef, name => $filename, }, body => $content, ); $newpart->header_set('Content-ID',"<$cid>"); # $newpart->header_set("Content-Transfer-Encoding", "base64"); push @html_mime_parts , $newpart; } return @html_mime_parts; } sub build_html_email { my($header, $html, $body_attributes, $html_mime_parts, $plain_text_mime) = @_; my $email; if ( ! scalar(@$html_mime_parts) && ! defined($plain_text_mime) ) { # HTML, no embedded objects, no text alternative $email = Email::MIME->create( header => $header, attributes => $body_attributes, body => $html, ); } elsif ( ! scalar(@$html_mime_parts) && defined($plain_text_mime) ) { # HTML, no embedded objects, with text alternative $email = Email::MIME->create( header => $header, attributes => {content_type=>'multipart/alternative'}, parts => [ $plain_text_mime, Email::MIME->create( attributes => $body_attributes, body => $html, ), ], ); } elsif ( scalar(@$html_mime_parts) && ! defined($plain_text_mime) ) { # HTML with embedded objects, no text alternative $email = Email::MIME->create( header => $header, attributes => {content_type=>'multipart/related'}, parts => [ Email::MIME->create( attributes => $body_attributes, body => $html, ), @$html_mime_parts, ], ); } elsif ( scalar(@$html_mime_parts) && defined($plain_text_mime) ) { # HTML with embedded objects, with text alternative $email = Email::MIME->create( header => $header, attributes => {content_type=>'multipart/alternative'}, parts => [ $plain_text_mime, Email::MIME->create( attributes => {content_type=>'multipart/related'}, parts => [ Email::MIME->create( attributes => $body_attributes, body => $html, ), @$html_mime_parts, ], ), ], ); } return $email; } # Add to Email::MIME package Email::MIME; use strict; use Carp; use Email::MIME::Creator; sub create_html { my ($class, %args) = @_; #Argument checking/defaulting my $html = $args{body} || croak "You must supply a body"; my $objects = $args{'objects'} || undef; # Make plain text Email::MIME object, we will never use this alone so we don't need the headers my $plain_text_mime; if ( exists($args{text_body}) ) { my %text_body_attributes = ( (content_type=>'text/plain'), %{$args{text_body_attributes} || {}} ); $plain_text_mime = $class->create( attributes => \%text_body_attributes, body => $args{text_body}, ); } # Parse the HTML and create a CID mapping for objects to embed my $embedded_cids; ($html, $embedded_cids) = Email::MIME::CreateHTML::embed_objects($html, \%args); # Create parts for each embedded object my @html_mime_parts; push @html_mime_parts, Email::MIME::CreateHTML::parts_for_objects($objects, \%args) if ($objects); push @html_mime_parts, Email::MIME::CreateHTML::parts_for_objects($embedded_cids, \%args) if(%$embedded_cids); # Create the mail my $header = $args{header}; my %body_attributes = ( (content_type=>'text/html'), %{$args{body_attributes} || {}}); my $email = Email::MIME::CreateHTML::build_html_email($header, $html, \%body_attributes, \@html_mime_parts, $plain_text_mime); return $email; } #Log::Trace stubs sub DUMP {} sub TRACE {} 1; __END__ =pod =head1 NAME Email::MIME::CreateHTML - Multipart HTML Email builder =head1 SYNOPSIS use Email::MIME::CreateHTML; my $email = Email::MIME->create_html( header => [ From => 'my@address', To => 'your@address', Subject => 'Here is the information you requested', ], body => $html, text_body => $plain_text ); use Email::Send; my $sender = Email::Send->new({mailer => 'SMTP'}); $sender->mailer_args([Host => 'smtp.example.com']); $sender->send($email); =head1 DESCRIPTION This module allows you to build HTML emails, optionally with a text-only alternative and embedded media objects. For example, an HTML email with an alternative version in plain text and with all the required images contained in the mail. The HTML content is parsed looking for embeddable media objects. A resource loading routine is used to fetch content from those URIs and replace the URIs in the HTML with CIDs. The default resource loading routine is deliberately conservative, only allowing resources to be fetched from the local filesystem. It's possible and relatively straightforward to plug in a custom resource loading routine that can resolve URIs using a broader range of protocols. An example of one using LWP is given later in the L. The MIME structure is then assembled, embedding the content of the resources where appropriate. Note that this module does not send any mail, it merely does the work of building the appropriate MIME message. The message can be sent with L or any other mailer that can be fed a string representation of an email message. =head2 Mail Construction The mail construction is compliant with rfc2557. HTML, no embedded objects (images, flash, etc), no text alternative text/html HTML, no embedded objects, with text alternative multipart/alternative text/plain text/html HTML with embedded objects, no text alternative multipart/related text/html embedded object one embedded object two ... HTML with embedded objects, with text alternative multipart/alternative text/plain multipart/related text/html embedded object one embedded object two ... =head1 METHODS There is only one method, which is installed into the Email::MIME package: =over 4 =item Email::MIME->create_html(%parameters) This method creates an Email::MIME object from a set of named parameters. Of these the C
and C parameters are mandatory and all others are optional. See the L section for more information. =back =head2 LOW-LEVEL API Email::MIME::CreateHTML also defines a lower-level interface of 3 building-block routines that you can use for finer-grain construction of HTML mails. These may be optionally imported: use Email::MIME::CreateHTML qw(embed_objects parts_for_objects build_html_mail); =over 4 =item ($modified_html, $cid_mapping) = embed_objects($html, \%options) This parses the HTML and replaces URIs in the embed list with a CID. The modified HTML and CID to URI mapping is returned. Relevant parameters are: embed inline_css base object_cache resolver The meanings and defaults of these parameters are explained below. =item @mime_parts = parts_for_objects($cid_mapping, \%options) This creates a list of Email::MIME parts for each of the objects in the supplied CID mapping. Relevant options are: base object_cache resolver The meanings and defaults of these parameters are explained below. =item $email = build_html_email(\@headers, $html, \%body_attributes, \@html_mime_parts, $plain_text_mime) The assembles a ready-to-send Email::MIME object (that can be sent with Email::Send). =back =head1 PARAMETERS =over 4 =item header =E I A list reference containing a set of headers to be created. If no Date header is specified, one will be provided for you based on the gmtime() of the local machine. =item body =E I A scalar value holding the HTML message body. =item body_attributes =E I This is passed as the attributes parameter to the C method (supplied by C) that creates the html part of the mail. The body content-type will be set to C unless it is overidden here. =item embed =E I Attach relative images and other media to the message. This is enabled by default. The module will attempt to embed objects defined by C. Note that this option only affects the parsing of the HTML and will not affect the C option. The object's URI will be rewritten as a Content ID. =item embed_elements =E I The set of elements that you want to be embedded. Defaults to the C<%Email::MIME::CreateHTML::EMBED> package global. This should be a data structure of the form: embed_elements => { $elementname_1 => {$attrname_1 => $boolean_1}, $elementname_2 => {$attrname_2 => $boolean_2}, ... } i.e. resource will be embedded if C<$embed_elements-E{$elementname}-E{$attrname}> is true. =item resolver =E I If a resolver is supplied this will be used to fetch the resources that are embedded as MIME objects in the email. If no resolver is given the default behaviour is to choose the best available resolver to read C<$uri> with any C<$base> value prefixed. Resources fetched using the resolver will be cached if an C is supplied. =item base =E I This must be a filepath or a URI. If C is true (the default) then C will be used when fetching the objects. Examples of good bases: ./local/images /home/somewhere/images http://mywebserver/images =item inline_css =E I Inline any CSS external CSS files referenced through link elements. Enabled by default. Some mail clients will only interpret css if it is inlined. =item objects =E I A reference to a hash of external objects. Keys are Content Ids and the values are filepaths or URIs used to fetch the resource with the resolver. We use C to derive the type from the file extension. For example in an HTML mail you would use the file keyed on '12345678@bbc.co.uk' like Cimg src="cid:12345678@bbc.co.uk" alt="a test" width="20" height="20" /E> =item object_cache =E I A cache object can be supplied to cache external resources such as images. This must support the following interface: $o = new ... $o->set($key, $value) $value = $o->get($key) Both the Cache and Cache::Cache distributions on CPAN conform to this. =item text_body =E I A scalar value holding the contents of an additional I message body. =item text_body_attributes =E I This is passed as the attributes parameter to the C method (supplied by C) that creates the plain text part of the mail. The body Content-Type will be set to C unless it is overidden here. =back =head1 GLOBAL VARIABLES =over 4 =item %Email::MIME::CreateHTML::EMBED This is the default set of elements (and the relevant attributes that point at a resource) that will be embedded. The for this is: 'bgsound' => {'src'=>1}, 'body' => {'background'=>1}, 'img' => {'src'=>1}, 'input' => {'src'=>1}, 'table' => {'background'=>1}, 'td' => {'background'=>1}, 'th' => {'background'=>1}, 'tr' => {'background'=>1} You can override this using the C parameter. =back =head1 COOKBOOK =head2 The basics This builds an HTML email: my $email = Email::MIME->create_html( header => [ From => 'my@address', To => 'your@address', Subject => 'My speedy HTML', ], body => $html ); If you want a plaintext alternative, include the C option: my $email = Email::MIME->create_html( header => [ From => 'my@address', To => 'your@address', Subject => 'Here is the information you requested', ], body => $html, text_body => $plain_text #<-- ); If you want your images to remain as links (rather than be embedded in the email) disable the C option: my $email = Email::MIME->create_html( header => [ From => 'my@address', To => 'your@address', Subject => 'My speedy HTML', ], body => $html, embed => 0 #<-- ); =head2 Optimising out HTML parsing By default, the HTML is parsed to look for objects and stylesheets that need embedding. If you are controlling the construction of the HTML yourself, you can use Content Ids as the URIs within your HTML and then pass in a set of objects to associate with those Content IDs: my $html = qq{ My Document

Here is a picture:

}; You then need to create a mapping of the Content IDs to object filenames: my %objects = ( "some_image_jpg@bbc.co.uk" => "/var/html/some_image.jpg" ); Finally you need to disable both the C and C options to turn off HTML parsing, and pass in your mapping: my $quick_to_assemble_mime = Email::MIME->create_html( header => [ From => 'my@address', To => 'your@address', Subject => 'My speedy HTML', ], body => $html, embed => 0, #<-- inline_css => 0, #<-- objects => \%objects #<-- ); =head3 Preprocessing templates If you have for example a personalised newsletter where your HTML will vary slightly from one email to the next, but you don't want to re-parse the HTML each time to re-fetch and attach objects, you can use the C function to pre-process the template, converting URIs into CIDs: use Email::MIME::CreateHTML qw(embed_objects); my ($preproc_tmpl_content, $cid_mapping) = embed_objects($tmpl_content); You can then reuse this and the CID mapping: my $template = compile_template($preproc_tmpl_content); foreach $newsletter (@newsletters) { #Do templating my $html = $template->process($newsletter); #Build MIME structure my $mime = Email::MIME->create_html( header => [ From => $reply_address, To => $newsletter->address, Subject => 'Weekly newsletter', ], body => $html, embed => 0, #Already done inline_css => 0, #Already done objects => $cid_mapping #Here's one we prepared earlier ); #Send email send_email($mime); } Note that one caveat with this approach is that all possible images that might be used in the template will be attached to the email. Depending on your template logic, it may be that some are never actually referenced from within the email (e.g. if an image is conditionally displayed) so this may create unnecessarily large emails. =head2 Plugging in a custom resource resolver A custom resource resolver can be specified by passing your own object to resolver: my $mime = Email::MIME->create_html( header => [ From => 'my@address', To => 'your@address', Subject => 'Here is the information you requested', ], body => $html, base => 'http://internal.foo.co.uk/images/', resolver => new MyResolver, #<-- ); The object needs to have the following API: package MyResolver; sub new { my ($self, $options) = @_; my $base_uri = $options->{base}; #... YOUR CODE HERE ... (probably want to stash $base_uri in $self) } sub get_resource { my ($self, $uri) = @_; my ($content,$filename,$mimetype,$xfer_encoding); #... YOUR CODE HERE ... return ($content,$filename,$mimetype,$xfer_encoding); } where: $uri is the URI of the object we are embedding (taken from the markup or passed in via the CID mapping) $base_uri is base URI used to resolve relative URIs $content is a scalar containing the contents of the file $filename is used to set the name attribute of the Email::MIME object $mimetype is used to set the content_type attribute of the Email::MIME object $xfer_encoding is used to set the encoding attribute of the Email::MIME object (note this is the suitable transfer encoding NOT a character encoding) =head2 Plugging in different types of object cache You can use a cache from the Cache::Cache distribution: use Cache::MemoryCache; my $mime = Email::MIME->create_html( header => \@headers, body => $html, object_cache => new Cache::MemoryCache( { 'namespace' => 'MyNamespace', 'default_expires_in' => 600 } ) ); Or a cache from the Cache distribution: use Cache::File; my $mime = Email::MIME->create_html( header => \@headers, body => $html, object_cache => Cache::File->new( cache_root => '/tmp/mycache', default_expires => '600 sec' ) ); Alternatively you can roll your own. You just need to define an object with get and set methods: my $mime = Email::MIME->create_html( header => \@headers, body => $html, object_cache => new MyCache() ); package MyCache; our %Cache; sub new {return bless({}, shift())} sub get {return $Cache{shift()}} sub set {$Cache{shift()} = shift()} 1; =head1 SEE ALSO Perl Email Project L L, L, L, L =head1 TODO Maybe add option to control the order that the text + html parts appear in the MIME message. =head1 VERSION $Revision: 1.30 $ on $Date: 2010/02/12 17:44:26 $ by $Author: jamiel $ =head1 AUTHOR Tony Hennessy and Simon Flack with cookbook + some refactoring by John Alden =head1 COPYRIGHT (c) BBC 2005,2006. This program is free software; you can redistribute it and/or modify it under the GNU GPL. See the file COPYING in this distribution, or http://www.gnu.org/licenses/gpl.txt =cut Email-MIME-CreateHTML-1.030/COPYING0000644016522100007720000004313111335311613017462 0ustar jamielengineers00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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 Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. Email-MIME-CreateHTML-1.030/Changes0000644016522100007720000000054011335311613017717 0ustar jamielengineers00000000000000Fri Feb 12 17:20:07 2010 - 1.030 Add no_index for Email::MIME Wed Feb 10 13:33:01 2010 - 1.029 Add Carp to fix warnings (CPAN RT51208) Insist on 3.15 of HTML::TokeParser::Simple (CPAN RT 28788) POD Syntax fix Thu Oct 26 10:25:31 2006 - 1.026 * General bug-avoidance changes * Update for PEP website * Misc spelling fixes Email-MIME-CreateHTML-1.030/MANIFEST0000644016522100007720000000124011335311643017556 0ustar jamielengineers00000000000000Changes COPYING Makefile.PL MANIFEST MANIFEST.SKIP README lib/Email/MIME/CreateHTML.pm lib/Email/MIME/CreateHTML/Resolver.pm lib/Email/MIME/CreateHTML/Resolver/LWP.pm lib/Email/MIME/CreateHTML/Resolver/Filesystem.pm lib/Email/MIME/CreateHTML/Resolver/Cached.pm t/Email-MIME-CreateHTML.t t/Email-MIME-CreateHTML-Resolver.t t/data/CreateHTML_01.html t/data/CreateHTML_02a.html t/data/CreateHTML_02b.html t/data/CreateHTML_03a.html t/data/CreateHTML_03b.html t/data/CreateHTML_04a.html t/data/CreateHTML_04b.html t/data/end.png t/data/landscape.jpg t/data/test_style.css t/pod.t t/pod_coverage.t META.yml Module meta-data (added by MakeMaker) Email-MIME-CreateHTML-1.030/MANIFEST.SKIP0000644016522100007720000000020111335311613020314 0ustar jamielengineers00000000000000^blib ^bak ~$ (?:^|/)[Mm]akefile(?:\.old)?$ (?:^|/)pm_to_blib$ ^Email-MIME-CreateHTML-\d+\.\d+\.(zip|tar\.gz|tgz)$ [Cc][Vv][Ss]/ Email-MIME-CreateHTML-1.030/README0000644016522100007720000000105611335311615017311 0ustar jamielengineers00000000000000Email::MIME::CreateHTML v1.030 (c) BBC 2006. This program is free software; you can redistribute it and/or modify it under the GNU GPL. See the file COPYING in this distribution, or http://www.gnu.org/licenses/gpl.txt QUICK START: Install Email::MIME::CreateHTML by unpacking the tarball and running the following commands in the source directory: perl Makefile.PL make make test make install Then delete the source directory tree since it's no longer needed. Run 'perldoc Email::MIME::CreateHTML' to read the full documentation. Email-MIME-CreateHTML-1.030/Makefile.PL0000644016522100007720000000164311335311613020403 0ustar jamielengineers00000000000000use ExtUtils::MakeMaker; WriteMakefile( NAME => 'Email::MIME::CreateHTML', VERSION_FROM => 'lib/Email/MIME/CreateHTML.pm', PREREQ_PM => { Log::Trace => 0, Test::Assertions => 0, Test::Assertions::TestScript => 0, Email::MIME::Creator => 0, HTML::TokeParser::Simple => 3.15, HTML::Tagset => 0, File::Slurp::WithinPolicy => 0, MIME::Types => 0, Data::Serializer => 0, }, ABSTRACT_FROM => 'lib/Email/MIME/CreateHTML.pm', AUTHOR => 'British Broadcasting Corporation', # We don't want to add to Email::MIME's docs, and don't have permission to anyway. META_ADD => { no_index => { package => ['Email::MIME'] } }, ); Email-MIME-CreateHTML-1.030/META.yml0000644016522100007720000000145011335311643017701 0ustar jamielengineers00000000000000--- #YAML:1.0 name: Email-MIME-CreateHTML version: 1.030 abstract: Multipart HTML Email builder author: - British Broadcasting Corporation license: unknown distribution_type: module configure_requires: ExtUtils::MakeMaker: 0 build_requires: ExtUtils::MakeMaker: 0 requires: Data::Serializer: 0 Email::MIME::Creator: 0 File::Slurp::WithinPolicy: 0 HTML::Tagset: 0 HTML::TokeParser::Simple: 3.15 Log::Trace: 0 MIME::Types: 0 Test::Assertions: 0 Test::Assertions::TestScript: 0 no_index: package: - Email::MIME generated_by: ExtUtils::MakeMaker version 6.56 meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: 1.4