1 # Copyright 2002-2010 Interchange Development Group and others
3 # This program is free software; you can redistribute it and/or modify
4 # it under the terms of the GNU General Public License as published by
5 # the Free Software Foundation; either version 2 of the License, or
6 # (at your option) any later version. See the LICENSE file for details.
8 UserTag email Order to subject reply from extra
9 UserTag email hasEndTag
11 UserTag email Interpolate
12 UserTag email Routine <<EOR
23 my ($string, $encoding) = @_;
24 return $string unless defined Encode::PERLQQ(); # nop if no Encode
26 unless(Encode::is_utf8($string)){
27 $string = Encode::decode('utf-8', $string);
29 return Encode::encode($encoding, $string);
33 my ($to, $subject, $reply, $from, $extra, $opt, $body) = @_;
35 my ($cc, $bcc, @extra, $utf8);
39 $subject = '<no subject>' unless defined $subject && $subject;
42 $from = $Vend::Cfg->{MailOrderTo};
46 # Use local copy to avoid mangling with caller's data
50 # See if UTF-8 support is required
51 $utf8 = $::Variable->{MV_UTF8} || $Global::Variable->{MV_UTF8};
53 # Prevent header injections from spammers' hostile content
54 for ($to, $subject, $reply, $from, $cc, $bcc) {
55 # unfold valid RFC 2822 "2.2.3. Long Header Fields"
57 # now remove any invalid extra lines left over
59 and ::logError("Header injection attempted in email tag: %s", $1);
63 for (grep /\S/, split /[\r\n]+/, $extra) {
64 # require header conformance with RFC 2822 section 2.2
65 push (@extra, $_), next if /^[\x21-\x39\x3b-\x7e]+:[\x00-\x09\x0b\x0c\x0e-\x7f]+$/;
66 ::logError("Invalid header given to email tag: %s", $_);
68 unshift @extra, "From: $from" if $from;
70 # force utf8 email through MIME as attachment
71 unless (($opt->{attach} || $opt->{html}) && $utf8){
72 $opt->{body_mime} = $opt->{mimetype};
73 $body = utf8_to_other($body, 'utf-8');
76 my $sent_with_attach = 0;
79 #::logDebug("Checking for attachment");
80 last ATTACH unless $opt->{attach} || $opt->{html};
82 unless ($Have_mime_lite) {
83 ::logError("email tag: attachment without MIME::Lite installed.");
89 $opt->{mimetype} ||= 'multipart/alternative';
90 $att1_format = 'flowed';
93 $opt->{mimetype} ||= 'multipart/mixed';
96 my $att = $opt->{attach};
100 # encode values if utf8 is supported
102 $to = utf8_to_other($to, 'MIME-Header');
103 $from = utf8_to_other($from, 'MIME-Header');
104 $subject = utf8_to_other($subject, 'MIME-Header');
105 $cc = utf8_to_other($cc, 'MIME-Header');
106 $bcc = utf8_to_other($bcc, 'MIME-Header');
107 $reply = utf8_to_other($reply, 'MIME-Header');
110 my $msg = new MIME::Lite
114 Type => $opt->{mimetype},
117 'Reply-To' => $reply,
122 my $name = $1 or next;
123 next if lc($name) eq 'from';
124 my $content = $2 or next;
125 $name =~ s/[-_]+/-/g;
126 $name =~ s/\b(\w)/\U$1/g;
127 $msg->add($name, ($utf8 ? utf8_to_other($content, 'UTF-8')
129 if $name && $content;
132 $opt->{body_mime} ||= 'text/plain' . ($utf8 ? '; charset=UTF-8' : '');
133 $opt->{body_encoding} ||= 'quoted-printable';
135 Type => $opt->{body_mime},
136 Encoding => $opt->{body_encoding},
138 Disposition => $opt->{body_disposition} || 'inline',
139 Format => $opt->{body_format} || $att1_format,
144 $att = [ { path => $fn } ];
146 elsif(ref($att) eq 'HASH') {
149 elsif(ref($att) eq 'ARRAY') {
150 # turn array of file names into array of hash references
155 push (@$new_att, $_);
158 push (@$new_att, {path => $_});
168 unshift @$att, {type => 'text/html'
169 .($utf8 ? '; charset=UTF-8': ''),
170 data => ($utf8 ? utf8_to_other($opt->{html}, 'UTF-8') : $opt->{html}),
171 disposition => 'inline',
175 my %encoding_types = (
176 'text/plain' => ($utf8 ? 'quoted-printable' : '8bit'),
177 'text/html' => 'quoted-printable',
180 for my $ref (@$att) {
182 next unless $ref->{path} || $ref->{data};
183 unless ($ref->{filename}) {
184 my $fn = $ref->{path};
186 $ref->{filename} = $fn;
189 $ref->{type} ||= 'AUTO';
190 $ref->{disposition} ||= 'attachment';
192 if(! $ref->{encoding}) {
193 $ref->{encoding} = $encoding_types{$ref->{type}};
197 Type => $ref->{type},
198 Path => $ref->{path},
200 Data => $ref->{data},
201 Filename => $ref->{filename},
202 Encoding => $ref->{encoding},
203 Disposition => $ref->{disposition},
207 ::logError("email tag: failed to attach %s: %s", $ref->{path}, $@);
208 $Tag->error({name => 'email',
209 set => errmsg('Failed to attach %s', $ref->{path})});
214 my $body = $msg->body_as_string;
215 my $header = $msg->header_as_string;
216 #::logDebug("[email] Mail: \n$header\n$body");
218 return "$header\n$body";
221 last ATTACH unless $header;
222 my @head = split(/\r?\n/,$header);
223 $ok = send_mail(\@head,$body);
225 $sent_with_attach = 1;
229 $reply = '' unless defined $reply;
230 $reply = "Reply-to: $reply\n" if $reply;
233 push(@extra, "Cc: $cc");
237 push(@extra, "Bcc: $bcc");
240 if ($utf8 && ! $opt->{mimetype}) {
241 push(@extra, 'MIME-Version: 1.0');
242 push(@extra, 'Content-Type: text/plain; charset=UTF-8');
243 push(@extra, 'Content-Transfer-Encoding: 8bit');
246 $ok = send_mail($to, $subject, $body, $reply, 0, @extra)
247 unless $sent_with_attach;
250 logError("Unable to send mail using $Vend::Cfg->{SendMailProgram}\n" .
253 "With extra headers '$extra'\n" .
254 "With reply-to '$reply'\n" .
255 "With subject '$subject'\n" .
259 return $opt->{hide} ? '' : $ok;