<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;">package URI::irc;  # draft-butcher-irc-url-04

use strict;
use warnings;

our $VERSION = '5.31';

use parent 'URI::_login';

use overload (
   '""'     =&gt; sub { $_[0]-&gt;as_string  },
   '=='     =&gt; sub {  URI::_obj_eq(@_) },
   '!='     =&gt; sub { !URI::_obj_eq(@_) },
   fallback =&gt; 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 *( "&amp;" 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-&gt;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 (@_ &amp;&amp; defined $new);
    my $val = $self-&gt;SUPER::path(@_ ? $new : ());
    $val =~ s|^/%23|/\#|;
    $val;
}
sub path_query {
    my $self = shift;
    my ($new) = @_;
    $new =~ s|^/\#|/%23| if (@_ &amp;&amp; defined $new);
    my $val = $self-&gt;SUPER::path_query(@_ ? $new : ());
    $val =~ s|^/%23|/\#|;
    $val;
}
sub as_string {
    my $self = shift;
    my $val = $self-&gt;SUPER::as_string;
    $val =~ s|^((?:[^:/?\#]+:)?(?://[^/?\#]*)?)/%23|$1/\#|s;
    $val;
}

sub entity {
    my $self = shift;

    my $path = $self-&gt;path;
    $path =~ s|^/||;
    my ($entity, @flags) = split /,/, $path;

    if (@_) {
        my $new = shift;
        $new = '' unless defined $new;
        $self-&gt;path( '/'.join(',', $new, @flags) );
    }

    return unless length $entity;
    $entity;
}

sub flags {
    my $self = shift;

    my $path = $self-&gt;path;
    $path =~ s|^/||;
    my ($entity, @flags) = split /,/, $path;

    if (@_) {
        $self-&gt;path( '/'.join(',', $entity, @_) );
    }

    @flags;
}

sub options { shift-&gt;query_form(@_) }

sub canonical {
    my $self = shift;
    my $other = $self-&gt;SUPER::canonical;

    # Clean up the flags
    my $path = $other-&gt;path;
    $path =~ s|^/||;
    my ($entity, @flags) = split /,/, $path;

    my @clean =
        map { $_ eq 'isnick' ? 'isuser' : $_ }  # convert isnick-&gt;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-&gt;clone if $other == $self;
        $other-&gt;path( '/'.join(',', $entity, @new) );
    }

    $other;
}

1;
</pre></body></html>