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