Revision a40f0c2f
Von Martin Helmling martin.helmling@octosoft.eu vor etwa 7 Jahren hinzugefügt
SL/Mailer.pm | ||
---|---|---|
24 | 24 |
package Mailer; |
25 | 25 |
|
26 | 26 |
use Email::Address; |
27 |
use Email::MIME::Creator; |
|
27 |
use MIME::Entity; |
|
28 |
use MIME::Parser; |
|
29 |
use File::MimeInfo::Magic; |
|
28 | 30 |
use File::Slurp; |
29 | 31 |
use List::UtilsBy qw(bundle_by); |
30 | 32 |
|
... | ... | |
32 | 34 |
use SL::DB::EmailJournal; |
33 | 35 |
use SL::DB::EmailJournalAttachment; |
34 | 36 |
use SL::DB::Employee; |
35 |
use SL::MIME; |
|
36 | 37 |
use SL::Template; |
37 | 38 |
|
38 | 39 |
use strict; |
40 |
use Encode; |
|
39 | 41 |
|
40 | 42 |
my $num_sent = 0; |
43 |
my $parser; |
|
41 | 44 |
|
42 | 45 |
my %mail_delivery_modules = ( |
43 | 46 |
sendmail => 'SL::Mailer::Sendmail', |
... | ... | |
47 | 50 |
sub new { |
48 | 51 |
my ($type, %params) = @_; |
49 | 52 |
my $self = { %params }; |
53 |
$parser = new MIME::Parser; |
|
54 |
$parser->output_under("users"); |
|
50 | 55 |
|
51 | 56 |
bless $self, $type; |
52 | 57 |
} |
... | ... | |
117 | 122 |
$addr_obj->phrase($phrase); |
118 | 123 |
} |
119 | 124 |
|
120 |
push @header_addresses, $addr_obj->format;
|
|
125 |
push @header_addresses, encode('MIME-Header',$addr_obj->format);
|
|
121 | 126 |
} |
122 | 127 |
|
123 | 128 |
push @{ $self->{headers} }, ( ucfirst($item) => join(', ', @header_addresses) ) if @header_addresses; |
... | ... | |
128 | 133 |
my ($self, $attachment) = @_; |
129 | 134 |
|
130 | 135 |
my %attributes = ( |
131 |
disposition => 'attachment',
|
|
132 |
encoding => 'base64',
|
|
136 |
Disposition => 'attachment',
|
|
137 |
Encoding => 'base64',
|
|
133 | 138 |
); |
134 | 139 |
|
140 |
my $file_id = 0; |
|
135 | 141 |
my $attachment_content; |
142 |
my $email_journal = $::instance_conf->get_email_journal; |
|
136 | 143 |
|
144 |
$main::lxdebug->message(LXDebug->DEBUG2(), "mail5 att=".$attachment." email_journal=". $email_journal." id=".$attachment->{id}); |
|
137 | 145 |
if (ref($attachment) eq "HASH") { |
138 |
$attributes{filename} = $attachment->{name}; |
|
139 |
$attachment_content = $attachment->{content} // eval { read_file($attachment->{filename}) }; |
|
146 |
$attributes{Path} = $attachment->{path} || $attachment->{filename}; |
|
147 |
$attributes{Filename} = $attachment->{name}; |
|
148 |
$file_id = $attachment->{id} || '0'; |
|
149 |
$attributes{Type} = $attachment->{type} || 'application/pdf'; |
|
150 |
$attachment_content = eval { read_file($attachment->{path}) } if $email_journal > 1; |
|
140 | 151 |
|
141 | 152 |
} else { |
142 | 153 |
# strip path |
143 |
$attributes{filename} = $attachment; |
|
144 |
$attributes{filename} =~ s:.*\Q$self->{fileid}\E:: if $self->{fileid}; |
|
145 |
$attributes{filename} =~ s:.*/::g; |
|
146 |
$attachment_content = eval { read_file($attachment) }; |
|
154 |
$attributes{Path} = $attachment; |
|
155 |
$attributes{Filename} = $attachment; |
|
156 |
$attributes{Filename} =~ s:.*\Q$self->{fileid}\E:: if $self->{fileid}; |
|
157 |
$attributes{Filename} =~ s:.*/::g; |
|
158 |
|
|
159 |
my $application = ($attachment =~ /(^\w+$)|\.(html|text|txt|sql)$/) ? 'text' : 'application'; |
|
160 |
$attributes{Type} = File::MimeInfo::Magic::magic($attachment); |
|
161 |
$attributes{Type} ||= "${application}/$self->{format}" if $self->{format}; |
|
162 |
$attributes{Type} ||= 'application/octet-stream'; |
|
163 |
$attachment_content = eval { read_file($attachment) } if $email_journal > 1; |
|
147 | 164 |
} |
148 | 165 |
|
149 |
return undef if !defined $attachment_content; |
|
166 |
return undef if $email_journal > 1 && !defined $attachment_content; |
|
167 |
$attachment_content ||= ' '; |
|
168 |
$main::lxdebug->message(LXDebug->DEBUG2(), "mail6 mtype=".$attributes{Type}." path=". |
|
169 |
$attributes{Path}." filename=".$attributes{Filename}); |
|
150 | 170 |
|
151 |
my $application = ($attachment =~ /(^\w+$)|\.(html|text|txt|sql)$/) ? 'text' : 'application'; |
|
152 |
$attributes{content_type} = SL::MIME->mime_type_from_ext($attributes{filename}); |
|
153 |
$attributes{content_type} ||= "${application}/$self->{format}" if $self->{format}; |
|
154 |
$attributes{content_type} ||= 'application/octet-stream'; |
|
155 |
$attributes{charset} = $self->{charset} if lc $application eq 'text' && $self->{charset}; |
|
171 |
# $attributes{Charset} = $self->{charset} if lc $application eq 'text' && $self->{charset}; |
|
172 |
$attributes{Charset} = $self->{charset} if $self->{charset}; |
|
156 | 173 |
|
157 |
return Email::MIME->create( |
|
158 |
attributes => \%attributes, |
|
159 |
body => $attachment_content, |
|
174 |
my $ent; |
|
175 |
if ( $attributes{Type} eq 'message/rfc822' ) { |
|
176 |
my $fh = IO::File->new($attributes{Path}, "r"); |
|
177 |
if (! defined $fh) { |
|
178 |
return undef; |
|
179 |
} |
|
180 |
$ent = $parser->parse($fh); |
|
181 |
undef $fh; |
|
182 |
my $head = $ent->head; |
|
183 |
$head->replace('Content-disposition','attachment; filename='.$attributes{Filename}); |
|
184 |
} else { |
|
185 |
$ent = MIME::Entity->build(%attributes); |
|
186 |
} |
|
187 |
push @{ $self->{mail_attachments}} , SL::DB::EmailJournalAttachment->new( |
|
188 |
name => $attributes{Filename}, |
|
189 |
mime_type => $attributes{Type}, |
|
190 |
content => ( $email_journal > 1 ? $attachment_content : ' '), |
|
191 |
file_id => $file_id, |
|
160 | 192 |
); |
193 |
return $ent; |
|
161 | 194 |
} |
162 | 195 |
|
163 | 196 |
sub _create_message { |
164 | 197 |
my ($self) = @_; |
165 | 198 |
|
166 |
my @parts;
|
|
167 |
|
|
199 |
push @{ $self->{headers} }, ('Type' =>"multipart/mixed" );
|
|
200 |
my $top = MIME::Entity->build(@{$self->{headers}}); |
|
168 | 201 |
if ($self->{message}) { |
169 |
push @parts, Email::MIME->create( |
|
170 |
attributes => { |
|
171 |
content_type => $self->{contenttype}, |
|
172 |
charset => $self->{charset}, |
|
173 |
encoding => 'quoted-printable', |
|
174 |
}, |
|
175 |
body_str => $self->{message}, |
|
176 |
); |
|
177 |
|
|
178 |
push @{ $self->{headers} }, ( |
|
179 |
'Content-Type' => qq|$self->{contenttype}; charset="$self->{charset}"|, |
|
180 |
); |
|
202 |
$top->attach(Data => encode($self->{charset},$self->{message}), |
|
203 |
Charset => $self->{charset}, |
|
204 |
Type => $self->{contenttype}, |
|
205 |
Encoding => 'quoted-printable'); |
|
181 | 206 |
} |
182 | 207 |
|
183 |
push @parts, grep { $_ } map { $self->_create_attachment_part($_) } @{ $self->{attachments} || [] }; |
|
184 |
|
|
185 |
return Email::MIME->create( |
|
186 |
header_str => $self->{headers}, |
|
187 |
parts => \@parts, |
|
188 |
); |
|
208 |
map { $top->add_part($self->_create_attachment_part($_)) } @{ $self->{attachments} || [] }; |
|
209 |
return $top; |
|
189 | 210 |
} |
190 | 211 |
|
191 | 212 |
sub send { |
... | ... | |
202 | 223 |
$self->{charset} = 'UTF-8'; |
203 | 224 |
$self->{contenttype} ||= "text/plain"; |
204 | 225 |
$self->{headers} = [ |
205 |
Subject => $self->{subject},
|
|
226 |
Subject => encode('MIME-Header',$self->{subject}),
|
|
206 | 227 |
'Message-ID' => '<' . $self->_create_message_id . '>', |
207 | 228 |
'X-Mailer' => "kivitendo $self->{version}", |
208 | 229 |
]; |
230 |
$self->{mail_attachments} = []; |
|
231 |
$self->{content_by_name} = $::instance_conf->get_email_journal == 1 && $::instance_conf->get_doc_files; |
|
209 | 232 |
|
210 | 233 |
my $error; |
211 | 234 |
my $ok = eval { |
... | ... | |
215 | 238 |
|
216 | 239 |
my $email = $self->_create_message; |
217 | 240 |
|
218 |
# $::lxdebug->message(0, "message: " . $email->as_string);
|
|
241 |
#$::lxdebug->message(0, "message: " . $email->as_string); |
|
219 | 242 |
# return "boom"; |
220 | 243 |
|
221 |
$self->{driver}->start_mail(from => $self->{from}, to => [ $self->_all_recipients ]);
|
|
244 |
$self->{driver}->start_mail(from => encode('MIME-Header',$self->{from}), to => [ $self->_all_recipients ]);
|
|
222 | 245 |
$self->{driver}->print($email->as_string); |
223 | 246 |
$self->{driver}->send; |
224 | 247 |
|
... | ... | |
227 | 250 |
|
228 | 251 |
$error = $@ if !$ok; |
229 | 252 |
|
230 |
$self->_store_in_journal; |
|
253 |
$self->{journalentry} = $self->_store_in_journal; |
|
254 |
$parser->filer->purge; |
|
231 | 255 |
|
232 | 256 |
return $ok ? '' : "send email: $error"; |
233 | 257 |
} |
234 | 258 |
|
235 | 259 |
sub _all_recipients { |
236 | 260 |
my ($self) = @_; |
237 |
|
|
238 | 261 |
$self->{addresses} ||= {}; |
239 | 262 |
return map { @{ $self->{addresses}->{$_} || [] } } qw(to cc bcc); |
240 | 263 |
} |
... | ... | |
251 | 274 |
$extended_status //= $self->{driver}->extended_status if $self->{driver}; |
252 | 275 |
$extended_status //= 'unknown error'; |
253 | 276 |
|
254 |
my @attachments; |
|
255 |
|
|
256 |
@attachments = grep { $_ } map { |
|
257 |
my $part = $self->_create_attachment_part($_); |
|
258 |
if ($part) { |
|
259 |
SL::DB::EmailJournalAttachment->new( |
|
260 |
name => $part->filename, |
|
261 |
mime_type => $part->content_type, |
|
262 |
content => $part->body, |
|
263 |
) |
|
264 |
} |
|
265 |
} @{ $self->{attachments} || [] } if $journal_enable > 1; |
|
266 |
|
|
267 | 277 |
my $headers = join "\r\n", (bundle_by { join(': ', @_) } 2, @{ $self->{headers} || [] }); |
268 | 278 |
|
269 |
SL::DB::EmailJournal->new( |
|
279 |
my $jentry = SL::DB::EmailJournal->new(
|
|
270 | 280 |
sender => SL::DB::Manager::Employee->current, |
271 | 281 |
from => $self->{from} // '', |
272 | 282 |
recipients => join(', ', $self->_all_recipients), |
... | ... | |
274 | 284 |
headers => $headers, |
275 | 285 |
body => $self->{message} // '', |
276 | 286 |
sent_on => DateTime->now_local, |
277 |
attachments => \@attachments,
|
|
287 |
attachments => \@{ $self->{mail_attachments} },
|
|
278 | 288 |
status => $status, |
279 | 289 |
extended_status => $extended_status, |
280 | 290 |
)->save; |
291 |
return $jentry->id; |
|
281 | 292 |
} |
282 | 293 |
|
283 | 294 |
1; |
Auch abrufbar als: Unified diff
Dateimanagement: Alle Anhänge per E-Mail versendbar machen
Es können an eine E-Mail alle Anhänge eines Dokumentes,
sowie die Anhänge am Kunden/Liefranten sowie die Anhänge an Artikeln
mitgesendet werden.
Falls ein Dokument bereits existiert muss es nicht noch neu erzeugt werden.
Als MIME Types werden die bereits in der Datenbank abgespeicherten Typen verwendet.
Es werden in Perl nun MIME::Entity und MIME::Parser verwendet,
deshalb ist der installationcheck erweitet.