Commit be84ed48 authored by Jesse Vincent's avatar Jesse Vincent
Browse files

Initial port of RTIR's IP Address customfield types.

parent c59352e9
......@@ -54,6 +54,9 @@ no warnings qw(redefine);
use RT::CustomFieldValues;
use RT::ObjectCustomFields;
use RT::ObjectCustomFieldValues;
use Regexp::Common qw(RE_net_IPv4);
use Regexp::Common::net::CIDR;
require Net::CIDR;
our %FieldTypes = (
......@@ -87,6 +90,22 @@ our %FieldTypes = (
'Enter up to [_1] values', # loc
]
},
IPAddress => {
selection_type => 0,
labels => [ 'Enter multiple ip addresses', # loc
'Enter one ip address', # loc
'Enter up to [_1] ip addresses', # loc
]
},
IPAddressRange => {
selection_type => 0,
labels => [ 'Enter multiple IP address ranges', # loc
'Enter one IP address range', # loc
'Enter up to [_1] IP address ranges', # loc
]
},
Text => {
selection_type => 0,
labels => [
......@@ -1615,4 +1634,29 @@ sub BasedOnObj {
return $obj;
}
sub ParseIPRange {
my $self = shift;
my $arg = shift or return ();
if ( $arg =~ /^\s*$RE{net}{CIDR}{IPv4}{-keep}\s*$/go ) {
my $cidr = join( '.', map $_||0, (split /\./, $1)[0..3] ) ."/$2";
$arg = (Net::CIDR::cidr2range( $cidr ))[0] || $arg;
}
my ($sIP, $eIP);
if ( $arg =~ /^\s*($RE{net}{IPv4})\s*$/o ) {
$sIP = $eIP = sprintf "%03d.%03d.%03d.%03d", split /\./, $1;
}
elsif ( $arg =~ /^\s*($RE{net}{IPv4})-($RE{net}{IPv4})\s*$/o ) {
$sIP = sprintf "%03d.%03d.%03d.%03d", split /\./, $1;
$eIP = sprintf "%03d.%03d.%03d.%03d", split /\./, $2;
}
else {
return ();
}
($sIP, $eIP) = ($eIP, $sIP) if $sIP gt $eIP;
return $sIP, $eIP;
}
1;
......@@ -68,6 +68,17 @@ sub Create {
@_,
);
my $cf_as_sys = RT::CustomField->new(RT->SystemUser);
$cf_as_sys->Load($args{'CustomField'});
if($cf_as_sys->Type eq 'IPAddress' || $cf_as_sys->Type eq 'IPAddressRange') {
if ($args{'Content'}) {
($args{'Content'}, $args{'LargeContent'}) = $self->ParseIPRange( $args{'Content'} );
}
}
if ( defined $args{'Content'} && length( Encode::encode_utf8($args{'Content'}) ) > 255 ) {
if ( defined $args{'LargeContent'} && length $args{'LargeContent'} ) {
$RT::Logger->error("Content is longer than 255 bytes and LargeContent specified");
......@@ -105,6 +116,32 @@ sub LargeContent {
);
}
=head2 LoadByCols
=cut
sub LoadByCols {
my $self = shift;
my %args = (@_);
my $cf;
if ( $args{CustomField} ) {
$cf = RT::CustomField->new( $self->CurrentUser );
$cf->Load( $args{CustomField} );
if ( $cf->Type eq 'IPAddressRange' ) {
my ( $sIP, $eIP ) = $cf->ParseIPRange( $args{'Content'} );
if ( $sIP && $eIP ) {
$self->SUPER::LoadByCols( %args,
Content => $sIP,
LargeContent => $eIP
);
}
}
}
return $self->SUPER::LoadByCols(%args);
}
=head2 LoadByTicketContentAndCustomField { Ticket => TICKET, CustomField => CUSTOMFIELD, Content => CONTENT }
Loads a custom field value by Ticket, Content and which CustomField it's tied to
......@@ -172,9 +209,29 @@ content, try "LargeContent"
=cut
my $re_ip_sunit = qr/[0-1][0-9][0-9]|2[0-4][0-9]|25[0-5]/;
my $re_ip_serialized = qr/$re_ip_sunit(?:\.$re_ip_sunit){3}/;
sub Content {
my $self = shift;
my $content = $self->_Value('Content');
if ( $self->CustomFieldObj->Type eq 'IPAddress' ||
$self->CustomFieldObj->Type eq 'IPAddressRange') {
if ($content =~ /^\s*($re_ip_serialized)\s*$/o ) {
$content = sprintf "%d.%d.%d.%d", split /\./, $1;
}
my $large_content = $self->__Value('LargeContent');
if ( $large_content =~ /^\s*($re_ip_serialized)\s*$/o ) {
my $eIP = sprintf "%d.%d.%d.%d", split /\./, $1;
return $content . "-".$eIP if ($content && $eIP);
} else {
return $content;
}
}
if ( !(defined $content && length $content) && $self->ContentType && $self->ContentType eq 'text/plain' ) {
return $self->LargeContent;
} else {
......
......@@ -1349,6 +1349,10 @@ Meta Data:
=cut
use Regexp::Common qw(RE_net_IPv4);
use Regexp::Common::net::CIDR;
sub _CustomFieldLimit {
my ( $self, $_field, $op, $value, %rest ) = @_;
......@@ -1375,6 +1379,14 @@ sub _CustomFieldLimit {
return 'NOT MATCHES' if $op eq '!=';
return $op;
};
if ( $cf && ( $cf->Type eq 'IPAddress' || $cf->Type eq 'IPAddressRange')) {
return unless $op =~ /^\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
$op = (Net::CIDR::cidr2range( $cidr ))[0] || $op;
}
my $single_value = !$cf || !$cfid || $cf->SingleValue;
......@@ -1403,6 +1415,51 @@ sub _CustomFieldLimit {
) if $CFs;
$self->_CloseParen;
}
elsif ( $op!~ /^[<>]=?$/ && ( $cf && ($cf->Type eq 'IPAddress' || $cf->Type eq 'IPAddressRange'))) {
$value =~ /^\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;
($start_ip, $end_ip) = ($end_ip, $start_ip) if $start_ip gt $end_ip;
my ($self, $field, $op, $value, %rest) = @_[0..($#_-1)];
$self->_OpenParen;
if ( $op !~ /NOT|!=|<>/i ) { # positive equation
$self->_CustomFieldLimit(
$field, '<=', $end_ip, %rest,
SUBKEY => $rest{'SUBKEY'}. '.Content',
);
$self->_CustomFieldLimit(
$field, '>=', $start_ip, %rest,
SUBKEY => $rest{'SUBKEY'}. '.LargeContent',
ENTRYAGGREGATOR => 'AND',
);
# as well limit borders so DB optimizers can use better
# estimations and scan less rows
$self->_CustomFieldLimit(
$field, '>=', '000.000.000.000', %rest,
SUBKEY => $rest{'SUBKEY'}. '.Content',
ENTRYAGGREGATOR => 'AND',
);
$self->_CustomFieldLimit(
$field, '<=', '255.255.255.255', %rest,
SUBKEY => $rest{'SUBKEY'}. '.LargeContent',
ENTRYAGGREGATOR => 'AND',
);
}
else { # negative equation
$self->_CustomFieldLimit($field, '>', $end_ip, %rest);
$self->_CustomFieldLimit(
$field, '<', $start_ip, %rest,
SUBKEY => $rest{'SUBKEY'}. '.LargeContent',
ENTRYAGGREGATOR => 'OR',
);
# TODO: as well limit borders so DB optimizers can use better
# estimations and scan less rows, but it's harder to do
# as we have OR aggregator
}
}
elsif ( !$negative_op || $single_value ) {
$cfkey .= '.'. $self->{'_sql_multiple_cfs_index'}++ if !$single_value && !$range_op;
my ($TicketCFs, $CFs) = $self->_CustomFieldJoin( $cfkey, $cfid, $field );
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment