line  
 stmt  
 bran  
 cond  
 sub  
 pod  
 time  
 code  
 
1 
 
 
 
 
 
 
 
 
 
 
 
 
 
 #!/usr/bin/perl  
 
2 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
3 
 
 
 
 
 
 
 
 
 
 
 
 
 
 #=======================================================================  
 
4 
 
 
 
 
 
 
 
 
 
 
 
 
 
 # Set.pm / IPTables::Log::Set  
 
5 
 
 
 
 
 
 
 
 
 
 
 
 
 
 # $Id: Set.pm 21 2010-12-17 21:07:37Z andys $  
 
6 
 
 
 
 
 
 
 
 
 
 
 
 
 
 # $HeadURL: https://daedalus.dmz.dn7.org.uk/svn/IPTables-Log/trunk/IPTables-Log/lib/IPTables/Log/Set.pm $  
 
7 
 
 
 
 
 
 
 
 
 
 
 
 
 
 # (c)2009 Andy Smith    
 
8 
 
 
 
 
 
 
 
 
 
 
 
 
 
 #-----------------------------------------------------------------------  
 
9 
 
 
 
 
 
 
 
 
 
 
 
 
 
 #:Description  
 
10 
 
 
 
 
 
 
 
 
 
 
 
 
 
 # This class holds a set of IPTables::Log::Set::Record objects  
 
11 
 
 
 
 
 
 
 
 
 
 
 
 
 
 #-----------------------------------------------------------------------  
 
12 
 
 
 
 
 
 
 
 
 
 
 
 
 
 #:Synopsis  
 
13 
 
 
 
 
 
 
 
 
 
 
 
 
 
 # NOTE: This class isn't designed to be created directly.  
 
14 
 
 
 
 
 
 
 
 
 
 
 
 
 
 #  
 
15 
 
 
 
 
 
 
 
 
 
 
 
 
 
 # use IPTables::Log;  
 
16 
 
 
 
 
 
 
 
 
 
 
 
 
 
 # my $l = IPTables::Log->new;  
 
17 
 
 
 
 
 
 
 
 
 
 
 
 
 
 # my $s = $l->create_set;  
 
18 
 
 
 
 
 
 
 
 
 
 
 
 
 
 # my $r = $s->create_record({text => '...IN=eth0 OUT=eth1 MAC=00:...'});  
 
19 
 
 
 
 
 
 
 
 
 
 
 
 
 
 # $r->parse;  
 
20 
 
 
 
 
 
 
 
 
 
 
 
 
 
 # $s->add($r);  
 
21 
 
 
 
 
 
 
 
 
 
 
 
 
 
 #=======================================================================  
 
22 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
23 
 
 
 
 
 
 
 
 
 
 
 
 
 
 # The pod (Perl Documentation) for this module is provided inline. For a  
 
24 
 
 
 
 
 
 
 
 
 
 
 
 
 
 # better-formatted version, please run:-  
 
25 
 
 
 
 
 
 
 
 
 
 
 
 
 
 # $ perldoc Set.pm  
 
26 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
27 
 
 
 
 
 
 
 
 
 
 
 
 
 
 =head1 NAME  
 
28 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
29 
 
 
 
 
 
 
 
 
 
 
 
 
 
 IPTables::Log::Set - Holds a set of IPTables::Log::Set::Record objects.  
 
30 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
31 
 
 
 
 
 
 
 
 
 
 
 
 
 
 =head1 SYNOPSIS  
 
32 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
33 
 
 
 
 
 
 
 
 
 
 
 
 
 
 Note that this class isn't designed to be created directly. You can create these objects via a C object.   
 
34 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
35 
 
 
 
 
 
 
 
 
 
 
 
 
 
   use IPTables::Log;  
 
36 
 
 
 
 
 
 
 
 
 
 
 
 
 
   my $l = IPTables::Log->new;  
 
37 
 
 
 
 
 
 
 
 
 
 
 
 
 
   my $s = $l->create_set;  
 
38 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
39 
 
 
 
 
 
 
 
 
 
 
 
 
 
 =head1 DEPENDENCIES  
 
40 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
41 
 
 
 
 
 
 
 
 
 
 
 
 
 
 =over 4  
 
42 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
43 
 
 
 
 
 
 
 
 
 
 
 
 
 
 =item * Class::Accessor - for accessor methods  
 
44 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
45 
 
 
 
 
 
 
 
 
 
 
 
 
 
 =item * Data::GUID - for GUID generation  
 
46 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
47 
 
 
 
 
 
 
 
 
 
 
 
 
 
 =item * NetAddr::IP - for the C and C methods (required by L)     
 
48 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
49 
 
 
 
 
 
 
 
 
 
 
 
 
 
 =back  
 
50 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
51 
 
 
 
 
 
 
 
 
 
 
 
 
 
 =cut  
 
52 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
53 
 
 
 
 
 
 
 
 
 
 
 
 
 
 # Set our package name  
 
54 
 
 
 
 
 
 
 
 
 
 
 
 
 
 package IPTables::Log::Set;  
 
55 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
56 
 
 
 
 
 
 
 
 
 
 
 
 
 
 # Minimum version  
 
57 
 
3
 
 
 
 
 
  
3
   
 
 
 
53
 
 use 5.010000;  
 
  
 
3
 
 
 
 
 
 
 
 
 
12
 
    
 
  
 
3
 
 
 
 
 
 
 
 
 
130
 
    
 
58 
 
 
 
 
 
 
 
 
 
 
 
 
 
 # Use strict and warnings  
 
59 
 
3
 
 
 
 
 
  
3
   
 
 
 
17
 
 use strict;  
 
  
 
3
 
 
 
 
 
 
 
 
 
7
 
    
 
  
 
3
 
 
 
 
 
 
 
 
 
89
 
    
 
60 
 
3
 
 
 
 
 
  
3
   
 
 
 
13
 
 use warnings;  
 
  
 
3
 
 
 
 
 
 
 
 
 
4
 
    
 
  
 
3
 
 
 
 
 
 
 
 
 
67
 
    
 
61 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
62 
 
 
 
 
 
 
 
 
 
 
 
 
 
 # Use Carp for erroring  
 
63 
 
3
 
 
 
 
 
  
3
   
 
 
 
14
 
 use Carp;  
 
  
 
3
 
 
 
 
 
 
 
 
 
5
 
    
 
  
 
3
 
 
 
 
 
 
 
 
 
166
 
    
 
64 
 
 
 
 
 
 
 
 
 
 
 
 
 
 # Use Data::GUID for generating GUIDs  
 
65 
 
3
 
 
 
 
 
  
3
   
 
 
 
3172
 
 use Data::GUID;  
 
  
 
3
 
 
 
 
 
 
 
 
 
95590
 
    
 
  
 
3
 
 
 
 
 
 
 
 
 
46
 
    
 
66 
 
 
 
 
 
 
 
 
 
 
 
 
 
 # Use IPTables::Log::Set::Record for individual log entries  
 
67 
 
3
 
 
 
 
 
  
3
   
 
 
 
2933
 
 use IPTables::Log::Set::Record;  
 
  
 
3
 
 
 
 
 
 
 
 
 
11
 
    
 
  
 
3
 
 
 
 
 
 
 
 
 
36
 
    
 
68 
 
 
 
 
 
 
 
 
 
 
 
 
 
 # Use Data::Dumper  
 
69 
 
3
 
 
 
 
 
  
3
   
 
 
 
224
 
 use Data::Dumper;  
 
  
 
3
 
 
 
 
 
 
 
 
 
7
 
    
 
  
 
3
 
 
 
 
 
 
 
 
 
315
 
    
 
70 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
71 
 
 
 
 
 
 
 
 
 
 
 
 
 
 # Inherit from Class::Accessor to simplify accessor method generation  
 
72 
 
3
 
 
 
 
 
  
3
   
 
 
 
19
 
 use base qw(Class::Accessor);  
 
  
 
3
 
 
 
 
 
 
 
 
 
15
 
    
 
  
 
3
 
 
 
 
 
 
 
 
 
3064
 
    
 
73 
 
 
 
 
 
 
 
 
 
 
 
 
 
 # Follow best practice  
 
74 
 
 
 
 
 
 
 
 
 
 
 
 
 
 __PACKAGE__->follow_best_practice;  
 
75 
 
 
 
 
 
 
 
 
 
 
 
 
 
 # Create log and guid as read-only accessor methods  
 
76 
 
 
 
 
 
 
 
 
 
 
 
 
 
 __PACKAGE__->mk_ro_accessors( qw(log guid) );  
 
77 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
78 
 
 
 
 
 
 
 
 
 
 
 
 
 
 # Set version information  
 
79 
 
 
 
 
 
 
 
 
 
 
 
 
 
 our $VERSION = '0.0005';  
 
80 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
81 
 
 
 
 
 
 
 
 
 
 
 
 
 
 =head1 CONSTRUCTORS  
 
82 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
83 
 
 
 
 
 
 
 
 
 
 
 
 
 
 =head2 Set->create  
 
84 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
85 
 
 
 
 
 
 
 
 
 
 
 
 
 
 Creates a new C object. This isn't the recommended way to do this, however. The proper way is to create an object via a L object with C.     
 
86 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
87 
 
 
 
 
 
 
 
 
 
 
 
 
 
 =cut  
 
88 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
89 
 
 
 
 
 
 
 
 
 
 
 
 
 
 sub create  
 
90 
 
 
 
 
 
 
 
 
 
 
 
 
 
 {  
 
91 
 
2
 
 
 
 
 
  
2
   
 
  
1
   
 
4
 
 	my ($class, $args) = @_;  
 
92 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
93 
 
2
 
 
 
 
 
 
 
 
 
19
 
 	my $self = __PACKAGE__->new($args);  
 
94 
 
2
 
 
 
 
 
 
 
 
 
30
 
 	$self->{records} = {};  
 
95 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
96 
 
 
 
 
 
 
 
 
 
 
 
 
 
 	# Generate a GUID for the set  
 
97 
 
2
 
 
 
 
 
 
 
 
 
23
 
 	my $g = Data::GUID->new;  
 
98 
 
2
 
 
 
 
 
 
 
 
 
390878
 
 	$self->{guid} = $g->as_string;  
 
99 
 
2
 
 
 
 
 
 
 
 
 
163
 
 	$self->{no_header} = $args->{'no_header'};  
 
100 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
101 
 
2
 
 
 
 
 
 
 
 
 
28
 
 	return $self;  
 
102 
 
 
 
 
 
 
 
 
 
 
 
 
 
 }  
 
103 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
104 
 
 
 
 
 
 
 
 
 
 
 
 
 
 =head1 METHODS  
 
105 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
106 
 
 
 
 
 
 
 
 
 
 
 
 
 
 =head2 $set->create_record(I<{text => '...IN=eth0 OUT=eth1 MAC=00:...'}>))  
 
107 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
108 
 
 
 
 
 
 
 
 
 
 
 
 
 
 Creates a new L object. This is the B way to create C objects, as it ensures various settings are inherited from the C class.      
 
109 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
110 
 
 
 
 
 
 
 
 
 
 
 
 
 
 The text of the log entry can be passed here, or it can be passed with the C accessor method to the C object itself.    
 
111 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
112 
 
 
 
 
 
 
 
 
 
 
 
 
 
 =cut  
 
113 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
114 
 
 
 
 
 
 
 
 
 
 
 
 
 
 sub create_record  
 
115 
 
 
 
 
 
 
 
 
 
 
 
 
 
 {  
 
116 
 
9
 
 
 
 
 
  
9
   
 
  
1
   
 
845
 
 	my ($self, $args) = @_;  
 
117 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
118 
 
 
 
 
 
 
 
 
 
 
 
 
 
 	#$args->{log} = $self->get_log;  
 
119 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
120 
 
9
 
 
 
 
 
 
 
 
 
49
 
 	my $record = IPTables::Log::Set::Record->create($args);  
 
121 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
122 
 
9
 
 
 
 
 
 
 
 
 
19
 
 	return $record;  
 
123 
 
 
 
 
 
 
 
 
 
 
 
 
 
 }  
 
124 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
125 
 
 
 
 
 
 
 
 
 
 
 
 
 
 =head2 $set->load_file($filename)  
 
126 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
127 
 
 
 
 
 
 
 
 
 
 
 
 
 
 Loads in logs from I<$filename>, discarding any which don't appear to be iptables/netfilter logs. A L object is then created for each entry, and the content is then parsed. Finally, each entry is then added to the set created with C.    
 
128 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
129 
 
 
 
 
 
 
 
 
 
 
 
 
 
 =cut  
 
130 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
131 
 
 
 
 
 
 
 
 
 
 
 
 
 
 sub load_file  
 
132 
 
 
 
 
 
 
 
 
 
 
 
 
 
 {  
 
133 
 
1
 
 
 
 
 
  
1
   
 
  
1
   
 
375
 
 	my ($self, $filename) = @_;  
 
134 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
135 
 
 
 
 
 
 
 
 
 
 
 
 
 
 	# Check we've been passed a filename  
 
136 
 
1
 
  
 50
   
 
 
 
 
 
 
 
3
 
 	if(!$filename)  
 
137 
 
 
 
 
 
 
 
 
 
 
 
 
 
 	{  
 
138 
 
0
 
 
 
 
 
 
 
 
 
0
 
 		croak "No filename given to load_file().";  
 
139 
 
 
 
 
 
 
 
 
 
 
 
 
 
 		#$self->get_log->fatal("No filename given!");  
 
140 
 
 
 
 
 
 
 
 
 
 
 
 
 
 	}  
 
141 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
142 
 
 
 
 
 
 
 
 
 
 
 
 
 
 	# Check that the file exists, and barf if not.  
 
143 
 
1
 
  
 50
   
 
 
 
 
 
 
 
22
 
 	if(!-f $filename)  
 
144 
 
 
 
 
 
 
 
 
 
 
 
 
 
 	{  
 
145 
 
0
 
 
 
 
 
 
 
 
 
0
 
 		croak $filename." does not exist.";  
 
146 
 
 
 
 
 
 
 
 
 
 
 
 
 
 		#$self->get_log->fatal("Cannot find ".$self->get_log->fcolour('yellow', $filename));  
 
147 
 
 
 
 
 
 
 
 
 
 
 
 
 
 	}  
 
148 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
149 
 
 
 
 
 
 
 
 
 
 
 
 
 
 	#$self->get_log->debug("Opening ".$self->get_log->fcolour('yellow', $filename)."...");  
 
150 
 
 
 
 
 
 
 
 
 
 
 
 
 
 	# Open the logfile  
 
151 
 
1
 
  
 50
   
 
 
 
 
 
 
 
40
 
 	open(LOGFILE, $filename) || $self->get_log->fatal("Cannot open ".$self->get_log->fcolour('yellow', $filename));  
 
152 
 
1
 
 
 
 
 
 
 
 
 
46
 
 	my @logs = ;   
 
153 
 
 
 
 
 
 
 
 
 
 
 
 
 
 	#$self->get_log->debug("Finished reading in logs.");  
 
154 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
155 
 
 
 
 
 
 
 
 
 
 
 
 
 
 	# It's a fair bet that if we don't have an IN= and an OUT= and it doesn't have a source of 'kernel', then it's not an iptables log.  
 
156 
 
 
 
 
 
 
 
 
 
 
 
 
 
 	# We'll discard those before even attempting to parse it.  
 
157 
 
1
 
 
 
 
 
 
 
 
 
4
 
 	foreach my $log (@logs)  
 
158 
 
 
 
 
 
 
 
 
 
 
 
 
 
 	{  
 
159 
 
8
 
  
 50
   
 
 
 
 
 
 
 
133
 
 		if($log =~ /kernel.+IN=.+OUT=/)  
 
160 
 
 
 
 
 
 
 
 
 
 
 
 
 
 		{  
 
161 
 
8
 
 
 
 
 
 
 
 
 
17
 
 			chomp($log);  
 
162 
 
 
 
 
 
 
 
 
 
 
 
 
 
 			#$self->get_log->debug_nolf("Parsing iptables log entry... ");  
 
163 
 
8
 
 
 
 
 
 
 
 
 
38
 
 			my $record = $self->create_record({'text' => $log, 'no_header' => $self->{no_header}});  
 
164 
 
8
 
 
 
 
 
 
 
 
 
29
 
 			$record->parse;  
 
165 
 
 
 
 
 
 
 
 
 
 
 
 
 
 			#$self->get_log->debug("done.");  
 
166 
 
8
 
 
 
 
 
 
 
 
 
19
 
 			$self->add($record);  
 
167 
 
8
 
 
 
 
 
 
 
 
 
22
 
 			$self->get_log->debug("Added record with GUID ".$self->get_log->fcolour('yellow', $record->get_guid). " to set.");  
 
168 
 
 
 
 
 
 
 
 
 
 
 
 
 
 			#return 1;  
 
169 
 
 
 
 
 
 
 
 
 
 
 
 
 
 		}  
 
170 
 
 
 
 
 
 
 
 
 
 
 
 
 
 		else  
 
171 
 
 
 
 
 
 
 
 
 
 
 
 
 
 		{  
 
172 
 
 
 
 
 
 
 
 
 
 
 
 
 
 			#$self->get_log->debug("Log entry is not an iptables log entry, so skipping...");  
 
173 
 
 
 
 
 
 
 
 
 
 
 
 
 
 		}  
 
174 
 
 
 
 
 
 
 
 
 
 
 
 
 
 	}  
 
175 
 
1
 
 
 
 
 
 
 
 
 
18
 
 	return 1;  
 
176 
 
 
 
 
 
 
 
 
 
 
 
 
 
 }  
 
177 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
178 
 
 
 
 
 
 
 
 
 
 
 
 
 
 =head2 $set->add($record)  
 
179 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
180 
 
 
 
 
 
 
 
 
 
 
 
 
 
 Adds a L object to a set created with C.    
 
181 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
182 
 
 
 
 
 
 
 
 
 
 
 
 
 
 =cut  
 
183 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
184 
 
 
 
 
 
 
 
 
 
 
 
 
 
 sub add  
 
185 
 
 
 
 
 
 
 
 
 
 
 
 
 
 {  
 
186 
 
8
 
 
 
 
 
  
8
   
 
  
1
   
 
13
 
 	my ($self, $record) = @_;  
 
187 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
188 
 
8
 
  
 50
   
 
 
 
 
 
 
 
15
 
 	if($record)  
 
189 
 
 
 
 
 
 
 
 
 
 
 
 
 
 	{  
 
190 
 
8
 
 
 
 
 
 
 
 
 
20
 
 		my $guid = $record->get_guid;  
 
191 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
192 
 
8
 
 
 
 
 
 
 
 
 
38
 
 		$self->{records}{$guid} = $record;  
 
193 
 
 
 
 
 
 
 
 
 
 
 
 
 
 	}  
 
194 
 
 
 
 
 
 
 
 
 
 
 
 
 
 }  
 
195 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
196 
 
 
 
 
 
 
 
 
 
 
 
 
 
 =head2 $set->get_by('field')  
 
197 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
198 
 
 
 
 
 
 
 
 
 
 
 
 
 
 Returns a hash of record identifiers, indexed by I. Field can be one of I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I.                      
 
199 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
200 
 
 
 
 
 
 
 
 
 
 
 
 
 
 If you attempt to sort on a field that isn't present in all records in the set, get_by will only return records which have that field. For example, if you attempt to get_by('dpt'), any ICMP log messages will be silently excluded from the returned set.  
 
201 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
202 
 
 
 
 
 
 
 
 
 
 
 
 
 
 =cut  
 
203 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
204 
 
 
 
 
 
 
 
 
 
 
 
 
 
 sub get_by  
 
205 
 
 
 
 
 
 
 
 
 
 
 
 
 
 {  
 
206 
 
19
 
 
 
 
 
  
19
   
 
  
1
   
 
10470
 
 	my ($self, $by) = @_;  
 
207 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
208 
 
 
 
 
 
 
 
 
 
 
 
 
 
 	# Check that $by is set  
 
209 
 
19
 
  
 50
   
 
 
 
 
 
 
 
63
 
 	if($by)  
 
210 
 
 
 
 
 
 
 
 
 
 
 
 
 
 	{  
 
211 
 
 
 
 
 
 
 
 
 
 
 
 
 
 		# Create a hash to hold the index values  
 
212 
 
19
 
 
 
 
 
 
 
 
 
88
 
 		my %indexes;  
 
213 
 
19
 
 
 
 
 
 
 
 
 
34
 
 		$indexes{by} = $by;  
 
214 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
215 
 
19
 
 
 
 
 
 
 
 
 
23
 
 		foreach my $r (keys %{$self->{records}})  
 
  
 
19
 
 
 
 
 
 
 
 
 
84
 
    
 
216 
 
 
 
 
 
 
 
 
 
 
 
 
 
 		{  
 
217 
 
 
 
 
 
 
 
 
 
 
 
 
 
 			# Step through each record.  
 
218 
 
152
 
 
 
 
 
 
 
 
 
264
 
 			my $record = $self->{records}{$r};  
 
219 
 
152
 
 
 
 
 
 
 
 
 
643
 
 			my $value = $record->get($by);  
 
220 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
221 
 
 
 
 
 
 
 
 
 
 
 
 
 
 			# If $value is blank, it means not all records have this field.  
 
222 
 
 
 
 
 
 
 
 
 
 
 
 
 
 			# For now, we'll refuse to add these.  
 
223 
 
152
 
  
100
   
 
 
 
 
 
 
 
313
 
 			if($value)  
 
224 
 
 
 
 
 
 
 
 
 
 
 
 
 
 			{  
 
225 
 
127
 
  
100
   
 
 
 
 
 
 
 
331
 
 				if(!$indexes{$by}{$record->get($by)})  
 
226 
 
 
 
 
 
 
 
 
 
 
 
 
 
 				{  
 
227 
 
49
 
 
 
 
 
 
 
 
 
174
 
 					$indexes{$by}{$record->get($by)} = [];  
 
228 
 
 
 
 
 
 
 
 
 
 
 
 
 
 				}  
 
229 
 
127
 
 
 
 
 
 
 
 
 
165
 
 				push (@{$indexes{$by}{$record->get($by)}}, $record);  
 
  
 
127
 
 
 
 
 
 
 
 
 
429
 
    
 
230 
 
 
 
 
 
 
 
 
 
 
 
 
 
 			}  
 
231 
 
 
 
 
 
 
 
 
 
 
 
 
 
 		}  
 
232 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
233 
 
19
 
 
 
 
 
 
 
 
 
200
 
 		return %indexes;  
 
234 
 
 
 
 
 
 
 
 
 
 
 
 
 
 	}  
 
235 
 
 
 
 
 
 
 
 
 
 
 
 
 
 }  
 
236 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
237 
 
 
 
 
 
 
 
 
 
 
 
 
 
 =head1 CAVEATS  
 
238 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
239 
 
 
 
 
 
 
 
 
 
 
 
 
 
 None.  
 
240 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
241 
 
 
 
 
 
 
 
 
 
 
 
 
 
 =head1 BUGS  
 
242 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
243 
 
 
 
 
 
 
 
 
 
 
 
 
 
 None that I'm aware of ;-)  
 
244 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
245 
 
 
 
 
 
 
 
 
 
 
 
 
 
 =head1 AUTHOR  
 
246 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
247 
 
 
 
 
 
 
 
 
 
 
 
 
 
 This module was written by B .    
 
248 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
249 
 
 
 
 
 
 
 
 
 
 
 
 
 
 =head1 COPYRIGHT  
 
250 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
251 
 
 
 
 
 
 
 
 
 
 
 
 
 
 $Id: Set.pm 21 2010-12-17 21:07:37Z andys $  
 
252 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
253 
 
 
 
 
 
 
 
 
 
 
 
 
 
 (c)2009 Andy Smith (L 
 
254 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
255 
 
 
 
 
 
 
 
 
 
 
 
 
 
 This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.  
 
256 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
257 
 
 
 
 
 
 
 
 
 
 
 
 
 
 =cut  
 
258 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
259 
 
 
 
 
 
 
 
 
 
 
 
 
 
 1