File Coverage

blib/lib/Mail/Log/Trace.pm
Criterion Covered Total %
statement 221 226 97.7
branch 35 42 83.3
condition n/a
subroutine 51 51 100.0
pod 8 8 100.0
total 315 327 96.3


line stmt bran cond sub pod time code
1             #!/usr/bin/perl
2              
3              
4             package Mail::Log::Trace;
5             {
6             =head1 NAME
7              
8             Mail::Log::Trace - Trace an email through the mailsystem logs.
9              
10             =head1 SYNOPSIS
11              
12             use Mail::Log::Trace;
13            
14             my $tracer = Mail::Log::Trace::SUBCLASS->new({log_file => 'path/to/log'});
15             $tracer->set_message_id('message_id');
16             $tracer->find_message();
17             my $from_address = $tracer->get_from_address();
18            
19             etc.
20              
21             =head1 DESCRIPTION
22              
23             This is the root-level class for a mail tracer: It allows you to search for
24             and find messages in maillogs. Accessors are provided for info common to
25             most maillogs: Specific subclasses may have further accessors depending on their
26             situation.
27              
28             Probably the two methods most commonly used (and sort of the point of this
29             module) are C and C. Both are simply stubs
30             for subclasses to implement: The first is defined to find the first (or first
31             from current location...) mention of the specified message in the log.
32             Depending on the log format that may or may not be the only mention, and there
33             may be information missing/incomplete at that point.
34              
35             C should find I information about a specific message
36             in the log. (Well, all information about a specific instance of the message:
37             If there are multiple messages that would match the info provided it must
38             find info on the first found.) That may mean searching through the log for
39             other information.
40              
41             If you just need to find if the message exists, use C: it will
42             be faster (or at the least, the same speed. It should never be slower.)
43              
44             =head1 USAGE
45              
46             This is a an object-orientend module, with specific methods documented below.
47              
48             The string coersion is overloaded to return the class name, and the file
49             we are working with. Boolean currently checks to see if we were able to
50             open the file. (Which is kinda silly, as we'd throw an error if we couldn't.)
51              
52             All times are expected to be in Unix epoc-time format.
53              
54             =cut
55              
56 4     4   362596 use strict;
  4         11  
  4         1302  
57 4     4   28 use warnings;
  4         6  
  4         173  
58 4     4   22 use Scalar::Util qw(refaddr blessed reftype);
  4         15  
  4         295  
59 4     4   2741 use Mail::Log::Exceptions 1.0100;
  4         53602  
  4         121  
60 4     4   34 use base qw(Exporter);
  4         8  
  4         308  
61              
62             BEGIN {
63 4     4   22 use Exporter ();
  4         7  
  4         80  
64 4     4   19 use vars qw($VERSION @EXPORT @EXPORT_OK %EXPORT_TAGS);
  4         9  
  4         394  
65 4     4   10 $VERSION = '1.0101';
66             #Give a hoot don't pollute, do not export more than needed by default
67 4         8 @EXPORT = qw();
68 4         9 @EXPORT_OK = qw();
69 4         5783 %EXPORT_TAGS = ();
70             }
71              
72             #
73             # Define class variables. Note that they are hashes...
74             #
75              
76             my %message_info;
77             my %log_info;
78             my %message_raw_info;
79              
80             # Accessors.
81             my %public = ( from_address => undef,
82             message_id => undef,
83             recieved_time => undef,
84             sent_time => undef,
85             relay => undef,
86             subject => undef,
87             );
88             my %public_set_only = ();
89             my %public_get_only = ( connect_time => undef, disconnect_time => undef, delay => undef );
90             my %array_accessors = ( to_address => undef );
91             my @valid_parameters;
92             my @checked_parameters = qw(from_address message_id recieved_time sent_time relay
93             subject to_address);
94             my %all_setters;
95             my %all_getters;
96              
97             my @cleared_parameters = qw(from_address message_id recieved_time sent_time
98             relay to_address subject connect_time disconnect_time
99             delay);
100              
101             #
102             # DESTROY class variables.
103             #
104             ### IF NOT DONE THERE IS A MEMORY LEAK. ###
105              
106             sub DESTROY {
107 36     36   265407 my ($self) = @_;
108            
109 36         351 delete $message_info{$$self};
110 36         273 delete $log_info{$$self};
111 36         15488 delete $message_raw_info{$$self};
112 36         534 delete $all_setters{$$self};
113 36         3729 delete $all_getters{$$self};
114            
115 36         494 return;
116             }
117              
118             #
119             # Set the coercions to something useful.
120             #
121              
122             use overload (
123             # Strings overload to the path and line number.
124 2     2   4 qw{""} => sub { my ($self) = @_;
125 2         63 return blessed($self)
126             .' File: '
127             .$log_info{$$self}{filename};
128             },
129            
130             # Boolean overloads to if we are usable. (Have a filehandle.)
131 2     2   6 qw{bool} => sub { my ($self) = @_;
132 2         15 return defined($log_info{$$self}{log_parser});
133             },
134            
135             # Numeric context just doesn't mean anything. Throw an error.
136 2     2   309 q{0+} => sub { Mail::Log::Exceptions->throw(q{Can't get a numeric value of a Mail::Log::Trace.} );
137             },
138            
139             # Perl standard for everything else.
140 4         132 fallback => 1,
141 4     4   42 );
  4         8  
142              
143              
144             =head2 new (constructor)
145              
146             The base constructor for the Mail::Log::Trace classes. It takes inital values
147             for the following in a hash: C, C, C,
148             C. The only required value is the path to the logfile.
149              
150             use Mail::Log::Trace;
151             my $object = Mail::Log::Trace->new({ from_address => 'from@example.com',
152             to_address => 'to@example.com',
153             message_id => 'messg.id.string',
154             log_file => 'path/to/log',
155             ...
156             });
157              
158             =cut
159              
160             sub new
161             {
162 36     36 1 51248 my ($class, $parameters_ref) = @_;
163              
164 36         83 my $self = bless \do{my $anon}, $class;
  36         158  
165 36         2635 $$self = refaddr $self;
166              
167             # Build accessors
168            
169             # Get stuff from the any base classes.
170 36         195 my @public = $self->_requested_public_accessors();
171 36         238 my %public_special = $self->_requested_special_accessors();
172 36         190 my @public_set_only = $self->_requested_public_set_only();
173 36         149 my @public_get_only = $self->_requested_public_get_only();
174 36         146 my @array = $self->_requested_array_accessors();
175            
176 36         141 @checked_parameters = ($self->_set_as_message_info(), @checked_parameters);
177 36 50       142 my %checked_parameters = map { $_ => undef if $_ ne ''; } @checked_parameters;
  378         1727  
178 36         243 @checked_parameters = keys %checked_parameters;
179              
180 36         106 foreach my $item ( @public ) {
181 69         170 $public{$item} = undef;
182             }
183 36         90 foreach my $item ( @public_set_only ) {
184 0         0 $public_set_only{$item} = undef;
185             }
186 36         70 foreach my $item ( @public_get_only ) {
187 0         0 $public_get_only{$item} = undef;
188             }
189 36         70 foreach my $item ( @array ) {
190 0         0 $array_accessors{$item} = undef;
191             }
192            
193             # Setters first.
194 36         261 my %merged_hash = (%public, %public_set_only, %public_special);
195 36         176 while ( my ($accessor, $action) = each %merged_hash ) {
196 308         801 $all_setters{$$self}{$accessor} = $self->_build_setter($accessor, 0, $action);
197 308         1818 push @valid_parameters, $accessor;
198             }
199            
200             # Now getters.
201 36         187 foreach my $accessor ( keys %public, keys %public_get_only, keys %public_special ) {
202 416         1015 $all_getters{$$self}{$accessor} = $self->_build_getter($accessor);
203             }
204              
205             # Now build the private.
206 36         287 $all_setters{$$self}{$_} = $self->_build_setter($_, 1) foreach ( keys %public_get_only );
207 36         153 $all_getters{$$self}{$_} = $self->_build_getter($_, 1) foreach ( keys %public_set_only );
208              
209             # And the complex...
210 36         239 $self->_build_array_accessors($_) foreach ( keys %array_accessors );
211 36         111 push @valid_parameters, keys %array_accessors;
212              
213             # Get the list of parameters to clear when 'clear' is called.
214 36         174 my @requested_cleared = $self->_requested_cleared_parameters();
215 36         91 @requested_cleared = grep { defined($_) } @requested_cleared;
  69         262  
216 36         96 push @cleared_parameters, @requested_cleared;
217              
218             # Set up any/all passed parameters.
219             # (Only does message info. Note this can only be called after the above!)
220 36         163 $self->_parse_args($parameters_ref, 0);
221              
222             # Log info.
223 35         237 $self->set_log($parameters_ref->{log_file}); # Better to keep validation together.
224              
225 32         276 return $self;
226             }
227              
228             #
229             # The method factories.
230             #
231              
232             sub _build_setter {
233 416     416   831 my ($self, $attribute, $private, $action) = @_;
234            
235             # Build the correct name.
236 416         1653 my $sub_name = "set_$attribute";
237 416 100       930 $sub_name = "_$sub_name" if $private;
238            
239             # The typeglob below sets off all kinds of warnings.
240             # (The 'redefine' is because this happens for _every_object_.)
241 4     4   2794 no strict 'refs';
  4         9  
  4         165  
242 4     4   20 no warnings qw(redefine);
  4         15  
  4         387  
243            
244             # Build the actual subroutine.
245 416 100       859 if ( defined($action) ) {
246             # If we do processing or validation, give it a chance to happen.
247 23         2428 return *{blessed($self)."::$sub_name"} = sub {
248 4     4   24 use strict 'refs';
  4         8  
  4         1329  
249 5     5   10 my ($self, $new_id) = @_;
250            
251             # True if they accept the value, false otherwise.
252             # (To make validation easier.)
253 5         17 $new_id = $action->($self, $new_id);
254 5 50       17 if ( $new_id ne '____INVALID__VALUE____' ) {
255 5         14 $message_info{$$self}{$attribute} = $new_id;
256             }
257             else {
258             # If they don't accept the value, tell the user.
259 0         0 Mail::Log::Exceptions::InvalidParameter->throw("'$new_id' is not a valid value for $attribute.\n");
260             }
261 5         9 return;
262             }
263 23         145 }
264             else {
265             # For basic setters, use a speed-optimized version.
266 393         37072 return *{blessed($self)."::$sub_name"} = sub {
267 667     667   840 $message_info{${$_[0]}}{$attribute} = $_[1];
  667         1397  
268 667         1486 return;
269             }
270 393         1628 }
271             }
272              
273             sub _build_getter {
274 416     416   809 my ($self, $attribute, $private) = @_;
275              
276             # Build the correct name.
277 416         681 my $sub_name = "get_$attribute";
278 416 50       766 $sub_name = "_$sub_name" if $private;
279              
280             # The typeglob below sets off all kinds of warnings.
281             # (The 'redefine' is because this happens for _every_object_.)
282 4     4   27 no strict 'refs';
  4         6  
  4         144  
283 4     4   22 no warnings qw(redefine);
  4         7  
  4         933  
284              
285             # Build the actual subroutine. (As fast as we can make it.)
286 416         41985 return *{blessed($self)."::$sub_name"} = sub {
287 1600     1600   14118 return $message_info{${$_[0]}}{$attribute};
  1600         9490  
288             }
289 416         1321 }
290              
291             sub _build_array_accessors {
292 36     36   79 my ($self, $attribute, $private) = @_;
293              
294 36         85 my $get_name = "get_$attribute";
295 36         84 my $set_name = "set_$attribute";
296 36         92 my $add_name = "add_$attribute";
297 36         71 my $remove_name = "remove_$attribute";
298              
299 36         104 foreach my $name ( ($get_name, $set_name, $add_name, $remove_name) ) {
300 144 50       281 $name = "_$name" if ( $private );
301 144         550 $name = blessed($self)."::$name";
302             }
303              
304 4     4   27 no strict 'refs';
  4         14  
  4         135  
305 4     4   19 no warnings qw(redefine);
  4         7  
  4         803  
306              
307             *$get_name = sub {
308 121     121   11711 return $message_info{${$_[0]}}{$attribute};
  121         650  
309 36         325 };
310 36         3090 $all_getters{$$self}{$attribute} = *$get_name;
311              
312             # Note that strict refs still aren't in effect.
313             # Needed for the call to $add_name below.
314             *$set_name = sub {
315 89     89   135 my ($self, $new_id) = @_;
316 89 100       176 if (defined($new_id) ) {
317 86         100 @{$message_info{$$self}{$attribute}} = ();
  86         225  
318 86         362 $add_name->($self, $new_id);
319             }
320             else {
321 3         11 $message_info{$$self}->{$attribute} = undef;
322             }
323 89         252 return;
324 36         439 };
325 36         3022 $all_setters{$$self}{$attribute} = *$set_name;
326              
327             *$add_name = sub {
328 4     4   22 use strict 'refs';
  4         7  
  4         7683  
329 176     176   287 my ($self, $new_id) = @_;
330            
331             # If we are given a single element, and we haven't seen it before,
332             # add it to the array.
333 176 100       558 if ( !defined(reftype($new_id)) ) {
    100          
334 163 100       172 unless ( grep { $_ eq $new_id } @{$message_info{$$self}{$attribute}} ) {
  1342         2009  
  163         471  
335 155         198 push @{$message_info{$$self}{$attribute}}, ($new_id);
  155         408  
336             }
337             }
338             # If we are given an array of elements, merge it with our current array.
339             elsif ( reftype($new_id) eq 'ARRAY' ) {
340 12         14 my %temp_hash;
341 12         15 foreach my $element (@{$message_info{$$self}{$attribute}}, @{$new_id}) {
  12         26  
  12         17  
342 26         58 $temp_hash{$element} = undef;
343             }
344 12         27 @{$message_info{$$self}{$attribute}} = keys %temp_hash;
  12         48  
345             }
346 176         487 return;
347 36         1958 };
348              
349             *$remove_name = sub {
350 1     1   4 my ($self, $id) = @_;
351 1         5 @{$message_info{$$self}{$attribute}}
  3         7  
352 1         2 = grep { $_ ne $id } @{$message_info{$$self}{$attribute}};
  1         4  
353 1         3 return;
354 36         504 };
355             }
356              
357             #
358             # Setters.
359             #
360              
361             =head2 SETTERS
362              
363             =head3 set_from_address
364              
365             Sets the from address of the message we are looking for.
366              
367             =head3 set_message_id
368              
369             Sets the message_id of the message we are looking for.
370             (Check with the specific parser class for what that means in a particular
371             log format.)
372              
373             =head3 set_recieved_time
374              
375             Sets the recieved time of the message we are looking for.
376             (The time this machine got the message.)
377              
378             =head3 set_sent_time
379              
380             Sets the sent time of the message we are looking for.
381             (The time this machine sent the message.)
382              
383             =head3 set_relay_host
384              
385             Sets the relay host of the message we are looking for. Commonly either
386             the relay we recieved it from, or the relay we sent it to. (Depending
387             on the logfile.)
388              
389             =head3 set_subject
390              
391             Sets the subject of the message we are looking for.
392              
393             =head3 set_parser_class
394              
395             Sets the parser class to use when searching the log file. A subclass will
396             have a 'default' parser that it will normally use: This is to allow easy
397             site-specific logfile formats based on more common formats. To use you
398             would subclass the default parser for the log file format of the base program
399             to handle the site's specific changes.
400              
401             Takes the name of a class as a string, and will throw an exception
402             (C) if that class name doesn't start
403             with Mail::Log::Parse.
404              
405             =cut
406              
407             sub set_parser_class {
408 5     5 1 14 my ($self, $new_id) = @_;
409 5 100       30 if ( $new_id =~ /Mail::Log::Parse::/ ) {
410 4         13 $log_info{$$self}{parser_class} = $new_id;
411             }
412             else {
413 1         13 Mail::Log::Exceptions::InvalidParameter->throw('Parser class needs to be a Mail::Log::Parse:: subclass.');
414             }
415 4         10 return;
416             }
417              
418             =head3 set_log
419              
420             Sets the log file we are searching throuh. Takes a full or relative path.
421             If it doesn't exist, or can't be read by the current user, it will throw an
422             exception. (C) Note that it does I
423             try to open it immedeately. That will be done at first attempt to read from
424             the logfile.
425              
426             =cut
427              
428             sub set_log {
429 37     37 1 74 my ($self, $new_name) = @_;
430              
431 37 100       94 if ( ! defined($new_name) ) {
432 1         29 Mail::Log::Exceptions::InvalidParameter->throw('No log file specified in call to '.blessed($self).'->new().');
433             }
434              
435             # Check to make sure the file exists,
436             # and then that we can read it, before accpeting the filename.
437 36 100       1332 if ( -e $new_name ) {
438 34 50       778 if ( -r $new_name ) {
439 34         239 $log_info{refaddr $self}{'filename'} = $new_name;
440             }
441             else {
442 0         0 Mail::Log::Exceptions::LogFile->throw("Log file $new_name is not readable.");
443             }
444             }
445             else {
446 2         43 Mail::Log::Exceptions::LogFile->throw("Log file $new_name does not exist.");
447             }
448              
449             # Reset the parser.
450 34         296 $self->_set_log_parser(undef);
451              
452 34         55 return;
453             }
454              
455             =head3 set_to_address
456              
457             Sets the to address of the message we are looking for. Multiple addresses can
458             be specified, they will all be added, with duplicates skipped. This method
459             completely clears the array: there will be no addresses in the list except
460             those given to it. Duplicates will be consolidated: Only one of any particular
461             address will be in the final array.
462              
463             As a special case, passing C to this will set the array to undef.
464              
465             =head3 add_to_address
466              
467             Adds to the list of to addresses we are looking for. It does I delete the
468             array first.
469              
470             Duplicates will be consolidated, so that the array will only have one of any
471             given address. (No matter the order they are given in.)
472              
473             =head3 remove_to_address
474              
475             Removes a single to address from the array.
476              
477             =cut
478              
479             #
480             # Getters.
481             #
482              
483             =head2 GETTERS
484              
485             =head3 get_from_address
486              
487             Gets the from address. (Either as set using the setter, or as found in the
488             log.)
489              
490             =head3 get_to_address
491              
492             Gets the to address array. (Either as set using the setters, or as found in the
493             log.)
494              
495             Will return a reference to an array, or 'undef' if the to address has not been
496             set/found.
497              
498             =head3 get_message_id
499              
500             Gets the message_id. (Either as set using the setter, or as found in the
501             log.)
502              
503             =head3 get_subject
504              
505             Gets the message subject. (Either as set using the setter, or as found in the
506             log.)
507              
508             =head3 get_recieved_time
509              
510             Gets the recieved time. (Either as set using the setter, or as found in the
511             log.)
512              
513             =head3 get_sent_time
514              
515             Gets the sent time. (Either as set using the setter, or as found in the
516             log.)
517              
518             =head3 get_relay_host
519              
520             Gets the relay host. (Either as set using the setter, or as found in the
521             log.)
522              
523             =head3 get_log
524              
525             Returns the path to the logfile we are reading.
526              
527             =cut
528              
529             sub get_log {
530 27     27 1 74 my ($self) = @_;
531 27         338 return $log_info{$$self}{'filename'};
532             }
533              
534             =head3 get_connect_time
535              
536             Returns the time the remote host connected to this host to send the message.
537              
538             =head3 get_disconnect_time
539              
540             Returns the time the remote host disconnected from this host after sending
541             the message.
542              
543             =head3 get_delay
544              
545             Returns the total delay in this stage in processing the message.
546              
547             =head3 get_all_info
548              
549             Returns message info as returned from the parser, for more direct/complete
550             access.
551              
552             (It's probably a good idea to avoid using this, but it is useful and arguably
553             needed under certain circumstances.)
554              
555             =cut
556              
557             sub get_all_info {
558 126     126 1 182 my ($self) = @_;
559 126         375 return $message_raw_info{$$self};
560             }
561              
562             #
563             # To be implemented by the sub-classes.
564             #
565              
566             =head2 Utility subroutines
567              
568             =head3 clear_message_info
569              
570             Clears I known information on the current message, but not on the log.
571              
572             Use to start searching for a new message.
573              
574             =cut
575              
576             sub clear_message_info {
577 3     3 1 27708 my ($self) = @_;
578              
579 3         11 foreach my $parameter ( @cleared_parameters ) {
580 66 50       363 $all_setters{$$self}{$parameter}->($self, undef) if defined($all_setters{$$self}{$parameter});
581             }
582              
583 3         17 $self->_set_message_raw_info(undef);
584              
585 3         8 return;
586             }
587              
588             =head3 find_message
589              
590             Finds the first/next occurance of a message in the log. Can be passed any
591             of the above information in a hash format.
592              
593             Default is to search I in the log: If you have already done a search,
594             this will start searching where the previous search ended. To start over
595             at the beginning of the logfile, set C as true in the parameter
596             hash.
597              
598             This method needs to be overridden by the subclass: by default it will throw
599             an C error.
600              
601             =cut
602              
603             sub find_message {
604 1     1 1 23 Mail::Log::Exceptions::Unimplemented->throw("Method 'find_message' needs to be implemented by subclass.\n");
605             # return 0; # Return false: The message couldn't be found. This will never be called.
606             }
607              
608             =head3 find_message_info
609              
610             Finds as much information as possible about a specific occurance of a message
611             in the logfile. Acts much the same as find_message, other than the fact that
612             once it finds a message it will do any searching necarry to find all information
613             on that message connection.
614              
615             (Also needs to be implemented by subclasses.)
616              
617             =cut
618              
619             sub find_message_info {
620 1     1 1 2117 Mail::Log::Exceptions::Unimplemented->throw("Method 'find_message_info' needs to be implemented by subclass.\n");
621             # return 0; # Return false: The message couldn't be found. This will never be called.
622             }
623              
624             =head1 SUBCLASSING
625              
626             There are two ways to subclass Mail::Log::Trace: The standard way, and the
627             automatic way. The old way is fairly straightforward: You create the accessors
628             for all the subclass-specific information, and overide C,
629             C, and C<_parse_args>. (Making sure for C<_parse_args> that
630             you call the SUPER version.)
631              
632             Or you can try to let Mail::Log::Trace do as much of that as possible, and only
633             do C and C.
634              
635             To do the latter, you need to override several of the following list of methods:
636              
637             _requested_public_accessors
638             _requested_public_set_only
639             _requested_public_get_only
640             _requested_array_accessors
641             _requested_special_accessors
642             _requested_cleared_parameters
643             _set_as_message_info
644              
645             That looks like a long list, but it is very rare that you'll need to override
646             all of them, and all they need to do is return a static list of keys that you
647             want the relevant action taken on.
648              
649             The first five build accessors for you, of the form C, C
650             for standard public, C<_get_$key> and C<_set_key> for private accessors (note
651             that if you request a private setter, you'll also get a I getter, and
652             vice-versa), and C, C, C and C for
653             keys which store arrays. All of these have been heavily optimised for speed.
654              
655             The last two set what keys are cleared when you call C and
656             what keys will be checked when C<_parse_args> is called. (If none of those are
657             present, an exception will be thrown, saying there is no message-specific data.)
658              
659             C<_requested_special_accessors> requires a little more discussion. Unlike the
660             rest, it expects not an array, but a hash (not a hashref: a hash). The keys of
661             the hash are the keys that will have accessors built for them (public, single,
662             only), and the values are code references to parsing/validation functions.
663              
664             An example:
665              
666             sub _requested_special_accessors {
667             return ( year => sub { my ($self, $year) = @_;
668             return '____INVALID__VALUE____' if $year < 1970;
669             my $maillog = $self->_get_log_parser();
670             if (defined($maillog)) {
671             $maillog->set_year($year);
672             }
673             return $year;
674             },
675             );
676             };
677              
678             The above is from L, and is for the key 'year'.
679             The coderef in this case does both validation and some extra action. The action
680             is to call C<$self->_get_log_parser()->set_year()> on the year being passed.
681             (Because in this case the parser needs to have the year to return info
682             correctly.) The validation is to check to make sure the year is greater than
683             1970. (The birth of UNIX, so we are unlikey to handle any logs earlier than
684             that.) If it is not, the special value C<____INVALID__VALUE____> is returned.
685             This will cause an exception to be thrown. If the value is valid, it is
686             returned.
687              
688             The purpose of all the above is to allow subclasses to check values, do any
689             parsing that is needed, and to any other actions that may be needed. (This is
690             in contrast to the normal accessors, which just store the value given blindly.)
691              
692             Note that C should always be considered a valid value.
693              
694             Normally keys should be in the 'public_accessors' list: those accessors are much
695             faster.
696              
697             These accessors are built at I, when the object is first created.
698             This means object creation is fairly expensive.
699              
700             Of course, you still need to write C and C...
701              
702             Mail::Log::Trace is a cached inside-out object. If you don't know what that
703             means, you can probably ignore it. However if you need to store object state
704             data (and aren't using the convience accessors), it may be useful to know that
705             C<$$self == refaddr $self>.
706              
707             =cut
708              
709             #
710             # Private to be implemented by the sub-classes...
711             # (If needed.)
712             #
713              
714 13     13   36 sub _requested_public_accessors { return (); };
715 36     36   87 sub _requested_public_set_only { return (); };
716 36     36   79 sub _requested_public_get_only { return (); };
717 36     36   76 sub _requested_array_accessors { return (); };
718 13     13   34 sub _requested_cleared_parameters { return (); };
719 13     13   35 sub _requested_special_accessors { return (); };
720 13     13   73 sub _set_as_message_info { return (); };
721              
722             sub _parse_args {
723 69     69   140 my ($self, $argref, $throw_error) = @_;
724            
725             # It is possible for them to pass the message info here.
726 69         100 my %args;
727 69         156 foreach my $parameter ( @valid_parameters ) {
728 3997 100       13437 $all_setters{$$self}{$parameter}->($self, $argref->{$parameter}) if exists $argref->{$parameter};
729             }
730            
731             # Not all parameters are checked...
732 69         163 foreach my $parameter ( @checked_parameters ) {
733 642 50       6143 $args{$parameter} = $all_getters{$$self}{$parameter}->($self) if defined($all_setters{$$self}{$parameter});
734             }
735 69 100       281 $args{from_start} = $argref->{from_start} ? 1 : 0;
736            
737             # And log info.
738 69 100       245 $self->set_parser_class($argref->{parser_class}) if exists $argref->{parser_class};
739            
740             # Speed things up a bit, and make it easier to read.
741            
742 68 100       181 if ($throw_error) {
743             # If none are defined...
744 30 100       154 if ( (grep { defined($args{$_}) } keys %args) == 1 ) {
  330         611  
745 1         20 Mail::Log::Exceptions::Message->throw("Warning: Trying to search for a message with no message-specific data.\n");
746             }
747             }
748              
749 67         216 return \%args;
750             }
751              
752             #
753             # Private functions/methods.
754             #
755              
756             =head1 UTILITY SUBROUTINES
757              
758             B
759              
760             There are a few subroutines especially for use by subclasses.
761              
762             =head2 _set_message_raw_info
763              
764             Give this the raw message info, in whatever format the parser gives it. The
765             user should hopefully never want it, but just in case...
766              
767             =cut
768              
769             sub _set_message_raw_info {
770 137     137   238 my ($self, $new_hash) = @_;
771 137         240 $message_raw_info{$$self} = $new_hash;
772 137         302 return;
773             }
774              
775             =head2 _set_log_parser
776              
777             Sets the log parser. Takes a reference to a parser object.
778              
779             =cut
780              
781             sub _set_log_parser {
782 52     52   101 my ($self, $log_parser) = @_;
783 52         138 $log_info{$$self}->{log_parser} = $log_parser;
784 52         110 return;
785             }
786              
787             =head2 _get_log_parser
788              
789             Returns the log parser object.
790              
791             =cut
792              
793             sub _get_log_parser {
794 34     34   69 my ($self) = @_;
795 34         131 return $log_info{$$self}->{log_parser};
796             }
797              
798             =head2 _get_parser_class
799              
800             Returns the name of the class the user wants you to use to parse the file.
801              
802             Please take it under advisement.
803              
804             =cut
805              
806             sub _get_parser_class {
807 24     24   46 my ($self) = @_;
808 24         108 return $log_info{$$self}->{parser_class};
809             }
810              
811             =head1 BUGS
812              
813             None known at the moment... (I am nervious about the way I'm storing some of
814             these coderefs. So far I haven't run into problems, but I'm not entirely sure
815             there aren't any. If you start getting weird behaviour when using multiple
816             Mail::Log::Trace subclasses at once, please tell me.)
817              
818             =head1 REQUIRES
819              
820             L, L.
821              
822             Some subclass, and probably a L class to be useful.
823              
824             =head1 HISTORY
825              
826             1.1.1 Feb 2, 2009 - Fixed a minor issue that could cause problems with multiple
827             subclass objects exisiting at the same time.
828              
829             1.1.0 Dec 23, 2008 - Major re-write to make subclassing easier. Or possibly
830             more confusing.
831              
832             1.00.03 Dec 5, 2208 - Licence clarification.
833              
834             1.00.02 Dec 2, 2008 - I really mean it this time.
835              
836             1.00.01 Dec 1, 2008 - Requirements fix, no code changes.
837              
838             1.00.00 Nov 28, 2008 - original version.
839              
840             =head1 AUTHOR
841              
842             Daniel T. Staal
843             CPAN ID: DSTAAL
844             dstaal@usa.net
845              
846             =head1 COPYRIGHT
847              
848             This program is free software; you can redistribute
849             it and/or modify it under the same terms as Perl itself.
850              
851             This copyright will expire in 30 years, or five years after the author's death,
852             whichever occurs last, at which time the code be released to the public domain.
853              
854             =cut
855              
856             #################### main pod documentation end ###################
857              
858             }
859             1;
860             # The preceding line will help the module return a true value
861