line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package POE::Component::Server::IRC::Plugin::Auth; |
2
|
|
|
|
|
|
|
our $AUTHORITY = 'cpan:BINGOS'; |
3
|
|
|
|
|
|
|
$POE::Component::Server::IRC::Plugin::Auth::VERSION = '1.62'; |
4
|
2
|
|
|
2
|
|
1200
|
use strict; |
|
2
|
|
|
|
|
28
|
|
|
2
|
|
|
|
|
67
|
|
5
|
2
|
|
|
2
|
|
12
|
use warnings; |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
82
|
|
6
|
2
|
|
|
2
|
|
11
|
use Carp 'croak'; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
111
|
|
7
|
2
|
|
|
2
|
|
13
|
use POE; |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
13
|
|
8
|
2
|
|
|
2
|
|
2058
|
use POE::Component::Client::Ident::Agent; |
|
2
|
|
|
|
|
10862
|
|
|
2
|
|
|
|
|
67
|
|
9
|
2
|
|
|
2
|
|
1148
|
use POE::Component::Client::DNS; |
|
2
|
|
|
|
|
145738
|
|
|
2
|
|
|
|
|
330
|
|
10
|
2
|
|
|
2
|
|
53
|
use POE::Component::Server::IRC::Plugin 'PCSI_EAT_NONE'; |
|
2
|
|
|
|
|
16
|
|
|
2
|
|
|
|
|
281
|
|
11
|
2
|
|
|
2
|
|
31
|
use Net::IP::Minimal qw[ip_is_ipv6]; |
|
2
|
|
|
|
|
15
|
|
|
2
|
|
|
|
|
5393
|
|
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
sub new { |
14
|
1
|
|
|
1
|
0
|
66
|
my ($package, %args) = @_; |
15
|
1
|
|
|
|
|
60
|
return bless \%args, $package; |
16
|
|
|
|
|
|
|
} |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
sub PCSI_register { |
19
|
1
|
|
|
1
|
0
|
288
|
my ($self, $ircd) = splice @_, 0, 2; |
20
|
|
|
|
|
|
|
|
21
|
1
|
|
|
|
|
11
|
$self->{ircd} = $ircd; |
22
|
|
|
|
|
|
|
|
23
|
1
|
|
|
|
|
40
|
POE::Session->create( |
24
|
|
|
|
|
|
|
object_states => [ |
25
|
|
|
|
|
|
|
$self => [qw( |
26
|
|
|
|
|
|
|
_start |
27
|
|
|
|
|
|
|
resolve_hostname |
28
|
|
|
|
|
|
|
resolve_ident |
29
|
|
|
|
|
|
|
got_hostname |
30
|
|
|
|
|
|
|
got_ip |
31
|
|
|
|
|
|
|
)], |
32
|
|
|
|
|
|
|
$self => { |
33
|
|
|
|
|
|
|
ident_agent_reply => 'got_ident', |
34
|
|
|
|
|
|
|
ident_agent_error => 'got_ident_error', |
35
|
|
|
|
|
|
|
} |
36
|
|
|
|
|
|
|
], |
37
|
|
|
|
|
|
|
); |
38
|
|
|
|
|
|
|
|
39
|
1
|
|
|
|
|
172
|
$ircd->plugin_register($self, 'SERVER', qw(connection)); |
40
|
1
|
|
|
|
|
82
|
return 1; |
41
|
|
|
|
|
|
|
} |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
sub PCSI_unregister { |
44
|
1
|
|
|
1
|
0
|
382
|
my ($self, $ircd) = splice @_, 0, 2; |
45
|
1
|
50
|
|
|
|
9
|
$self->{resolver}->shutdown() if $self->{resolver}; |
46
|
1
|
|
|
|
|
248
|
return 1; |
47
|
|
|
|
|
|
|
} |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
sub _start { |
50
|
1
|
|
|
1
|
|
860
|
my ($self, $session) = @_[OBJECT, SESSION]; |
51
|
1
|
|
|
|
|
5
|
$self->{session_id} = $session->ID; |
52
|
|
|
|
|
|
|
|
53
|
1
|
|
|
|
|
12
|
$self->{resolver} = POE::Component::Client::DNS->spawn( |
54
|
|
|
|
|
|
|
Timeout => 10, |
55
|
|
|
|
|
|
|
); |
56
|
1
|
|
|
|
|
1923
|
return; |
57
|
|
|
|
|
|
|
} |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
sub IRCD_connection { |
60
|
1
|
|
|
1
|
0
|
67
|
my ($self, $ircd) = splice @_, 0, 2; |
61
|
1
|
|
|
|
|
5
|
my $server = $ircd->server_name(); |
62
|
1
|
|
|
|
|
2
|
pop @_; |
63
|
|
|
|
|
|
|
my ($conn_id, $peeraddr, $peerport, $sockaddr, $sockport, $needs_auth) |
64
|
1
|
|
|
|
|
3
|
= map { $$_ } @_; |
|
8
|
|
|
|
|
27
|
|
65
|
|
|
|
|
|
|
|
66
|
1
|
50
|
|
|
|
5
|
return PCSI_EAT_NONE if !$needs_auth; |
67
|
1
|
50
|
|
|
|
25
|
return PCSI_EAT_NONE if !$ircd->connection_exists($conn_id); |
68
|
|
|
|
|
|
|
|
69
|
1
|
|
|
|
|
5
|
$self->{conns}{$conn_id} = { |
70
|
|
|
|
|
|
|
hostname => '', |
71
|
|
|
|
|
|
|
ident => '', |
72
|
|
|
|
|
|
|
done => 0, |
73
|
|
|
|
|
|
|
}; |
74
|
|
|
|
|
|
|
|
75
|
1
|
|
|
|
|
26
|
$ircd->send_output( |
76
|
|
|
|
|
|
|
{ |
77
|
|
|
|
|
|
|
prefix => $server, |
78
|
|
|
|
|
|
|
command => 'NOTICE', |
79
|
|
|
|
|
|
|
params => ['*', '*** Checking Ident'], |
80
|
|
|
|
|
|
|
}, |
81
|
|
|
|
|
|
|
$conn_id, |
82
|
|
|
|
|
|
|
); |
83
|
|
|
|
|
|
|
|
84
|
1
|
|
|
|
|
8
|
$ircd->send_output( |
85
|
|
|
|
|
|
|
{ |
86
|
|
|
|
|
|
|
prefix => $server, |
87
|
|
|
|
|
|
|
command => 'NOTICE', |
88
|
|
|
|
|
|
|
params => ['*', '*** Checking Hostname'], |
89
|
|
|
|
|
|
|
}, |
90
|
|
|
|
|
|
|
$conn_id, |
91
|
|
|
|
|
|
|
); |
92
|
|
|
|
|
|
|
|
93
|
1
|
50
|
|
|
|
7
|
if ($peeraddr =~ /^127\./) { |
94
|
1
|
|
|
|
|
6
|
$ircd->send_output( |
95
|
|
|
|
|
|
|
{ |
96
|
|
|
|
|
|
|
prefix => $server, |
97
|
|
|
|
|
|
|
command => 'NOTICE', |
98
|
|
|
|
|
|
|
params => ['*', '*** Found your hostname'] |
99
|
|
|
|
|
|
|
}, |
100
|
|
|
|
|
|
|
$conn_id, |
101
|
|
|
|
|
|
|
); |
102
|
1
|
|
|
|
|
3
|
$self->{conns}{$conn_id}{hostname} = 'localhost'; |
103
|
1
|
|
|
|
|
4
|
$self->_auth_done($conn_id); |
104
|
|
|
|
|
|
|
} |
105
|
|
|
|
|
|
|
else { |
106
|
|
|
|
|
|
|
$poe_kernel->call( |
107
|
0
|
|
|
|
|
0
|
$self->{session_id}, 'resolve_hostname', |
108
|
|
|
|
|
|
|
$conn_id, $peeraddr, |
109
|
|
|
|
|
|
|
); |
110
|
|
|
|
|
|
|
} |
111
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
$poe_kernel->call( |
113
|
1
|
|
|
|
|
7
|
$self->{session_id}, 'resolve_ident', |
114
|
|
|
|
|
|
|
$conn_id, $peeraddr, $peerport, $sockaddr, $sockport, |
115
|
|
|
|
|
|
|
); |
116
|
|
|
|
|
|
|
|
117
|
1
|
|
|
|
|
20
|
return PCSI_EAT_NONE; |
118
|
|
|
|
|
|
|
} |
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
sub resolve_hostname { |
121
|
0
|
|
|
0
|
0
|
0
|
my ($self, $conn_id, $peeraddr) = @_[OBJECT, ARG0, ARG1]; |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
my $response = $self->{resolver}->resolve( |
124
|
0
|
|
|
|
|
0
|
event => 'got_hostname', |
125
|
|
|
|
|
|
|
host => $peeraddr, |
126
|
|
|
|
|
|
|
type => 'PTR', |
127
|
|
|
|
|
|
|
context => { |
128
|
|
|
|
|
|
|
conn_id => $conn_id, |
129
|
|
|
|
|
|
|
peeraddr => $peeraddr, |
130
|
|
|
|
|
|
|
}, |
131
|
|
|
|
|
|
|
); |
132
|
|
|
|
|
|
|
|
133
|
0
|
0
|
|
|
|
0
|
$poe_kernel->call('got_hostname', $response) if $response; |
134
|
0
|
|
|
|
|
0
|
return; |
135
|
|
|
|
|
|
|
} |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
sub resolve_ident { |
138
|
1
|
|
|
1
|
0
|
79
|
my ($kernel, $self, $conn_id, $peeraddr, $peerport, $sockaddr, $sockport) |
139
|
|
|
|
|
|
|
= @_[KERNEL, OBJECT, ARG0..$#_]; |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
POE::Component::Client::Ident::Agent->spawn( |
142
|
|
|
|
|
|
|
PeerAddr => $peeraddr, |
143
|
|
|
|
|
|
|
PeerPort => $peerport, |
144
|
|
|
|
|
|
|
SockAddr => $sockaddr, |
145
|
|
|
|
|
|
|
SockPort => $sockport, |
146
|
|
|
|
|
|
|
BuggyIdentd => 1, |
147
|
|
|
|
|
|
|
TimeOut => ( $self->{ircd}{config}{ident_timeout} || 10 ), |
148
|
|
|
|
|
|
|
Reference => $conn_id, |
149
|
1
|
|
50
|
|
|
41
|
IdentPort => ( $self->{identport} || '' ), |
|
|
|
50
|
|
|
|
|
150
|
|
|
|
|
|
|
); |
151
|
1
|
|
|
|
|
1403
|
return; |
152
|
|
|
|
|
|
|
} |
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
sub got_hostname { |
155
|
0
|
|
|
0
|
0
|
0
|
my ($kernel, $self, $response) = @_[KERNEL, OBJECT, ARG0]; |
156
|
0
|
|
|
|
|
0
|
my $conn_id = $response->{context}{conn_id}; |
157
|
0
|
|
|
|
|
0
|
my $peer_ip = $response->{context}{peeraddr}; |
158
|
0
|
|
|
|
|
0
|
my $ircd = $self->{ircd}; |
159
|
|
|
|
|
|
|
|
160
|
0
|
0
|
|
|
|
0
|
if (!$ircd->connection_exists($conn_id)) { |
161
|
0
|
|
|
|
|
0
|
delete $self->{conns}{$conn_id}; |
162
|
0
|
|
|
|
|
0
|
return; |
163
|
|
|
|
|
|
|
} |
164
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
my $fail = sub { |
166
|
|
|
|
|
|
|
$ircd->send_output( |
167
|
|
|
|
|
|
|
{ |
168
|
0
|
|
|
0
|
|
0
|
prefix => $self->{ircd}->server_name(), |
169
|
|
|
|
|
|
|
command => 'NOTICE', |
170
|
|
|
|
|
|
|
params => [ |
171
|
|
|
|
|
|
|
'*', |
172
|
|
|
|
|
|
|
"*** Couldn\'t look up your hostname", |
173
|
|
|
|
|
|
|
], |
174
|
|
|
|
|
|
|
}, |
175
|
|
|
|
|
|
|
$conn_id, |
176
|
|
|
|
|
|
|
); |
177
|
|
|
|
|
|
|
|
178
|
0
|
|
|
|
|
0
|
$self->_auth_done($conn_id); |
179
|
0
|
|
|
|
|
0
|
}; |
180
|
|
|
|
|
|
|
|
181
|
0
|
0
|
|
|
|
0
|
return $fail->() if !defined $response->{response}; |
182
|
0
|
|
|
|
|
0
|
my @answers = $response->{response}->answer(); |
183
|
0
|
0
|
|
|
|
0
|
return $fail->() if !@answers; |
184
|
|
|
|
|
|
|
|
185
|
0
|
|
|
|
|
0
|
for my $answer (@answers) { |
186
|
0
|
|
|
|
|
0
|
my $context = $response->{context}; |
187
|
0
|
|
|
|
|
0
|
$context->{hostname} = $answer->rdatastr(); |
188
|
|
|
|
|
|
|
|
189
|
0
|
0
|
|
|
|
0
|
chop $context->{hostname} if $context->{hostname} =~ /\.$/; |
190
|
|
|
|
|
|
|
my $query = $self->{resolver}->resolve( |
191
|
0
|
0
|
|
|
|
0
|
event => 'got_ip', |
192
|
|
|
|
|
|
|
host => $answer->rdatastr(), |
193
|
|
|
|
|
|
|
context => $context, |
194
|
|
|
|
|
|
|
type => ( ip_is_ipv6( $peer_ip ) ? 'AAAA' : 'A' ), |
195
|
|
|
|
|
|
|
); |
196
|
0
|
0
|
|
|
|
0
|
if (defined $query) { |
197
|
0
|
|
|
|
|
0
|
$kernel->call($self->{session_id}, 'got_ip', $query); |
198
|
|
|
|
|
|
|
} |
199
|
|
|
|
|
|
|
} |
200
|
|
|
|
|
|
|
|
201
|
0
|
|
|
|
|
0
|
return; |
202
|
|
|
|
|
|
|
} |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
sub got_ip { |
205
|
0
|
|
|
0
|
0
|
0
|
my ($kernel, $self, $response) = @_[KERNEL, OBJECT, ARG0]; |
206
|
0
|
|
|
|
|
0
|
my $conn_id = $response->{context}{conn_id}; |
207
|
0
|
|
|
|
|
0
|
my $ircd = $self->{ircd}; |
208
|
0
|
|
|
|
|
0
|
my $server = $ircd->server_name(); |
209
|
|
|
|
|
|
|
|
210
|
0
|
0
|
|
|
|
0
|
if (!$ircd->connection_exists($conn_id)) { |
211
|
0
|
|
|
|
|
0
|
delete $self->{conns}{$conn_id}; |
212
|
0
|
|
|
|
|
0
|
return; |
213
|
|
|
|
|
|
|
} |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
my $fail = sub { |
216
|
0
|
|
|
0
|
|
0
|
$ircd->send_output( |
217
|
|
|
|
|
|
|
{ |
218
|
|
|
|
|
|
|
prefix => $server, |
219
|
|
|
|
|
|
|
command => 'NOTICE', |
220
|
|
|
|
|
|
|
params => [ |
221
|
|
|
|
|
|
|
'*', |
222
|
|
|
|
|
|
|
"*** Couldn't look up your hostname", |
223
|
|
|
|
|
|
|
], |
224
|
|
|
|
|
|
|
}, |
225
|
|
|
|
|
|
|
$conn_id, |
226
|
|
|
|
|
|
|
); |
227
|
0
|
|
|
|
|
0
|
$self->_auth_done($conn_id); |
228
|
0
|
|
|
|
|
0
|
}; |
229
|
|
|
|
|
|
|
|
230
|
0
|
0
|
|
|
|
0
|
return $fail->() if !defined $response->{response}; |
231
|
0
|
|
|
|
|
0
|
my @answers = $response->{response}->answer(); |
232
|
0
|
0
|
|
|
|
0
|
return $fail->() if !@answers; |
233
|
|
|
|
|
|
|
|
234
|
0
|
|
|
|
|
0
|
my $peeraddr = $response->{context}{peeraddr}; |
235
|
0
|
|
|
|
|
0
|
my $hostname = $response->{context}{hostname}; |
236
|
0
|
|
|
|
|
0
|
for my $answer (@answers) { |
237
|
0
|
0
|
|
|
|
0
|
if ($answer->rdatastr() eq $peeraddr) { |
238
|
0
|
|
|
|
|
0
|
$ircd->send_output( |
239
|
|
|
|
|
|
|
{ |
240
|
|
|
|
|
|
|
prefix => $server, |
241
|
|
|
|
|
|
|
command => 'NOTICE', |
242
|
|
|
|
|
|
|
params => ['*', '*** Found your hostname'], |
243
|
|
|
|
|
|
|
}, |
244
|
|
|
|
|
|
|
$conn_id, |
245
|
|
|
|
|
|
|
); |
246
|
0
|
|
|
|
|
0
|
$self->{conns}{$conn_id}{hostname} = $hostname; |
247
|
0
|
|
|
|
|
0
|
$self->_auth_done($conn_id); |
248
|
0
|
|
|
|
|
0
|
return; |
249
|
|
|
|
|
|
|
} |
250
|
|
|
|
|
|
|
} |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
$ircd->send_output( |
253
|
|
|
|
|
|
|
{ |
254
|
0
|
|
|
|
|
0
|
prefix => $server, |
255
|
|
|
|
|
|
|
command => 'NOTICE', |
256
|
|
|
|
|
|
|
params => [ |
257
|
|
|
|
|
|
|
'*', |
258
|
|
|
|
|
|
|
'*** Your forward and reverse DNS do not match', |
259
|
|
|
|
|
|
|
], |
260
|
|
|
|
|
|
|
}, |
261
|
|
|
|
|
|
|
$conn_id, |
262
|
|
|
|
|
|
|
); |
263
|
0
|
|
|
|
|
0
|
$self->_auth_done($conn_id); |
264
|
0
|
|
|
|
|
0
|
return; |
265
|
|
|
|
|
|
|
} |
266
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
sub _auth_done { |
268
|
2
|
|
|
2
|
|
12
|
my ($self, $conn_id) = @_; |
269
|
|
|
|
|
|
|
|
270
|
2
|
|
|
|
|
91
|
$self->{conns}{$conn_id}{done}++; |
271
|
2
|
100
|
|
|
|
11
|
return if $self->{conns}{$conn_id}{done} != 2; |
272
|
|
|
|
|
|
|
|
273
|
1
|
|
|
|
|
2
|
my $auth = delete $self->{conns}{$conn_id}; |
274
|
|
|
|
|
|
|
$self->{ircd}->send_event( |
275
|
|
|
|
|
|
|
'auth_done', |
276
|
|
|
|
|
|
|
$conn_id, |
277
|
|
|
|
|
|
|
{ |
278
|
|
|
|
|
|
|
ident => $auth->{ident}, |
279
|
|
|
|
|
|
|
hostname => $auth->{hostname}, |
280
|
|
|
|
|
|
|
}, |
281
|
1
|
|
|
|
|
6
|
); |
282
|
1
|
|
|
|
|
115
|
return; |
283
|
|
|
|
|
|
|
} |
284
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
sub got_ident_error { |
286
|
1
|
|
|
1
|
0
|
588
|
my ($kernel, $self, $ref, $error) = @_[KERNEL, OBJECT, ARG0, ARG1]; |
287
|
1
|
|
|
|
|
3
|
my $conn_id = $ref->{Reference}; |
288
|
1
|
|
|
|
|
2
|
my $ircd = $self->{ircd}; |
289
|
|
|
|
|
|
|
|
290
|
1
|
50
|
|
|
|
5
|
if (!$ircd->connection_exists($conn_id)) { |
291
|
0
|
|
|
|
|
0
|
delete $self->{conns}{$conn_id}; |
292
|
0
|
|
|
|
|
0
|
return; |
293
|
|
|
|
|
|
|
} |
294
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
$ircd->send_output( |
296
|
|
|
|
|
|
|
{ |
297
|
1
|
|
|
|
|
7
|
prefix => $ircd->server_name(), |
298
|
|
|
|
|
|
|
command => 'NOTICE', |
299
|
|
|
|
|
|
|
params => ['*', "*** No Ident response"], |
300
|
|
|
|
|
|
|
}, |
301
|
|
|
|
|
|
|
$conn_id, |
302
|
|
|
|
|
|
|
); |
303
|
1
|
|
|
|
|
5
|
$self->_auth_done($conn_id); |
304
|
1
|
|
|
|
|
3
|
return; |
305
|
|
|
|
|
|
|
} |
306
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
sub got_ident { |
308
|
0
|
|
|
0
|
0
|
|
my ($kernel, $self, $ref, $opsys, $other) |
309
|
|
|
|
|
|
|
= @_[KERNEL, OBJECT, ARG0, ARG1, ARG2]; |
310
|
0
|
|
|
|
|
|
my $conn_id = $ref->{Reference}; |
311
|
0
|
|
|
|
|
|
my $ircd = $self->{ircd}; |
312
|
|
|
|
|
|
|
|
313
|
0
|
0
|
|
|
|
|
if (!$ircd->connection_exists($conn_id)) { |
314
|
0
|
|
|
|
|
|
delete $self->{conns}{$conn_id}; |
315
|
0
|
|
|
|
|
|
return; |
316
|
|
|
|
|
|
|
} |
317
|
|
|
|
|
|
|
|
318
|
0
|
|
|
|
|
|
my $ident = ''; |
319
|
0
|
0
|
|
|
|
|
$ident = $other if uc $opsys ne 'OTHER'; |
320
|
0
|
|
|
|
|
|
$ircd->send_output( |
321
|
|
|
|
|
|
|
{ |
322
|
|
|
|
|
|
|
prefix => $ircd->server_name(), |
323
|
|
|
|
|
|
|
command => 'NOTICE', |
324
|
|
|
|
|
|
|
params => ['*', "*** Got Ident response"], |
325
|
|
|
|
|
|
|
}, |
326
|
|
|
|
|
|
|
$conn_id, |
327
|
|
|
|
|
|
|
); |
328
|
0
|
|
|
|
|
|
$self->{conns}{$conn_id}{ident} = $ident; |
329
|
0
|
|
|
|
|
|
$self->_auth_done($conn_id); |
330
|
0
|
|
|
|
|
|
return; |
331
|
|
|
|
|
|
|
} |
332
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
1; |
334
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
=encoding utf8 |
336
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
=head1 NAME |
338
|
|
|
|
|
|
|
|
339
|
|
|
|
|
|
|
POE::Component::Server::IRC::Plugin::Auth - Authentication subsystem of POE::Component::Server::IRC::Backend |
340
|
|
|
|
|
|
|
|
341
|
|
|
|
|
|
|
=head1 DESCRIPTION |
342
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
This module is used internally by |
344
|
|
|
|
|
|
|
L. |
345
|
|
|
|
|
|
|
No need for you to use it. |
346
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
=head1 AUTHOR |
348
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
Hinrik Ern SigurEsson |
350
|
|
|
|
|
|
|
|
351
|
|
|
|
|
|
|
Chris 'BinGOs' Williams |
352
|
|
|
|
|
|
|
|
353
|
|
|
|
|
|
|
=cut |