File Coverage

blib/lib/URI/irc.pm
Criterion Covered Total %
statement 50 74 67.5
branch 10 26 38.4
condition 2 6 33.3
subroutine 12 16 75.0
pod 5 8 62.5
total 79 130 60.7


line stmt bran cond sub pod time code
1             package URI::irc; # draft-butcher-irc-url-04
2              
3 2     2   630 use strict;
  2         4  
  2         76  
4 2     2   7 use warnings;
  2         3  
  2         139  
5              
6             our $VERSION = '5.34';
7              
8 2     2   10 use parent 'URI::_login';
  2         3  
  2         16  
9              
10             use overload (
11 2     2   1255 '""' => sub { $_[0]->as_string },
12 1     1   6 '==' => sub { URI::_obj_eq(@_) },
13 0     0   0 '!=' => sub { !URI::_obj_eq(@_) },
14 2         45 fallback => 1,
15 2     2   238 );
  2         3  
16              
17 2     2 1 5 sub default_port { 6667 }
18              
19             # ircURL = ircURI "://" location "/" [ entity ] [ flags ] [ options ]
20             # ircURI = "irc" / "ircs"
21             # location = [ authinfo "@" ] hostport
22             # authinfo = [ username ] [ ":" password ]
23             # username = *( escaped / unreserved )
24             # password = *( escaped / unreserved ) [ ";" passtype ]
25             # passtype = *( escaped / unreserved )
26             # entity = [ "#" ] *( escaped / unreserved )
27             # flags = ( [ "," enttype ] [ "," hosttype ] )
28             # /= ( [ "," hosttype ] [ "," enttype ] )
29             # enttype = "," ( "isuser" / "ischannel" )
30             # hosttype = "," ( "isserver" / "isnetwork" )
31             # options = "?" option *( "&" option )
32             # option = optname [ "=" optvalue ]
33             # optname = *( ALPHA / "-" )
34             # optvalue = optparam *( "," optparam )
35             # optparam = *( escaped / unreserved )
36              
37             # XXX: Technically, passtype is part of the protocol, but is rarely used and
38             # not defined in the RFC beyond the URL ABNF.
39              
40             # Starting the entity with /# is okay per spec, but it needs to be encoded to
41             # %23 for the URL::_generic::path operations to parse correctly.
42             sub _init {
43 2     2   5 my $class = shift;
44 2         13 my $self = $class->SUPER::_init(@_);
45 2         199 $$self =~ s|^((?:[^:/?\#]+:)?(?://[^/?\#]*)?)/\#|$1/%23|s;
46 2         13 $self;
47             }
48              
49             # Return the /# form, since this is most common for channel names.
50             sub path {
51 5     5 1 12 my $self = shift;
52 5         10 my ($new) = @_;
53 5 100 66     20 $new =~ s|^/\#|/%23| if (@_ && defined $new);
54 5 100       20 my $val = $self->SUPER::path(@_ ? $new : ());
55 5         12 $val =~ s|^/%23|/\#|;
56 5         12 $val;
57             }
58             sub path_query {
59 0     0 1 0 my $self = shift;
60 0         0 my ($new) = @_;
61 0 0 0     0 $new =~ s|^/\#|/%23| if (@_ && defined $new);
62 0 0       0 my $val = $self->SUPER::path_query(@_ ? $new : ());
63 0         0 $val =~ s|^/%23|/\#|;
64 0         0 $val;
65             }
66             sub as_string {
67 2     2 1 3 my $self = shift;
68 2         19 my $val = $self->SUPER::as_string;
69 2         12 $val =~ s|^((?:[^:/?\#]+:)?(?://[^/?\#]*)?)/%23|$1/\#|s;
70 2         8 $val;
71             }
72              
73             sub entity {
74 0     0 0 0 my $self = shift;
75              
76 0         0 my $path = $self->path;
77 0         0 $path =~ s|^/||;
78 0         0 my ($entity, @flags) = split /,/, $path;
79              
80 0 0       0 if (@_) {
81 0         0 my $new = shift;
82 0 0       0 $new = '' unless defined $new;
83 0         0 $self->path( '/'.join(',', $new, @flags) );
84             }
85              
86 0 0       0 return unless length $entity;
87 0         0 $entity;
88             }
89              
90             sub flags {
91 0     0 0 0 my $self = shift;
92              
93 0         0 my $path = $self->path;
94 0         0 $path =~ s|^/||;
95 0         0 my ($entity, @flags) = split /,/, $path;
96              
97 0 0       0 if (@_) {
98 0         0 $self->path( '/'.join(',', $entity, @_) );
99             }
100              
101 0         0 @flags;
102             }
103              
104 2     2 0 725 sub options { shift->query_form(@_) }
105              
106             sub canonical {
107 1     1 1 2 my $self = shift;
108 1         6 my $other = $self->SUPER::canonical;
109              
110             # Clean up the flags
111 1         2 my $path = $other->path;
112 1         3 $path =~ s|^/||;
113 1         4 my ($entity, @flags) = split /,/, $path;
114              
115             my @clean =
116 5 100       23 map { $_ eq 'isnick' ? 'isuser' : $_ } # convert isnick->isuser
117 5         7 map { lc }
118             # NOTE: Allow flags from draft-mirashi-url-irc-01 as well
119 1         2 grep { /^(?:is(?:user|channel|server|network|nick)|need(?:pass|key))$/i }
  5         13  
120             @flags
121             ;
122              
123             # Only allow the first type of each category, per the Butcher draft
124 1         5 my ($enttype) = grep { /^is(?:user|channel)$/ } @clean;
  5         12  
125 1         3 my ($hosttype) = grep { /^is(?:server|network)$/ } @clean;
  5         12  
126 1         2 my @others = grep { /^need(?:pass|key)$/ } @clean;
  5         12  
127              
128 1 50       6 my @new = (
    50          
129             $enttype ? $enttype : (),
130             $hosttype ? $hosttype : (),
131             @others,
132             );
133              
134 1 50       8 unless (join(',', @new) eq join(',', @flags)) {
135 1 50       5 $other = $other->clone if $other == $self;
136 1         16 $other->path( '/'.join(',', $entity, @new) );
137             }
138              
139 1         16 $other;
140             }
141              
142             1;