File Coverage

blib/lib/POE/Filter/IRCD.pm
Criterion Covered Total %
statement 75 117 64.1
branch 21 64 32.8
condition 6 17 35.2
subroutine 8 13 61.5
pod 8 8 100.0
total 118 219 53.8


line stmt bran cond sub pod time code
1             package POE::Filter::IRCD;
2             {
3             $POE::Filter::IRCD::VERSION = '2.44';
4             }
5              
6             #ABSTRACT: A POE-based parser for the IRC protocol
7              
8 3     3   504083 use strict;
  3         8  
  3         127  
9 3     3   20 use warnings;
  3         7  
  3         107  
10 3     3   17 use Carp;
  3         6  
  3         383  
11 3     3   40 use base qw[POE::Filter];
  3         6  
  3         2574  
12              
13             sub _PUT_LITERAL () { 1 }
14              
15             # Probably some other stuff should go here.
16              
17             my $g = {
18             space => qr/\x20+/o,
19             trailing_space => qr/\x20*/o,
20             };
21              
22             my $irc_regex = qr/^
23             (?:
24             \x40 # '@'-prefixed IRCv3.2 messsage tags.
25             (\S+) # [tags] Semi-colon delimited key=value list
26             $g->{space}
27             )?
28             (?:
29             \x3a # : comes before hand
30             (\S+) # [prefix]
31             $g->{'space'} # Followed by a space
32             )? # but is optional.
33             (
34             \d{3}|[a-zA-Z]+ # [command]
35             ) # required.
36             (?:
37             $g->{'space'} # Strip leading space off [middle]s
38             ( # [middle]s
39             (?:
40             [^\x00\x0a\x0d\x20\x3a]
41             [^\x00\x0a\x0d\x20]*
42             ) # Match on 1 of these,
43             (?:
44             $g->{'space'}
45             [^\x00\x0a\x0d\x20\x3a]
46             [^\x00\x0a\x0d\x20]*
47             )* # then match as many of these as possible
48             )
49             )? # otherwise dont match at all.
50             (?:
51             $g->{'space'}\x3a # Strip off leading spacecolon for [trailing]
52             ([^\x00\x0a\x0d]*) # [trailing]
53             )? # [trailing] is not necessary.
54             $g->{'trailing_space'}
55             $/x;
56              
57             sub new {
58 4     4 1 508 my $type = shift;
59 4 50       22 croak "$type requires an even number of parameters" if @_ % 2;
60 4         14 my $buffer = { @_ };
61 4         10 $buffer->{uc $_} = delete $buffer->{$_} for keys %{ $buffer };
  4         18  
62 4         12 $buffer->{BUFFER} = [];
63 4         16 return bless $buffer, $type;
64             }
65              
66             sub debug {
67 0     0 1 0 my $self = shift;
68 0         0 my $value = shift;
69              
70 0 0       0 if ( defined $value ) {
71 0         0 $self->{DEBUG} = $value;
72 0         0 return $self->{DEBUG};
73             }
74 0         0 $self->{DEBUG} = $value;
75             }
76              
77             sub get {
78 5     5 1 2925 my ($self, $raw_lines) = @_;
79 5         10 my $events = [];
80              
81 5         14 foreach my $raw_line (@$raw_lines) {
82 5 50       32 warn "->$raw_line \n" if $self->{DEBUG};
83 5 50       96 if ( my($tags, $prefix, $command, $middles, $trailing) = $raw_line =~ m/$irc_regex/ ) {
84 5         20 my $event = { raw_line => $raw_line };
85 5 100       24 if ($tags) {
86 1         6 for my $tag_pair (split /;/, $tags) {
87 3         7 my ($thistag, $thisval) = split /=/, $tag_pair;
88 3         9 $event->{tags}->{$thistag} = $thisval
89             }
90             }
91 5 50       24 $event->{'prefix'} = $prefix if $prefix;
92 5         17 $event->{'command'} = uc $command;
93 5 50 33     23 $event->{'params'} = [] if defined ( $middles ) || defined ( $trailing );
94 5 50       16 push @{$event->{'params'}}, (split /$g->{'space'}/, $middles) if defined $middles;
  5         44  
95 5 50       17 push @{$event->{'params'}}, $trailing if defined $trailing;
  5         12  
96 5         17 push @$events, $event;
97             }
98             else {
99 0         0 warn "Received line $raw_line that is not IRC protocol\n";
100             }
101             }
102 5         18 return $events;
103             }
104              
105             sub get_one_start {
106 0     0 1 0 my ($self, $raw_lines) = @_;
107 0         0 push @{ $self->{BUFFER} }, $_ for @$raw_lines;
  0         0  
108             }
109              
110             sub get_one {
111 0     0 1 0 my $self = shift;
112 0         0 my $events = [];
113              
114 0 0       0 if ( my $raw_line = shift ( @{ $self->{BUFFER} } ) ) {
  0         0  
115 0 0       0 warn "->$raw_line \n" if $self->{DEBUG};
116 0 0       0 if ( my($tags, $prefix, $command, $middles, $trailing) = $raw_line =~ m/$irc_regex/ ) {
117 0         0 my $event = { raw_line => $raw_line };
118 0 0       0 if ($tags) {
119 0         0 for my $tag_pair (split /;/, $tags) {
120 0         0 my ($thistag, $thisval) = split /=/, $tag_pair;
121 0         0 $event->{tags}->{$thistag} = $thisval
122             }
123             }
124 0 0       0 $event->{'prefix'} = $prefix if $prefix;
125 0         0 $event->{'command'} = uc $command;
126 0 0 0     0 $event->{'params'} = [] if defined ( $middles ) || defined ( $trailing );
127 0 0       0 push @{$event->{'params'}}, (split /$g->{'space'}/, $middles) if defined $middles;
  0         0  
128 0 0       0 push @{$event->{'params'}}, $trailing if defined $trailing;
  0         0  
129 0         0 push @$events, $event;
130             }
131             else {
132 0         0 warn "Received line $raw_line that is not IRC protocol\n";
133             }
134             }
135 0         0 return $events;
136             }
137              
138             sub get_pending {
139 0     0 1 0 return;
140             }
141              
142             sub put {
143 4     4 1 3451 my ($self, $events) = @_;
144 4         8 my $raw_lines = [];
145              
146 4         8 foreach my $event (@$events) {
147 4 50       13 if (ref $event eq 'HASH') {
148 4 50       15 my $colonify = ( defined $event->{colonify} ? $event->{colonify} : $self->{COLONIFY} );
149 4         5 if ( _PUT_LITERAL || _checkargs($event) ) {
150 4         6 my $raw_line = '';
151 4 100 66     16 if ( ref $event->{tags} eq 'HASH' && keys %{ $event->{tags} } ) {
  1         6  
152 1         2 $raw_line .= '@';
153 1         2 my @tags = %{ $event->{tags} };
  1         4  
154 1         6 while (my ($thistag, $thisval) = splice @tags, 0, 2) {
155 3 100       14 $raw_line .= $thistag . ( defined $thisval ? '='.$thisval : '' );
156 3 100       12 $raw_line .= ';' if @tags;
157             }
158 1         17 $raw_line .= ' ';
159             }
160 4 50       18 $raw_line .= (':' . $event->{'prefix'} . ' ') if exists $event->{'prefix'};
161 4         7 $raw_line .= $event->{'command'};
162 4 50 33     23 if ( $event->{'params'} and ref $event->{'params'} eq 'ARRAY' ) {
163 4         6 my $params = [ @{ $event->{'params'} } ];
  4         10  
164 4         6 $raw_line .= ' ';
165 4         5 my $param = shift @$params;
166 4         11 while (@$params) {
167 4         8 $raw_line .= $param . ' ';
168 4         579 $param = shift @$params;
169             }
170 4 50 66     54 $raw_line .= ':' if $param =~ m/\x20/ or $colonify;
171 4         9 $raw_line .= $param;
172             }
173 4         6 push @$raw_lines, $raw_line;
174 4 50       18 warn "<-$raw_line \n" if $self->{DEBUG};
175             }
176             else {
177             next;
178             }
179             }
180             else {
181 0         0 warn __PACKAGE__ . " non hashref passed to put(): \"$event\"\n";
182 0 0       0 push @$raw_lines, $event if ref $event eq 'SCALAR';
183             }
184             }
185 4         11 return $raw_lines;
186             }
187              
188             sub clone {
189 1     1 1 417 my $self = shift;
190 1         2 my $nself = { };
191 1         2 $nself->{$_} = $self->{$_} for keys %{ $self };
  1         7  
192 1         4 $nself->{BUFFER} = [ ];
193 1         4 return bless $nself, ref $self;
194             }
195              
196             # This thing is far from correct, dont use it.
197             sub _checkargs {
198 0   0 0     my $event = shift || return;
199 0 0         warn("Invalid characters in prefix: " . $event->{'prefix'} . "\n")
200             if ($event->{'prefix'} =~ m/[\x00\x0a\x0d\x20]/);
201 0 0         warn("Undefined command passed.\n")
202             unless ($event->{'command'} =~ m/\S/o);
203 0 0         warn("Invalid command: " . $event->{'command'} . "\n")
204             unless ($event->{'command'} =~ m/^(?:[a-zA-Z]+|\d{3})$/o);
205 0           foreach my $middle (@{$event->{'middles'}}) {
  0            
206 0 0         warn("Invalid middle: $middle\n")
207             unless ($middle =~ m/^[^\x00\x0a\x0d\x20\x3a][^\x00\x0a\x0d\x20]*$/);
208             }
209 0 0         warn("Invalid trailing: " . $event->{'trailing'} . "\n")
210             unless ($event->{'trailing'} =~ m/^[\x00\x0a\x0d]*$/);
211             }
212              
213             1;
214              
215             __END__