File Coverage

blib/lib/Mail/Box/Search.pm
Criterion Covered Total %
statement 80 87 91.9
branch 54 76 71.0
condition 31 46 67.3
subroutine 12 16 75.0
pod 9 10 90.0
total 186 235 79.1


line stmt bran cond sub pod time code
1             # This code is part of Perl distribution Mail-Box version 4.01.
2             # The POD got stripped from this file by OODoc version 3.05.
3             # For contributors see file ChangeLog.
4              
5             # This software is copyright (c) 2001-2025 by Mark Overmeer.
6              
7             # This is free software; you can redistribute it and/or modify it under
8             # the same terms as the Perl 5 programming language system itself.
9             # SPDX-License-Identifier: Artistic-1.0-Perl OR GPL-1.0-or-later
10              
11              
12             package Mail::Box::Search;{
13             our $VERSION = '4.01';
14             }
15              
16 2     2   900 use parent 'Mail::Reporter';
  2         4  
  2         12  
17              
18 2     2   137 use strict;
  2         3  
  2         47  
19 2     2   7 use warnings;
  2         4  
  2         154  
20              
21 2     2   10 use Log::Report 'mail-box', import => [ qw/__x error/ ];
  2         4  
  2         19  
22              
23             #--------------------
24              
25             sub init($)
26 12     12 0 33 { my ($self, $args) = @_;
27              
28 12         43 $self->SUPER::init($args);
29              
30 12   50     67 my $in = $args->{in} || 'BODY';
31 12 50       85 @$self{ qw/MBS_check_head MBS_check_body/ }
    100          
    100          
32             = $in eq 'BODY' ? (0,1)
33             : $in eq 'HEAD' ? (1,0)
34             : $in eq 'MESSAGE' ? (1,1)
35             : error __x"search in BODY, HEAD or MESSAGE, not {what UNKNOWN}.", what => $in;
36              
37 12 50 66     87 ! $self->{MBS_check_head} || $self->can('inHead') or error __x"cannot search in header.";
38 12 50 66     103 ! $self->{MBS_check_body} || $self->can('inBody') or error __x"cannot search in body.";
39              
40 12         29 my $deliver = $args->{deliver};
41             $self->{MBS_deliver}
42 518     518   1206 = ref $deliver eq 'CODE' ? sub { $deliver->($self, $_[0]) }
43             : !defined $deliver ? undef
44 0     0   0 : $deliver eq 'DELETE' ? sub { $_[0]->{part}->toplevel->label(deleted => 1) }
45 12 0       73 : error __x"don't know how to deliver results in {what UNKNOWN}.", what => $deliver;
    50          
    100          
46              
47 12   50     216 my $logic = $args->{logical} || 'REPLACE';
48 12         57 $self->{MBS_negative} = $logic =~ s/\s*NOT\s*$//;
49 12         113 $self->{MBS_logical} = $logic;
50              
51 12         48 $self->{MBS_label} = $args->{label};
52 12   100     58 $self->{MBS_binaries} = $args->{binaries} || 0;
53 12   100     48 $self->{MBS_limit} = $args->{limit} || 0;
54 12   50     53 $self->{MBS_decode} = $args->{decode} || 1;
55 12         41 $self->{MBS_no_deleted} = not $args->{deleted};
56 12 50       43 $self->{MBS_delayed} = exists $args->{delayed} ? $args->{delayed} : 1;
57 12 100       74 $self->{MBS_multiparts} = exists $args->{multiparts} ? $args->{multiparts} : 1;
58              
59 12         33 $self;
60             }
61              
62             #--------------------
63              
64 63     63 1 240 sub deliver() { $_[0]->{MBS_deliver} }
65 25     25 1 88 sub doMultiparts() { $_[0]->{MBS_multiparts} }
66 343     343 1 906 sub parseDelayed() { $_[0]->{MBS_delayed} }
67 376     376 1 1824 sub skipDeleted() { $_[0]->{MBS_no_deleted} }
68              
69             #--------------------
70              
71             sub search(@)
72 14     14 1 42 { my ($self, $object) = @_;
73              
74 14         32 my $label = $self->{MBS_label};
75 14         28 my $limit = $self->{MBS_limit};
76              
77             my @messages
78 14 50       189 = ref $object eq 'ARRAY' ? @$object
    100          
    100          
    100          
79             : $object->isa('Mail::Box') ? $object->messages
80             : $object->isa('Mail::Message') ? ($object)
81             : $object->isa('Mail::Box::Thread::Node') ? $object->threadMessages
82             : error __x"expect messages to search, not {what UNKNOWN}.", what => $object;
83              
84 14         29 my $take = 0;
85 14 100 100     138 if($limit < 0) { $take = -$limit; @messages = reverse @messages }
  1 50       4  
  1 100       3  
86 0         0 elsif($limit > 0) { $take = $limit }
87 1         3 elsif(!defined $label && !wantarray && !$self->deliver) {$take = 1 }
88              
89 14         31 my $logic = $self->{MBS_logical};
90 14         24 my @selected;
91 14         20 my $count = 0;
92              
93 14         32 foreach my $message (@messages)
94 376 100 100     1090 { next if $self->skipDeleted && $message->isDeleted;
95 343 50 33     2828 next unless $self->parseDelayed || !$message->isDelayed;
96              
97 343 50       710 my $set = defined $label ? $message->label($label) : 0;
98              
99             my $selected
100             = $set && $logic eq 'OR' ? 1
101             : !$set && $logic eq 'AND' ? 0
102 343 50 33     2027 : $self->{MBS_negative} ? ! $self->searchPart($message)
    50 33        
    50          
103             : $self->searchPart($message);
104              
105 343 50       921 $message->label($label => $selected) if defined $label;
106 343 100       781 if($selected)
107 167         310 { push @selected, $message;
108 167         289 $count++;
109 167 100 100     572 last if $take && $count == $take;
110             }
111             }
112              
113 14 100       242 $limit < 0 ? reverse @selected : @selected;
114             }
115              
116              
117              
118             sub searchPart($)
119 376     376 1 835 { my ($self, $part) = @_;
120              
121 376         622 my $matched = 0;
122             $matched = $self->inHead($part, $part->head)
123 376 100       1123 if $self->{MBS_check_head};
124              
125 376 100       954 return $matched unless $self->{MBS_check_body};
126 335 50 66     739 return $matched if $matched && !$self->deliver;
127              
128 335         950 my $body = $part->body;
129 335         1570 my @bodies;
130              
131             # Handle multipart parts.
132              
133 335 100       1341 if($body->isMultipart)
    50          
134 25 100       578 { $self->doMultiparts or return $matched;
135              
136 24         146 @bodies = ($body->preamble, $body->epilogue);
137              
138 24         318 foreach my $piece (grep defined, $body->parts)
139 33 50 33     336 { next if $piece->isDelayed && ! $self->parseDelayed;
140              
141 33         538 $matched += $self->searchPart($piece);
142 33 50 66     175 return $matched if $matched && !$self->deliver;
143             }
144             }
145             elsif($body->isNested)
146 0 0       0 { $self->doMultiparts or return $matched;
147 0         0 $matched += $self->searchPart($body->nested);
148             }
149             else
150 310         762 { @bodies = ($body);
151             }
152              
153             # Handle normal bodies.
154              
155 334         913 foreach (grep defined, @bodies)
156 349 100 100     1291 { next if !$self->{MBS_binaries} && $_->isBinary;
157 326 50       80969 my $body = $self->{MBS_decode} ? $_->decoded : $_;
158 326         375412 my $inbody = $self->inBody($part, $body);
159 326         974 $matched += $inbody;
160             }
161              
162 334         7219 $matched;
163             }
164              
165              
166 0     0 1   sub inHead(@) { $_[0]->notImplemented }
167              
168              
169 0     0 1   sub inBody(@) { $_[0]->notImplemented }
170              
171             #--------------------
172              
173 0     0 1   sub printMatch($) { $_[0]->notImplemented }
174              
175             1;