|
| 1 | +%# BEGIN BPS TAGGED BLOCK {{{ |
| 2 | +%# |
| 3 | +%# COPYRIGHT: |
| 4 | +%# |
| 5 | +%# This software is Copyright (c) 1996-2025 Best Practical Solutions, LLC |
| 6 | +%# <sales@bestpractical.com> |
| 7 | +%# |
| 8 | +%# (Except where explicitly superseded by other copyright notices) |
| 9 | +%# |
| 10 | +%# |
| 11 | +%# LICENSE: |
| 12 | +%# |
| 13 | +%# This work is made available to you under the terms of Version 2 of |
| 14 | +%# the GNU General Public License. A copy of that license should have |
| 15 | +%# been provided with this software, but in any event can be snarfed |
| 16 | +%# from www.gnu.org. |
| 17 | +%# |
| 18 | +%# This work is distributed in the hope that it will be useful, but |
| 19 | +%# WITHOUT ANY WARRANTY; without even the implied warranty of |
| 20 | +%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 21 | +%# General Public License for more details. |
| 22 | +%# |
| 23 | +%# You should have received a copy of the GNU General Public License |
| 24 | +%# along with this program; if not, write to the Free Software |
| 25 | +%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
| 26 | +%# 02110-1301 or visit their web page on the internet at |
| 27 | +%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. |
| 28 | +%# |
| 29 | +%# |
| 30 | +%# CONTRIBUTION SUBMISSION POLICY: |
| 31 | +%# |
| 32 | +%# (The following paragraph is not intended to limit the rights granted |
| 33 | +%# to you to modify and distribute this software under the terms of |
| 34 | +%# the GNU General Public License and is only of importance to you if |
| 35 | +%# you choose to contribute your changes and enhancements to the |
| 36 | +%# community by submitting them to Best Practical Solutions, LLC.) |
| 37 | +%# |
| 38 | +%# By intentionally submitting any modifications, corrections or |
| 39 | +%# derivatives to this work, or any other work intended for use with |
| 40 | +%# Request Tracker, to Best Practical Solutions, LLC, you confirm that |
| 41 | +%# you are the copyright holder for those contributions and you grant |
| 42 | +%# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, |
| 43 | +%# royalty-free, perpetual, license to use, copy, create derivative |
| 44 | +%# works based on those contributions, and sublicense and distribute |
| 45 | +%# those contributions and any derivatives thereof. |
| 46 | +%# |
| 47 | +%# END BPS TAGGED BLOCK }}} |
| 48 | +<& /RTIR/Elements/Header, Title => $title &> |
| 49 | +<& /Elements/Tabs &> |
| 50 | + |
| 51 | +<div class="m-2"> |
| 52 | + |
| 53 | +<&| /Widgets/TitleBox, |
| 54 | + title => $title, |
| 55 | + class => 'ipinfo', |
| 56 | +&> |
| 57 | +<div class="container"> |
| 58 | + <div class="row"> |
| 59 | + <div class="col m-3"> |
| 60 | + <pre><samp> |
| 61 | +<%perl> |
| 62 | +$SavedContent .= $ipinfo_results . "\n"; |
| 63 | +$m->out( $ipinfo_results ); |
| 64 | +</%perl> |
| 65 | + </samp></pre> |
| 66 | + </div> |
| 67 | + </div> |
| 68 | +</div> |
| 69 | +</&> |
| 70 | + |
| 71 | +% if ($TicketObj and $SavedContent) { |
| 72 | +% my $url = RT::IR->HREFTo( |
| 73 | +% "Tools/Lookup.html?" |
| 74 | +% . $m->comp( |
| 75 | +% '/Elements/QueryString', |
| 76 | +% ticket => $ticket, |
| 77 | +% q => $q, |
| 78 | +% ) |
| 79 | +% ); |
| 80 | + <div class="container"> |
| 81 | + <div class="row mt-2 justify-content-center"> |
| 82 | + <div class="col-auto"> |
| 83 | + <form method="post" action="<% $url %>"> |
| 84 | + <input type="hidden" name="LookupResults" value="<% $SavedContent %>" /> |
| 85 | + <input class="btn btn-primary" type="submit" value="<% loc('Save these results to the ticket') %>" id="save-results-to-ticket" /> |
| 86 | + </form> |
| 87 | + </div> |
| 88 | + </div> |
| 89 | + </div> |
| 90 | +% } |
| 91 | + |
| 92 | +</div> |
| 93 | +<%init> |
| 94 | +# IPinfo only works with IP addresses |
| 95 | +use Regexp::Common qw(net); |
| 96 | + |
| 97 | +unless ($q =~ /^($RE{net}{IPv4})$/ or $q =~ /^($RE{net}{IPv6})$/) { |
| 98 | + Abort(loc('IPinfo accepts only IPv4 or IPv6 IP addresses for lookups.')); |
| 99 | +} |
| 100 | + |
| 101 | +my $title = loc("IPinfo lookup for '[_1]'", $q); |
| 102 | +my $token = RT->Config->Get('LookupIPinfoToken'); |
| 103 | +my ($ipinfo_results, $TicketObj, $SavedContent); |
| 104 | + |
| 105 | +# Set a sensible order for the default info available for all IPinfo plans |
| 106 | +my @ipinfo_order = qw( |
| 107 | + ip |
| 108 | + hostname |
| 109 | + org |
| 110 | + city |
| 111 | + postal |
| 112 | + region |
| 113 | + country_name |
| 114 | + continent |
| 115 | + timezone |
| 116 | + longitude |
| 117 | + latitude |
| 118 | +); |
| 119 | + |
| 120 | +my $required_module = RT::StaticUtil::RequireModule('Geo::IPinfo'); |
| 121 | +if ( $required_module && $token ) { |
| 122 | + RT->Logger->debug('Running Geo::IPinfo lookup'); |
| 123 | + |
| 124 | + if ($ticket) { |
| 125 | + $TicketObj = LoadTicket($ticket); |
| 126 | + } |
| 127 | + |
| 128 | + my $ipinfo = Geo::IPinfo->new($token); |
| 129 | + my $ip_details; |
| 130 | + |
| 131 | + if ( $q =~ /^($RE{net}{IPv6})$/ ) { |
| 132 | + $ip_details = $ipinfo->info_v6($q); |
| 133 | + } |
| 134 | + else { |
| 135 | + $ip_details = $ipinfo->info($q); |
| 136 | + } |
| 137 | + |
| 138 | + if ( $ip_details ) { |
| 139 | + foreach my $core_field ( @ipinfo_order ) { |
| 140 | + if ( exists $ip_details->{$core_field} ) { |
| 141 | + my $label = ucfirst($core_field); |
| 142 | + # Special cases |
| 143 | + $label = 'IP' if $core_field eq 'ip'; |
| 144 | + $label = 'Country Name' if $core_field eq 'country_name'; |
| 145 | + |
| 146 | + if ( $core_field eq 'continent' ) { |
| 147 | + $ipinfo_results |
| 148 | + .= loc($label) . ': ' |
| 149 | + . Encode::decode( 'UTF-8', $ip_details->{$core_field}{'name'} ) . ' (' |
| 150 | + . Encode::decode( 'UTF-8', $ip_details->{$core_field}{'code'} ) . ")\n"; |
| 151 | + } |
| 152 | + else { |
| 153 | + $ipinfo_results |
| 154 | + .= loc($label) . ': ' . Encode::decode( 'UTF-8', $ip_details->{$core_field} ) . "\n"; |
| 155 | + } |
| 156 | + delete $ip_details->{$core_field}; |
| 157 | + } |
| 158 | + } |
| 159 | + |
| 160 | + # IPinfo returns more data with higher paid plans, so if we have anything |
| 161 | + # remaining, dump it to the screen. |
| 162 | + if ( keys %$ip_details ) { |
| 163 | + $ipinfo_results .= "\nAdditional Data:\n\n"; |
| 164 | + } |
| 165 | + |
| 166 | + foreach my $more_data ( sort keys %$ip_details ) { |
| 167 | + |
| 168 | + # IPinfo seems to only have one layer of extra data |
| 169 | + if ( ref $ip_details->{$more_data} eq 'HASH' ) { |
| 170 | + $ipinfo_results .= loc(ucfirst($more_data)) . ':' . "\n"; |
| 171 | + foreach my $data_detail ( sort keys %{$ip_details->{$more_data}} ) { |
| 172 | + $ipinfo_results |
| 173 | + .= ' ' |
| 174 | + . loc( ucfirst($data_detail) ) . ': ' |
| 175 | + . Encode::decode( 'UTF-8', $ip_details->{$more_data}{$data_detail} ) . "\n"; |
| 176 | + } |
| 177 | + } |
| 178 | + else { |
| 179 | + $ipinfo_results |
| 180 | + .= loc( ucfirst($more_data) ) . ': ' . Encode::decode( 'UTF-8', $ip_details->{$more_data} ) . "\n"; |
| 181 | + } |
| 182 | + } |
| 183 | + } |
| 184 | + else { |
| 185 | + RT->Logger->error('Error calling IPinfo: ' . $ipinfo->error_msg); |
| 186 | + $ipinfo_results .= loc('Error calling IPinfo') . ': ' . $ipinfo->error_msg; |
| 187 | + } |
| 188 | +} |
| 189 | +else { |
| 190 | + RT->Logger->warn('Install the module Geo::IPinfo to use the IPinfo lookup tool') unless $required_module; |
| 191 | + RT->Logger->warn('IPinfo lookups require a token from IPinfo.io to be set with the config option LookupIPinfoToken') unless $token; |
| 192 | + return; |
| 193 | +} |
| 194 | + |
| 195 | +</%init> |
| 196 | +<%args> |
| 197 | +$ticket => undef |
| 198 | +$q => '' |
| 199 | +</%args> |
0 commit comments