summaryrefslogtreecommitdiff
path: root/rt/bin
diff options
context:
space:
mode:
Diffstat (limited to 'rt/bin')
-rwxr-xr-xrt/bin/rt1
-rwxr-xr-xrt/bin/rt-crontool468
-rwxr-xr-xrt/bin/rt-mailgate526
-rw-r--r--rt/bin/rt.in1
-rw-r--r--rt/bin/webmux.pl205
5 files changed, 1201 insertions, 0 deletions
diff --git a/rt/bin/rt b/rt/bin/rt
index b87d50d2e..8c3a51421 100755
--- a/rt/bin/rt
+++ b/rt/bin/rt
@@ -322,6 +322,7 @@ sub list {
}
if ( ! $rawprint and ! exists $data{format} ) {
$data{format} = 'l';
+ $data{fields} = 'subject,status,queue,created,told,owner,requestors';
}
if ( $reverse_sort and $data{orderby} =~ /^-/ ) {
$data{orderby} =~ s/^-/+/;
diff --git a/rt/bin/rt-crontool b/rt/bin/rt-crontool
new file mode 100755
index 000000000..be189b5ce
--- /dev/null
+++ b/rt/bin/rt-crontool
@@ -0,0 +1,468 @@
+#!/usr/bin/perl
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2014 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
+# 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 }}}
+use strict;
+use warnings;
+use Carp;
+
+# fix lib paths, some may be relative
+BEGIN {
+ require File::Spec;
+ my @libs = ("/opt/rt3/lib", "/opt/rt3/local/lib");
+ my $bin_path;
+
+ for my $lib (@libs) {
+ unless ( File::Spec->file_name_is_absolute($lib) ) {
+ unless ($bin_path) {
+ if ( File::Spec->file_name_is_absolute(__FILE__) ) {
+ $bin_path = ( File::Spec->splitpath(__FILE__) )[1];
+ }
+ else {
+ require FindBin;
+ no warnings "once";
+ $bin_path = $FindBin::Bin;
+ }
+ }
+ $lib = File::Spec->catfile( $bin_path, File::Spec->updir, $lib );
+ }
+ unshift @INC, $lib;
+ }
+
+}
+
+use RT;
+
+use Getopt::Long;
+
+use RT::Interface::CLI qw(CleanEnv GetCurrentUser GetMessageContent loc);
+
+#Clean out all the nasties from the environment
+CleanEnv();
+
+my ( $search, $condition, $action, $search_arg, $condition_arg, $action_arg,
+ $template, $template_id, $transaction, $transaction_type, $help, $log, $verbose );
+GetOptions(
+ "search=s" => \$search,
+ "search-arg=s" => \$search_arg,
+ "condition=s" => \$condition,
+ "condition-arg=s" => \$condition_arg,
+ "action-arg=s" => \$action_arg,
+ "action=s" => \$action,
+ "template=s" => \$template,
+ "template-id=s" => \$template_id,
+ "transaction=s" => \$transaction,
+ "transaction-type=s" => \$transaction_type,
+ "log=s" => \$log,
+ "verbose|v" => \$verbose,
+ "help" => \$help,
+);
+
+# Load the config file
+RT::LoadConfig();
+
+# adjust logging to the screen according to options
+RT->Config->Set( LogToScreen => $log ) if $log;
+
+#Connect to the database and get RT::SystemUser and RT::Nobody loaded
+RT::Init();
+
+require RT::Tickets;
+require RT::Template;
+
+#Get the current user all loaded
+my $CurrentUser = GetCurrentUser();
+
+# show help even if there is no current user
+help() if $help;
+
+unless ( $CurrentUser->Id ) {
+ print loc("No RT user found. Please consult your RT administrator.");
+ exit(1);
+}
+
+help() unless $search && $action;
+
+$transaction = lc( $transaction||'' );
+if ( $transaction && $transaction !~ /^(first|all|last)$/i ) {
+ print STDERR loc("--transaction argument could be only 'first', 'last' or 'all'");
+ exit 1;
+}
+
+if ( $template && $template_id ) {
+ print STDERR loc("--template-id is deprecated argument and can not be used with --template");
+ exit 1;
+}
+elsif ( $template_id ) {
+# don't warn
+ $template = $template_id;
+}
+
+# We _must_ have a search object
+load_module($search);
+load_module($action) if ($action);
+load_module($condition) if ($condition);
+
+my $void_scrip = RT::Scrip->new( $CurrentUser );
+my $void_scrip_action = RT::ScripAction->new( $CurrentUser );
+
+#At the appointed time:
+
+#find a bunch of tickets
+my $tickets = RT::Tickets->new($CurrentUser);
+$search = $search->new(
+ TicketsObj => $tickets,
+ Argument => $search_arg,
+ CurrentUser => $CurrentUser
+);
+$search->Prepare();
+
+#for each ticket we've found
+while ( my $ticket = $tickets->Next() ) {
+ print $ticket->Id() . ":\n" if ($verbose);
+
+ my $template_obj = get_template( $ticket );
+
+ if ( $transaction ) {
+ my $txns = get_transactions($ticket);
+ my $found = 0;
+ while ( my $txn = $txns->Next ) {
+ print "\t".loc("Using transaction #[_1]...", $txn->id)."\n"
+ if $verbose;
+ process($ticket, $txn, $template_obj);
+ $found = 1;
+ }
+ print "\t".loc("Couldn't find suitable transaction, skipping")."\n"
+ if $verbose && !$found;
+ } else {
+ print "\t".loc("Processing without transaction, some conditions and actions may fail. Consider using --transaction argument")."\n"
+ if $verbose;
+
+ process($ticket, undef, $template_obj);
+ }
+}
+
+sub process {
+ my $ticket = shift;
+ my $transaction = shift;
+ my $template_obj = shift;
+
+ # perform some more advanced check
+ if ($condition) {
+ my $condition_obj = $condition->new(
+ TransactionObj => $transaction,
+ TicketObj => $ticket,
+ ScripObj => $void_scrip,
+ TemplateObj => $template_obj,
+ Argument => $condition_arg,
+ CurrentUser => $CurrentUser,
+ );
+
+ # if the condition doesn't apply, get out of here
+
+ return unless $condition_obj->IsApplicable;
+ print "\t".loc("Condition matches...")."\n" if $verbose;
+ }
+
+ #prepare our action
+ my $action_obj = $action->new(
+ TicketObj => $ticket,
+ TransactionObj => $transaction,
+ TemplateObj => $template_obj,
+ Argument => $action_arg,
+ ScripObj => $void_scrip,
+ ScripActionObj => $void_scrip_action,
+ CurrentUser => $CurrentUser,
+ );
+
+ #if our preparation, move onto the next ticket
+ return unless $action_obj->Prepare;
+ print "\t".loc("Action prepared...")."\n" if $verbose;
+
+ #commit our action.
+ return unless $action_obj->Commit;
+ print "\t".loc("Action committed.")."\n" if $verbose;
+}
+
+# =head2 get_transactions
+#
+# Takes ticket and returns L<RT::Transactions> object with transactions
+# of the ticket according to command line arguments C<--transaction>
+# and <--transaction-type>.
+#
+# =cut
+
+sub get_transactions {
+ my $ticket = shift;
+ my $txns = $ticket->Transactions;
+ my $order = $transaction eq 'last'? 'DESC': 'ASC';
+ $txns->OrderByCols(
+ { FIELD => 'Created', ORDER => $order },
+ { FIELD => 'id', ORDER => $order },
+ );
+ if ( $transaction_type ) {
+ $transaction_type =~ s/^\s+//;
+ $transaction_type =~ s/\s+$//;
+ foreach my $type ( split /\s*,\s*/, $transaction_type ) {
+ $txns->Limit( FIELD => 'Type', VALUE => $type, ENTRYAGGREGATOR => 'OR' );
+ }
+ }
+ $txns->RowsPerPage(1) unless $transaction eq 'all';
+ return $txns;
+}
+
+# =head2 get_template
+#
+# Takes a ticket and returns a template according to command line options.
+#
+# =cut
+
+{ my $cache = undef;
+sub get_template {
+ my $ticket = shift;
+ return undef unless $template;
+
+ unless ( $template =~ /\D/ ) {
+ # by id
+ return $cache if $cache;
+
+ my $cache = RT::Template->new( RT->SystemUser );
+ $cache->Load( $template );
+ die "Failed to load template '$template'"
+ unless $cache->id;
+ return $cache;
+ }
+
+ my $queue = $ticket->Queue;
+ return $cache->{ $queue } if $cache->{ $queue };
+
+ my $res = RT::Template->new( RT->SystemUser );
+ $res->LoadQueueTemplate( Queue => $queue, Name => $template );
+ unless ( $res->id ) {
+ $res->LoadGlobalTemplate( $template );
+ die "Failed to load template '$template', either for queue #$queue or global"
+ unless $res->id;
+ }
+ return $cache->{ $queue } = $res;
+} }
+
+
+# =head2 load_module
+#
+# Loads a perl module, dying nicely if it can't find it.
+#
+# =cut
+
+sub load_module {
+ my $modname = shift;
+ eval "require $modname";
+ if ($@) {
+ die loc( "Failed to load module [_1]. ([_2])", $modname, $@ );
+ }
+
+}
+
+
+sub help {
+
+ print loc( "[_1] is a tool to act on tickets from an external scheduling tool, such as cron.", $0 )
+ . "\n";
+ print loc("It takes several arguments:") . "\n\n";
+
+ print " "
+ . loc( "[_1] - Specify the search module you want to use", "--search" )
+ . "\n";
+ print " "
+ . loc( "[_1] - An argument to pass to [_2]", "--search-arg", "--search" )
+ . "\n";
+
+ print " "
+ . loc( "[_1] - Specify the condition module you want to use", "--condition" )
+ . "\n";
+ print " "
+ . loc( "[_1] - An argument to pass to [_2]", "--condition-arg", "--condition" )
+ . "\n";
+ print " "
+ . loc( "[_1] - Specify the action module you want to use", "--action" )
+ . "\n";
+ print " "
+ . loc( "[_1] - An argument to pass to [_2]", "--action-arg", "--action" )
+ . "\n";
+ print " "
+ . loc( "[_1] - Specify name or id of template(s) you want to use", "--template" )
+ . "\n";
+ print " "
+ . loc( "[_1] - Specify if you want to use either 'first', 'last' or 'all' transactions", "--transaction" )
+ . "\n";
+ print " "
+ . loc( "[_1] - Specify the comma separated list of transactions' types you want to use", "--transaction-type" )
+ . "\n";
+ print " "
+ . loc( "[_1] - Adjust LogToScreen config option", "--log" ) . "\n";
+ print " "
+ . loc( "[_1] - Output status updates to STDOUT", "--verbose" ) . "\n";
+ print "\n";
+ print "\n";
+ print loc("Security:")."\n";
+ print loc("This tool allows the user to run arbitrary perl modules from within RT.")." ".
+ loc("If this tool were setgid, a hostile local user could use this tool to gain administrative access to RT.")." ".
+ loc("It is incredibly important that nonprivileged users not be allowed to run this tool."). " " .
+ loc("It is suggested that you create a non-privileged unix user with the correct group membership and RT access to run this tool.")."\n";
+ print "\n";
+ print loc("Example:");
+ print "\n";
+ print " "
+ . loc( "The following command will find all active tickets in the queue 'general' and set their priority to 99 if they are overdue:"
+ )
+ . "\n\n";
+
+ print " bin/rt-crontool \\\n";
+ print " --search RT::Search::ActiveTicketsInQueue --search-arg general \\\n";
+ print " --condition RT::Condition::Overdue \\\n";
+ print " --action RT::Action::SetPriority --action-arg 99 \\\n";
+ print " --verbose\n";
+
+ print "\n";
+ print loc("Escalate tickets"). "\n";
+ print " bin/rt-crontool \\\n";
+ print " --search RT::Search::ActiveTicketsInQueue --search-arg general \\\n";
+ print" --action RT::Action::EscalatePriority\n";
+
+
+
+
+
+
+ exit(0);
+}
+
+__END__
+
+=head1 NAME
+
+rt-crontool - a tool to act on tickets from an external scheduling tool
+
+=head1 SYNOPSIS
+
+ # find all active tickets in the queue 'general' and set their priority to 99 if they are overdue:
+ rt-crontool \
+ --search RT::Search::ActiveTicketsInQueue --search-arg general \
+ --condition RT::Condition::Overdue \
+ --action RT::Action::SetPriority --action-arg 99 \
+ --verbose
+
+ # Escalate tickets
+ rt-crontool \
+ --search RT::Search::ActiveTicketsInQueue --search-arg general \
+ --action RT::Action::EscalatePriority
+
+=head1 DESCRIPTION
+
+This script is a tool to act on tickets from an external scheduling tool, such
+as cron.
+
+Security:
+
+This tool allows the user to run arbitrary perl modules from within RT. If
+this tool were setgid, a hostile local user could use this tool to gain
+administrative access to RT. It is incredibly important that nonprivileged
+users not be allowed to run this tool. It is suggested that you create a
+non-privileged unix user with the correct group membership and RT access to
+run this tool.
+
+
+=head1 OPTIONS
+
+=over
+
+=item search
+
+Specify the search module you want to use
+
+=item search-arg
+
+An argument to pass to --search
+
+=item condition
+
+Specify the condition module you want to use
+
+=item condition-arg
+
+An argument to pass to --condition
+
+=item action
+
+Specify the action module you want to use
+
+=item action-arg
+
+An argument to pass to --action
+
+=item template
+
+Specify name or id of template(s) you want to use
+
+=item transaction
+
+Specify if you want to use either 'first', 'last' or 'all' transactions
+
+
+=item transaction-type
+
+Specify the comma separated list of transactions' types you want to use
+
+=item log
+
+Adjust LogToScreen config option
+
+=item verbose
+
+Output status updates to STDOUT
+
+=back
+
diff --git a/rt/bin/rt-mailgate b/rt/bin/rt-mailgate
new file mode 100755
index 000000000..5148aa541
--- /dev/null
+++ b/rt/bin/rt-mailgate
@@ -0,0 +1,526 @@
+#!/usr/bin/perl
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2014 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
+# 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 }}}
+=head1 NAME
+
+rt-mailgate - Mail interface to RT.
+
+=cut
+
+use strict;
+use warnings;
+
+use Getopt::Long;
+
+my $opts = { };
+GetOptions( $opts, "queue=s", "action=s", "url=s",
+ "jar=s", "help", "debug", "extension=s",
+ "timeout=i", "verify-ssl!", "ca-file=s",
+ );
+
+my $gateway = RT::Client::MailGateway->new();
+
+$gateway->run($opts);
+
+package RT::Client::MailGateway;
+
+use LWP::UserAgent;
+use HTTP::Request::Common qw($DYNAMIC_FILE_UPLOAD);
+use File::Temp qw(tempfile tempdir);
+$DYNAMIC_FILE_UPLOAD = 1;
+
+use constant EX_TEMPFAIL => 75;
+use constant BUFFER_SIZE => 8192;
+
+sub new {
+ my $class = shift;
+ my $self = bless {}, $class;
+ return $self;
+}
+
+sub run {
+ my $self = shift;
+ my $opts = shift;
+
+ if ( $opts->{running_in_test_harness} ) {
+ $self->{running_in_test_harness} = 1;
+ }
+
+ $self->validate_cli_flags($opts);
+
+ my $ua = $self->get_useragent($opts);
+ my $post_params = $self->setup_session($opts);
+ $self->upload_message( $ua => $post_params );
+ $self->exit_with_success();
+}
+
+sub exit_with_success {
+ my $self = shift;
+ if ( $self->{running_in_test_harness} ) {
+ return 1;
+ } else {
+ exit 0;
+ }
+}
+
+sub tempfail {
+ my $self = shift;
+ if ( $self->{running_in_test_harness} ) {
+ die "tempfail";
+ } else {
+
+ exit EX_TEMPFAIL;
+ }
+}
+
+sub permfail {
+ my $self = shift;
+ if ( $self->{running_in_test_harness} ) {
+ die "permfail";
+ } else {
+
+ exit 1;
+ }
+}
+
+sub validate_cli_flags {
+ my $self = shift;
+ my $opts = shift;
+ if ( $opts->{'help'} ) {
+ require Pod::Usage;
+ Pod::Usage::pod2usage( { verbose => 2 } );
+ return $self->permfail()
+ ; # Don't want to succeed if this is really an email!
+ }
+
+ unless ( $opts->{'url'} ) {
+ print STDERR
+ "$0 invoked improperly\n\nNo 'url' provided to mail gateway!\n";
+ return $self->permfail();
+ }
+
+ if (($opts->{'ca-file'} or $opts->{"verify-ssl"})
+ and not LWP::UserAgent->can("ssl_opts")) {
+ print STDERR "Verifying SSL certificates requires LWP::UserAgent 6.0 or higher.\n";
+ return $self->tempfail();
+ }
+
+ $opts->{"verify-ssl"} = 1 unless defined $opts->{"verify-ssl"};
+}
+
+sub get_useragent {
+ my $self = shift;
+ my $opts = shift;
+ my $ua = LWP::UserAgent->new();
+ $ua->cookie_jar( { file => $opts->{'jar'} } ) if $opts->{'jar'};
+
+ if ( $ua->can("ssl_opts") ) {
+ $ua->ssl_opts( verify_hostname => $opts->{'verify-ssl'} );
+ $ua->ssl_opts( SSL_ca_file => $opts->{'ca-file'} )
+ if $opts->{'ca-file'};
+ }
+
+ return $ua;
+}
+
+sub setup_session {
+ my $self = shift;
+ my $opts = shift;
+ my %post_params;
+ foreach (qw(queue action)) {
+ $post_params{$_} = $opts->{$_} if defined $opts->{$_};
+ }
+
+ if ( ( $opts->{'extension'} || '' ) =~ /^(?:action|queue|ticket)$/i ) {
+ $post_params{ lc $opts->{'extension'} } = $ENV{'EXTENSION'}
+ || $opts->{ $opts->{'extension'} };
+ } elsif ( $opts->{'extension'} && $ENV{'EXTENSION'} ) {
+ print STDERR
+ "Value of the --extension argument is not action, queue or ticket"
+ . ", but environment variable EXTENSION is also defined. The former is ignored.\n";
+ }
+
+ # add ENV{'EXTENSION'} as X-RT-MailExtension to the message header
+ if ( my $value = ( $ENV{'EXTENSION'} || $opts->{'extension'} ) ) {
+
+ # prepare value to avoid MIME format breakage
+ # strip trailing newline symbols
+ $value =~ s/(\r*\n)+$//;
+
+ # make a correct multiline header field,
+ # with tabs in the beginning of each line
+ $value =~ s/(\r*\n)/$1\t/g;
+ $opts->{'headers'} .= "X-RT-Mail-Extension: $value\n";
+ }
+
+ # Read the message in from STDIN
+ # _raw_message is used for testing
+ my $message = $opts->{'_raw_message'} || $self->slurp_message();
+ unless ( $message->{'filename'} ) {
+ $post_params{'message'} = [
+ undef, '',
+ 'Content-Type' => 'application/octet-stream',
+ Content => ${ $message->{'content'} },
+ ];
+ } else {
+ $post_params{'message'} = [
+ $message->{'filename'}, '',
+ 'Content-Type' => 'application/octet-stream',
+ ];
+ }
+
+ return \%post_params;
+}
+
+sub upload_message {
+ my $self = shift;
+ my $ua = shift;
+ my $post_params = shift;
+ my $full_url = $opts->{'url'} . "/REST/1.0/NoAuth/mail-gateway";
+ print STDERR "$0: connecting to $full_url\n" if $opts->{'debug'};
+
+ $ua->timeout( exists( $opts->{'timeout'} ) ? $opts->{'timeout'} : 180 );
+ my $r = $ua->post( $full_url, $post_params, Content_Type => 'form-data' );
+ $self->check_failure($r);
+
+ my $content = $r->content;
+ print STDERR $content . "\n" if $opts->{'debug'};
+
+ return if ( $content =~ /^(ok|not ok)/ );
+
+ # It's not the server's fault if the mail is bogus. We just want to know that
+ # *something* came out of the server.
+ print STDERR <<EOF;
+RT server error.
+
+The RT server which handled your email did not behave as expected. It
+said:
+
+$content
+EOF
+
+ return $self->tempfail();
+}
+
+sub check_failure {
+ my $self = shift;
+ my $r = shift;
+ return if $r->is_success;
+
+ # XXX TODO 4.2: Remove the multi-line error strings in favor of something more concise
+ print STDERR <<" ERROR";
+An Error Occurred
+=================
+
+@{[ $r->status_line ]}
+ ERROR
+ print STDERR "\n$0: undefined server error\n" if $opts->{'debug'};
+ return $self->tempfail();
+}
+
+sub slurp_message {
+ my $self = shift;
+
+ local $@;
+
+ my %message;
+ my ( $fh, $filename )
+ = eval { tempfile( DIR => tempdir( CLEANUP => 1 ) ) };
+ if ( !$fh || $@ ) {
+ print STDERR "$0: Couldn't create temp file, using memory\n";
+ print STDERR "error: $@\n" if $@;
+
+ my $message = \do { local ( @ARGV, $/ ); <STDIN> };
+ unless ( $$message =~ /\S/ ) {
+ print STDERR "$0: no message passed on STDIN\n";
+ $self->exit_with_success;
+ }
+ $$message = $opts->{'headers'} . $$message if $opts->{'headers'};
+ return ( { content => $message } );
+ }
+
+ binmode $fh;
+ binmode \*STDIN;
+
+ print $fh $opts->{'headers'} if $opts->{'headers'};
+
+ my $buf;
+ my $empty = 1;
+ while (1) {
+ my $status = read \*STDIN, $buf, BUFFER_SIZE;
+ unless ( defined $status ) {
+ print STDERR "$0: couldn't read message: $!\n";
+ return $self->tempfail();
+ } elsif ( !$status ) {
+ last;
+ }
+ $empty = 0 if $buf =~ /\S/;
+ print $fh $buf;
+ }
+ close $fh;
+
+ if ($empty) {
+ print STDERR "$0: no message passed on STDIN\n";
+ $self->exit_with_success;
+ }
+ print STDERR "$0: temp file is '$filename'\n" if $opts->{'debug'};
+ return ( { filename => $filename } );
+}
+
+=head1 SYNOPSIS
+
+ rt-mailgate --help : this text
+
+Usual invocation (from MTA):
+
+ rt-mailgate --action (correspond|comment|...) --queue queuename
+ --url http://your.rt.server/
+ [ --debug ]
+ [ --extension (queue|action|ticket) ]
+ [ --timeout seconds ]
+
+
+
+=head1 OPTIONS
+
+=over 3
+
+=item C<--action>
+
+Specifies what happens to email sent to this alias. The avaliable
+basic actions are: C<correspond>, C<comment>.
+
+
+If you've set the RT configuration variable B<< C<UnsafeEmailCommands> >>,
+C<take> and C<resolve> are also available. You can execute two or more
+actions on a single message using a C<-> separated list. RT will execute
+the actions in the listed order. For example you can use C<take-comment>,
+C<correspond-resolve> or C<take-comment-resolve> as actions.
+
+Note that C<take> and C<resolve> actions ignore message text if used
+alone. Include a C<comment> or C<correspond> action if you want RT
+to record the incoming message.
+
+The default action is C<correspond>.
+
+=item C<--queue>
+
+This flag determines which queue this alias should create a ticket in if no ticket identifier
+is found.
+
+=item C<--url>
+
+This flag tells the mail gateway where it can find your RT server. You should
+probably use the same URL that users use to log into RT.
+
+If your RT server uses SSL, you will need to install additional Perl
+libraries. RT will detect and install these dependencies if you pass the
+C<--enable-ssl-mailgate> flag to configure as documented in RT's README.
+
+If you have a self-signed SSL certificate, you may also need to pass
+C<--ca-file> or C<--no-verify-ssl>, below.
+
+=item C<--ca-file> I<path>
+
+Specifies the path to the public SSL certificate for the certificate
+authority that should be used to verify the website's SSL certificate.
+If your webserver uses a self-signed certificate, you should
+preferentially use this option over C<--no-verify-ssl>, as it will
+ensure that the self-signed certificate that the mailgate is seeing the
+I<right> self-signed certificate.
+
+=item C<--no-verify-ssl>
+
+This flag tells the mail gateway to trust all SSL certificates,
+regardless of if their hostname matches the certificate, and regardless
+of CA. This is required if you have a self-signed certificate, or some
+other certificate which is not traceable back to an certificate your
+system ultimitely trusts.
+
+Verifying SSL certificates requires L<LWP::UserAgent> version 6.0 or
+higher; explicitly passing C<--verify-ssl> on prior versions will error.
+
+=item C<--extension> OPTIONAL
+
+Some MTAs will route mail sent to user-foo@host or user+foo@host to user@host
+and present "foo" in the environment variable $EXTENSION. By specifying
+the value "queue" for this parameter, the queue this message should be
+submitted to will be set to the value of $EXTENSION. By specifying
+"ticket", $EXTENSION will be interpreted as the id of the ticket this message
+is related to. "action" will allow the user to specify either "comment" or
+"correspond" in the address extension.
+
+=item C<--debug> OPTIONAL
+
+Print debugging output to standard error
+
+
+=item C<--timeout> OPTIONAL
+
+Configure the timeout for posting the message to the web server. The
+default timeout is 3 minutes (180 seconds).
+
+=back
+
+
+=head1 DESCRIPTION
+
+The RT mail gateway is the primary mechanism for communicating with RT
+via email. This program simply directs the email to the RT web server,
+which handles filing correspondence and sending out any required mail.
+It is designed to be run as part of the mail delivery process, either
+called directly by the MTA or C<procmail>, or in a F<.forward> or
+equivalent.
+
+=head1 SETUP
+
+Much of the set up of the mail gateway depends on your MTA and mail
+routing configuration. However, you will need first of all to create an
+RT user for the mail gateway and assign it a password; this helps to
+ensure that mail coming into the web server did originate from the
+gateway.
+
+Next, you need to route mail to C<rt-mailgate> for the queues you're
+monitoring. For instance, if you're using F</etc/aliases> and you have a
+"bugs" queue, you will want something like this:
+
+ bugs: "|/opt/rt4/bin/rt-mailgate --queue bugs --action correspond
+ --url http://rt.mycorp.com/"
+
+ bugs-comment: "|/opt/rt4/bin/rt-mailgate --queue bugs --action comment
+ --url http://rt.mycorp.com/"
+
+Note that you don't have to run your RT server on your mail server, as
+the mail gateway will happily relay to a different machine.
+
+=head1 CUSTOMIZATION
+
+By default, the mail gateway will accept mail from anyone. However,
+there are situations in which you will want to authenticate users
+before allowing them to communicate with the system. You can do this
+via a plug-in mechanism in the RT configuration.
+
+You can set the array C<@MailPlugins> to be a list of plugins. The
+default plugin, if this is not given, is C<Auth::MailFrom> - that is,
+authentication of the person is done based on the C<From> header of the
+email. If you have additional filters or authentication mechanisms, you
+can list them here and they will be called in order:
+
+ Set( @MailPlugins =>
+ "Filter::SpamAssassin",
+ "Auth::LDAP",
+ # ...
+ );
+
+See the documentation for any additional plugins you have.
+
+You may also put Perl subroutines into the C<@MailPlugins> array, if
+they behave as described below.
+
+=head1 WRITING PLUGINS
+
+What's actually going on in the above is that C<@MailPlugins> is a
+list of Perl modules; RT prepends C<RT::Interface::Email::> to the name,
+to form a package name, and then C<use>'s this module. The module is
+expected to provide a C<GetCurrentUser> subroutine, which takes a hash of
+several parameters:
+
+=over 4
+
+=item Message
+
+A C<MIME::Entity> object representing the email
+
+=item CurrentUser
+
+An C<RT::CurrentUser> object
+
+=item AuthStat
+
+The authentication level returned from the previous plugin.
+
+=item Ticket [OPTIONAL]
+
+The ticket under discussion
+
+=item Queue [OPTIONAL]
+
+If we don't already have a ticket id, we need to know which queue we're talking about
+
+=item Action
+
+The action being performed. At the moment, it's one of "comment" or "correspond"
+
+=back
+
+It returns two values, the new C<RT::CurrentUser> object, and the new
+authentication level. The authentication level can be zero, not allowed
+to communicate with RT at all, (a "permission denied" error is mailed to
+the correspondent) or one, which is the normal mode of operation.
+Additionally, if C<-1> is returned, then the processing of the plug-ins
+stops immediately and the message is ignored.
+
+=head1 ENVIRONMENT
+
+=over 4
+
+=item EXTENSION
+
+Some MTAs will route mail sent to user-foo@host or user+foo@host to user@host
+and present "foo" in the environment variable C<EXTENSION>. Mailgate adds value
+of this variable to message in the C<X-RT-Mail-Extension> field of the message
+header.
+
+See also C<--extension> option. Note that value of the environment variable is
+always added to the message header when it's not empty even if C<--extension>
+option is not provided.
+
+=back
+
+=cut
+
diff --git a/rt/bin/rt.in b/rt/bin/rt.in
index 480f178b4..83c38acf6 100644
--- a/rt/bin/rt.in
+++ b/rt/bin/rt.in
@@ -322,6 +322,7 @@ sub list {
}
if ( ! $rawprint and ! exists $data{format} ) {
$data{format} = 'l';
+ $data{fields} = 'subject,status,queue,created,told,owner,requestors';
}
if ( $reverse_sort and $data{orderby} =~ /^-/ ) {
$data{orderby} =~ s/^-/+/;
diff --git a/rt/bin/webmux.pl b/rt/bin/webmux.pl
new file mode 100644
index 000000000..8ce68ca14
--- /dev/null
+++ b/rt/bin/webmux.pl
@@ -0,0 +1,205 @@
+#!/usr/bin/perl
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2011 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
+# 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 }}}
+use strict;
+local $ENV{'PATH'} = '/bin:/usr/bin'; # or whatever you need
+local $ENV{'CDPATH'} = '' if defined $ENV{'CDPATH'};
+local $ENV{'SHELL'} = '/bin/sh' if defined $ENV{'SHELL'};
+local $ENV{'ENV'} = '' if defined $ENV{'ENV'};
+local $ENV{'IFS'} = '' if defined $ENV{'IFS'};
+
+package HTML::Mason::Commands;
+our %session;
+
+package RT::Mason;
+
+our ($Nobody, $SystemUser, $Handler, $r);
+
+my $protect_fd;
+
+sub handler {
+ ($r) = @_;
+
+ if ( !$protect_fd && $ENV{'MOD_PERL'} && exists $ENV{'MOD_PERL_API_VERSION'}
+ && $ENV{'MOD_PERL_API_VERSION'} >= 2 && fileno(STDOUT) != 1
+ ) {
+ # under mod_perl2, STDOUT gets closed and re-opened, however new STDOUT
+ # is not on FD #1. In this case next IO operation will occupy this FD
+ # and make all system() and open "|-" dangerouse, for example DBI
+ # can get this FD for DB connection and system() call will close
+ # by putting grabage into the socket
+ open( $protect_fd, '>', '/dev/null' )
+ or die "Couldn't open /dev/null: $!";
+ unless ( fileno($protect_fd) == 1 ) {
+ warn "We opened /dev/null to protect FD #1, but descriptor #1 is already occupied";
+ }
+ }
+
+ local $SIG{__WARN__};
+ local $SIG{__DIE__};
+ RT::InitSignalHandlers();
+
+ if ($r->content_type =~ m/^httpd\b.*\bdirectory/i) {
+ use File::Spec::Unix;
+ # Our DirectoryIndex is always index.html, regardless of httpd settings
+ $r->filename( File::Spec::Unix->catfile( $r->filename, 'index.html' ) );
+ }
+
+ Module::Refresh->refresh if RT->Config->Get('DevelMode');
+
+ RT::ConnectToDatabase();
+
+ # none of the methods in $r gives us the information we want (most
+ # canonicalize /foo/../bar to /bar which is exactly what we want to avoid)
+ my (undef, $requested) = split ' ', $r->the_request, 3;
+ my $uri = URI->new("http://".$r->hostname.$requested);
+ my $path = URI::Escape::uri_unescape($uri->path);
+
+ ## Each environment has its own way of handling .. and so on in paths,
+ ## so RT consistently forbids such paths.
+ if ( $path =~ m{/\.} ) {
+ $RT::Logger->crit("Invalid request for ".$path." aborting");
+ RT::Interface::Web::Handler->CleanupRequest();
+ return 400;
+ }
+
+ my (%session, $status);
+ {
+ local $@;
+ $status = eval { $Handler->handle_request($r) };
+ $RT::Logger->crit( $@ ) if $@;
+ }
+ undef %session;
+
+ RT::Interface::Web::Handler->CleanupRequest();
+
+ return $status;
+}
+
+package main;
+
+# check mod_perl version if it's mod_perl
+BEGIN {
+ die "RT does not support mod_perl 1.99. Please upgrade to mod_perl 2.0"
+ if $ENV{'MOD_PERL'}
+ and $ENV{'MOD_PERL'} =~ m{mod_perl/(?:1\.9)};
+}
+
+require CGI;
+CGI->import(qw(-private_tempfiles));
+
+# fix lib paths, some may be relative
+BEGIN {
+ require File::Spec;
+ my @libs = ("/opt/rt3/lib", "/opt/rt3/local/lib");
+ my $bin_path;
+
+ for my $lib (@libs) {
+ unless ( File::Spec->file_name_is_absolute($lib) ) {
+ unless ($bin_path) {
+ if ( File::Spec->file_name_is_absolute(__FILE__) ) {
+ $bin_path = ( File::Spec->splitpath(__FILE__) )[1];
+ }
+ else {
+ require FindBin;
+ no warnings "once";
+ $bin_path = $FindBin::Bin;
+ }
+ }
+ $lib = File::Spec->catfile( $bin_path, File::Spec->updir, $lib );
+ }
+ unshift @INC, $lib;
+ }
+
+}
+
+require RT;
+die "Wrong version of RT $RT::Version found; need 3.8.*"
+ unless $RT::VERSION =~ /^3\.8\./;
+RT::LoadConfig();
+if ( RT->Config->Get('DevelMode') ) {
+ require Module::Refresh;
+}
+RT::Init();
+
+# check compatibility of the DB
+{
+ my $dbh = $RT::Handle->dbh;
+ if ( $dbh ) {
+ my ($status, $msg) = $RT::Handle->CheckCompatibility( $dbh, 'post' );
+ die $msg unless $status;
+ }
+}
+
+require RT::Interface::Web::Handler;
+$RT::Mason::Handler = RT::Interface::Web::Handler->new(
+ RT->Config->Get('MasonParameters')
+);
+
+# load more for mod_perl before forking
+RT::InitClasses( Heavy => 1 ) if $ENV{'MOD_PERL'} || $ENV{RT_WEBMUX_HEAVY_LOAD};
+
+# we must disconnect DB before fork
+$RT::Handle->dbh(undef);
+undef $RT::Handle;
+
+if ( $ENV{'MOD_PERL'} && !RT->Config->Get('DevelMode')) {
+ # Under static_source, we need to purge the component cache
+ # each time we restart, so newer components may be reloaded.
+ #
+ # We can't do this in FastCGI or we'll blow away the component
+ # root _every_ time a new server starts which happens every few
+ # hits.
+
+ require File::Path;
+ require File::Glob;
+ my @files = File::Glob::bsd_glob("$RT::MasonDataDir/obj/*");
+ File::Path::rmtree([ @files ], 0, 1) if @files;
+}
+
+1;