File Coverage

blib/lib/Net/IRC3/Client/Connection.pm
Criterion Covered Total %
statement 12 221 5.4
branch 0 36 0.0
condition 0 29 0.0
subroutine 4 39 10.2
pod 12 31 38.7
total 28 356 7.8


line stmt bran cond sub pod time code
1             package Net::IRC3::Client::Connection;
2 1     1   1254 use base "Net::IRC3::Connection";
  1         2  
  1         106  
3 1     1   6 use Net::IRC3::Util qw/prefix_nick decode_ctcp/;
  1         2  
  1         55  
4 1     1   6 use strict;
  1         2  
  1         38  
5 1     1   6 no warnings;
  1         1  
  1         8734  
6              
7             =head1 NAME
8              
9             Net::IRC3::Client::Connection - A highlevel IRC connection
10              
11             =head1 SYNOPSIS
12              
13             use AnyEvent;
14             use Net::IRC3::Client::Connection;
15              
16             my $c = AnyEvent->condvar;
17              
18             my $timer;
19             my $con = new Net::IRC3::Client::Connection;
20              
21             $con->reg_cb (registered => sub { print "I'm in!\n"; 0 });
22             $con->reg_cb (disconnect => sub { print "I'm out!\n"; 0 });
23             $con->reg_cb (
24             sent => sub {
25             if ($_[2] eq 'PRIVMSG') {
26             print "Sent message!\n";
27             $timer = AnyEvent->timer (after => 1, cb => sub { $c->broadcast });
28             }
29             1
30             }
31             );
32              
33             $con->send_srv (PRIVMSG => "Hello there i'm the cool Net::IRC3 test script!", 'elmex');
34              
35             $con->connect ("localhost", 6667);
36             $con->register (qw/testbot testbot testbot/);
37              
38             $c->wait;
39             undef $timer;
40              
41             $con->disconnect;
42              
43             =head1 DESCRIPTION
44              
45             B This module is B, please use L for new programs,
46             and possibly port existing L applications to L. Though the
47             API of L has incompatible changes, it's still fairly similar.
48              
49              
50             L is a (nearly) highlevel client connection,
51             that manages all the stuff that noone wants to implement again and again
52             when handling with IRC. For example it PONGs the server or keeps track
53             of the users on a channel.
54              
55             Please note that CTCP handling is still up to you. It will be decoded
56             for you and events will be generated. But generating replies
57             is up to you.
58              
59             =head2 A NOTE TO CASE MANAGEMENT
60              
61             The case insensitivity of channelnames and nicknames can lead to headaches
62             when dealing with IRC in an automated client which tracks channels and nicknames.
63              
64             I tried to preserve the case in all channel and nicknames
65             Net::IRC3::Client::Connection passes to his user. But in the internal
66             structures i'm using lower case for the channel names.
67              
68             The returned hash from C for example has the lower case of the
69             joined channels as keys.
70              
71             But i tried to preserve the case in all events that are emitted.
72             Please keep this in mind when handling the events.
73              
74             For example a user might joins #TeSt and parts #test later.
75              
76             =head1 EVENTS
77              
78             The following events are emitted by L.
79             Use C as described in L to register to such an
80             event.
81              
82             =over 4
83              
84             =item B
85              
86             Emitted when the connection got successfully registered.
87              
88             =item B
89              
90             Emitted when C<@nicks> are added to the channel C<$channel>,
91             this happens for example when someone JOINs a channel or when you
92             get a RPL_NAMREPLY (see RFC2812).
93              
94             C<$msg> ist he IRC message hash that as returned by C.
95              
96             =item B
97              
98             Emitted when C<@nicks> are removed from the channel C<$channel>,
99             happens for example when they PART, QUIT or get KICKed.
100              
101             C<$msg> ist he IRC message hash that as returned by C
102             or undef if the reason for the removal was a disconnect on our end.
103              
104             =item B
105              
106             Emitted when a nickname on a channel changes. This is emitted when a NICK
107             change occurs from C<$old_nick> to C<$new_nick> give the application a chance
108             to quickly analyze what channels were affected. C<$is_myself> is true when
109             youself was the one who changed the nick.
110              
111             =item B
112              
113             This is emitted when the topic for a channel is discovered. C<$channel>
114             is the channel for which C<$topic> is the current topic now.
115             Which is set by C<$who>. C<$who> might be undefined when it's not known
116             who set the channel topic.
117              
118             =item B
119              
120             Emitted when C<$nick> enters the channel C<$channel> by JOINing.
121             C<$is_myself> is true if youself are the one who JOINs.
122              
123             =item B
124              
125             Emitted when C<$nick> PARTs the channel C<$channel>.
126             C<$is_myself> is true if youself are the one who PARTs.
127             C<$msg> is the PART message.
128              
129             =item B
130              
131             Emitted when C<$kicked_nick> is KICKed from the channel C<$channel>.
132             C<$is_myself> is true if youself are the one who got KICKed.
133             C<$msg> is the PART message.
134              
135             =item B
136              
137             Emitted when C<$old_nick> is renamed to C<$new_nick>.
138             C<$is_myself> is true when youself was the one who changed the nick.
139              
140             =item B
141              
142             Emitted when a CTCP message was found in either a NOTICE or PRIVMSG
143             message. C<$tag> is the CTCP message tag. (eg. "PING", "VERSION", ...).
144             C<$msg> is the CTCP message and C<$type> is either "NOTICE" or "PRIVMSG".
145              
146             C<$src> is the source nick the message came from.
147             C<$target> is the target nickname (yours) or the channel the ctcp was sent
148             on.
149              
150             =item B<"ctcp_$tag", $src, $target, $msg, $type>
151              
152             Emitted when a CTCP message was found in either a NOTICE or PRIVMSG
153             message. C<$tag> is the CTCP message tag (in lower case). (eg. "ping", "version", ...).
154             C<$msg> is the CTCP message and C<$type> is either "NOTICE" or "PRIVMSG".
155              
156             C<$src> is the source nick the message came from.
157             C<$target> is the target nickname (yours) or the channel the ctcp was sent
158             on.
159              
160             =item B
161              
162             Emitted when the nickname C<$nick> QUITs with the message C<$msg>.
163              
164             =item B
165              
166             Emitted for NOTICE and PRIVMSG where the target C<$channel> is a channel.
167             C<$ircmsg> is the original IRC message hash like it is returned by C.
168              
169             The trailing part of the C<$ircmsg> will have all CTCP messages stripped off.
170              
171             =item B
172              
173             Emitted for NOTICE and PRIVMSG where the target C<$nick> (most of the time you) is a nick.
174             C<$ircmsg> is the original IRC message hash like it is returned by C.
175              
176             The trailing part of the C<$ircmsg> will have all CTCP messages stripped off.
177              
178             =item B
179              
180             Emitted when any error occurs. C<$code> is the 3 digit error id string from RFC
181             2812 and C<$message> is a description of the error. C<$ircmsg> is the complete
182             error irc message.
183              
184             You may use Net::IRC3::Util::rfc_code_to_name to convert C<$code> to the error
185             name from the RFC 2812. eg.:
186              
187             rfc_code_to_name ('471') => 'ERR_CHANNELISFULL'
188              
189             =item B
190              
191             Is emitted everytime some command is sent.
192              
193             =item B
194              
195             Is emitted everytime some command was received.
196              
197             =back
198              
199             =head1 METHODS
200              
201             =over 4
202              
203             =item B
204              
205             This constructor takes no arguments.
206              
207             =cut
208              
209             sub new {
210 0     0 1   my $this = shift;
211 0   0       my $class = ref($this) || $this;
212 0           my $self = $class->SUPER::new (@_);
213              
214 0           $self->reg_cb ('irc_*' => \&debug_cb);
215 0           $self->reg_cb (irc_001 => \&welcome_cb);
216 0           $self->reg_cb (irc_join => \&join_cb);
217 0           $self->reg_cb (irc_nick => \&nick_cb);
218 0           $self->reg_cb (irc_part => \&part_cb);
219 0           $self->reg_cb (irc_kick => \&kick_cb);
220 0           $self->reg_cb (irc_quit => \&quit_cb);
221 0           $self->reg_cb (irc_353 => \&namereply_cb);
222 0           $self->reg_cb (irc_366 => \&endofnames_cb);
223 0           $self->reg_cb (irc_ping => \&ping_cb);
224 0           $self->reg_cb (irc_pong => \&pong_cb);
225              
226 0           $self->reg_cb (irc_privmsg => \&privmsg_cb);
227 0           $self->reg_cb (irc_notice => \&privmsg_cb);
228              
229 0           $self->reg_cb ('irc_*' => \&anymsg_cb);
230              
231 0           $self->reg_cb (channel_remove => \&channel_remove_event_cb);
232 0           $self->reg_cb (channel_add => \&channel_add_event_cb);
233 0           $self->reg_cb (disconnect => \&disconnect_cb);
234              
235 0           $self->reg_cb (irc_437 => \&change_nick_login_cb);
236 0           $self->reg_cb (irc_433 => \&change_nick_login_cb);
237              
238 0           $self->reg_cb (irc_332 => \&rpl_topic_cb);
239 0           $self->reg_cb (irc_topic => \&topic_change_cb);
240              
241             $self->{def_nick_change} = $self->{nick_change} =
242             sub {
243 0     0     my ($old_nick) = @_;
244 0           "${old_nick}_"
245 0           };
246              
247 0           return $self;
248             }
249              
250             =item B
251              
252             Sends the IRC registration commands NICK and USER.
253             If C<$server_pass> is passed also a PASS command is generated.
254              
255             =cut
256              
257             sub register {
258 0     0 1   my ($self, $nick, $user, $real, $pass) = @_;
259              
260 0           $self->{nick} = $nick;
261 0           $self->{user} = $user;
262 0           $self->{real} = $real;
263 0           $self->{server_pass} = $pass;
264              
265 0 0         $self->send_msg (undef, "PASS", undef, $pass) if defined $pass;
266 0           $self->send_msg (undef, "NICK", undef, $nick);
267 0   0       $self->send_msg (undef, "USER", $real || $nick, $user || $nick, "*", "0");
      0        
268             }
269              
270             =item B
271              
272             This method lets you modify the nickname renaming mechanism when registering
273             the connection. C<$callback> is called with the current nickname as first
274             argument when a ERR_NICKNAMEINUSE or ERR_UNAVAILRESOURCE error occurs on login.
275             The returnvalue of C<$callback> will then be used to change the nickname.
276              
277             If C<$callback> is not defined the default nick change callback will be used
278             again.
279              
280             The default callback appends '_' to the end of the nickname supplied in the
281             C routine.
282              
283             If the callback returns the same nickname that was given it the connection
284             will be terminated.
285              
286             =cut
287              
288             sub set_nick_change_cb {
289 0     0 1   my ($self, $cb) = @_;
290 0 0         $cb = $self->{def_nick_change} unless defined $cb;
291 0           $self->{nick_change} = $cb;
292             }
293              
294             =item B
295              
296             Returns the current nickname, under which this connection
297             is registered at the IRC server. It might be different from the
298             one that was passed to C as a nick-collision might happened
299             on login.
300              
301             =cut
302              
303 0     0 1   sub nick { $_[0]->{nick} }
304              
305             =item B
306              
307             Returns a true value when the connection has been registered successfull and
308             you can send commands.
309              
310             =cut
311              
312 0     0 1   sub registered { $_[0]->{registered} }
313              
314             =item B
315              
316             This returns a hash reference. The keys are the currently joined channels in lower case.
317             The values are hash references which contain the joined nicks as key.
318              
319             NOTE: Future versions might preserve the case from the JOIN command to the channels.
320              
321             =cut
322              
323             sub channel_list {
324 0     0 1   my ($self) = @_;
325 0   0       return $self->{channel_list} || {};
326             }
327              
328             =item B
329              
330             See also L.
331              
332             =cut
333              
334             sub send_msg {
335 0     0 1   my ($self, @a) = @_;
336 0           $self->event (debug_send => @a);
337 0           $self->SUPER::send_msg (@a);
338             }
339              
340             =item B
341              
342             This function sends an IRC message that is constructed by C (see L).
343             If the connection isn't yet registered (for example if the connection is slow) and hasn't got a
344             welcome (IRC command 001) from the server yet, the IRC message is queued until it gets a welcome.
345              
346             =cut
347              
348             sub send_srv {
349 0     0 1   my ($self, @msg) = @_;
350              
351 0 0         if ($self->registered) {
352 0           $self->send_msg (undef, @msg);
353              
354             } else {
355 0           push @{$self->{con_queue}}, \@msg;
  0            
356             }
357             }
358              
359             =item B
360              
361             Clears the server send queue.
362              
363             =cut
364              
365             sub clear_srv_queue {
366 0     0 1   my ($self) = @_;
367 0           $self->{con_queue} = [];
368             }
369              
370              
371             =item B
372              
373             This function sends a message (constructed by C
374             $trailing, @params)> to the server, like C only that it will queue
375             the messages if it hasn't joined the channel C<$channel> yet. The queued
376             messages will be send once the connection successfully JOINed the C<$channel>.
377              
378             C<$channel> will be lowercased so that any case that comes from the server matches.
379             (Yes, IRC handles upper and lower case as equal :-(
380              
381             Be careful with this, there are chances you might not join the channel you
382             wanted to join. You may wanted to join #bla and the server redirects that
383             and sends you that you joined #blubb. You may use C to
384             remove the queue after some timeout after joining, so that you don't end up
385             with a memory leak.
386              
387             =cut
388              
389             sub send_chan {
390 0     0 1   my ($self, $chan, @msg) = @_;
391              
392 0 0         if ($self->{channel_list}->{lc $chan}) {
393 0           $self->send_msg (undef, @msg);
394              
395             } else {
396 0           push @{$self->{chan_queue}->{lc $chan}}, \@msg;
  0            
397             }
398             }
399              
400             =item B
401              
402             Clears the channel queue of the channel C<$channel>.
403              
404             =cut
405              
406             sub clear_chan_queue {
407 0     0 1   my ($self, $chan) = @_;
408 0           $self->{chan_queue}->{lc $chan} = [];
409             }
410              
411             =item B
412              
413             This method enables a periodical ping to the server with an interval of
414             C<$interval> seconds. If no PONG was received from the server until the next
415             interval the connection will be terminated or the callback in C<$cb> will be called.
416              
417             (C<$cb> will have the connection object as it's first argument.)
418              
419             Make sure you call this method after the connection has been established.
420             (eg. in the callback for the C event).
421              
422             =cut
423              
424             sub enable_ping {
425 0     0 1   my ($self, $int, $cb) = @_;
426              
427 0           $self->{last_pong_recv} = 0;
428 0           $self->{last_ping_sent} = time;
429              
430 0           $self->send_srv (PING => "Net::IRC3");
431              
432             $self->{_ping_timer} =
433             AnyEvent->timer (after => $int, cb => sub {
434 0 0   0     if ($self->{last_pong_recv} < $self->{last_ping_sent}) {
435 0           delete $self->{_ping_timer};
436 0 0         if ($cb) {
437 0           $cb->($self);
438             } else {
439 0           $self->disconnect ("Server timeout");
440             }
441              
442             } else {
443 0           $self->enable_ping ($int, $cb);
444             }
445 0           });
446             }
447              
448             ################################################################################
449             # Private utility functions
450             ################################################################################
451              
452             sub _was_me {
453 0     0     my ($self, $msg) = @_;
454 0           lc prefix_nick ($msg) eq lc $self->nick ()
455             }
456              
457             ################################################################################
458             # Callbacks
459             ################################################################################
460              
461             sub channel_remove_event_cb {
462 0     0 0   my ($self, $msg, $chan, @nicks) = @_;
463              
464 0           for my $nick (@nicks) {
465 0 0         if (lc ($nick) eq lc ($self->nick ())) {
466 0           delete $self->{chan_queue}->{lc $chan};
467 0           delete $self->{channel_list}->{lc $chan};
468 0           last;
469             } else {
470 0           delete $self->{channel_list}->{lc $chan}->{$nick};
471             }
472             }
473              
474 0           1;
475             }
476              
477             sub channel_add_event_cb {
478 0     0 0   my ($self, $msg, $chan, @nicks) = @_;
479              
480 0           for my $nick (@nicks) {
481 0 0         if (lc ($nick) eq lc ($self->nick ())) {
482 0           for (@{$self->{chan_queue}->{lc $chan}}) {
  0            
483 0           $self->send_msg (undef, @$_);
484             }
485 0           $self->clear_chan_queue ($chan);
486             }
487              
488 0           $self->{channel_list}->{lc $chan}->{$nick} = 1;
489             }
490              
491 0           1;
492             }
493              
494             sub _filter_new_nicks_from_channel {
495 0     0     my ($self, $chan, @nicks) = @_;
496 0           grep { not exists $self->{channel_list}->{lc $chan}->{$_} } @nicks;
  0            
497             }
498              
499             sub anymsg_cb {
500 0     0 0   my ($self, $msg) = @_;
501              
502 0           my $cmd = lc $msg->{command};
503              
504 0 0 0       if ( $cmd ne "privmsg"
    0 0        
      0        
      0        
      0        
      0        
505             and $cmd ne "notice"
506             and $cmd ne "part"
507             and $cmd ne "join"
508             and not ($cmd >= 400 and $cmd <= 599)
509             )
510             {
511 0           $self->event (statmsg => $msg);
512             } elsif ($cmd >= 400 and $cmd <= 599) {
513 0           $self->event (error => $msg->{command}, $msg->{trailing}, $msg);
514             }
515              
516 0           1;
517             }
518              
519             sub privmsg_cb {
520 0     0 0   my ($self, $msg) = @_;
521              
522 0           my ($trail, $ctcp) = decode_ctcp ($msg->{trailing});
523              
524 0           for (@$ctcp) {
525 0           $self->event (ctcp => prefix_nick ($msg), $msg->{params}->[0], $_->[0], $_->[1], $msg->{command});
526 0           $self->event ("ctcp_".lc ($_->[0]), prefix_nick ($msg), $msg->{params}->[0], $_->[1], $msg->{command});
527             }
528              
529 0           $msg->{trailing} = $trail;
530              
531 0 0         if ($msg->{trailing} ne '') {
532 0           my $targ = $msg->{params}->[0];
533 0 0         if ($targ =~ m/^(?:[#+&]|![A-Z0-9]{5})/) {
534 0           $self->event (publicmsg => $targ, $msg);
535              
536             } else {
537 0           $self->event (privatemsg => $targ, $msg);
538             }
539             }
540              
541 0           1;
542             }
543              
544             sub welcome_cb {
545 0     0 0   my ($self, $msg) = @_;
546              
547 0           $self->{registered} = 1;
548              
549 0           for (@{$self->{con_queue}}) {
  0            
550 0           $self->send_msg (undef, @$_);
551             }
552 0           $self->clear_srv_queue ();
553              
554 0           $self->event ('registered');
555              
556 0           1;
557             }
558              
559             sub ping_cb {
560 0     0 0   my ($self, $msg) = @_;
561 0           $self->send_msg (undef, "PONG", $msg->{params}->[0]);
562              
563 0           1;
564             }
565              
566             sub pong_cb {
567 0     0 0   my ($self, $msg) = @_;
568 0           $self->{last_pong_recv} = time;
569 0           1;
570             }
571              
572             sub nick_cb {
573 0     0 0   my ($self, $msg) = @_;
574 0           my $nick = prefix_nick ($msg);
575 0           my $newnick = $msg->{params}->[0];
576 0           my $wasme = $self->_was_me ($msg);
577              
578 0 0         if ($wasme) { $self->{nick} = $newnick }
  0            
579              
580 0           my @chans;
581              
582 0           for my $channame (keys %{$self->{channel_list}}) {
  0            
583 0           my $chan = $self->{channel_list}->{$channame};
584 0 0         if (exists $chan->{$nick}) {
585 0           delete $chan->{$nick};
586 0           $chan->{$newnick} = 1;
587              
588 0           push @chans, $channame;
589             }
590             }
591              
592 0           for (@chans) {
593 0           $self->event (channel_change => $_, $nick, $newnick, $wasme);
594             }
595 0           $self->event (nick_change => $nick, $newnick, $wasme);
596              
597 0           1;
598             }
599              
600             sub namereply_cb {
601 0     0 0   my ($self, $msg) = @_;
602 0           my @nicks = split / /, $msg->{trailing};
603 0           push @{$self->{_tmp_namereply}}, @nicks;
  0            
604              
605 0           1;
606             }
607              
608             sub endofnames_cb {
609 0     0 0   my ($self, $msg) = @_;
610 0           my $chan = $msg->{params}->[1];
611 0           my @nicks =
612             $self->_filter_new_nicks_from_channel (
613 0           $chan, map { s/^[~@\+%&]//; $_ } @{delete $self->{_tmp_namereply}}
  0            
  0            
614             );
615              
616 0 0         $self->event (channel_add => $msg, $chan, @nicks) if @nicks;
617              
618 0           1;
619             }
620              
621             sub join_cb {
622 0     0 0   my ($self, $msg) = @_;
623 0           my $chan = $msg->{params}->[0];
624 0           my $nick = prefix_nick ($msg);
625              
626 0           $self->event (channel_add => $msg, $chan, $nick);
627 0           $self->event (join => $nick, $chan, $self->_was_me ($msg));
628              
629 0           1;
630             }
631              
632             sub part_cb {
633 0     0 0   my ($self, $msg) = @_;
634 0           my $chan = $msg->{params}->[0];
635 0           my $nick = prefix_nick ($msg);
636              
637 0           $self->event (part => $nick, $chan, $self->_was_me ($msg), $msg->{params}->[1]);
638 0           $self->event (channel_remove => $msg, $chan, $nick);
639              
640 0           1;
641             }
642              
643             sub kick_cb {
644 0     0 0   my ($self, $msg) = @_;
645 0           my $chan = $msg->{params}->[0];
646 0           my $kicked_nick = $msg->{params}->[1];
647              
648 0           $self->event (kick => $kicked_nick, $chan, $self->_was_me ($msg), $msg->{params}->[1]);
649 0           $self->event (channel_remove => $msg, $chan, $kicked_nick);
650              
651 0           1;
652             }
653              
654             sub quit_cb {
655 0     0 0   my ($self, $msg) = @_;
656 0           my $nick = prefix_nick ($msg);
657              
658 0           $self->event (quit => $nick, $msg->{params}->[1]);
659              
660 0           for (keys %{$self->{channel_list}}) {
  0            
661 0 0         $self->event (channel_remove => $msg, $_, $nick)
662             if $self->{channel_list}->{$_}->{$nick};
663             }
664              
665 0           1;
666             }
667              
668             sub debug_cb {
669 0     0 0   my ($self, $msg) = @_;
670 0           $self->event (debug_recv => $msg);
671             #print "$self->{h}:$self->{p} > ";
672             #print (join " ", map { $_ => $msg->{$_} } grep { $_ ne 'params' } sort keys %$msg);
673             #print " params:";
674             #print (join ",", @{$msg->{params}});
675             #print "\n";
676              
677 0           1;
678             }
679              
680             sub change_nick_login_cb {
681 0     0 0   my ($self, $msg) = @_;
682              
683 0 0         unless ($self->registered) {
684 0           my $newnick = $self->{nick_change}->($self->nick);
685              
686 0 0         if (lc $newnick eq lc $self->{nick}) {
687 0           $self->disconnect;
688 0           return 0;
689             }
690              
691 0           $self->{nick} = $newnick;
692 0           $self->send_msg (undef, "NICK", undef, $newnick);
693             }
694              
695 0           not ($self->registered) # kill the cb when registered
696             }
697              
698             sub disconnect_cb {
699 0     0 0   my ($self) = @_;
700              
701 0           for (keys %{$self->{channel_list}}) {
  0            
702 0           $self->event (channel_remove => undef, $_, $self->nick)
703             }
704              
705             1
706 0           }
707              
708             sub rpl_topic_cb {
709 0     0 0   my ($self, $msg) = @_;
710 0           my $chan = $msg->{params}->[1];
711 0           my $topic = $msg->{trailing};
712              
713 0           $self->event (channel_topic => $chan, $topic);
714              
715 0           1
716             }
717              
718             sub topic_change_cb {
719 0     0 0   my ($self, $msg) = @_;
720 0           my $who = prefix_nick ($msg);
721 0           my $chan = $msg->{params}->[0];
722 0           my $topic = $msg->{trailing};
723              
724 0           $self->event (channel_topic => $chan, $topic, $who);
725              
726 0           1
727             }
728              
729             =back
730              
731             =head1 EXAMPLES
732              
733             See samples/netirc3cl and other samples in samples/ for some examples on how to use Net::IRC3::Client::Connection.
734              
735             =head1 AUTHOR
736              
737             Robin Redeker, C<< >>
738              
739             =head1 SEE ALSO
740              
741             L
742              
743             RFC 2812 - Internet Relay Chat: Client Protocol
744              
745             =head1 COPYRIGHT & LICENSE
746              
747             Copyright 2006 Robin Redeker, all rights reserved.
748              
749             This program is free software; you can redistribute it and/or modify it
750             under the same terms as Perl itself.
751              
752             =cut
753              
754             1;