%# BEGIN BPS TAGGED BLOCK {{{
%#
%# COPYRIGHT:
%#
%# This software is Copyright (c) 1996-2026 Best Practical Solutions, LLC
%#                                          <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%#
%# LICENSE:
%#
%# This work is made available to you under the terms of Version 2 of
%# the GNU General Public License. A copy of that license should have
%# been provided with this software, but in any event can be snarfed
%# from www.gnu.org.
%#
%# This work is distributed in the hope that it will be useful, but
%# WITHOUT ANY WARRANTY; without even the implied warranty of
%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
%# General Public License for more details.
%#
%# You should have received a copy of the GNU General Public License
%# along with this program; if not, write to the Free Software
%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
%# 02110-1301 or visit their web page on the internet at
%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
%#
%#
%# CONTRIBUTION SUBMISSION POLICY:
%#
%# (The following paragraph is not intended to limit the rights granted
%# to you to modify and distribute this software under the terms of
%# the GNU General Public License and is only of importance to you if
%# you choose to contribute your changes and enhancements to the
%# community by submitting them to Best Practical Solutions, LLC.)
%#
%# By intentionally submitting any modifications, corrections or
%# derivatives to this work, or any other work intended for use with
%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
%# you are the copyright holder for those contributions and you grant
%# Best Practical Solutions,  LLC a nonexclusive, worldwide, irrevocable,
%# royalty-free, perpetual, license to use, copy, create derivative
%# works based on those contributions, and sublicense and distribute
%# those contributions and any derivatives thereof.
%#
%# END BPS TAGGED BLOCK }}}
<%init>
use Regexp::Common qw(RE_net_IPv4);
use URI::URL;

my $escaper = sub {
    my $content = shift;
    RT::Interface::Web::EscapeHTML( \$content );
    return $content;
};

my %super = %{$ARGS{actions}};

# Don't show links in the Self Service interface
my $SelfService = 0;
my $request_path = $HTML::Mason::Commands::r->path_info;
$request_path =~ s!/{2,}!/!g;
$SelfService = 1 if $request_path =~ m{^/SelfService/};

my %actions;
%actions = (
    default => sub {
        my %args = @_;
        $args{value} = $escaper->($args{value});
        # don't execute on non-rtir tickets or in SelfService
        return $args{value} unless defined $args{type} and !$SelfService;
        return qq{ <a href="}. RT::IR->HREFTo("Tools/Lookup.html?".$args{lookup_params}."q=".$args{value}).qq{">$args{value}</a>};
    },
    url => sub {
        my %args = @_;

        my $host = $args{'all_matches'}[3];
        my $escaped_host = $escaper->($host);
        my $super = $super{url} ? $super{url}->(%args) : $escaper->($args{value});

        # don't execute on non-rtir tickets or in SelfService
        return $super unless defined $args{type} and !$SelfService;

        my $result = qq{ <a class="btn btn-outline-primary rtir-lookup-button" href="}. RT::IR->HREFTo("Tools/Lookup.html?".$args{lookup_params}."type=host&q=".$escaped_host).qq{">}
            .loc('lookup "[_1]"', $host) .qq{</a>};
        return $super . qq{ <span class="clickylink">$result</span>};
    },
    url_overwrite => sub {
        my %args = @_;

        my $host = $args{'all_matches'}[3];
        my $escaped_host = $escaper->($host);
        my $super = $super{url_overwrite} ? $super{url_overwrite}->(%args) : $escaper->($args{value});

        # don't execute on non-rtir tickets or in SelfService
        return $super unless defined $args{type} and !$SelfService;

        my $result = qq{ <a class="btn btn-outline-primary rtir-lookup-button" href="}. RT::IR->HREFTo("Tools/Lookup.html?".$args{lookup_params}."type=host&q=".$escaped_host).qq{">}
            .loc('lookup "[_1]"', $host) .qq{</a>};
        return $super . qq{ <span class="clickylink">$result</span>};
    },
    ip => sub {
        my %args = @_;

        # don't execute on non-rtir tickets or in SelfService
        return $escaper->($args{value}) unless defined $args{type} and !$SelfService;

        $args{host} ||= $args{value};
        my $escaped_host = $escaper->($args{host});
        my $result .= qq{ <a class="btn btn-outline-primary rtir-lookup-button" href="}. RT::IR->HREFTo("Tools/Lookup.html?".$args{lookup_params}."type=ip&q=".$escaped_host).qq{">}
                      .loc('Lookup IP') .q{</a>};
        if ( $args{incident} && !RT->Config->Get('RTIR_DisableCountermeasures') ) {
            $result .= qq{ <a class="btn btn-outline-primary rtir-lookup-button create_child_} . RT::IR->lifecycle_countermeasure . qq{" href="} .
            RT::IR->HREFTo("Create.html?Incident=".$args{incident}."&Lifecycle=".RT::IR->lifecycle_countermeasure.qq{&IP-Value=$escaped_host})
            .qq{">Block</a>};
        }
        if ( $args{'object'} && $args{'object'}->id ) {
            my $cf = RT::IR::GetCustomField('IP');
            my $tid = $args{'object'}->id;
            $result .= qq{ <a class="btn btn-outline-primary rtir-lookup-button" href="}.
            RT::IR->HREFTo("Display.html?id=".$tid
                .qq{&Object-RT::Ticket-$tid-CustomField-}. $cf->id .qq{-AddValue=$escaped_host})
                .qq{">}. loc('Add IP') .q{</a>};
        }
        return $escaper->($args{value}) . qq{ <span class="clickylink">$result</span>};
    },
    ipdecimal => sub {
        my %args = @_;

        # don't execute on non-rtir tickets or in SelfService
        return $escaper->($args{value}) unless defined $args{type} and !$SelfService;

        my $i = unpack "L",$args{value};
        return $args{value} unless $args{value} > 33554432;

        my $host = do {
            my $hostnum = sprintf "%lx", $i;
            my $a = hex( substr( $hostnum, 0, 2 ) );
            my $b = hex( substr( $hostnum, 2, 2 ) );
            my $c = hex( substr( $hostnum, 4, 2 ) );
            my $d = hex( substr( $hostnum, 6, 2 ) );
            "$a.$b.$c.$d";
        };
        return $actions{ip}->(%args, host => $host);
    },
    email => sub {
        my %args = @_;

        # don't execute on non-rtir tickets or in SelfService
        return $escaper->($args{value}) unless defined $args{type} and !$SelfService;

        my $email = $args{'value'}; $email =~ s/^<|>$//g;
        my $escaped_email = $escaper->($email);

        my $result = qq{<a class="btn btn-outline-primary rtir-lookup-button" href="}.
            RT::IR->HREFTo(qq{Tools/Lookup.html?$args{'lookup_params'}type=email&q=$escaped_email}). qq{">}
            .loc('lookup email') .q{</a>};
        if ( $args{'incident'} ) {
	    my $uri_escaped_email = $escaped_email;
	    RT::Interface::Web::EscapeURI(\$uri_escaped_email);
            if ( $r->uri =~ /Lookup.html$/ ) {
                $result .= qq{<div class="form-check d-inline-block"><input type="checkbox" class="form-check-input" id="Requestorbox-$escaped_email" name="Requestorbox-$escaped_email" /> <label class="form-check-label" for="Requestorbox-$escaped_email"></label></div>};
            }
            $result .= qq{ <a class="btn btn-outline-primary rtir-lookup-button" href="}.
                RT::IR->HREFTo( "Create.html"
                . qq{?Incident=$args{'incident'}}
                . qq{&Lifecycle=}.RT::IR->lifecycle_investigation
                . "&Requestors=$uri_escaped_email")
                . qq{">}
                . loc('Investigate to') .qq{</a>};
        }
        my $domain = (split /@/, $email, 2)[1];
        my $escaped_domain = $escaper->($domain);
        $result .= ' <a class="btn btn-outline-primary rtir-lookup-button" href="'. RT::IR->HREFTo(qq{Tools/Lookup.html?$args{'lookup_params'}type=host&q=$escaped_domain}). '">'
               .loc('lookup "[_1]"', $domain) .'</a>';
        return $escaper->($args{'value'}) . qq{ <span class="clickylink">$result</span>};
    },
    noc => sub {
        my %args = @_;

        # don't execute on non-rtir tickets or in SelfService
        return $escaper->($args{value}) unless defined $args{type} and !$SelfService;

        $args{value} = $escaper->($args{value});
        my $result = qq{ <a class="btn btn-outline-primary rtir-lookup-button" href="}. RT::IR->HREFTo(qq{Tools/Lookup.html?$args{lookup_params}type=handle&q=$args{value}}).qq{">}
               .loc('lookup') .qq{</a>};
        return $args{value} . qq{ <span class="clickylink">$result</span>};
    },
    host => sub {
        my %args = @_;

        # don't execute on non-rtir tickets or in SelfService
        return $escaper->($args{value}) unless defined $args{type} and !$SelfService;

        $args{value} = $escaper->($args{value});
        my $result = qq{ <a class="btn btn-outline-primary rtir-lookup-button" href="}. RT::IR->HREFTo(qq{Tools/Lookup.html?$args{lookup_params}type=host&q=$args{value}}). qq{">}
               .loc('lookup host') .qq{</a>};
        return $args{value} . qq{ <span class="clickylink">$result</span>};
    },
);

use Regexp::IPv6 qw($IPv6_re);

my @types = (
    {
        name   => "ip",
        regex  => qr[(?<!\d)$RE{'net'}{'IPv4'}(?!\d)|(?<![0-9a-zA-Z:.])$IPv6_re(?!\.?[0-9a-zA-Z:])]o,
        action => "ip",
    },
    {
        name   => "ipdecimal",
        regex  => q[\d{8,13}(?!=)],
        action => "ipdecimal",
    },
    {
        name   => "email",
        regex  => qr{(?:mailto:)?<?[\w\.\+-]+\@[\w\.-]+\.\w{2,}>?},
        action => "email",
    },
    {
        name   => "domain",
        regex  => q[(?:[\w-]+\.)+[A-Za-z]{2,3}\b],
        action => "host",
    },
    {
        name   => "RIPE",
        regex  => q[(?-i:[A-Z][A-Z0-9-]+)],
        action => "noc",
    },
);

my $handle = ${$ARGS{handle}};
${$ARGS{handle}} = sub {
    my %args = @_;

    if ($args{object}) {
        if ( $args{object}->isa('RT::Ticket') ) {
            $args{type} = RT::IR::TicketType( Lifecycle => $args{object}->QueueObj->Lifecycle );

            $args{incident} = $args{object}->id
              if $args{type} && $args{type} eq 'Incident';

            $args{lookup_params} = "ticket=".$args{object}->id
                if !$args{lookup_params};
        }

        $args{lookup_params} .= "&"
          if $args{lookup_params} and $args{lookup_params} !~ /&$/;

        $args{lookup_params} = $escaper->($args{lookup_params});
    }

    return $handle->(%args);
};

unless (ref $ARGS{cache} && defined ${$ARGS{cache}}) {
    ${$ARGS{cache}} = sub {
        my $type = shift;
        my $content = shift;
        if ($type eq 'fetch') {
            my $parent_args = $m->caller_args(1);
            return if $parent_args->{lookup_params};

            require Digest::SHA;
            return $m->cache->get(Digest::SHA::sha512_base64(Encode::encode( "UTF-8", $$content)));
        } elsif ($type eq 'store') {
            require Digest::SHA;
            $m->cache->set(Digest::SHA::sha512_base64(Encode::encode( "UTF-8", $$content)),
                           $$content,
                           "6 hours");
        } else {
            RT->Logger->error("Invalid type $type passed to MakeClicky's cache");
        }
    }
} else {
    RT->Logger->info("Someone else defined a MakeClicky cache, skipping ours");
}

%{$ARGS{actions}} = ( %{$ARGS{actions}}, %actions );
push @{$ARGS{types}}, @types;

</%init>
