Projekt

Allgemein

Profil

« Zurück | Weiter » 

Revision 16cc4053

Von Moritz Bunkus vor etwa 12 Jahren hinzugefügt

  • ID 16cc40534360c3c173d6928070bd2873abee8c0c
  • Vorgänger eb8e38d2
  • Nachfolger a7121495

Selbst geschriebenen E-Mail-Erstellungscode durch Verwendung von Email::MIME ersetzt

Fixt #2035.

Unterschiede anzeigen:

SL/Mailer.pm
5 5
# Web http://www.lx-office.org
6 6
#
7 7
#=====================================================================
8
# SQL-Ledger Accounting
9
# Copyright (C) 2001
10
#
11
#  Author: Dieter Simader
12
#   Email: dsimader@sql-ledger.org
13
#     Web: http://www.sql-ledger.org
14
#
15
# Contributors:
16 8
#
17 9
# This program is free software; you can redistribute it and/or modify
18 10
# it under the terms of the GNU General Public License as published by
......
31 23
package Mailer;
32 24

  
33 25
use Email::Address;
34
use Encode;
26
use Email::MIME::Creator;
35 27
use File::Slurp;
36 28

  
37 29
use SL::Common;
......
43 35
my $num_sent = 0;
44 36

  
45 37
sub new {
46
  $main::lxdebug->enter_sub();
47

  
48 38
  my ($type, %params) = @_;
49 39
  my $self = { %params };
50 40

  
51
  $main::lxdebug->leave_sub();
52

  
53 41
  bless $self, $type;
54 42
}
55 43

  
......
62 50
    myconfig => \%::myconfig,
63 51
  );
64 52

  
65
  my $cfg = $::lx_office_conf{mail_delivery};
66
  if (($cfg->{method} || 'smtp') ne 'smtp') {
67
    require SL::Mailer::Sendmail;
68
    return SL::Mailer::Sendmail->new(%params);
69
  } else {
70
    require SL::Mailer::SMTP;
71
    return SL::Mailer::SMTP->new(%params);
72
  }
73
}
74

  
75
sub mime_quote_text {
76
  $main::lxdebug->enter_sub();
77

  
78
  my ($self, $text, $chars_left) = @_;
53
  my $module = ($::lx_office_conf{mail_delivery}->{method} || 'smtp') ne 'smtp' ? 'SL::Mailer::Sendmail' : 'SL::Mailer::SMTP';
54
  eval "require $module" or return undef;
79 55

  
80
  my $q_start = "=?$self->{charset}?Q?";
81
  my $l_start = length($q_start);
82

  
83
  my $new_text = "$q_start";
84
  $chars_left -= $l_start if (defined $chars_left);
85

  
86
  for (my $i = 0; $i < length($text); $i++) {
87
    my $char = ord(substr($text, $i, 1));
88

  
89
    if (($char < 32) || ($char > 127) || ($char == ord('?')) || ($char == ord('_'))) {
90
      if ((defined $chars_left) && ($chars_left < 5)) {
91
        $new_text .= "?=\n $q_start";
92
        $chars_left = 75 - $l_start;
93
      }
56
  return $module->new(%params);
57
}
94 58

  
95
      $new_text .= sprintf("=%02X", $char);
96
      $chars_left -= 3 if (defined $chars_left);
59
sub _cleanup_addresses {
60
  my ($self) = @_;
97 61

  
98
    } else {
99
      $char = ord('_') if ($char == ord(' '));
100
      if ((defined $chars_left) && ($chars_left < 5)) {
101
        $new_text .= "?=\n $q_start";
102
        $chars_left = 75 - $l_start;
103
      }
62
  foreach my $item (qw(to cc bcc)) {
63
    next unless $self->{$item};
104 64

  
105
      $new_text .= chr($char);
106
      $chars_left-- if (defined $chars_left);
107
    }
65
    $self->{$item} =~ s/\&lt;/</g;
66
    $self->{$item} =~ s/\$<\$/</g;
67
    $self->{$item} =~ s/\&gt;/>/g;
68
    $self->{$item} =~ s/\$>\$/>/g;
108 69
  }
109

  
110
  $new_text .= "?=";
111

  
112
  $main::lxdebug->leave_sub();
113

  
114
  return $new_text;
115 70
}
116 71

  
117
sub send {
118
  $main::lxdebug->enter_sub();
119

  
72
sub _create_message_id {
120 73
  my ($self) = @_;
121 74

  
122
  local (*IN);
123

  
124
  $num_sent++;
125
  my $boundary    = time() . "-$$-${num_sent}";
126
  $boundary       =  "kivitendo-$self->{version}-$boundary";
127
  my $domain      =  $self->{from};
128
  $domain         =~ s/(.*?\@|>)//g;
129
  my $msgid       =  "$boundary\@$domain";
130

  
131
  my $form        =  $main::form;
132
  my $myconfig    =  \%main::myconfig;
75
  $num_sent  +=  1;
76
  my $domain  =  $self->{from};
77
  $domain     =~ s/.*\@//;
78
  $domain     =~ s/>.*//;
133 79

  
134
  my $driver = eval { $self->_create_driver };
135
  if (!$driver) {
136
    $main::lxdebug->leave_sub();
137
    return "send email : $@";
138
  }
80
  return  "kivitendo-$self->{version}-" . time() . "-${$}-${num_sent}\@$domain";
81
}
139 82

  
140
  $self->{charset}     ||= Common::DEFAULT_CHARSET;
141
  $self->{contenttype} ||= "text/plain";
83
sub _create_address_headers {
84
  my ($self) = @_;
142 85

  
143
  foreach my $item (qw(to cc bcc)) {
144
    next unless ($self->{$item});
145
    $self->{$item} =~ s/\&lt;/</g;
146
    $self->{$item} =~ s/\$<\$/</g;
147
    $self->{$item} =~ s/\&gt;/>/g;
148
    $self->{$item} =~ s/\$>\$/>/g;
149
  }
86
  $self->{addresses} = {};
150 87

  
151
  my %addresses;
152
  my $headers = '';
153 88
  foreach my $item (qw(from to cc bcc)) {
154
    $addresses{$item} = [];
155
    next unless ($self->{$item});
89
    $self->{addresses}->{$item} = [];
90
    next if !$self->{$item} || $self->{driver}->keep_from_header($item);
156 91

  
157
    my (@addr_objects) = Email::Address->parse($self->{$item});
158
    next unless (scalar @addr_objects);
92
    my @header_addresses;
159 93

  
160
    foreach my $addr_obj (@addr_objects) {
161
      push @{ $addresses{$item} }, $addr_obj->address;
94
    foreach my $addr_obj (Email::Address->parse($self->{$item})) {
95
      push @{ $self->{addresses}->{$item} }, $addr_obj->address;
162 96
      my $phrase = $addr_obj->phrase();
163 97
      if ($phrase) {
164 98
        $phrase =~ s/^\"//;
165 99
        $phrase =~ s/\"$//;
166
        $addr_obj->phrase($self->mime_quote_text($phrase));
100
        $addr_obj->phrase($phrase);
167 101
      }
168 102

  
169
      $headers .= sprintf("%s: %s\n", ucfirst($item), $addr_obj->format()) unless $driver->keep_from_header($item);
103
      push @header_addresses, $addr_obj->format;
170 104
    }
171
  }
172

  
173
  $headers .= sprintf("Subject: %s\n", $self->mime_quote_text($self->{subject}, 60));
174

  
175
  $driver->start_mail(from => $self->{from}, to => [ map { @{ $addresses{$_} } } qw(to cc bcc) ]);
176 105

  
177
  $driver->print(qq|${headers}Message-ID: <$msgid>
178
X-Mailer: kivitendo $self->{version}
179
MIME-Version: 1.0
180
|);
181

  
182
  if ($self->{attachments}) {
183
    $driver->print(qq|Content-Type: multipart/mixed; boundary="$boundary"\n\n|);
184
    if ($self->{message}) {
185
      $driver->print(qq|--${boundary}
186
Content-Type: $self->{contenttype}; charset="$self->{charset}"
187

  
188
$self->{message}
106
    push @{ $self->{headers} }, ( ucfirst($item) => join(', ', @header_addresses) ) if @header_addresses;
107
  }
108
}
189 109

  
190
|);
191
    }
110
sub _create_attachment_part {
111
  my ($self, $attachment) = @_;
192 112

  
193
    foreach my $attachment (@{ $self->{attachments} }) {
113
  my $source_file_name;
194 114

  
195
      my $filename;
115
  my %attributes = (
116
    disposition  => 'attachment',
117
    encoding     => 'base64',
118
  );
196 119

  
197
      if (ref($attachment) eq "HASH") {
198
        $filename = $attachment->{"name"};
199
        $attachment = $attachment->{"filename"};
200
      } else {
201
        $filename = $attachment;
202
        # strip path
203
        $filename =~ s/(.*\/|\Q$self->{fileid}\E)//g;
204
      }
120
  if (ref($attachment) eq "HASH") {
121
    $attributes{filename} = $attachment->{name};
122
    $source_file_name     = $attachment->{filename};
205 123

  
206
      my $attachment_content = eval { read_file($attachment) };
207
      if (!defined $attachment_content) {
208
        $main::lxdebug->leave_sub();
209
        return "$attachment : $!";
210
      }
211

  
212
      my $application    = ($attachment =~ /(^\w+$)|\.(html|text|txt|sql)$/) ? "text" : "application";
213
      my $content_type   = SL::MIME->mime_type_from_ext($filename);
214
      $content_type      = "${application}/$self->{format}" if (!$content_type && $self->{format});
215
      $content_type    ||= 'application/octet-stream';
124
  } else {
125
    # strip path
126
    $attributes{filename} =  $attachment;
127
    $attributes{filename} =~ s:.*\Q$self->{fileid}\E:: if $self->{fileid};
128
    $attributes{filename} =~ s:.*/::g;
129
    $source_file_name     =  $attachment;
130
  }
216 131

  
217
      # only set charset for attachements of type text. every other type should not have this field
218
      # refer to bug 883 for detailed information
219
      my $attachment_charset;
220
      if (lc $application eq 'text' && $self->{charset}) {
221
        $attachment_charset = qq|; charset="$self->{charset}" |;
222
      }
132
  my $attachment_content = eval { read_file($source_file_name) };
133
  return undef if !defined $attachment_content;
223 134

  
224
      $driver->print(qq|--${boundary}
225
Content-Type: ${content_type}; name="$filename"$attachment_charset
226
Content-Transfer-Encoding: BASE64
227
Content-Disposition: attachment; filename="$filename"\n\n|);
135
  my $application             = ($attachment =~ /(^\w+$)|\.(html|text|txt|sql)$/) ? 'text' : 'application';
136
  $attributes{content_type}   = SL::MIME->mime_type_from_ext($attributes{filename});
137
  $attributes{content_type} ||= "${application}/$self->{format}" if $self->{format};
138
  $attributes{content_type} ||= 'application/octet-stream';
139
  $attributes{charset}        = $self->{charset} if lc $application eq 'text' && $self->{charset};
228 140

  
229
      $driver->print(encode_base64($attachment_content));
230
    }
231
    $driver->print(qq|--${boundary}--\n|);
141
  return Email::MIME->create(
142
    attributes => \%attributes,
143
    body       => $attachment_content,
144
  );
145
}
232 146

  
233
  } else {
234
    $driver->print(qq|Content-Type: $self->{contenttype}; charset="$self->{charset}"
147
sub _create_message {
148
  my ($self) = @_;
235 149

  
236
$self->{message}
237
|);
150
  my @parts;
151

  
152
  if ($self->{message}) {
153
    push @parts, Email::MIME->create(
154
      attributes => {
155
        content_type => $self->{contenttype},
156
        charset      => $self->{charset},
157
        encoding     => 'quoted-printable',
158
      },
159
      body_str => $self->{message},
160
    );
161

  
162
    push @{ $self->{headers} }, (
163
      'Content-Type' => qq|$self->{contenttype}; charset="$self->{charset}"|,
164
    );
238 165
  }
239 166

  
240
  $driver->send;
241

  
242
  $main::lxdebug->leave_sub();
167
  push @parts, grep { $_ } map { $self->_create_attachment_part($_) } @{ $self->{attachments} || [] };
243 168

  
244
  return "";
169
  return Email::MIME->create(
170
    header_str => $self->{headers},
171
    parts      => \@parts,
172
  );
245 173
}
246 174

  
247
sub encode_base64 ($;$) {
248
  $main::lxdebug->enter_sub();
175
sub send {
176
  my ($self) = @_;
249 177

  
250
  # this code is from the MIME-Base64-2.12 package
251
  # Copyright 1995-1999,2001 Gisle Aas <gisle@ActiveState.com>
178
  # Create driver for delivery method (sendmail/SMTP)
179
  $self->{driver} = eval { $self->_create_driver };
180
  if (!$self->{driver}) {
181
    $::lxdebug->leave_sub();
182
    return "send email : $@";
183
  }
252 184

  
253
  my $res = "";
254
  my $eol = $_[1];
255
  $eol = "\n" unless defined $eol;
256
  pos($_[0]) = 0;    # ensure start at the beginning
185
  # Set defaults & headers
186
  $self->{charset}     ||=  Common::DEFAULT_CHARSET;
187
  $self->{contenttype} ||=  "text/plain";
188
  $self->{headers}       =  [
189
    Subject              => $self->{subject},
190
    'Message-ID'         => $self->_create_message_id,
191
    'X-Mailer'           => "kivitendo $self->{version}",
192
  ];
257 193

  
258
  $res = join '', map(pack('u', $_) =~ /^.(\S*)/, ($_[0] =~ /(.{1,45})/gs));
194
  # Clean up To/Cc/Bcc address fields
195
  $self->_cleanup_addresses;
196
  $self->_create_address_headers;
259 197

  
260
  $res =~ tr|` -_|AA-Za-z0-9+/|;    # `# help emacs
261
                                    # fix padding at the end
262
  my $padding = (3 - length($_[0]) % 3) % 3;
263
  $res =~ s/.{$padding}$/'=' x $padding/e if $padding;
198
  my $email = $self->_create_message;
264 199

  
265
  # break encoded string into lines of no more than 60 characters each
266
  if (length $eol) {
267
    $res =~ s/(.{1,60})/$1$eol/g;
268
  }
200
  # $::lxdebug->message(0, "message: " . $email->as_string);
201
  # return "boom";
269 202

  
270
  $main::lxdebug->leave_sub();
203
  $self->{driver}->start_mail(from => $self->{from}, to => [ map { @{ $self->{addresses}->{$_} } } qw(to cc bcc) ]);
204
  $self->{driver}->print($email->as_string);
205
  $self->{driver}->send;
271 206

  
272
  return $res;
207
  return '';
273 208
}
274 209

  
275 210
1;

Auch abrufbar als: Unified diff