File Coverage

blib/lib/Convert/BulkDecoder.pm
Criterion Covered Total %
statement 6 298 2.0
branch 0 174 0.0
condition 0 57 0.0
subroutine 2 10 20.0
pod 0 6 0.0
total 8 545 1.4


line stmt bran cond sub pod time code
1             package Convert::BulkDecoder;
2              
3             # Convert::BulkDecoder - Extract binary data from mail and news messages
4             # RCS Info : $Id: BulkDecoder.pm,v 1.12 2005-06-19 17:35:38+02 jv Exp jv $
5             # Author : Johan Vromans
6             # Created On : Wed Jan 29 16:59:58 2003
7             # Last Modified By: Johan Vromans
8             # Last Modified On: Mon Jul 11 11:18:53 2022
9             # Update Count : 91
10             # Status : Unknown, Use with caution!
11              
12             $VERSION = "1.041";
13              
14 1     1   696 use strict;
  1         2  
  1         24  
15 1     1   411 use integer;
  1         12  
  1         4  
16              
17             sub new {
18 0     0 0   my ($pkg, %atts) = @_;
19 0 0         $pkg = ref $pkg if ref $pkg;
20              
21 0           my $self = bless {
22             # Set explicit defaults.
23             tmpdir => "/var/tmp",
24             destdir => "",
25             force => 0,
26             verbose => 1,
27             crc => 1,
28             md5 => 1,
29             debug => 0,
30             neat => \&_neat,
31             }, $pkg;
32              
33             # Copy constructor attributes.
34 0           foreach ( keys(%$self) ) {
35 0 0         if ( defined($atts{$_}) ) {
36 0           $self->{$_} = delete($atts{$_});
37             }
38             }
39              
40             # Bail of if any remain.
41 0           my $err = "";
42 0           foreach my $k ( sort keys %atts ) {
43 0           $err .= $pkg . ": invalid constructor attribute: $k\n";
44             }
45 0 0         die($err) if $err;
46              
47             # Polish.
48 0           foreach ( $self->{destdir}, $self->{tmpdir} ) {
49 0 0         next unless $_;
50 0           $_ .= "/";
51 0           s;/+$;/;;
52             }
53              
54 0 0         if ( $self->{md5} ) {
55 0           require Digest::MD5;
56 0           $self->{_md5} = Digest::MD5->new;
57             }
58              
59 0           $self;
60             }
61              
62             sub decode {
63              
64 0     0 0   my ($self, $a) = @_;
65              
66             # Try uudecode, or find out better.
67 0           my $ret = $self->uudecode($a);
68              
69             # MIME.
70 0 0         $ret = $self->mimedecode($a) if $ret eq 'M';
71              
72             # yEnc.
73 0 0         $ret = $self->ydecode($a) if $ret =~ /^Y/;
74              
75             # UNSUPPORTED -- FOR TESTING ONLY!
76             # $ret = $self->ydecode_ydecode($a, $1) if $ret =~ /^Y(.*)/;
77              
78 0           $ret;
79             }
80              
81             sub uudecode {
82 0     0 0   my ($self, $a) = @_;
83              
84 0           my $doing = 0;
85 0           my $size = 0;
86 0           my $name;
87 0           $self->{result} = "EMPTY";
88              
89             # Process the message lines.
90 0           foreach ( @$a ) {
91 0 0         if ( $doing ) { # uudecoding...
92 0 0         if ( /^end/ ) {
93 0           close(OUT);
94 0 0         $self->{md5} = $self->{_md5}->b64digest if $self->{md5};
95 0           $self->{size} = $size;
96 0           $doing = 2; # done
97 0           $self->{result} = "OK";
98 0           last;
99             }
100             # Select lines to process.
101 0 0         next if /[a-z]/;
102 0 0         next unless int((((ord() - 32) & 077) + 2) / 3)
103             == int(length() / 4);
104             # Decode.
105 0           my $t = unpack("u",$_);
106 0 0         print OUT $t or die("print(".$self->{file}."): $!\n");
107 0           $size += length($t);
108 0 0         $self->{_md5}->add($t) if $self->{md5};
109 0           next;
110             }
111              
112             # Check for MIME.
113 0 0         if ( m;^content-type:.*(image/|multipart);i ) {
114 0           return 'M'; # MIME
115             }
116              
117 0 0         if ( m/^=ybegin\s+.*\s+name=(.+)/i ) {
118 0           return "Y$1"; # yEnc
119             }
120              
121             # Otherwise, search for the uudecode 'begin' line.
122 0 0         if ( /^begin\s+\d+\s+(.+)$/ ) {
123 0           $name = $self->{neat}->($1);
124 0           $self->{type} = "U";
125 0           $self->{name} = $name;
126 0           $self->{file} = $self->{destdir} . $name;
127 0           $doing = 2; # Done
128             warn("Decoding(UU) to ", $self->{file}, "\n")
129 0 0         if $self->{verbose};
130             # Skip duplicates.
131             # Note that testing for -s fails if it is a
132             # notexisting symlink.
133 0 0 0       if ( (-l $self->{file} || -s _ ) && !$self->{force} ) {
      0        
134 0           $self->{size} = -s _;
135 0           $self->{result} = "DUP";
136 0           last;
137             }
138              
139             open (OUT, ">".$self->{file})
140 0 0         or die("create(".$self->{file}."): $!\n");
141 0           binmode(OUT);
142 0           $doing = 1; # Doing
143 0           $self->{result} = "FAIL";
144 0           next;
145             }
146             }
147 0           push(@{$self->{parts}},
148             { type => $self->{type},
149             size => $self->{size},
150             md5 => $self->{md5},
151             result => $self->{result},
152             name => $self->{name},
153 0           file => $self->{file} });
154 0           return $self->{result};
155             }
156              
157             my @crctab;
158              
159             sub ydecode {
160 0     0 0   my ($self, $a) = @_;
161 0           $self->{type} = "Y";
162 0           $self->{result} = "EMPTY";
163              
164 0 0 0       _fill_crctab() unless @crctab || !$self->{crc};
165              
166 0           my @lines = @$a;
167              
168 0           my ($ydec_part, $ydec_line, $ydec_size, $ydec_name, $ydec_pcrc,
169             $ydec_begin, $ydec_end);
170 0           my $pcrc;
171              
172 0           while ( $_ = shift(@lines) ) {
173             # Newlines a fakes and should not be decoded.
174 0           chomp;
175 0           s/\r//g;
176             # If we've started decoding $ydec_name will be set.
177 0 0         if ( !$ydec_name ) {
178             # Skip until beginning of yDecoded part.
179 0 0         next unless /^=ybegin/;
180 0 0         if ( / part=(\d+)/ ) {
181 0           $ydec_part = $1;
182             }
183              
184 0 0         if ( / size=(\d+)/ ) {
185 0           $self->{size} = $ydec_size = $1;
186             }
187             else {
188 0           die("Mandatory field 'size' missing\n");
189             }
190 0 0         if ( / line=(\d+)/ ) {
191 0           $ydec_line = $1;
192             }
193 0 0         if( / name=(.*)$/ ) {
194 0           $ydec_name = $self->{neat}->($1);
195 0           $self->{file} = $self->{destdir} . $ydec_name;
196 0           $self->{name} = $ydec_name;
197 0 0 0       if ( !defined($ydec_part) || $ydec_part == 1 ) {
198             warn("Decoding(yEnc) to ", $self->{file}, "\n")
199 0 0         if $self->{verbose};
200 0 0         if ( -s $self->{file} ) {
201 0 0         if ( $self->{force} ) {
202 0           unlink($self->{file});
203             }
204             else {
205 0           $self->{size} = -s _;
206 0           $self->{result} = "DUP";
207 0           last;
208             }
209             }
210             }
211             }
212             else {
213 0           die("Unknown attach name\n");
214             }
215              
216             # Multipart messages contain more information on.
217             # the second line.
218 0 0         if ( $ydec_part ) {
219 0           $_ = shift(@lines);
220 0           chomp;
221 0           s/\r//g;
222 0 0         if ( /^=ypart/ ) {
223 0 0         if ( / begin=(\d+)/ ) {
224             # We need this to check if the size of this message
225             # is correct.
226 0           $ydec_begin = $1;
227 0           $pcrc = 0xffffffff;
228 0           undef $ydec_pcrc;
229             }
230             else {
231 0           warn("No begin field found in part, ignoring\n");
232 0           undef $ydec_part;
233             }
234 0 0         if ( / end=(\d+)/ ) {
235             # We need this to calculate the size of this message.
236 0           $ydec_end = $1;
237             }
238             else {
239 0           warn("No end field found in part, ignoring");
240 0           undef $ydec_part;
241             }
242             }
243             else {
244 0           warn("Article described as multipart message, however ".
245             "it doesn't seem that way\n");
246 0           undef $ydec_part;
247             }
248             }
249             else {
250 0           $pcrc = 0xffffffff;
251             }
252              
253             # If the $ydec_part is different from 1
254             # we need to open the file for appending.
255 0 0         if ( -e $self->{file} ) {
256 0 0 0       if ( defined($ydec_part) && $ydec_part != 1 ) {
    0          
257             # If we have a multipart message, the file exists
258             # and we are not at the first part, we should just
259             # open the file as an append. We assume that this is
260             # the multipart we were already processing.
261             #print "Opening $ydec_name for appending\n";
262 0 0         if ( !open(OUT, ">>".$self->{file}) ) {
263             die("Couldn't open ".$self->{file}.
264 0           " for appending: $!\n");
265             }
266             }
267             elsif ( !open(OUT, ">".$self->{file}) ) {
268 0           die("Couldn't create ".$self->{file}.": $!\n");
269             }
270             }
271             else {
272             # File doesn't exist. We open it for writing O' so plain.
273 0 0 0       if ( defined($ydec_part) && $ydec_part != 1 ) {
274 0           die("Missing ".$self->{file}. " for appending: $!\n");
275             }
276 0 0         if ( !open(OUT, ">".$self->{file}) ) {
277 0           die("Couldn't create ".$self->{file}.": $!\n");
278             }
279 0           $self->{result} = "FAIL";
280             }
281             # Cancel any file translations.
282 0           binmode(OUT);
283             # Excellent.. We have determed all the info for this file we
284             # need.. Skip till next line, this should contain the real
285             # data.
286 0           next;
287             }
288              
289             # Looking for the end tag.
290 0 0         if ( /^=yend/ ) {
291             # We are done.. Check the sanity of article.
292             # and unset $ydec_name in case that there are more
293             # ydecoded files in the same article.
294 0           $self->{result} = "OK";
295 0 0         if ( / part=(\d+)/ ) {
296 0 0         if ( $ydec_part != $1 ) {
297 0           die("Part number '$1' different from beginning part '$ydec_part'\n");
298             }
299             }
300 0 0         if ( / size=(\d+)/ ) {
301             # Check size, but first calculate it.
302 0           my $size;
303 0 0         if ( defined($ydec_part) ) {
304 0           $size = ($ydec_end - $ydec_begin + 1);
305             }
306             else {
307 0           $size = $ydec_size;
308             }
309 0 0         if ( $1 != $size ) {
310 0           die("Size '$1' different from beginning size '$size'\n");
311             }
312             }
313 0 0 0       if ( / pcrc32=([0-9a-f]+)/i && @crctab ) {
314 0 0 0       if ( defined($ydec_pcrc) && ($ydec_pcrc != $1) ) {
315 0           die("CRC '$1' different from beginning CRC '$ydec_pcrc'\n");
316             }
317 0           $ydec_pcrc = hex($1);
318 0           $pcrc = $pcrc ^ 0xffffffff;
319 0 0         if ( $pcrc == $ydec_pcrc ) {
320             warn("Part $ydec_part, checksum OK\n")
321 0 0         if $self->{verbose};
322             }
323             else {
324 0           warn(sprintf("Part $ydec_part, checksum mismatch, ".
325             "got 0x%08x, expected 0x%08x\n",
326             $pcrc, $ydec_pcrc));
327             }
328              
329             }
330 0 0 0       if ( !defined($ydec_part) && / crc32=([0-9a-f]+)/i && @crctab ) {
      0        
331 0           $ydec_pcrc = hex($1);
332 0           $pcrc = $pcrc ^ 0xffffffff;
333 0 0         if ( $pcrc == $ydec_pcrc ) {
334             warn("Checksum OK\n")
335 0 0         if $self->{verbose};
336             }
337             else {
338 0           warn(sprintf("Checksum mismatch, ".
339             "got 0x%08x, expected 0x%08x\n",
340             $pcrc, $ydec_pcrc));
341             }
342              
343             }
344 0           undef $ydec_name;
345             # Dont encode the endline, we skip to the next line
346             # in search for any more parts.
347 0           next;
348             }
349              
350             # If we got here, we are within an encoded article, an
351             # we will take meassures to decode it.
352             # We decode line by line.
353              
354             # Decoder by jvromans@squirrel.nl.
355 0           s/=(.)/chr(ord($1)+(256-64) & 255)/ge;
  0            
356 0           tr{\000-\377}{\326-\377\000-\325};
357              
358 0           my $data = $_;
359             # CRC check code by jvromans@squirrel.nl.
360 0 0         if ( @crctab ) {
361 0           foreach ( split(//, $data) ) {
362 0           $pcrc = $crctab[($pcrc^ord($_))&0xff] ^ (($pcrc >> 8) & 0x00ffffff);
363             }
364             }
365              
366 0           print OUT $data;
367 0 0         $self->{_md5}->add($data) if $self->{md5};
368             }
369              
370 0           close(OUT);
371 0 0         $self->{md5} = $self->{_md5}->b64digest if $self->{md5};
372 0           push(@{$self->{parts}},
373             { type => $self->{type},
374             size => $self->{size},
375             md5 => $self->{md5},
376             result => $self->{result},
377             name => $self->{name},
378 0           file => $self->{file} });
379 0           return $self->{result};
380             }
381              
382             sub _fill_crctab {
383 0     0     @crctab =
384             ( 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
385             0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
386             0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
387             0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
388             0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
389             0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
390             0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
391             0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
392             0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
393             0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
394             0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
395             0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
396             0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
397             0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
398             0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
399             0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
400             0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
401             0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
402             0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
403             0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
404             0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
405             0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
406             0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
407             0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
408             0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
409             0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
410             0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
411             0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
412             0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
413             0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
414             0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
415             0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
416             0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
417             0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
418             0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
419             0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
420             0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
421             0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
422             0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
423             0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
424             0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
425             0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
426             0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
427             );
428             }
429              
430             sub ydecode_ydecode {
431 0     0 0   my ($self, $a, $name) = @_;
432 0           my $tmp = $self->{tmpdir} . "mfetch.$$.";
433              
434 0           $self->{type} = "Y";
435 0 0         if ( $name ) {
436 0           $self->{file} = $self->{destdir} . $name;
437             warn("Decoding(ydecode) to ", $self->{file}, "\n")
438 0 0         if $self->{verbose};
439 0 0         if ( -s $self->{file} ) {
440 0 0         if ( $self->{force} ) {
441 0           unlink($self->{file});
442             }
443             else {
444 0           $self->{size} = -s _;
445 0           $self->{result} = "DUP";
446 0           goto QXIT;
447             }
448             }
449             }
450              
451 0           my @files;
452 0           my $copy = 0;
453 0           my $part;
454 0           foreach ( @$a ) {
455 0 0 0       if ( $copy && /^=yend/ ) {
456 0           print TMP $_;
457 0           close(TMP);
458 0           $copy = 0;
459 0           next;
460             }
461 0 0 0       if ( !$copy && /^=ybegin.*\s+part=(\d+)/ ) {
462 0           my $file = sprintf("$tmp%03d", $part = $1);
463 0           $files[$1-1] = $file;
464 0 0         $copy = $1 if /\s+line=(\d+)/;
465 0 0         $self->{size} = $1 if /\s+size=(\d+)/;
466 0 0         $self->{name} = $1 if /\s+name=(.+)/;
467 0           $self->{file} = $self->{destdir} . $self->{name};
468 0 0         if ( -s $self->{file} ) {
469 0 0         if ( $self->{force} ) {
470 0           unlink($self->{file});
471             }
472             else {
473 0           $self->{size} = -s _;
474 0           $self->{result} = "DUP";
475 0           goto QXIT;
476             }
477             }
478 0 0         open(TMP, ">$file") || die("$file: $!\n");
479 0           binmode(TMP);
480 0           $copy++;
481             }
482 0 0         if ( $copy > 1 ) { # check length
483             # If it starts with an unescaped period, the line will be
484             # one too short. Add the period since ydecode requires it.
485 0 0 0       if ( /^\./ && length($_) == $copy ) {
486 0           $_ = ".$_";
487             }
488             }
489 0 0         print TMP $_ if $copy;
490             }
491              
492             system("ydecode", "-k",
493 0 0         $self->{destdir} ? "--output=".$self->{destdir} : (),
494             @files);
495              
496 0           $self->{result} = "FAIL";
497 0 0         if ( -s $self->{file} == $self->{size} ) {
498 0           unlink(@files);
499 0 0         if ( $self->{md5} ) {
500             open(F, $self->{file})
501 0 0         or die($self->{file} . " (reopen) $!\n");
502 0           binmode(F);
503 0           local($/) = undef;
504 0           $self->{_md5}->add();
505 0           close(F);
506 0           $self->{md5} = $self->{_md5}->b64digest;
507             }
508 0           $self->{result} = "OK";
509             }
510             QXIT:
511 0           push(@{$self->{parts}},
512             { type => $self->{type},
513             size => $self->{size},
514             md5 => $self->{md5},
515             result => $self->{result},
516             name => $self->{name},
517 0           file => $self->{file} });
518 0           return $self->{result};
519             }
520              
521             sub mimedecode {
522 0     0 0   my ($self, $a) = @_;
523              
524 0           require MIME::Parser;
525              
526 0           $self->{type} = "M";
527 0           my $parser = new MIME::Parser;
528             # Store everything in memory.
529 0           $parser->output_to_core(1);
530 0           my $e = $parser->parse_data($a);
531              
532 0 0 0       unless ( defined $e->{ME_Parts} && @{$e->{ME_Parts}} ) {
  0            
533 0           $e->{ME_Parts} = [ $e ];
534             }
535              
536 0           foreach my $part ( @{$e->{ME_Parts}} ) {
  0            
537 0           my $name;
538 0           foreach ( 'Content-Type', 'Content-Disposition' ) {
539              
540 0           my $ct = $part->{mail_inet_head}->{mail_hdr_hash}->{$_};
541 0 0 0       next unless defined $ct && defined ($ct = ${$ct->[0]});
  0            
542 0 0         if ( $ct =~ m{((file)?name)="([^"]+)"}i ) {
543 0           $name = $self->{name} = $self->{neat}->($3);
544 0           $self->{file} = $self->{destdir} . $name;
545             warn("Decoding(MIME) to ", $self->{file}, "\n")
546 0 0         if $self->{verbose};
547 0 0 0       if ( -s $self->{file} && !$self->{force} ) {
548 0           $self->{size} = -s _;
549 0           $self->{result} = "DUP";
550 0           push(@{$self->{parts}},
551             { type => $self->{type},
552             size => $self->{size},
553             result => $self->{result},
554             name => $self->{name},
555 0           file => $self->{file} });
556 0           next;
557             }
558             }
559             }
560              
561             # Skip body.
562 0 0         next unless $name;
563 0 0         next if $name eq $self->{destdir}."body";
564              
565             # Skip duplicates.
566 0 0 0       if ( -s $name && !$self->{force} ) {
567 0           $self->{size} = -s _;
568 0           $self->{result} = "DUP";
569 0           push(@{$self->{parts}},
570             { type => $self->{type},
571             size => $self->{size},
572             result => $self->{result},
573             name => $self->{name},
574 0           file => $self->{file} });
575 0           next;
576             }
577              
578             # Store it.
579 0           my $bh = $part->{ME_Bodyhandle};
580 0 0 0       if ( $bh && defined $bh->{MBC_Data} && open (OUT, ">".$self->{file}) ) {
      0        
581 0           binmode(OUT);
582 0           my $size = 0;
583 0           foreach ( @{$bh->{MBC_Data}} ) {
  0            
584 0           print OUT $_;
585 0 0         $self->{_md5}->add($_) if $self->{md5};
586 0           $size += length($_);
587             }
588 0           close (OUT);
589 0 0         $self->{md5} = $self->{_md5}->b64digest if $self->{md5};
590 0           $self->{size} = $size;
591 0           $self->{result} = "OK";
592 0           push(@{$self->{parts}},
593             { type => $self->{type},
594             size => $self->{size},
595             md5 => $self->{md5},
596             result => $self->{result},
597             name => $self->{name},
598 0           file => $self->{file} });
599             }
600             else {
601 0           $self->{result} = "FAIL";
602 0           push(@{$self->{parts}},
603             { type => $self->{type},
604             result => $self->{result},
605             name => $self->{name},
606 0           file => $self->{file} });
607             }
608             }
609              
610             # Return values for the first file.
611 0           while ( my($k,$v) = each(%{$self->{parts}->[0]}) ) {
  0            
612 0           $self->{$k} = $v;
613             }
614 0           return $self->{result};
615              
616             }
617              
618             sub _neat {
619 0     0     local ($_) = @_;
620 0           s/^\[a-z]://i;
621 0           s/^.*?([^\\]+$)/$1/;
622             # Spaces and unprintables to _.
623 0           s/\s+/_/g;
624 0           s/\.\.+/./g;
625 0           s/[\0-\040'`"\177-\240\/]/_/g;
626             # Remove leading dots.
627 0           s/^\.+//;
628 0           $_;
629             }
630              
631             1;
632              
633             __END__