[Rt-commit] rtir branch, ipv6, created. 2.6.0-23-gd7198e7

Ruslan Zakirov ruz at bestpractical.com
Thu Apr 21 15:53:24 EDT 2011


The branch, ipv6 has been created
        at  d7198e76bbf953dfe0cb2a0abc65e95046e8f3da (commit)

- Log -----------------------------------------------------------------
commit 064a660e745a7fcf518cfd8670297e9bc613eb8d
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Thu Apr 21 23:25:34 2011 +0400

    backport IPv6 support from RT4

diff --git a/lib/RT/IR.pm b/lib/RT/IR.pm
index 19c5a4f..8ca0b25 100644
--- a/lib/RT/IR.pm
+++ b/lib/RT/IR.pm
@@ -318,6 +318,7 @@ sub DefaultConstituency {
 use Hook::LexWrap;
 use Regexp::Common qw(RE_net_IPv4);
 use Regexp::Common::net::CIDR;
+use Regexp::IPv6 qw($IPv6_re);
 require Net::CIDR;
 
 sub ParseIPRange {
@@ -327,6 +328,9 @@ sub ParseIPRange {
         my $cidr = join( '.', map $_||0, (split /\./, $1)[0..3] ) ."/$2";
         $arg = (Net::CIDR::cidr2range( $cidr ))[0] || $arg;
     }
+    elsif ( $arg =~ /^(?:$IPv6_re|::)(?:\/\d+)?$/o ) {
+        $arg = (Net::CIDR::cidr2range( $arg ))[0] || $arg;
+    }
 
     my ($sIP, $eIP);
     if ( $arg =~ /^\s*($RE{net}{IPv4})\s*$/o ) {
@@ -336,6 +340,15 @@ sub ParseIPRange {
         $sIP = sprintf "%03d.%03d.%03d.%03d", split /\./, $1;
         $eIP = sprintf "%03d.%03d.%03d.%03d", split /\./, $2;
     }
+    elsif ( $arg =~ /^($IPv6_re|::)$/o ) {
+        $sIP = ParseIP( $1 );
+        $eIP = $sIP;
+    }
+    elsif ( $arg =~ /^($IPv6_re|::)-($IPv6_re|::)$/o ) {
+        ($sIP, $eIP) = ( $1, $2 );
+        $sIP = ParseIP( $sIP );
+        $eIP = ParseIP( $eIP );
+    }
     else {
         return ();
     }
@@ -345,6 +358,52 @@ sub ParseIPRange {
 }
 
 
+sub ParseIP {
+    my $value = shift or return;
+    $value = lc $value;
+    $value =~ s!^\s+!!;
+    $value =~ s!\s+$!!;
+
+    if ( $value =~ /^($RE{net}{IPv4})$/o ) {
+        return sprintf "%03d.%03d.%03d.%03d", split /\./, $1;
+    }
+    elsif ( $value =~ /^(?:$IPv6_re|::)$/o ) {
+        # up_fields are before '::'
+        # low_fields are after '::' but without v4
+        # v4_fields are the v4
+        my ( @up_fields, @low_fields, @v4_fields );
+        my $v6;
+        if ( $value =~ /(.*:)(\d+\..*)/ ) {
+            ( $v6, my $v4 ) = ( $1, $2 );
+            chop $v6 unless $v6 =~ /::$/;
+            while ( $v4 =~ /(\d+)\.(\d+)/g ) {
+                push @v4_fields, sprintf '%.2x%.2x', $1, $2;
+            }
+        }
+        else {
+            $v6 = $value;
+        }
+
+        my ( $up, $low );
+        if ( $v6 =~ /::/ ) {
+            ( $up, $low ) = split /::/, $v6;
+        }
+        else {
+            $up = $v6;
+        }
+
+        @up_fields = split /:/, $up;
+        @low_fields = split /:/, $low if $low;
+
+        my @zero_fields =
+          ('0000') x ( 8 - @v4_fields - @up_fields - @low_fields );
+        my @fields = ( @up_fields, @zero_fields, @low_fields, @v4_fields );
+
+        return join ':', map { sprintf "%.4x", hex "0x$_" } @fields;
+    }
+    return;
+}
+
 # limit formatting "%03d.%03d.%03d.%03d"
 # "= 'sIP-eIP'" => "( >=sIP AND <=eIP)"
 # "!= 'sIP-eIP'" => "( <sIP OR >eIP)"
@@ -353,10 +412,14 @@ require RT::Tickets;
 wrap 'RT::Tickets::_CustomFieldLimit',
     pre => sub {
         return if $_[2] && $_[2] =~ /^[<>]=?$/;
-        return unless $_[3] =~ /^\s*($RE{net}{IPv4})\s*(?:-\s*($RE{net}{IPv4})\s*)?$/o;
-        my ($start_ip, $end_ip) = ($1, ($2 || $1));
-        $_ = sprintf "%03d.%03d.%03d.%03d", split /\./, $_
-            for $start_ip, $end_ip;
+
+        my ($start_ip, $end_ip);
+        return unless
+            $_[3] =~ /^\s*($RE{net}{IPv4})\s*(?:-\s*($RE{net}{IPv4})\s*)?$/o
+            || $_[3] =~ /^\s*($IPv6_re|::)\s*(?:-\s*($IPv6_re|::)\s*)?$/o;
+    
+        ($start_ip, $end_ip) = ($1, ($2 || $1));
+        $_ = ParseIP( $_ ) for $start_ip, $end_ip;
         ($start_ip, $end_ip) = ($end_ip, $start_ip) if $start_ip gt $end_ip;
 
         my ($tickets, $field, $op, $value, %rest) = @_[0..($#_-1)];
@@ -400,14 +463,20 @@ wrap 'RT::Tickets::_CustomFieldLimit',
         $_[-1] = ref $_[-1]? [1]: 1;
     };
 
+my $IPv6_mask_re = qr{12[0-8]|1[01][0-9]|[1-9]?[0-9]};
+
 # "[!]= 'CIDR'" => "op 'sIP-eIP'"
 wrap 'RT::Tickets::_CustomFieldLimit',
     pre => sub {
-        return unless $_[3] =~ /^\s*$RE{net}{CIDR}{IPv4}{-keep}\s*$/o;
-        # convert incomplete 192.168/24 to 192.168.0.0/24 format
-        my $cidr = join( '.', map $_||0, (split /\./, $1)[0..3] ) ."/$2";
-        # convert to range and continue, it will be catched by next wrapper
-        $_[3] = (Net::CIDR::cidr2range( $cidr ))[0] || $_[3];
+        # convert CIDR (IP/mask) to range (IP-IP) and continue,
+        # it will be catched by next wrapper
+        if ( $_[3] =~ /^\s*$RE{net}{CIDR}{IPv4}{-keep}\s*$/o ) {
+            # convert incomplete 192.168/24 to 192.168.0.0/24 format
+            my $cidr = join( '.', map $_||0, (split /\./, $1)[0..3] ) ."/$2";
+            $_[3] = (Net::CIDR::cidr2range( $cidr ))[0] || $_[3];
+        } elsif ( $_[3] =~ m{^\s*($IPv6_re/$IPv6_mask_re)\s*$}o ) {
+            $_[3] = (Net::CIDR::cidr2range( $1 ))[0] || $_[3];
+        }
     };
 $RT::Tickets::dispatch{'CUSTOMFIELD'} = \&RT::Tickets::_CustomFieldLimit;
 
@@ -454,14 +523,21 @@ wrap 'RT::ObjectCustomFieldValue::Content',
     post => sub {
         return unless $_[-1];
         my $val = ref $_[-1]? \$_[-1][0]: \$_[-1];
-        return unless $$val =~ /^\s*($re_ip_serialized)\s*$/o;
-        $$val = sprintf "%d.%d.%d.%d", split /\./, $1;
-
-        my $large_content = $obj->__Value('LargeContent');
-        return if !$large_content
-            || $large_content !~ /^\s*($re_ip_serialized)\s*$/o;
-        my $eIP = sprintf "%d.%d.%d.%d", split /\./, $1;
-        $$val .= '-'. $eIP unless $$val eq $eIP;
+
+        if ( $$val =~ /^\s*($re_ip_serialized)\s*$/o ) {
+            $$val = sprintf "%d.%d.%d.%d", split /\./, $1;
+
+            my $large_content = $obj->__Value('LargeContent');
+            return if !$large_content
+                || $large_content !~ /^\s*($re_ip_serialized)\s*$/o;
+            my $eIP = sprintf "%d.%d.%d.%d", split /\./, $1;
+            $$val .= '-'. $eIP unless $$val eq $eIP;
+        } elsif ( $$val =~ /^\s*($IPv6_re)\s*$/o ) {
+            my $large_content = $obj->__Value('LargeContent');
+            return if !$large_content || $large_content eq $$val
+                || $large_content !~ /^\s*($IPv6_re)\s*$/o;
+            $$val .= '-'. $1;
+        }
         return;
     };
 }

commit 5bc5f7b604e4caa4de017a4eda25c232bcdf3a9b
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Thu Apr 21 23:26:13 2011 +0400

    update IPs extraction action with IPv6 support

diff --git a/lib/RT/Action/RTIR_FindIP.pm b/lib/RT/Action/RTIR_FindIP.pm
index f3872d9..5e4465a 100644
--- a/lib/RT/Action/RTIR_FindIP.pm
+++ b/lib/RT/Action/RTIR_FindIP.pm
@@ -7,8 +7,15 @@ use base qw(RT::Action::RTIR);
 
 use Regexp::Common qw(net);
 use Regexp::Common::net::CIDR ();
+use Regexp::IPv6 qw($IPv6_re);
 use Net::CIDR ();
 
+my $IPv4_mask_re = qr{3[0-2]|[1-2]?[0-9]};
+my $IPv6_mask_re = qr{12[0-8]|1[01][0-9]|[1-9]?[0-9]};
+my $IP4_re = qr[(?<![0-9.])(?!0\.0\.0\.0)($RE{net}{IPv4})(?!/$IPv4_mask_re)(?![0-9.])];
+my $IP6_re = qr[(?<![0-9a-fA-F:.])($IPv6_re)(?:/($IPv6_mask_re))?(?![0-9a-fA-F:.])];
+my $IP_re = qr{$IP6_re|$IP4_re|$RE{net}{CIDR}{IPv4}{-keep}};
+
 =head2 Prepare
 
 Always run this.
@@ -48,25 +55,30 @@ sub Commit {
     my $spots_left = $how_many_can - keys %existing;
 
     my $content = $attach->Content || '';
-# 0.0.0.0 is illegal IP address
-    my @IPs = ( $content =~ /(?<!\d)(?!0\.0\.0\.0)($RE{net}{IPv4})(?!\d)(?!\/(?:3[0-2]|[1-2]?[0-9])(?:\D|\z))/go );
-    foreach my $ip ( @IPs ) {
-        $spots_left -= $self->AddIP(
-            IP          => $ip,
-            CustomField => $cf,
-            Skip        => \%existing,
-        );
-        return 1 unless $spots_left;
-    }
-
-# but 0.0.0.0/0 is legal CIDR
-    my @CIDRs = ( $content =~ /(?<![0-9.])$RE{net}{CIDR}{IPv4}{-keep}(?!\.?[0-9])/go );
-    while ( my ($addr, $bits) = splice @CIDRs, 0, 2 ) {
-        my $cidr = join( '.', map $_||0, (split /\./, $addr)[0..3] ) ."/$bits";
-        my $range = (Net::CIDR::cidr2range( $cidr ))[0] or next;
-        $spots_left -= $self->AddIP(
-            IP => $range, CustomField => $cf, Skip => \%existing
-        );
+    while ( $content =~ m/$IP_re/go ) {
+        if ( $1 && defined $2 ) { # IPv6/mask
+            my $range = (Net::CIDR::cidr2range( "$1/$2" ))[0] or next;
+            $spots_left -= $self->AddIP(
+                IP => $range, CustomField => $cf, Skip => \%existing
+            );
+        }
+        elsif ( $1 ) { # IPv6
+            $spots_left -= $self->AddIP(
+                IP => $1, CustomField => $cf, Skip => \%existing
+            );
+        }
+        elsif ( $3 ) { # IPv4
+            $spots_left -= $self->AddIP(
+                IP => $3, CustomField => $cf, Skip => \%existing
+            );
+        }
+        elsif ( $4 && defined $5 ) { # IPv4/mask
+            my $cidr = join( '.', map $_||0, (split /\./, $4)[0..3] ) ."/$5";
+            my $range = (Net::CIDR::cidr2range( $cidr ))[0] or next;
+            $spots_left -= $self->AddIP(
+                IP => $range, CustomField => $cf, Skip => \%existing
+            );
+        }
         return 1 unless $spots_left;
     }
 

commit 9e5d73d048c177e84a3280f4d1e68ce890104a7c
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Thu Apr 21 23:27:51 2011 +0400

    extend IP's pattern with IPv6

diff --git a/etc/initialdata b/etc/initialdata
index 3aebac3..eb811ef 100644
--- a/etc/initialdata
+++ b/etc/initialdata
@@ -33,7 +33,8 @@
 
 use Regexp::Common qw(net);
 use Regexp::Common::net::CIDR();
-my $ip_pattern = qr{(?#IP/IP-IP/CIDR)^(?:|\s*$RE{net}{IPv4}(?:\s*-\s*$RE{net}{IPv4})?\s*|$RE{net}{CIDR}{IPv4})$};
+use Regexp::IPv6 qw($IPv6_re);
+my $ip_pattern = qr{(?#IP/IP-IP/CIDR)^(?:|\s*$RE{net}{IPv4}(?:\s*-\s*$RE{net}{IPv4})?\s*|$RE{net}{CIDR}{IPv4}|$IPv6_re(?:\/\d+)?|$IPv6_re-$IPv6_re)$};
 
 
 @CustomFields = (

commit 687617e35fbd29c12732735125a3cb5d1a6dab5b
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Thu Apr 21 23:29:32 2011 +0400

    test IPv6 support

diff --git a/t/custom-fields/ipv6.t b/t/custom-fields/ipv6.t
new file mode 100644
index 0000000..b8b5c54
--- /dev/null
+++ b/t/custom-fields/ipv6.t
@@ -0,0 +1,556 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use RT::IR::Test tests => 772;
+
+RT::Test->started_ok;
+my $agent = default_agent();
+
+use_ok('RT::IR');
+
+my %valid = (
+    'abcd:' x 7 . 'abcd' => 'abcd:' x 7 . 'abcd',
+    '034:' x 7 . '034'   => '0034:' x 7 . '0034',
+    'abcd::'             => 'abcd:' . '0000:' x 6 . '0000',
+    '::abcd'             => '0000:' x 7 . 'abcd',
+    'abcd::034'          => 'abcd:' . '0000:' x 6 . '0034',
+    'abcd::192.168.1.1'  => 'abcd:' . '0000:' x 5 . 'c0a8:0101',
+    '::192.168.1.1'      => '0000:' x 6 . 'c0a8:0101',
+);
+
+my %test_set = (
+    'abcd:' x 7 . 'abcd' => 'abcd:' x 7 . 'abcd',
+    'abcd::034'          => 'abcd:' . '0000:' x 6 . '0034',
+    '::192.168.1.1'      => '0000:' x 6 . 'c0a8:0101',
+);
+my %test_cidr = (
+    'abcd:' x 7 . 'abcd/32' => 'abcd:abcd'. ':0000' x 6 .'-'. 'abcd:abcd'. ':ffff' x 6,
+    '::192.168.1.1/120'     => '0000:' x 6 . 'c0a8:0100' .'-'. '0000:' x 6 . 'c0a8:01ff',
+);
+
+my $cf;
+diag "load and check basic properties of the IP CF" if $ENV{'TEST_VERBOSE'};
+{
+    my $cfs = RT::CustomFields->new( $RT::SystemUser );
+    $cfs->Limit( FIELD => 'Name', VALUE => 'IP' );
+    is( $cfs->Count, 1, "found one CF with name 'IP'" );
+
+    $cf = $cfs->First;
+    is( $cf->Type, 'Freeform', 'type check' );
+    is( $cf->LookupType, 'RT::Queue-RT::Ticket', 'lookup type check' );
+    ok( !$cf->MaxValues, "unlimited number of values" );
+    ok( !$cf->Disabled, "not disabled" );
+}
+
+diag "check that CF applies to all RTIR's queues" if $ENV{'TEST_VERBOSE'};
+{
+    foreach ( 'Incidents', 'Incident Reports', 'Investigations', 'Blocks' ) {
+        my $queue = RT::Queue->new( $RT::SystemUser );
+        $queue->Load( $_ );
+        ok( $queue->id, 'loaded queue '. $_ );
+        my $cfs = $queue->TicketCustomFields;
+        $cfs->Limit( FIELD => 'id', VALUE => $cf->id, ENTRYAGGREGATOR => 'AND' );
+        is( $cfs->Count, 1, 'field applies to queue' );
+    }
+}
+my $rtir_user = RT::CurrentUser->new( rtir_user() );
+
+diag "create a ticket via web and set IP" if $ENV{'TEST_VERBOSE'};
+while ( my ($short, $full) = each %valid ) {
+    my $incident_id; # block couldn't be created without incident id
+    foreach my $queue( 'Incidents', 'Incident Reports', 'Investigations', 'Blocks' ) {
+        diag "create a ticket in the '$queue' queue" if $ENV{'TEST_VERBOSE'};
+
+        my $id = $agent->create_rtir_ticket_ok(
+            $queue,
+            {
+                Subject => "test ip",
+                ( $queue eq 'Blocks' ? ( Incident => $incident_id ) : () ),
+            },
+            { IP => $short },
+        );
+        $incident_id = $id if $queue eq 'Incidents';
+
+        $agent->display_ticket( $id);
+        $agent->content_like( qr/\Q$full/, "IP on the page" );
+
+        my $ticket = RT::Ticket->new( $RT::SystemUser );
+        $ticket->Load( $id );
+        ok( $ticket->id, 'loaded ticket' );
+        is( $ticket->FirstCustomFieldValue('IP'), $full, 'correct value' );
+    }
+}
+
+diag "create a ticket via web with IP in message" if $ENV{'TEST_VERBOSE'};
+while ( my ($short, $full) = each %test_set ) {
+    my $incident_id; # block couldn't be created without incident id
+    foreach my $queue( 'Incidents', 'Incident Reports', 'Investigations', 'Blocks' ) {
+        diag "create a ticket in the '$queue' queue" if $ENV{'TEST_VERBOSE'};
+
+        my $id = $agent->create_rtir_ticket_ok(
+            $queue,
+            {
+                Subject => "test ip in message",
+                ($queue eq 'Blocks'? (Incident => $incident_id): ()),
+                Content => "$short",
+            },
+        );
+        $incident_id = $id if $queue eq 'Incidents';
+
+        $agent->display_ticket( $id );
+        $agent->content_like( qr/\Q$full/, "IP on the page" );
+
+        my $ticket = RT::Ticket->new( $RT::SystemUser );
+        $ticket->Load( $id );
+        ok( $ticket->id, 'loaded ticket' );
+        is( $ticket->FirstCustomFieldValue('IP'), $full, 'correct value' );
+    }
+}
+
+diag "create a ticket via web with CIDR" if $ENV{'TEST_VERBOSE'};
+while ( my ($short, $full) = each %test_cidr ) {
+    my $incident_id; # block couldn't be created without incident id
+    foreach my $queue( 'Incidents', 'Incident Reports', 'Investigations', 'Blocks' ) {
+        diag "create a ticket in the '$queue' queue" if $ENV{'TEST_VERBOSE'};
+
+        my $id = $agent->create_rtir_ticket_ok(
+            $queue,
+            {
+                Subject => "test ip",
+                ($queue eq 'Blocks'? (Incident => $incident_id): ()),
+            },
+            { IP => $short },
+        );
+        $incident_id = $id if $queue eq 'Incidents';
+
+        $agent->display_ticket( $id);
+        $agent->content_like( qr/\Q$full/, "IP range on the page" );
+
+        my $ticket = RT::Ticket->new( $RT::SystemUser );
+        $ticket->Load( $id );
+        ok( $ticket->id, 'loaded ticket' );
+        my $values = $ticket->CustomFieldValues('IP');
+        my %has = map { $_->Content => 1 } @{ $values->ItemsArrayRef };
+        ok( $has{ $full }, "has value" )
+            or diag "but has values ". join ", ", keys %has;
+    }
+}
+
+diag "create a ticket via web with CIDR in message" if $ENV{'TEST_VERBOSE'};
+while ( my ($short, $full) = each %test_cidr ) {
+    my $incident_id; # block couldn't be created without incident id
+    foreach my $queue( 'Incidents', 'Incident Reports', 'Investigations', 'Blocks' ) {
+        diag "create a ticket in the '$queue' queue" if $ENV{'TEST_VERBOSE'};
+
+        my $id = $agent->create_rtir_ticket_ok(
+            $queue,
+            {
+                Subject => "test ip in message",
+                ($queue eq 'Blocks'? (Incident => $incident_id): ()),
+                Content => "$short",
+            },
+        );
+        $incident_id = $id if $queue eq 'Incidents';
+
+        $agent->display_ticket( $id);
+        $agent->content_like( qr/\Q$full/, "IP range on the page" );
+
+        my $ticket = RT::Ticket->new( $RT::SystemUser );
+        $ticket->Load( $id );
+        ok( $ticket->id, 'loaded ticket' );
+        my $values = $ticket->CustomFieldValues('IP');
+        my %has = map { $_->Content => 1 } @{ $values->ItemsArrayRef };
+        ok( $has{ $full }, "has value" )
+            or diag "but has values ". join ", ", keys %has;
+    }
+}
+
+diag "create a ticket and edit IP field using Edit page" if $ENV{'TEST_VERBOSE'};
+{
+    my $i = 0;
+    my $incident_id; # block couldn't be created without incident id
+    foreach my $queue( 'Incidents', 'Incident Reports', 'Investigations', 'Blocks' ) {
+        diag "create a ticket in the '$queue' queue" if $ENV{'TEST_VERBOSE'};
+
+        my $id = $agent->create_rtir_ticket_ok(
+            $queue,
+            {
+                Subject => "test ip in message",
+                ($queue eq 'Blocks'? (Incident => $incident_id): ()),
+            },
+        );
+        $incident_id = $id if $queue eq 'Incidents';
+        $agent->display_ticket( $id);
+
+        my $field_name = "Object-RT::Ticket-$id-CustomField-". $cf->id ."-Values";
+
+diag "set IP" if $ENV{'TEST_VERBOSE'};
+        my $val = 'abcd::192.168.1.1';
+        $agent->follow_link_ok({text => 'Edit', n => "1"}, "Followed 'Edit' link");
+        $agent->form_number(3);
+        like( $agent->value($field_name), qr/^\s*$/, 'IP is empty' );
+        $agent->field( $field_name => $val );
+        $agent->click('SaveChanges');
+
+        $agent->content_like( qr/\Q$valid{$val}/, "IP on the page" );
+
+        my $ticket = RT::Ticket->new( $RT::SystemUser );
+        $ticket->Load( $id );
+        ok( $ticket->id, 'loaded ticket' );
+        my $values = $ticket->CustomFieldValues('IP');
+        my %has = map { $_->Content => 1 } @{ $values->ItemsArrayRef };
+        is( scalar values %has, 1, "one IP were added");
+        ok( $has{ $valid{ $val } }, "has value" )
+            or diag "but has values ". join ", ", keys %has;
+
+diag "set IP with spaces around" if $ENV{'TEST_VERBOSE'};
+        $val = "  ::192.168.1.1  \n  ";
+        $agent->follow_link_ok({text => 'Edit', n => "1"}, "Followed 'Edit' link");
+        $agent->form_number(3);
+        like( $agent->value($field_name), qr/^\s*\Q$valid{'abcd::192.168.1.1'}\E\s*$/, 'IP is in input box' );
+        $agent->field( $field_name => $val );
+        $agent->click('SaveChanges');
+
+        $agent->content_like( qr/\Q$valid{'::192.168.1.1'}/, "IP on the page" );
+
+        $ticket = RT::Ticket->new( $RT::SystemUser );
+        $ticket->Load( $id );
+        ok( $ticket->id, 'loaded ticket' );
+        $values = $ticket->CustomFieldValues('IP');
+        %has = map { $_->Content => 1 } @{ $values->ItemsArrayRef };
+        is( scalar values %has, 1, "one IP were added");
+        ok( $has{ $valid{'::192.168.1.1'} }, "has value" )
+            or diag "but has values ". join ", ", keys %has;
+
+diag "replace IP with a range" if $ENV{'TEST_VERBOSE'};
+        $val = '::192.168.1.1/120';
+        $agent->follow_link_ok({text => 'Edit', n => "1"}, "Followed 'Edit' link");
+        $agent->form_number(3);
+        like( $agent->value($field_name), qr/^\s*\Q$valid{'::192.168.1.1'}\E\s*$/, 'IP is in input box' );
+        $agent->field( $field_name => $val );
+        $agent->click('SaveChanges');
+
+        $agent->content_like( qr/\Q$test_cidr{ $val }/, "IP on the page" );
+
+        $ticket = RT::Ticket->new( $RT::SystemUser );
+        $ticket->Load( $id );
+        ok( $ticket->id, 'loaded ticket' );
+        $values = $ticket->CustomFieldValues('IP');
+        %has = map { $_->Content => 1 } @{ $values->ItemsArrayRef };
+        is( scalar values %has, 1, "one IP were added");
+        ok( $has{ $test_cidr{ $val } }, "has value" )
+            or diag "but has values ". join ", ", keys %has;
+    }
+}
+
+#diag "check that we parse correct IPs only" if $ENV{'TEST_VERBOSE'};
+# XXX: waiting for regressions
+
+diag "check that IPs in messages don't add duplicates" if $ENV{'TEST_VERBOSE'};
+{
+    my $id = $agent->create_ir( {
+        Subject => "test ip",
+        Content => 'abcd::192.168.1.1 abcd::192.168.1.1 abcd::192.168.1.1/128'
+    } );
+    ok($id, "created first ticket");
+
+    my $ticket = RT::Ticket->new( $RT::SystemUser );
+    $ticket->Load( $id );
+    ok( $ticket->id, 'loaded ticket' );
+
+    my $values = $ticket->CustomFieldValues('IP');
+    my %has;
+    $has{ $_->Content }++ foreach @{ $values->ItemsArrayRef };
+    is(scalar values %has, 1, "one IP were added");
+    ok(!grep( $_ != 1, values %has ), "no duplicated values");
+    ok($has{ $valid{ 'abcd::192.168.1.1' } }, "IP is there")
+            or diag "but has values ". join ", ", keys %has;
+}
+
+diag "search tickets by IP" if $ENV{'TEST_VERBOSE'};
+{
+    my $id = $agent->create_ir( {
+        Subject => "test ip",
+        Content => '::192.168.1.1/120',
+    } );
+    ok($id, "created first ticket");
+
+    my $tickets = RT::Tickets->new( $rtir_user );
+    $tickets->FromSQL("id = $id AND CF.{IP} = '::192.168.1.1'");
+    ok( $tickets->Count, "found tickets" );
+
+    my $flag = 1;
+    while ( my $ticket = $tickets->Next ) {
+        my %has = map { $_->Content => 1 } @{ $ticket->CustomFieldValues('IP')->ItemsArrayRef };
+        next if $has{ $test_cidr{'::192.168.1.1/120'} };
+        $flag = 0;
+        ok(0, "ticket #". $ticket->id ." has no range ::192.168.1.1/120, but should")
+            or diag "but has values ". join ", ", keys %has;
+        last;
+    }
+    ok(1, "all tickets has IP ::192.168.1.1/120") if $flag;
+}
+
+diag "search tickets by IP range" if $ENV{'TEST_VERBOSE'};
+{
+    my $id = $agent->create_ir( {
+        Subject => "test ip",
+        Content => '::c0a8:01a0'
+    } );
+    ok($id, "created first ticket");
+
+    my $tickets = RT::Tickets->new( $rtir_user );
+    $tickets->FromSQL("id = $id AND CF.{IP} = '::c0a8:0101-::c0a8:01ff'");
+    ok( $tickets->Count, "found tickets" );
+
+    my $flag = 1;
+    while ( my $ticket = $tickets->Next ) {
+        my %has = map { $_->Content => 1 } @{ $ticket->CustomFieldValues('IP')->ItemsArrayRef };
+        next if grep /^0000(:0000){5}:c0a8:01/, keys %has;
+        $flag = 0;
+        ok(0, "ticket #". $ticket->id ." has no IP from '::c0a8::-::c0a8:01ff', but should");
+        last;
+    }
+    ok(1, "all tickets have at least one IP from the range") if $flag;
+}
+
+diag "create two tickets with different IPs and check several searches" if $ENV{'TEST_VERBOSE'};
+{
+    my $id1 = $agent->create_ir( { Subject => "test ip" }, { IP => '::c0a8:3310' } );
+    ok($id1, "created first ticket");
+    my $id2 = $agent->create_ir( { Subject => "test ip" }, { IP => '::c0a8:aa10' } );
+    ok($id2, "created second ticket");
+
+    my $tickets = RT::Tickets->new( $rtir_user );
+    $tickets->FromSQL("id = $id1 OR id = $id2");
+    is( $tickets->Count, 2, "found both tickets by 'id = x OR y'" );
+
+    # IP
+    $tickets = RT::Tickets->new( $rtir_user );
+    $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = '::c0a8:3310'");
+    is( $tickets->Count, 1, "found one ticket" );
+    is( $tickets->First->id, $id1, "correct value" );
+    $tickets = RT::Tickets->new( $rtir_user );
+    $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = '::c0a8:aa10'");
+    is( $tickets->Count, 1, "found one ticket" );
+    is( $tickets->First->id, $id2, "correct value" );
+
+    # IP/32 - one address
+    $tickets = RT::Tickets->new( $rtir_user );
+    $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = '::c0a8:3310/128'");
+    is( $tickets->Count, 1, "found one ticket" ) or diag $tickets->BuildSelectQuery;
+    is( $tickets->First->id, $id1, "correct value" );
+    $tickets = RT::Tickets->new( $rtir_user );
+    $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = '::c0a8:aa10/128'");
+    is( $tickets->Count, 1, "found one ticket" );
+    is( $tickets->First->id, $id2, "correct value" );
+
+    # IP range
+    $tickets = RT::Tickets->new( $rtir_user );
+    $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = '::c0a8:3300-::c0a8:33ff'");
+    is( $tickets->Count, 1, "found one ticket" );
+    is( $tickets->First->id, $id1, "correct value" );
+    $tickets = RT::Tickets->new( $rtir_user );
+    $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = '::c0a8:aa00-::c0a8:aaff'");
+    is( $tickets->Count, 1, "found one ticket" );
+    is( $tickets->First->id, $id2, "correct value" );
+
+    # IP range, with start IP greater than end
+    $tickets = RT::Tickets->new( $rtir_user );
+    $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = '::c0a8:33ff-::c0a8:3300'");
+    is( $tickets->Count, 1, "found one ticket" );
+    is( $tickets->First->id, $id1, "correct value" );
+    $tickets = RT::Tickets->new( $rtir_user );
+    $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = '::c0a8:aaff-::c0a8:aa00'");
+    is( $tickets->Count, 1, "found one ticket" );
+    is( $tickets->First->id, $id2, "correct value" );
+
+    # CIDR/120
+    $tickets = RT::Tickets->new( $rtir_user );
+    $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = '::c0a8:3300/120'");
+    is( $tickets->Count, 1, "found one ticket" );
+    is( $tickets->First->id, $id1, "correct value" );
+    $tickets = RT::Tickets->new( $rtir_user );
+    $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = '::c0a8:aa00/120'");
+    is( $tickets->Count, 1, "found one ticket" );
+    is( $tickets->First->id, $id2, "correct value" );
+
+    # IP is not in CIDR/120
+    $tickets = RT::Tickets->new( $rtir_user );
+    $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} != '::c0a8:3300/120'");
+    is( $tickets->Count, 1, "found one ticket" );
+    is( $tickets->First->id, $id2, "correct value" );
+    $tickets = RT::Tickets->new( $rtir_user );
+    $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} != '::c0a8:aa00/120'");
+    is( $tickets->Count, 1, "found one ticket" );
+    is( $tickets->First->id, $id1, "correct value" );
+
+    # CIDR or CIDR
+    $tickets = RT::Tickets->new( $rtir_user );
+    $tickets->FromSQL("(id = $id1 OR id = $id2) AND "
+        ."(CF.{IP} = '::c0a8:3300/120' OR CF.{IP} = '::c0a8:aa00/120')");
+    is( $tickets->Count, 2, "found both tickets" );
+
+    $tickets = RT::Tickets->new( $rtir_user );
+    $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = '::c0a8:0000/0'");
+    is( $tickets->Count, 2, "found both tickets" ) or diag $tickets->BuildSelectQuery;
+}
+
+diag "create two tickets with different IP ranges and check several searches" if $ENV{'TEST_VERBOSE'};
+{
+    my $id1 = $agent->create_ir( { Subject => "test ip" }, { IP => '::192.168.21.0-::192.168.21.127' } );
+    ok($id1, "created first ticket");
+    my $id2 = $agent->create_ir( { Subject => "test ip" }, { IP => '::192.168.21.128-::192.168.21.255' } );
+    ok($id2, "created second ticket");
+
+    my $tickets = RT::Tickets->new( $rtir_user );
+    $tickets->FromSQL("id = $id1 OR id = $id2");
+    is( $tickets->Count, 2, "found both tickets by 'id = x OR y'" );
+
+    # IP
+    $tickets = RT::Tickets->new( $rtir_user );
+    $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = '::192.168.21.0'");
+    is( $tickets->Count, 1, "found one ticket" );
+    is( $tickets->First->id, $id1, "correct value" );
+    $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = '::192.168.21.64'");
+    is( $tickets->Count, 1, "found one ticket" );
+    is( $tickets->First->id, $id1, "correct value" );
+    $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = '::192.168.21.127'");
+    is( $tickets->Count, 1, "found one ticket" );
+    is( $tickets->First->id, $id1, "correct value" );
+    $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = '::192.168.21.128'");
+    is( $tickets->Count, 1, "found one ticket" );
+    is( $tickets->First->id, $id2, "correct value" );
+    $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = '::192.168.21.191'");
+    is( $tickets->Count, 1, "found one ticket" );
+    is( $tickets->First->id, $id2, "correct value" );
+    $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = '::192.168.21.255'");
+    is( $tickets->Count, 1, "found one ticket" );
+    is( $tickets->First->id, $id2, "correct value" );
+
+    # IP/32 - one address
+    $tickets = RT::Tickets->new( $rtir_user );
+    $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = '::192.168.21.63/128'");
+    is( $tickets->Count, 1, "found one ticket" );
+    is( $tickets->First->id, $id1, "correct value" );
+    $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = '::192.168.21.191/128'");
+    is( $tickets->Count, 1, "found one ticket" );
+    is( $tickets->First->id, $id2, "correct value" );
+
+    # IP range, lower than both
+    $tickets = RT::Tickets->new( $rtir_user );
+    $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = '::192.168.20.0-::192.168.20.255'");
+    is( $tickets->Count, 0, "didn't finnd ticket" ) or diag "but found ". $tickets->First->id;
+
+    # IP range, intersect with the first range
+    $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = '::192.168.20.0-::192.168.21.63'");
+    is( $tickets->Count, 1, "found one ticket" );
+    is( $tickets->First->id, $id1, "correct value" );
+
+    # IP range, equal to the first range
+    $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = '::192.168.21.0-::192.168.21.127'");
+    is( $tickets->Count, 1, "found one ticket" );
+    is( $tickets->First->id, $id1, "correct value" );
+
+    # IP range, lay inside the first range
+    $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = '::192.168.21.31-::192.168.21.63'");
+    is( $tickets->Count, 1, "found one ticket" );
+    is( $tickets->First->id, $id1, "correct value" );
+
+    # IP range, intersect with the ranges
+    $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = '::192.168.21.31-::192.168.21.191'");
+    is( $tickets->Count, 2, "found both tickets" );
+
+    # IP range, equal to range from the starting IP of the first ticket to the ending IP of the second
+    $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = '::192.168.21.0-::192.168.21.255'");
+    is( $tickets->Count, 2, "found both tickets" );
+
+    # IP range, has the both ranges inside it
+    $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = '::192.168.0.0/112'");
+    is( $tickets->Count, 2, "found both tickets" );
+
+    # IP range, greater than both
+    $tickets = RT::Tickets->new( $rtir_user );
+    $tickets->FromSQL("(id = $id1 OR id = $id2) AND CF.{IP} = '::192.168.22.0/120'");
+    is( $tickets->Count, 0, "didn't finnd ticket" ) or diag "but found ". $tickets->First->id;
+}
+
+diag "merge ticket, IPs should be merged";
+{
+    my $incident_id = $agent->create_rtir_ticket_ok(
+        'Incidents',
+        { Subject => "test" },
+    );
+    my $b1_id = $agent->create_block(
+        {
+            Subject => "test ip",
+            Incident => $incident_id,
+        },
+        { IP => '::172.16.0.1' },
+    );
+    my $b2_id = $agent->create_block(
+        {
+            Subject => "test ip",
+            Incident => $incident_id,
+        },
+        { IP => '::172.16.0.2' },
+    );
+
+    $agent->display_ticket( $b1_id);
+    $agent->follow_link_ok({ text => 'Merge' }, "Followed merge link");
+    $agent->form_number(3);
+    $agent->field('SelectedTicket', $b2_id);
+    $agent->submit;
+    $agent->ok_and_content_like( qr{Merge Successful}, 'Merge Successful');
+
+    my $ticket = RT::Ticket->new( $RT::SystemUser );
+    $ticket->Load( $b1_id );
+    ok $ticket->id, 'loaded ticket';
+    my $values = $ticket->CustomFieldValues('IP');
+    my %has = map { $_->Content => 1 } @{ $values->ItemsArrayRef };
+    is( scalar values %has, 2, "both IPs are there");
+    ok( $has{ '0000:'x6 .'ac10:0001' }, "has value" )
+        or diag "but has values ". join ", ", keys %has;
+    ok( $has{ '0000:'x6 .'ac10:0002' }, "has value" )
+        or diag "but has values ". join ", ", keys %has;
+}
+
+diag "merge ticket with the same IP";
+{
+    my $incident_id = $agent->create_rtir_ticket_ok(
+        'Incidents',
+        { Subject => "test" },
+    );
+    my $b1_id = $agent->create_block(
+        {
+            Subject => "test ip",
+            Incident => $incident_id,
+        },
+        { IP => '::172.16.0.1' },
+    );
+    my $b2_id = $agent->create_block(
+        {
+            Subject => "test ip",
+            Incident => $incident_id,
+        },
+        { IP => '::172.16.0.1' },
+    );
+
+    $agent->display_ticket( $b1_id);
+    $agent->follow_link_ok({ text => 'Merge' }, "Followed merge link");
+    $agent->form_number(3);
+    $agent->field('SelectedTicket', $b2_id);
+    $agent->submit;
+    $agent->ok_and_content_like( qr{Merge Successful}, 'Merge Successful');
+
+    my $ticket = RT::Ticket->new( $RT::SystemUser );
+    $ticket->Load( $b1_id );
+    ok $ticket->id, 'loaded ticket';
+    my $values = $ticket->CustomFieldValues('IP');
+    my @has = map $_->Content, @{ $values->ItemsArrayRef };
+    is( scalar @has, 1, "only one IP") or diag "values: @has";
+    is( $has[0], '0000:'x6 .'ac10:0001', "has value" );
+}
+

commit d56f63a53ece8a70a62977027f82c9fb9bb91ab3
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Thu Apr 21 23:52:16 2011 +0400

    IPv6 in quick search and make clicky

diff --git a/html/Callbacks/RTIR/Elements/MakeClicky/Default b/html/Callbacks/RTIR/Elements/MakeClicky/Default
index 0adfe9e..f33d7ca 100644
--- a/html/Callbacks/RTIR/Elements/MakeClicky/Default
+++ b/html/Callbacks/RTIR/Elements/MakeClicky/Default
@@ -99,10 +99,12 @@ my %actions;
     },
 );
 
+use Regexp::IPv6 qw($IPv6_re);
+
 my @types = (
     {
         name   => "ip",
-        regex  => qr[(?<!\d)$RE{'net'}{'IPv4'}(?!\d)]o,
+        regex  => qr[(?<!\d)$RE{'net'}{'IPv4'}(?!\d)|(?<![0-9a-fA-F:])$IPv6_re(?![0-9a-fA-F:])]o,
         action => "ip",
     },
     {
diff --git a/html/RTIR/index.html b/html/RTIR/index.html
index b1c58c9..865921c 100644
--- a/html/RTIR/index.html
+++ b/html/RTIR/index.html
@@ -39,6 +39,7 @@ unless ( exists $session{'my_rtir_portlets'} ) {
 my $portlets = $session{'my_rtir_portlets'};
 
 use Regexp::Common qw(RE_net_IPv4);
+use Regexp::IPv6 qw($IPv6_re);
 
 if ( $ARGS{'q'} ) {
     my $query = $ARGS{'q'};
@@ -68,7 +69,7 @@ if ( $ARGS{'q'} ) {
         }
         $query ||= 'id = 0';
     }
-    elsif ( $query =~ /^\s*($RE{net}{IPv4})\s*$/o && RT::IR->CustomFields('IP') ) {
+    elsif ( $query =~ /^\s*($RE{net}{IPv4}|$IPv6_re)\s*$/o && RT::IR->CustomFields('IP') ) {
 
         $query = join ' OR ', map "Queue = '$_'", ('Incidents', 'Incident Reports', 'Investigations', 'Blocks');
         $query = "($query) AND 'CustomField.{IP}' = '$1'";

commit d7198e76bbf953dfe0cb2a0abc65e95046e8f3da
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Thu Apr 21 23:53:00 2011 +0400

    update M::I::RTx

diff --git a/inc/Module/Install/RTx.pm b/inc/Module/Install/RTx.pm
index f027f78..726b3fb 100644
--- a/inc/Module/Install/RTx.pm
+++ b/inc/Module/Install/RTx.pm
@@ -8,7 +8,7 @@ no warnings 'once';
 
 use Module::Install::Base;
 use base 'Module::Install::Base';
-our $VERSION = '0.27';
+our $VERSION = '0.28';
 
 use FindBin;
 use File::Glob     ();
@@ -44,8 +44,8 @@ sub RTx {
         local @INC = (
             $ENV{RTHOME} ? ( $ENV{RTHOME}, "$ENV{RTHOME}/lib" ) : (),
             @INC,
-            map { ( "$_/rt3/lib", "$_/lib/rt3", "$_/lib" ) } grep $_,
-            @prefixes
+            map { ( "$_/rt4/lib", "$_/lib/rt4", "$_/rt3/lib", "$_/lib/rt3", "$_/lib" ) 
+                } grep $_, @prefixes
         );
         until ( eval { require RT; $RT::LocalPath } ) {
             warn

-----------------------------------------------------------------------


More information about the Rt-commit mailing list