File Coverage

blib/lib/Protocol/IRC/Message.pm
Criterion Covered Total %
statement 110 115 95.6
branch 56 62 90.3
condition 11 12 91.6
subroutine 18 19 94.7
pod 15 15 100.0
total 210 223 94.1


line stmt bran cond sub pod time code
1             # You may distribute under the terms of either the GNU General Public License
2             # or the Artistic License (the same terms as Perl itself)
3             #
4             # (C) Paul Evans, 2008-2014 -- leonerd@leonerd.org.uk
5              
6             package Protocol::IRC::Message;
7              
8 18     18   83338 use strict;
  18         32  
  18         555  
9 18     18   89 use warnings;
  18         40  
  18         685  
10              
11             our $VERSION = '0.10';
12              
13 18     18   81 use Carp;
  18         28  
  18         14860  
14             our @CARP_NOT = qw( Net::Async::IRC );
15              
16             =head1 NAME
17              
18             C - encapsulates a single IRC message
19              
20             =head1 SYNOPSIS
21              
22             use Protocol::IRC::Message;
23              
24             my $hello = Protocol::IRC::Message->new(
25             "PRIVMSG",
26             undef,
27             "World",
28             "Hello, world!"
29             );
30              
31             printf "The command is %s and the final argument is %s\n",
32             $hello->command, $hello->arg( -1 );
33              
34             =head1 DESCRIPTION
35              
36             An object in this class represents a single IRC message, either received from
37             or to be sent to the server. These objects are immutable once constructed, but
38             provide a variety of methods to access the contained information.
39              
40             This class also understands IRCv3 message tags.
41              
42             =cut
43              
44             =head1 CONSTRUCTOR
45              
46             =cut
47              
48             =head2 $message = Protocol::IRC::Message->new_from_line( $line )
49              
50             Returns a new C object, constructed by parsing the
51             given IRC line. Most typically used to create a new object to represent a
52             message received from the server.
53              
54             =cut
55              
56             sub new_from_line
57             {
58 81     81 1 6958 my $class = shift;
59 81         128 my ( $line ) = @_;
60              
61 81         118 my %tags;
62 81 100       368 if( $line =~ s/^\@([^ ]+) +// ) {
63 1         5 foreach ( split m/;/, $1 ) {
64 3 100       13 if( m/^([^=]+)=(.*)$/ ) {
65 2         6 $tags{$1} = $2;
66             }
67             else {
68 1         4 $tags{$_} = undef;
69             }
70             }
71             }
72              
73 81         101 my $prefix;
74 81 100       507 if( $line =~ s/^:([^ ]+) +// ) {
75 71         278 $prefix = $1;
76             }
77              
78 81         383 my ( $mid, $final ) = split( m/ +:/, $line, 2 );
79 81         353 my @args = split( m/ +/, $mid );
80              
81 81 100       256 push @args, $final if defined $final;
82              
83 81         139 my $command = shift @args;
84              
85 81         387 return $class->new_with_tags( $command, \%tags, $prefix, @args );
86             }
87              
88             =head2 $message = Protocol::IRC::Message->new( $command, $prefix, @args )
89              
90             Returns a new C object, intialised from the given
91             components. Most typically used to create a new object to send to the server
92             using C. The message will contain no IRCv3 tags.
93              
94             =cut
95              
96             sub new
97             {
98 30     30 1 3028 my $class = shift;
99 30         193 return $class->new_with_tags( $_[0], {}, $_[1], @_[2..$#_] );
100             }
101              
102             =head2 $mesage = Protocol::IRC::Message->new_with_tags( $command, \%tags, $prefix, @args )
103              
104             Returns a new C object, as with C but also
105             containing the given IRCv3 tags.
106              
107             =cut
108              
109             sub new_with_tags
110             {
111 114     114 1 1267 my $class = shift;
112 114         303 my ( $command, $tags, $prefix, @args ) = @_;
113              
114             # IRC is case-insensitive for commands, but we'd like them in uppercase
115             # to keep things simpler
116 114         254 $command = uc $command;
117              
118             # Less strict checking than RFC 2812 because a lot of servers lately seem
119             # to be more flexible than that.
120              
121 114 100 100     1277 $command =~ m/^[A-Z]+$/ or $command =~ m/^\d\d\d$/ or
122             croak "Command must be just letters or three digits";
123              
124 112         382 foreach my $key ( keys %$tags ) {
125 6 100       191 $key =~ m{^[a-zA-Z0-9./-]+$} or
126             croak "Tag key '$key' is invalid";
127              
128 5         8 my $value = $tags->{$key};
129 5 100 100     406 defined $value and $value =~ m{[ ;]} and
130             croak "Tag value '$value' for key '$key' is invalid";
131             }
132              
133 110 100       281 if( defined $prefix ) {
134 72 100       405 $prefix =~ m/[ \t\x0d\x0a]/ and
135             croak "Prefix must not contain whitespace";
136             }
137              
138 109         380 foreach ( @args[0 .. $#args-1] ) { # Not the final
139 148 100       433 defined or croak "Argument must be defined";
140 147 100       598 m/[ \t\x0d\x0a]/ and
141             croak "Argument must not contain whitespace";
142             }
143              
144 107 100       289 if( @args ) {
145 102 100       570 defined $args[-1] or croak "Final argument must be defined";
146 101 100       440 $args[-1] =~ m/[\x0d\x0a]/ and croak "Final argument must not contain a linefeed";
147             }
148              
149 105         608 my $self = {
150             command => $command,
151             prefix => $prefix,
152             args => \@args,
153             tags => { %$tags },
154             };
155              
156 105         594 return bless $self, $class;
157             }
158              
159             =head1 METHODS
160              
161             =cut
162              
163             =head2 $str = $message->STRING
164              
165             =head2 $str = "$message"
166              
167             Returns a string representing the message, suitable for use in a debugging
168             message or similar. I: This is not the same as the IRC wire form, to
169             send to the IRC server; for that see C.
170              
171             =cut
172              
173 18     18   29228 use overload '""' => "STRING";
  18         14472  
  18         119  
174             sub STRING
175             {
176 0     0 1 0 my $self = shift;
177 0         0 my $class = ref $self;
178 0         0 return $class . "[" .
179             ( defined $self->{prefix} ? "prefix=$self->{prefix}," : "" ) .
180             "cmd=$self->{command}," .
181 0 0       0 "args=(" . join( ",", @{ $self->{args} } ) . ")]";
182             }
183              
184             =head2 $command = $message->command
185              
186             Returns the command name or numeric stored in the message object.
187              
188             =cut
189              
190             sub command
191             {
192 215     215 1 5845 my $self = shift;
193 215         1457 return $self->{command};
194             }
195              
196             =head2 $name = $message->command_name
197              
198             For named commands, returns the command name directly. For server numeric
199             replies, returns the name of the numeric.
200              
201             =cut
202              
203             my %NUMERIC_NAMES;
204              
205             sub command_name
206             {
207 74     74 1 107 my $self = shift;
208 74   66     173 return $NUMERIC_NAMES{ $self->command } || $self->command;
209             }
210              
211             =head2 $tags = $message->tags
212              
213             Returns a hash reference containing IRCv3 message tags. This is a reference to
214             the hash stored directly by the object itself, so the caller should be careful
215             not to modify it.
216              
217             =cut
218              
219             sub tags
220             {
221 2     2 1 10 my $self = shift;
222 2         15 return $self->{tags}
223             }
224              
225             =head2 $prefix = $message->prefix
226              
227             Returns the line prefix stored in the object, or the empty string if one was
228             not supplied.
229              
230             =cut
231              
232             sub prefix
233             {
234 96     96 1 185 my $self = shift;
235 96 100       847 return defined $self->{prefix} ? $self->{prefix} : "";
236             }
237              
238             =head2 ( $nick, $ident, $host ) = $message->prefix_split
239              
240             Splits the prefix into its nick, ident and host components. If the prefix
241             contains only a hostname (such as the server name), the first two components
242             will be returned as C.
243              
244             =cut
245              
246             sub prefix_split
247             {
248 66     66 1 99 my $self = shift;
249              
250 66         172 my $prefix = $self->prefix;
251              
252 66 100       448 return ( $1, $2, $3 ) if $prefix =~ m/^(.*?)!(.*?)@(.*)$/;
253              
254             # $prefix doesn't split into nick!ident@host so presume host only
255 36         106 return ( undef, undef, $prefix );
256             }
257              
258             =head2 $arg = $message->arg( $index )
259              
260             Returns the argument at the given index. Uses normal perl array indexing, so
261             negative indices work as expected.
262              
263             =cut
264              
265             sub arg
266             {
267 122     122 1 160 my $self = shift;
268 122         166 my ( $index ) = @_;
269 122         348 return $self->{args}[$index];
270             }
271              
272             =head2 @args = $message->args
273              
274             Returns a list containing all the message arguments.
275              
276             =cut
277              
278             sub args
279             {
280 30     30 1 113 my $self = shift;
281 30         37 return @{$self->{args}};
  30         173  
282             }
283              
284             =head2 $line = $message->stream_to_line
285              
286             Returns a string suitable for sending the message to the IRC server.
287              
288             =cut
289              
290             sub stream_to_line
291             {
292 31     31 1 95 my $self = shift;
293              
294 31         47 my $line = "";
295              
296 31 100       86 if( keys %{ $self->{tags} } ) {
  31         268  
297 1         4 my $tags = $self->{tags};
298 1 50       5 $line .= "\@" . join( ";", map { defined $tags->{$_} ? "$_=$tags->{$_}" : $_ } keys %$tags ) . " ";
  1         13  
299             }
300              
301 31 100       87 if( defined $self->{prefix} ) {
302 2         9 $line .= ":$self->{prefix} ";
303             }
304              
305 31         67 $line .= $self->{command};
306              
307 31         116 foreach ( @{$self->{args}} ) {
  31         75  
308 51 100 100     264 if( m/ / or m/^:/ ) {
309 11         34 $line .= " :$_";
310             }
311             else {
312 40         115 $line .= " $_";
313             }
314             }
315              
316 31         189 return $line;
317             }
318              
319             # Argument naming information
320              
321             # This hash holds HASH refs giving the names of the positional arguments of
322             # any message. The hash keys store the argument names, and the values store
323             # an argument index, the string "pn" meaning prefix nick, or "$n~$m" meaning
324             # an index range. Endpoint can be absent.
325              
326             my %ARG_NAMES = (
327             INVITE => { inviter_nick => "pn",
328             invited_nick => 0,
329             target_name => 1 },
330             KICK => { kicker_nick => "pn",
331             target_name => 0,
332             kicked_nick => 1,
333             text => 2 },
334             MODE => { target_name => 0,
335             modechars => 1,
336             modeargs => "2.." },
337             NICK => { old_nick => "pn",
338             new_nick => 0 },
339             NOTICE => { targets => 0,
340             text => 1 },
341             PING => { text => 0 },
342             PONG => { text => 0 },
343             QUIT => { text => 0 },
344             PART => { target_name => 0,
345             text => 1 },
346             PRIVMSG => { targets => 0,
347             text => 1 },
348             TOPIC => { target_name => 0,
349             text => 1 },
350             );
351              
352             # Misc. named commands
353             $ARG_NAMES{$_} = { target_name => 0 } for qw(
354             JOIN LIST NAMES WHO WHOIS WHOWAS
355             );
356              
357             # TODO: 472 ERR_UNKNOWNMODE: :is unknown mode char to me for
358             # How to parse this one??
359              
360             =head2 $names = $message->arg_names
361              
362             Returns a hash giving details on how to parse named arguments for the command
363             given in this message.
364              
365             This will be a hash whose keys give the names of the arguments, and the values
366             of these keys indicate how that argument is derived from the simple positional
367             arguments.
368              
369             Normally this method is only called internally by the C method,
370             but is documented here for the benefit of completeness, and in case extension
371             modules wish to define parsing of new message types.
372              
373             Each value should be one of the following:
374              
375             =over 4
376              
377             =item * String literal C
378              
379             The value is a string, the nickname given in the message prefix
380              
381             =item * NUMBER..NUMBER
382              
383             The value is an ARRAY ref, containing a list of all the numbered arguments
384             between the (inclusive) given limits. Either or both limits may be negative;
385             they will count backwards from the end.
386              
387             =item * NUMBER
388              
389             The value is the argument at that numeric index. May be negative to count
390             backwards from the end.
391              
392             =item * NUMBER@
393              
394             The value is the argument at that numeric index as for C, except that
395             the result will be split on spaces and stored in an ARRAY ref.
396              
397             =back
398              
399             =cut
400              
401             sub arg_names
402             {
403             # Usage: Class->arg_names($command) or $self->arg_names()
404 75     75 1 138 my $command;
405              
406 75 100       200 if( ref $_[0] ) {
407 74         88 my $self = shift;
408 74         196 $command = $self->{command};
409             }
410             else {
411 1         3 my $class = shift; # ignore
412 1         2 ( $command ) = @_;
413 1 50       7 defined $command or croak 'Usage: '.__PACKAGE__.'->arg_names($command)';
414             }
415              
416 75         342 return $ARG_NAMES{$command};
417             }
418              
419             =head2 $args = $message->named_args
420              
421             Parses arguments in the message according to the specification given by the
422             C method. Returns a hash of parsed arguments.
423              
424             TODO: More complete documentation on the exact arg names/values per message
425             type.
426              
427             =cut
428              
429             sub named_args
430             {
431 68     68 1 92 my $self = shift;
432              
433 68 100       169 my $argnames = $self->arg_names or return;
434              
435 60         92 my %named_args;
436 60         178 foreach my $name ( keys %$argnames ) {
437 132         601 my $argindex = $argnames->{$name};
438              
439 132         147 my $value;
440 132 100       738 if( $argindex eq "pn" ) {
    100          
    100          
    50          
441 2         7 ( $value, undef, undef ) = $self->prefix_split;
442             }
443             elsif( $argindex =~ m/^(-?\d+)?\.\.(-?\d+)?$/ ) {
444 17         40 my ( $start, $end ) = ( $1, $2 );
445 17         41 my @args = $self->args;
446              
447 17 50       51 defined $start or $start = 0;
448 17 100       40 defined $end or $end = $#args;
449              
450 17 100       47 $end += @args if $end < 0;
451              
452 17         68 $value = [ splice( @args, $start, $end-$start+1 ) ];
453             }
454             elsif( $argindex =~ m/^-?\d+$/ ) {
455 109         236 $value = $self->arg( $argindex );
456             }
457             elsif( $argindex =~ m/^(-?\d+)\@$/ ) {
458 4         10 $value = [ split ' ', $self->arg( $1 ) ];
459             }
460             else {
461 0         0 die "Unrecognised argument specification $argindex";
462             }
463              
464 132         365 $named_args{$name} = $value;
465             }
466              
467 60         277 return \%named_args;
468             }
469              
470             =head2 $disp = $message->gate_disposition
471              
472             Returns the "gating disposition" of the message. This defines how a reply
473             message from the server combines with other messages in response of a command
474             sent by the client. The disposition is either C, or a string consisting
475             of a type symbol and a gate name. If defined, the symbol defines what effect
476             it has on the gate name.
477              
478             =over 4
479              
480             =item -GATE
481              
482             Adds more information to the response for that gate, but doesn't yet complete
483             it.
484              
485             =item +GATE
486              
487             Completes the gate with a successful result.
488              
489             =item !GATE
490              
491             Completes the gate with a failure result.
492              
493             =back
494              
495             =cut
496              
497             my %GATE_DISPOSITIONS;
498              
499             sub gate_disposition
500             {
501 62     62 1 89 my $self = shift;
502 62         141 return $GATE_DISPOSITIONS{ $self->command };
503             }
504              
505             =head1 AUTHOR
506              
507             Paul Evans
508              
509             =cut
510              
511             while( ) {
512             chomp;
513             my ( $numname, $args, $gating ) = split m/\s*\|\s*/, $_ or next;
514             my ( $num, $name ) = split m/=/, $numname;
515              
516             my $index = 0;
517             my %args = map {
518             if( m/^(.*)=(.*)$/ ) {
519             $index = $1;
520             ( $2 => $1 )
521             }
522             else {
523             ( $_ => ++$index );
524             }
525             } split m/,/, $args;
526              
527             $NUMERIC_NAMES{$num} = $name;
528             $ARG_NAMES{$num} = \%args;
529             $GATE_DISPOSITIONS{$num} = $gating if defined $gating;
530             }
531             close DATA;
532              
533             0x55AA;
534              
535             # And now the actual numeric definitions, given in columns
536             # number=NAME | argname,argname,argname
537              
538             # arg may be position=argname
539              
540             # See also
541             # http://www.alien.net.au/irc/irc2numerics.html
542              
543             __DATA__