100 } |
102 } |
101 if ($opt_pod) { |
103 if ($opt_pod) { |
102 system("pod2usage -v 3 $0") and exit 0; |
104 system("pod2usage -v 3 $0") and exit 0; |
103 } |
105 } |
104 |
106 |
105 $out_std = *STDOUT; |
|
106 |
|
107 if ($opt_fake) { $opt_debug = 1 } |
107 if ($opt_fake) { $opt_debug = 1 } |
108 if ($opt_log) { |
108 if ($opt_log) { |
109 die "$ME: can't fake in log mode!\n" if $opt_fake; |
109 die "$ME: can't fake in log mode!\n" if $opt_fake; |
110 die "$ME: can't debug in log mode!\n" if $opt_debug; |
110 die "$ME: can't debug in log mode!\n" if $opt_debug; |
111 open(STDERR, ">> $LOGDIR/$ME.log") |
111 open(STDERR, ">> $LOGDIR/$ME.log") |
112 or die "$ME: can't open logfile!\n"; |
112 or die "$ME: can't open logfile!\n"; |
113 $opt_debug = 1; |
113 $opt_debug = 1; |
114 } |
114 } |
115 elsif (!$opt_debug) { open(STDERR, ">/dev/null") } |
115 elsif (!$opt_debug) { open(STDERR, ">/dev/null") } |
116 |
116 |
117 if ($opt_strain) { |
117 if ($opt_strain) { |
118 if ($opt_fake) { |
118 if ($opt_fake) { |
119 print $out_err "$ME: can't fake in strain mode!\n"; |
119 warn "$ME: can't fake in strain mode!\n"; |
120 exit 0; |
120 exit 0; |
121 } |
121 } |
122 if ($opt_text) { |
122 if ($opt_text) { |
123 print $out_err "$ME: can't use text-only in strain mode!\n"; |
123 warn "$ME: can't use text-only in strain mode!\n"; |
124 exit 0; |
124 exit 0; |
125 } |
125 } |
126 } |
126 } |
127 |
127 |
128 $mimes = read_conf($conf_mimes) unless $opt_text; |
128 @mimes = read_conf($conf_mimes) unless $opt_text; |
129 $vips = read_conf($conf_vips); |
129 @vips = read_conf($conf_vips); |
130 |
130 |
131 if ($opt_mimes) { |
131 if ($opt_mimes) { |
132 $mimes =~ s/\|/\n/g; |
132 local $" = ", "; |
133 print "$mimes\n"; |
133 print "mimes: @mimes\n"; |
134 exit 0 unless $opt_vips; |
134 exit 0; |
135 } |
135 } |
136 if ($opt_vips) { |
136 if ($opt_vips) { |
137 $vips =~ s/\|/\n/g; |
137 local $" = ", "; |
138 print "$vips\n"; |
138 print "vips: @vips\n"; |
139 exit 0; |
139 exit 0; |
140 } |
140 } |
141 |
141 |
142 die "$ME: no mail on stdin!\n$!" if (-z *STDIN); |
142 die "$ME: no mail on stdin!\n$!" if (-z *STDIN); |
143 |
143 |
144 ### |
144 ### |
145 |
145 |
146 my $mail = new_parser(); |
146 my $parser = new MIME::Parser; |
147 my %data = get_mail_data($mail); |
147 $parser->output_to_core(1); # FIXME: was ist bei sehr großen Mails? |
148 |
148 |
149 print $out_err "\n$ME\[$MEPID\]: " . scalar localtime() . "\n"; |
149 my $mail = $parser->parse(\*STDIN); |
150 print $out_err "<< " . $data{from} . "\n>> " . $data{to} . "\n"; |
150 my %header = get_mail_header($mail); |
|
151 |
|
152 warn "\n$ME\[$MEPID\]: @{[scalar localtime]}\n" |
|
153 . "<<$header{from}\n" |
|
154 . ">>$header{to}\n"; |
151 |
155 |
152 if ($opt_strain) { |
156 if ($opt_strain) { |
153 print $out_err " STRAIN MODE\n"; |
157 warn " STRAIN MODE\n"; |
154 new_mail_send($mail); |
158 new_mail_send($mail); |
155 exit 0; |
159 exit 0; |
156 } |
160 } |
157 |
161 |
158 if (my $result = check_vip(%data)) { |
162 { |
159 print $out_err " $result\n"; |
163 #### checking vips: \@vips |
160 new_mail_send($mail); |
164 my $result; |
161 exit 0; |
165 if (@vips and $result = check_vip(\@vips, %header)) { |
162 } |
166 warn " $result\n"; |
163 |
167 new_mail_send($mail); |
164 if ($data{mtype} =~ /multipart/) { |
168 exit 0; |
|
169 } |
|
170 } |
|
171 |
|
172 if ($header{mtype} =~ /multipart/) { |
165 |
173 |
166 my $hl1 = '-' x 32; |
174 my $hl1 = '-' x 32; |
167 my $hl2 = '-' x 8; |
175 my $hl2 = '-' x 8; |
168 my $hl3 = '-' x 36; |
176 my $hl3 = '-' x 36; |
169 my $hl = '-' x 78; |
177 my $hl = '-' x 78; |
170 |
178 |
171 print $out_err ",$hl1.$hl2.$hl3.\n"; |
179 warn ",$hl1.$hl2.$hl3.\n"; |
172 printf $out_err "| %-30s | %-6s | %-34s |\n", "part [subparts]", |
180 warn sprintf "| %-30s | %-6s | %-34s |\n", "part [subparts]", |
173 "status", "filename"; |
181 "status", "filename"; |
174 print $out_err "+$hl1+$hl2+$hl3+\n"; |
182 warn "+$hl1+$hl2+$hl3+\n"; |
175 |
183 |
176 my $mail_new = new_mail(%data); |
184 my $mail_new = new_mail(%header); |
177 $mail_new = check_multipart($mail, $mail_new); |
185 $mail_new = check_multipart(\@mimes, $mail, $mail_new, 0); |
178 |
186 |
179 print $out_err "`$hl1'$hl2'$hl3'\n"; |
187 warn "`$hl1'$hl2'$hl3'\n"; |
180 new_mail_send($mail_new); |
188 new_mail_send($mail_new); |
181 exit 0; |
189 exit 0; |
182 |
190 |
183 } |
191 } |
184 else { |
192 else { |
185 |
193 |
186 print $out_err "** SINGLEPART\n"; |
194 warn "** SINGLEPART\n"; |
187 new_mail_send($mail); |
195 new_mail_send($mail); |
188 exit 0; |
196 exit 0; |
189 } |
197 } |
190 } |
198 } |
191 |
199 |
196 |
204 |
197 die "$ME: can't find $conf!\n" if (!-e $conf); |
205 die "$ME: can't find $conf!\n" if (!-e $conf); |
198 |
206 |
199 my $fh; |
207 my $fh; |
200 open($fh, "< $conf") or die "$ME: can't read $conf!\n"; |
208 open($fh, "< $conf") or die "$ME: can't read $conf!\n"; |
201 my $re = join('|', my @re = grep (!/(?:^\s{0,}#|^\s{0,}$)/, <$fh>)); |
209 |
202 $re =~ s/(?:\n|\s)//g; |
210 return map { chomp; $_ } grep !/(?:^\s*#|^\s*$)/, <$fh>; |
203 return $re; |
211 } |
204 } |
212 |
205 |
213 sub get_mail_header($) { |
206 sub new_parser() { |
214 my $mail = shift; |
207 my $parser = new MIME::Parser; |
215 |
208 $parser->output_to_core(1); |
216 my %data; |
209 return $parser->parse(\*STDIN); |
217 |
210 } |
218 $data{mtype} = $mail->mime_type; |
211 |
219 $data{preamble} = $mail->preamble || ''; |
212 sub get_mail_data($) { |
220 $data{epilogue} = $mail->epilogue || ''; |
213 my $mail = shift @_; |
221 $data{date} = $mail->head->get('Date') || ''; |
214 |
222 $data{subject} = $mail->head->get('Subject') || ''; |
215 my $mt = $mail->mime_type; |
223 $data{from} = $mail->head->get('From') || ''; |
216 my $pr = $mail->preamble || ''; |
224 $data{to} = $mail->head->get('To') || ''; |
217 my $ep = $mail->epilogue || ''; |
225 $data{cc} = $mail->head->get('CC') || ''; |
218 my $da = $mail->head->get('Date') || ''; |
226 $data{bcc} = $mail->head->get('BCC') || ''; |
219 my $su = $mail->head->get('Subject') || ''; |
227 |
220 my $fr = $mail->head->get('From') || ''; |
228 map { chomp } values %data; |
221 my $to = $mail->head->get('To') || ''; |
|
222 my $cc = $mail->head->get('CC') || ''; |
|
223 my $bc = $mail->head->get('BCC') || ''; |
|
224 |
|
225 chomp($mt, $pr, $ep, $da, $su, $fr, $to, $cc, $bc); |
|
226 |
|
227 my %data = (mtype => $mt, |
|
228 preamble => $pr, |
|
229 epilogue => $ep, |
|
230 date => $da, |
|
231 subject => $su, |
|
232 from => $fr, |
|
233 to => $to, |
|
234 cc => $cc, |
|
235 bcc => $bc, |
|
236 ); |
|
237 |
229 |
238 return (%data); |
230 return (%data); |
239 } |
231 } |
240 |
232 |
241 sub new_mail(%) { |
233 sub new_mail(%) { |
242 my %data = @_; |
234 my %data = @_; |
243 my $mail_new = MIME::Entity->build(Type => $data{mtype}, |
235 return |
244 Date => $data{date}, |
236 MIME::Entity->build(Type => $data{mtype}, |
245 From => $data{from}, |
237 Date => $data{date}, |
246 To => $data{to}, |
238 From => $data{from}, |
247 CC => $data{cc}, |
239 To => $data{to}, |
248 BCC => $data{bcc}, |
240 CC => $data{cc}, |
249 Subject => $data{subject}, |
241 BCC => $data{bcc}, |
250 ); |
242 Subject => $data{subject}, |
251 return $mail_new; |
243 ); |
252 } |
244 } |
253 |
245 |
254 sub new_mail_send($) { |
246 sub new_mail_send($) { |
255 my $mail = shift @_; |
247 my $mail = shift @_; |
256 if (!$opt_fake) { |
248 if (!$opt_fake) { |
257 print $out_std "From $ME " . scalar localtime() . "\n"; |
249 print "From $ME " . scalar localtime() . "\n"; |
258 $mail->print; |
250 $mail->print; |
259 } |
251 } |
260 } |
252 } |
261 |
253 |
262 sub check_multipart($$) { |
254 sub check_multipart($$$$) { |
263 my ($multipart, $mail_new) = @_; |
255 my ($mimes, $old, $new, $level) = @_; |
264 my $parts_count = $multipart->parts; |
256 my $parts_count = $old->parts; |
265 |
257 |
266 printf $out_err "| %-30s | %-6s | %-34s |\n", |
258 warn sprintf "| %-30s | %-6s | %-34s |\n", |
267 "" . $prefix . $multipart->mime_type . " [$parts_count]", '', ''; |
259 " " x $level . $old->mime_type . " [$parts_count]", "", ""; |
268 $prefix = $prefix . " "; |
260 |
269 |
261 my @parts = $old->parts; |
270 my @parts = $multipart->parts; |
|
271 foreach my $part (@parts) { |
262 foreach my $part (@parts) { |
272 my $mtype = $part->mime_type; |
263 my $mtype = $part->mime_type; |
273 check_part($part, $mtype, $mail_new) unless $mtype =~ /^multipart/; |
264 |
274 $mail_new = check_multipart($part, $mail_new) |
265 if ($mtype =~ /multipart/) { |
275 if $mtype =~ /^multipart/; |
266 $new = check_multipart($mimes, $part, $new, $level + 1); |
276 } |
267 } |
277 return $mail_new; |
268 else { |
278 } |
269 check_part($mimes, $part, $mtype, $new, $level); |
279 |
270 } |
280 sub check_vip(%) { |
271 } |
281 if ($vips) { |
272 |
282 my %data = @_; |
273 return $new; |
283 return "VIP FROM" if $data{from} =~ /<(?:$vips)>/i; |
274 } |
284 return "VIP RCPT" if $data{to} =~ /<(?:$vips)>/i; |
275 |
285 return "VIP CC" if $data{cc} =~ /<(?:$vips)>/i; |
276 sub check_vip($%) { |
286 return "VIP BCC" if $data{bcc} =~ /<(?:$vips)>/i; |
277 my ($vips, %data) = @_; |
287 } |
278 my $re = join "|", map { $a = $_; $a =~ s/([^\da-z])/\\$1/gi; $a } @$vips; |
288 } |
279 my @matched; |
289 |
280 |
290 sub check_part($$$) { |
281 foreach (qw(from to cc bcc)) { |
291 my ($part, $mtype, $mail_new) = @_; |
282 push @matched, "VIP $_" if $data{$_} =~ /$re/o; |
|
283 } |
|
284 |
|
285 return @matched if wantarray; |
|
286 return join ", ", @matched; |
|
287 } |
|
288 |
|
289 sub check_part($$$$$) { |
|
290 my ($mimes, $part, $mtype, $mail_new, $level) = @_; |
292 my $status = 'cut'; |
291 my $status = 'cut'; |
|
292 |
|
293 my $re = join "|", map { $a = $_; $a =~ s/([^\da-z])/\\$1/gi; $a } @$mimes; |
293 |
294 |
294 my $disposition = ($part->get('Content-Disposition') =~ /filename="(.+)"/) |
295 my $disposition = ($part->get('Content-Disposition') =~ /filename="(.+)"/) |
295 and my $filename = $1 |
296 and my $filename = $1 |
296 if $part->get('Content-Disposition'); |
297 if $part->get('Content-Disposition'); |
|
298 |
297 $filename = 'n/a' unless $filename; |
299 $filename = 'n/a' unless $filename; |
298 |
300 |
299 if ($opt_text) { |
301 if ($opt_text) { |
300 if ($mtype =~ m[text/plain]) { $status = 'kept' } |
302 if ($mtype =~ m[text/plain]) { $status = 'kept' } |
301 else { $status = 'cut' } |
303 else { $status = 'cut' } |
302 } |
304 } |
303 elsif ($mimes) { |
305 elsif ($re) { |
304 if ($mtype =~ m[(?:$mimes)]) { $status = 'kept' } |
306 if ($mtype =~ /$re/) { $status = 'kept' } |
305 } |
307 } |
306 |
308 |
307 if ($status eq 'kept') { |
309 if ($status eq 'kept') { |
308 $mail_new->add_part($part); |
310 $mail_new->add_part($part); |
309 } |
311 } |
310 elsif ($status eq 'cut') { replace_part($part, $filename, $mail_new) } |
312 elsif ($status eq 'cut') { replace_part($part, $filename, $mail_new) } |
311 |
313 |
312 printf $out_err "| %-30s | %-6s | %34.34s |\n", "$prefix$mtype", |
314 warn sprintf "| %-30s | %-6s | %34.34s |\n", |
313 "$status", "$filename"; |
315 " " x ($level+1) . $mtype, |
|
316 $status, |
|
317 $filename; |
314 } |
318 } |
315 |
319 |
316 sub replace_part($$$) { |
320 sub replace_part($$$) { |
317 |
321 |
318 # TODO part ersetzen ohne boundary zu verlieren |
322 # TODO part ersetzen ohne boundary zu verlieren |