%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2017 Best Practical Solutions, LLC %# %# %# (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 }}} %# REST/1.0/search/dhandler %# <%ARGS> $query $format => undef $orderby => undef $fields => undef <%INIT> my $type = $m->dhandler_arg; my ( $status, $output ); if ( $type =~ /^(ticket|queue|user|group)$/i ) { $status = "200 Ok"; $output = ''; my $type = lc $1; if ( $type eq 'user' && !$session{CurrentUser}->HasRight( Object => $RT::System, Right => 'AdminUsers', ) ) { $status = "403 Forbidden"; $output = "Permission denied"; goto OUTPUT; } my $class = 'RT::' . ucfirst $type . 's'; my $objects = $class->new( $session{CurrentUser} ); # Parse and validate any field specifications. require RT::Interface::REST; my $field = RT::Interface::REST->field_spec; my ( %fields, @fields ); if ($fields) { $format ||= "l"; unless ( $fields =~ /^(?:$field,)*$field$/ ) { $status = "400 Bad Request"; $output = "Invalid field specification: $fields"; goto OUTPUT; } @fields = map lc, split /\s*,\s*/, $fields; @fields{@fields} = (); unless ( exists $fields{id} ) { unshift @fields, "id"; $fields{id} = (); } } $format ||= "s"; if ( $format !~ /^[isl]$/ ) { $status = "400 Bad request"; $output = "Unknown listing format: $format. (Use i, s, or l.)\n"; goto OUTPUT; } my ( $n, $s ); $n = 0; my @output; if ( $type eq 'group' ) { $objects->LimitToUserDefinedGroups; } if ( defined $query && length $query ) { if ( $type eq 'ticket' ) { my ( $n, $s ); eval { ( $n, $s ) = $objects->FromSQL($query); }; if ( $@ || $n == 0 ) { $s ||= $@; $status = "400 Bad request"; $output = "Invalid query: '$s'.\n"; goto OUTPUT; } } else { require Text::ParseWords; my ( $field, $op, $value ) = Text::ParseWords::shellwords($query); if ( $op !~ /^(?:[!<>]?=|[<>]|(NOT )?LIKE|STARTSWITH|ENDSWITH|MATCHES)$/i ) { $status = "400 Bad Request"; $output = "Invalid operator specification: $op"; goto OUTPUT; } if ( ! $search_whitelist{$type}{lc $field} ) { $status = "400 Bad Request"; $output = "Invalid field specification: $field"; goto OUTPUT; } if ( $field && $op && defined $value ) { if ( $field eq 'Disabled' ) { if ($value) { if ( $type eq 'queue' ) { $objects->FindAllRows; $objects->Limit( FIELD => $field, OPERATOR => uc $op, VALUE => $value ); } else { $objects->LimitToDeleted; } } else { if ( $type eq 'queue' ) { $objects->UnLimit; } else { $objects->LimitToEnabled; } } } else { $objects->Limit( FIELD => $field, OPERATOR => uc $op, VALUE => $value, CASESENSITIVE => 0, ); } } else { $output = "Invalid query specification: $query"; goto OUTPUT; } } } else { if ( $type eq 'queue' ) { $objects->UnLimit; } elsif ( $type eq 'user' ) { $objects->LimitToPrivileged; } } if ($orderby) { my ( $order, $field ) = $orderby =~ /^([\+\-])?(.+)/; $order = $order && $order eq '-' ? 'DESC' : 'ASC'; $objects->OrderBy( FIELD => $field, ORDER => $order ); } while ( my $object = $objects->Next ) { next if $type eq 'user' && ( $object->id == RT->SystemUser->id || $object->id == RT->Nobody->id ); $n++; my $id = $object->Id; if ( $format eq "i" ) { $output .= "$type/" . $id . "\n"; } elsif ( $format eq "s" ) { if ($fields) { my $result = $m->comp( "/REST/1.0/Forms/$type/default", id => $id, format => $format, fields => \%fields ); my ( $notes, $order, $key_values, $errors ) = @$result; # If it's the first time through, add our header if ( $n == 1 ) { $output .= join( "\t", @$order ) . "\n"; } # Cut off the annoying $type/ before the id; $key_values->{'id'} = $id; $output .= join( "\t", map { ref $key_values->{$_} eq 'ARRAY' ? join( ', ', @{ $key_values->{$_} } ) : $key_values->{$_} } @$order ) . "\n"; } else { if ( $type eq 'ticket' ) { $output .= $object->Id . ": " . $object->Subject . "\n"; } else { $output .= $object->Id . ": " . $object->Name . "\n"; } } } else { my $d = $m->comp( "/REST/1.0/Forms/$type/default", id => $id, format => $format, fields => \%fields ); my ( $c, $o, $k, $e ) = @$d; push @output, [ $c, $o, $k ]; } } if ( $n == 0 && $format ne "i" ) { $output = "No matching results.\n"; } $output = form_compose( \@output ) if @output; } else { $status = "500 Server Error"; $output = "Unsupported object type."; goto OUTPUT; } OUTPUT: $m->out("RT/". $RT::VERSION . " " . $status ."\n\n"); $m->out($output ); <%ONCE> my %search_whitelist = ( queue => { map { lc $_ => 1 } grep { $RT::Record::_TABLE_ATTR->{'RT::Queue'}{$_}{read} } keys %{ $RT::Record::_TABLE_ATTR->{'RT::Queue'} } }, user => { disabled => 1, map { lc $_ => 1 } grep { $RT::Record::_TABLE_ATTR->{'RT::User'}{$_}{read} } keys %{ $RT::Record::_TABLE_ATTR->{'RT::User'} } }, group => { disabled => 1, map { lc $_ => 1 } grep { $RT::Record::_TABLE_ATTR->{'RT::Group'}{$_}{read} } keys %{ $RT::Record::_TABLE_ATTR->{'RT::Group'} } } );