#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2011 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
# Abhijit Menon-Sen <ams@wiw.org>
use strict;
+use warnings;
+
+if ( $ARGV[0] && $ARGV[0] =~ /^(?:--help|-h)$/ ) {
+ require Pod::Usage;
+ print Pod::Usage::pod2usage( { verbose => 2 } );
+ exit;
+}
# This program is intentionally written to have as few non-core module
# dependencies as possible. It should stay that way.
use HTTP::Headers;
use Term::ReadLine;
use Time::Local; # used in prettyshow
+use File::Temp;
# strong (GSSAPI based) authentication is supported if the server does provide
# it and the perl modules GSSAPI and LWP::Authen::Negotiate are installed
config_from_file($ENV{RTCONFIG} || ".rtrc"),
config_from_env()
);
-my $session = new Session("$HOME/.rt_sessions");
+my $session = Session->new("$HOME/.rt_sessions");
my $REST = "$config{server}/REST/1.0";
$no_strong_auth = 'switched off by externalauth=0'
if defined $config{externalauth};
# (XXX: Ask Autrijus how i18n changes these definitions.)
my $name = '[\w.-]+';
-my $CF_name = '[\sa-z0-9_ :()/-]+';
+my $CF_name = '[^,]+?';
my $field = '(?i:[a-z][a-z0-9_-]*|C(?:ustom)?F(?:ield)?-'.$CF_name.'|CF\.\{'.$CF_name.'\})';
-my $label = '[a-zA-Z0-9@_.+-]+';
+my $label = '[^,\\/]+';
my $labels = "(?:$label,)*$label";
my $idlist = '(?:(?:\d+-)?\d+,)*(?:\d+-)?\d+';
sub shell {
$|=1;
- my $term = new Term::ReadLine 'RT CLI';
+ my $term = Term::ReadLine->new('RT CLI');
while ( defined ($_ = $term->readline($prompt)) ) {
next if /^#/ || /^\s*$/;
}
elsif (my $spec = is_object_spec($_, $type)) {
push @objects, $spec;
- $rawprint = 1 if $_ =~ /\/content$/ or $_ !~ /^ticket/;
+ $rawprint = 1 if $_ =~ /\/content$/ or $_ =~ /\/links/ or $_ !~ /^ticket/;
}
else {
my $datum = /^-/ ? "option" : "argument";
if (@ARGV == 3) {
my ($from, $rel, $to) = @ARGV;
- if ($from !~ /^\d+$/ || $to !~ /^\d+$/) {
- my $bad = $from =~ /^\d+$/ ? $to : $from;
- whine "Invalid $type ID '$bad' specified.";
- $bad = 1;
- }
if (($type eq "ticket") && ( ! exists $ltypes{lc $rel})) {
whine "Invalid link '$rel' for type $type specified.";
$bad = 1;
sub grant {
my ($cmd) = @_;
- my $revoke = 0;
- while (@ARGV) {
- }
-
- $revoke = 1 if $cmd->{action} eq 'revoke';
- return 0;
+ whine "$cmd is unimplemented.";
+ return 1;
}
# Client <-> Server communication.
sub submit {
my ($uri, $content) = @_;
my ($req, $data);
- my $ua = new LWP::UserAgent(agent => "RT/3.0b", env_proxy => 1);
+ my $ua = LWP::UserAgent->new(agent => "RT/3.0b", env_proxy => 1);
my $h = HTTP::Headers->new;
# Did the caller specify any data to send with the request?
sub load {
my ($self, $file) = @_;
$file ||= $self->{file};
- local *F;
-
- open(F, $file) && do {
- $self->{file} = $file;
- my $sids = $self->{sids} = {};
- while (<F>) {
- chomp;
- next if /^$/ || /^#/;
- next unless m#^https?://[^ ]+ \w+ [^;,\s]+=[0-9A-Fa-f]+$#;
- my ($server, $user, $cookie) = split / /, $_;
- $sids->{$server}{$user} = $cookie;
- }
- return 1;
- };
- return 0;
+
+ open( my $handle, '<', $file ) or return 0;
+
+ $self->{file} = $file;
+ my $sids = $self->{sids} = {};
+ while (<$handle>) {
+ chomp;
+ next if /^$/ || /^#/;
+ next unless m#^https?://[^ ]+ \w+ [^;,\s]+=[0-9A-Fa-f]+$#;
+ my ($server, $user, $cookie) = split / /, $_;
+ $sids->{$server}{$user} = $cookie;
+ }
+ return 1;
}
# Writes the current session cache to the specified file.
sub save {
my ($self, $file) = shift;
$file ||= $self->{file};
- local *F;
-
- open(F, ">$file") && do {
- my $sids = $self->{sids};
- foreach my $server (keys %$sids) {
- foreach my $user (keys %{ $sids->{$server} }) {
- my $sid = $sids->{$server}{$user};
- if (defined $sid) {
- print F "$server $user $sid\n";
- }
+
+ open( my $handle, '>', "$file" ) or return 0;
+
+ my $sids = $self->{sids};
+ foreach my $server (keys %$sids) {
+ foreach my $user (keys %{ $sids->{$server} }) {
+ my $sid = $sids->{$server}{$user};
+ if (defined $sid) {
+ print $handle "$server $user $sid\n";
}
}
- close(F);
- chmod 0600, $file;
- return 1;
- };
- return 0;
+ }
+ close($handle);
+ chmod 0600, $file;
+ return 1;
}
sub DESTROY {
my ($file) = @_;
local $_; # $_ may be aliased to a constant, from line 1163
- open(CFG, $file) && do {
- while (<CFG>) {
- chomp;
- next if (/^#/ || /^\s*$/);
+ open( my $handle, '<', $file ) or return;
- if (/^(externalauth|user|passwd|server|query|orderby|queue)\s+(.*)\s?$/) {
- $cfg{$1} = $2;
- }
- else {
- die "rt: $file:$.: unknown configuration directive.\n";
- }
+ while (<$handle>) {
+ chomp;
+ next if (/^#/ || /^\s*$/);
+
+ if (/^(externalauth|user|passwd|server|query|orderby|queue)\s+(.*)\s?$/) {
+ $cfg{$1} = $2;
+ }
+ else {
+ die "rt: $file:$.: unknown configuration directive.\n";
}
- };
+ }
return %cfg;
}
sub vi {
my ($text) = @_;
- my $file = "/tmp/rt.form.$$";
my $editor = $ENV{EDITOR} || $ENV{VISUAL} || "vi";
- local *F;
local $/ = undef;
- open(F, ">$file") || die "$file: $!\n"; print F $text; close(F);
- system($editor, $file) && die "Couldn't run $editor.\n";
- open(F, $file) || die "$file: $!\n"; $text = <F>; close(F);
- unlink($file);
+ my $handle = File::Temp->new;
+ print $handle $text;
+ close($handle);
+
+ system($editor, $handle->filename) && die "Couldn't run $editor.\n";
+
+ open( $handle, '<', $handle->filename ) or die "$handle: $!\n";
+ $text = <$handle>;
+ close($handle);
return $text;
}
}
print "$k->{Content}\n" if exists $k->{Content} and
$k->{Content} !~ /to have no content$/ and
- $k->{Type} ne 'EmailRecord';
+ ($k->{Type}||'') ne 'EmailRecord';
print "$k->{Attachments}\n" if exists $k->{Attachments} and
$k->{Attachments};
}
ticket/1-3,5-7/history
user/ams
- user/ams/rights
- user/ams,rai,1/rights
For more information:
- edit
- create
- In addition, the following type-specific actions exist:
-
- - grant
- - revoke
-
- Attributes:
-
- The following attributes can be used with "rt show" or "rt edit"
- to retrieve or edit other information associated with users and
- groups:
-
- rights Global rights granted to this user.
- rights/<queue> Queue rights for this user.
-
--
Title: queue
("ls", "list", and "search" are synonyms.)
Conditions are expressed in the SQL-like syntax used internally by
- RT3. (For more information, see "rt help query".) The query string
+ RT. (For more information, see "rt help query".) The query string
must be supplied as one argument.
(Right now, the server doesn't support listing anything but tickets.
--
-Title: grant
-Title: revoke
-Text:
-
---
-
Title: query
Text:
- RT3 uses an SQL-like syntax to specify object selection constraints.
+ RT uses an SQL-like syntax to specify object selection constraints.
See the <RT:...> documentation for details.
(XXX: I'm going to have to write it, aren't I?)
$ rt shell
rt> quit
$
+
+__END__
+
+=head1 NAME
+
+rt - command-line interface to RT 3.0 or newer
+
+=head1 SYNOPSIS
+
+ rt help
+
+=head1 DESCRIPTION
+
+This script allows you to interact with an RT server over HTTP, and offers an
+interface to RT's functionality that is better-suited to automation and
+integration with other tools.
+
+In general, each invocation of this program should specify an action to
+perform on one or more objects, and any other arguments required to complete
+the desired action.
+