rt 4.0.6
[freeside.git] / rt / bin / rt.in
index aa3ac33..e54a07a 100644 (file)
@@ -1,41 +1,41 @@
 #!@PERL@ -w
 # BEGIN BPS TAGGED BLOCK {{{
-# 
+#
 # COPYRIGHT:
-# 
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-#                                          <jesse@bestpractical.com>
-# 
+#
+# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+#                                          <sales@bestpractical.com>
+#
 # (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
 # 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 }}}
 # Designed and implemented for Best Practical Solutions, LLC by
 # Abhijit Menon-Sen <ams@wiw.org>
 
 use strict;
 
+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.
 
@@ -98,7 +104,7 @@ my %config = (
     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};
@@ -179,7 +185,7 @@ exit handler();
 
 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*$/;
 
@@ -899,11 +905,6 @@ sub link {
     
     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;
@@ -984,7 +985,7 @@ sub grant {
 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?
@@ -1164,44 +1165,40 @@ sub submit {
     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 {
@@ -1347,7 +1344,7 @@ sub Form::compose {
                         $line .= ",\n$sp$v";
                     }
                     else {
-                        $line = $line ? "$line, $v" : "$key: $v";
+                        $line = $line ? "$line,$v" : "$key: $v";
                     }
                 }
 
@@ -1415,7 +1412,7 @@ sub config_from_file {
         }
 
         # Still nothing? We'll fall back to some likely defaults.
-        for ("$HOME/$rc", "/etc/rt.conf") {
+        for ("$HOME/$rc", "@LOCAL_ETC_PATH@/rt.conf", "/etc/rt.conf") {
             return parse_config_file($_) if (-r $_);
         }
     }
@@ -1429,19 +1426,19 @@ sub parse_config_file {
     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;
 }
@@ -1476,12 +1473,18 @@ sub vi {
     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);
+    open( my $handle, '>', $file ) or die "$file: $!\n";
+    print $handle $text;
+    close($handle);
+
     system($editor, $file) && die "Couldn't run $editor.\n";
-    open(F, $file) || die "$file: $!\n"; $text = <F>; close(F);
+
+    open( $handle, '<', $file ) or die "$file: $!\n";
+    $text = <$handle>;
+    close($handle);
+
     unlink($file);
 
     return $text;
@@ -1514,7 +1517,7 @@ sub vsplit {
         # XXX: This should become a real parser, à la Text::ParseWords.
         $line =~ s/^\s+//;
         $line =~ s/\s+$//;
-        my ( $a, $b ) = split /,/, $line, 2;
+        my ( $a, $b ) = split /\s*,\s*/, $line, 2;
 
         while ($a) {
             no warnings 'uninitialized';
@@ -1522,7 +1525,7 @@ sub vsplit {
                 my $s = $a;
                 while ( $a !~ /'$/ || (   $a !~ /(\\\\)+'$/
                             && $a =~ /(\\)+'$/ )) {
-                    ( $a, $b ) = split /,/, $b, 2;
+                    ( $a, $b ) = split /\s*,\s*/, $b, 2;
                     $s .= ',' . $a;
                 }
                 push @words, $s;
@@ -1531,7 +1534,7 @@ sub vsplit {
                 my $s = $a;
                 while ( $a !~ /}$/ ) {
                     ( $a, $b ) =
-                      split /,/, $b, 2;
+                      split /\s*,\s*/, $b, 2;
                     $s .= ',' . $a;
                 }
                 $s =~ s/^q{/'/;
@@ -1541,7 +1544,7 @@ sub vsplit {
             else {
                 push @words, $a;
             }
-            ( $a, $b ) = split /,/, $b, 2;
+            ( $a, $b ) = split /\s*,\s*/, $b, 2;
         }
 
 
@@ -1556,7 +1559,7 @@ sub expand_list {
     my ($list) = @_;
 
     my @elts;
-    foreach (split /,/, $list) {
+    foreach (split /\s*,\s*/, $list) {
         push @elts, /^(\d+)-(\d+)$/? ($1..$2): $_;
     }
 
@@ -1702,7 +1705,7 @@ sub prettyshow {
         }
         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};
     }
@@ -1751,10 +1754,7 @@ Title: intro
 Title: introduction
 Text:
 
-     ** THIS IS AN UNSUPPORTED PREVIEW RELEASE **
-     ** PLEASE REPORT BUGS TO rt-bugs@bestpractical.com **
-
-    This is a command-line interface to RT 3.0 or newer
+    This is a command-line interface to RT 3.0 or newer.
 
     It allows you to interact with an RT server over HTTP, and offers an
     interface to RT's functionality that is better-suited to automation
@@ -1829,7 +1829,8 @@ Text:
     The program looks for configuration directives in a file named .rtrc
     (or $RTCONFIG; see below) in the current directory, and then in more
     distant ancestors, until it reaches /. If no suitable configuration
-    files are found, it will also check for ~/.rtrc and /etc/rt.conf.
+    files are found, it will also check for ~/.rtrc, @LOCAL_ETC_PATH@/rt.conf
+    and /etc/rt.conf.
 
     Configuration directives:
 
@@ -2159,7 +2160,7 @@ Text:
     ("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.
@@ -2392,7 +2393,7 @@ 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?)
@@ -2587,3 +2588,24 @@ Text:
         $ 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.
+