summaryrefslogtreecommitdiff
path: root/rt/lib/RT/Interface
diff options
context:
space:
mode:
Diffstat (limited to 'rt/lib/RT/Interface')
-rw-r--r--rt/lib/RT/Interface/CLI.pm2
-rwxr-xr-xrt/lib/RT/Interface/Email.pm37
-rwxr-xr-xrt/lib/RT/Interface/Email/Auth/GnuPG.pm2
-rw-r--r--rt/lib/RT/Interface/Email/Auth/MailFrom.pm2
-rw-r--r--rt/lib/RT/Interface/REST.pm44
-rw-r--r--rt/lib/RT/Interface/Web.pm36
-rw-r--r--rt/lib/RT/Interface/Web/Handler.pm2
-rw-r--r--rt/lib/RT/Interface/Web/Menu.pm2
-rwxr-xr-xrt/lib/RT/Interface/Web/QueryBuilder.pm2
-rwxr-xr-xrt/lib/RT/Interface/Web/QueryBuilder/Tree.pm2
-rw-r--r--rt/lib/RT/Interface/Web/Request.pm2
-rw-r--r--rt/lib/RT/Interface/Web/Session.pm55
12 files changed, 145 insertions, 43 deletions
diff --git a/rt/lib/RT/Interface/CLI.pm b/rt/lib/RT/Interface/CLI.pm
index c1a6f4fe8..feef6b802 100644
--- a/rt/lib/RT/Interface/CLI.pm
+++ b/rt/lib/RT/Interface/CLI.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Interface/Email.pm b/rt/lib/RT/Interface/Email.pm
index ab319e665..74120ba07 100755
--- a/rt/lib/RT/Interface/Email.pm
+++ b/rt/lib/RT/Interface/Email.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -431,21 +431,24 @@ sub SendEmail {
# SetOutgoingMailFrom and bounces conflict, since they both want -f
if ( $args{'Bounce'} ) {
push @args, shellwords(RT->Config->Get('SendmailBounceArguments'));
- } elsif ( RT->Config->Get('SetOutgoingMailFrom') ) {
- my $OutgoingMailAddress;
+ } elsif ( my $MailFrom = RT->Config->Get('SetOutgoingMailFrom') ) {
+ my $OutgoingMailAddress = $MailFrom =~ /\@/ ? $MailFrom : undef;
+ my $Overrides = RT->Config->Get('OverrideOutgoingMailFrom') || {};
if ($TicketObj) {
my $QueueName = $TicketObj->QueueObj->Name;
- my $QueueAddressOverride = RT->Config->Get('OverrideOutgoingMailFrom')->{$QueueName};
+ my $QueueAddressOverride = $Overrides->{$QueueName};
if ($QueueAddressOverride) {
$OutgoingMailAddress = $QueueAddressOverride;
} else {
- $OutgoingMailAddress = $TicketObj->QueueObj->CorrespondAddress;
+ $OutgoingMailAddress ||= $TicketObj->QueueObj->CorrespondAddress
+ || RT->Config->Get('CorrespondAddress');
}
}
-
- $OutgoingMailAddress ||= RT->Config->Get('OverrideOutgoingMailFrom')->{'Default'};
+ elsif ($Overrides->{'Default'}) {
+ $OutgoingMailAddress = $Overrides->{'Default'};
+ }
push @args, "-f", $OutgoingMailAddress
if $OutgoingMailAddress;
@@ -1084,7 +1087,7 @@ sub ParseCcAddressesFromHead {
&& !IgnoreCcAddress( $_ )
}
map lc $user->CanonicalizeEmailAddress( $_->address ),
- map Email::Address->parse( $args{'Head'}->get( $_ ) ),
+ map RT::EmailParser->CleanupAddresses( Email::Address->parse( $args{'Head'}->get( $_ ) ) ),
qw(To Cc);
}
@@ -1464,6 +1467,9 @@ sub Gateway {
my $head = $Message->head;
my $ErrorsTo = ParseErrorsToAddressFromHead( $head );
+ my $Sender = (ParseSenderAddressFromHead( $head ))[0];
+ my $From = $head->get("From");
+ chomp $From if defined $From;
my $MessageId = $head->get('Message-ID')
|| "<no-message-id-". time . rand(2000) .'@'. RT->Config->Get('Organization') .'>';
@@ -1548,7 +1554,8 @@ sub Gateway {
);
return (
0,
- "$ErrorsTo tried to submit a message to "
+ ($CurrentUser->EmailAddress || $CurrentUser->Name)
+ . " ($Sender) tried to submit a message to "
. $args{'Queue'}
. " without permission.",
undef
@@ -1595,7 +1602,7 @@ sub Gateway {
Explanation => $ErrStr,
MIMEObj => $Message
);
- return ( 0, "Ticket creation failed: $ErrStr", $Ticket );
+ return ( 0, "Ticket creation From: $From failed: $ErrStr", $Ticket );
}
# strip comments&corresponds from the actions we don't need
@@ -1640,7 +1647,7 @@ sub Gateway {
Explanation => $msg,
MIMEObj => $Message
);
- return ( 0, "Message not recorded: $msg", $Ticket );
+ return ( 0, "Message From: $From not recorded: $msg", $Ticket );
}
} elsif ($unsafe_actions) {
my ( $status, $msg ) = _RunUnsafeAction(
@@ -1739,6 +1746,8 @@ sub _RunUnsafeAction {
@_
);
+ my $From = $args{Message}->head->get("From");
+
if ( $args{'Action'} =~ /^take$/i ) {
my ( $status, $msg ) = $args{'Ticket'}->SetOwner( $args{'CurrentUser'}->id );
unless ($status) {
@@ -1748,7 +1757,7 @@ sub _RunUnsafeAction {
Explanation => $msg,
MIMEObj => $args{'Message'}
);
- return ( 0, "Ticket not taken" );
+ return ( 0, "Ticket not taken, by email From: $From" );
}
} elsif ( $args{'Action'} =~ /^resolve$/i ) {
my $new_status = $args{'Ticket'}->FirstInactiveStatus;
@@ -1763,11 +1772,11 @@ sub _RunUnsafeAction {
Explanation => $msg,
MIMEObj => $args{'Message'}
);
- return ( 0, "Ticket not resolved" );
+ return ( 0, "Ticket not resolved, by email From: $From" );
}
}
} else {
- return ( 0, "Not supported unsafe action $args{'Action'}", $args{'Ticket'} );
+ return ( 0, "Not supported unsafe action $args{'Action'}, by email From: $From", $args{'Ticket'} );
}
return ( 1, "Success" );
}
diff --git a/rt/lib/RT/Interface/Email/Auth/GnuPG.pm b/rt/lib/RT/Interface/Email/Auth/GnuPG.pm
index c14bcf074..5137707e5 100755
--- a/rt/lib/RT/Interface/Email/Auth/GnuPG.pm
+++ b/rt/lib/RT/Interface/Email/Auth/GnuPG.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Interface/Email/Auth/MailFrom.pm b/rt/lib/RT/Interface/Email/Auth/MailFrom.pm
index bfe493958..b353907fb 100644
--- a/rt/lib/RT/Interface/Email/Auth/MailFrom.pm
+++ b/rt/lib/RT/Interface/Email/Auth/MailFrom.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Interface/REST.pm b/rt/lib/RT/Interface/REST.pm
index 5f8ff99b7..17fe44669 100644
--- a/rt/lib/RT/Interface/REST.pm
+++ b/rt/lib/RT/Interface/REST.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -47,12 +47,13 @@
# END BPS TAGGED BLOCK }}}
package RT::Interface::REST;
+use LWP::MediaTypes qw(guess_media_type);
use strict;
use warnings;
use RT;
use base 'Exporter';
-our @EXPORT = qw(expand_list form_parse form_compose vpush vsplit);
+our @EXPORT = qw(expand_list form_parse form_compose vpush vsplit process_attachments);
sub custom_field_spec {
my $self = shift;
@@ -296,6 +297,45 @@ sub vsplit {
return \@words;
}
+sub process_attachments {
+ my $entity = shift;
+ my @list = @_;
+ return 1 unless @list;
+
+ my $m = $HTML::Mason::Commands::m;
+ my $cgi = $m->cgi_object;
+
+ my $i = 1;
+ foreach my $e ( @list ) {
+
+ my $fh = $cgi->upload("attachment_$i");
+ return (0, "No attachment for $e") unless $fh;
+
+ local $/=undef;
+
+ my $file = $e;
+ $file =~ s#^.*[\\/]##;
+
+ my ($tmp_fh, $tmp_fn) = File::Temp::tempfile( UNLINK => 1 );
+
+ my $buf;
+ while (sysread($fh, $buf, 8192)) {
+ syswrite($tmp_fh, $buf);
+ }
+
+ my $info = $cgi->uploadInfo($fh);
+ my $new_entity = $entity->attach(
+ Path => $tmp_fn,
+ Type => $info->{'Content-Type'} || guess_media_type($tmp_fn),
+ Filename => $file,
+ Disposition => "attachment",
+ );
+ $new_entity->bodyhandle->{'_dirty_hack_to_save_a_ref_tmp_fh'} = $tmp_fh;
+ $i++;
+ }
+ return (1);
+}
+
RT::Base->_ImportOverlays();
1;
diff --git a/rt/lib/RT/Interface/Web.pm b/rt/lib/RT/Interface/Web.pm
index 5ca4a9f63..1f2f488fb 100644
--- a/rt/lib/RT/Interface/Web.pm
+++ b/rt/lib/RT/Interface/Web.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -1283,10 +1283,16 @@ our %is_whitelisted_component = (
# While these can be used for denial-of-service against RT
# (construct a very inefficient query and trick lots of users into
# running them against RT) it's incredibly useful to be able to link
- # to a search result or bookmark a result page.
+ # to a search result (or chart) or bookmark a result page.
'/Search/Results.html' => 1,
'/Search/Simple.html' => 1,
- '/m/tickets/search' => 1,
+ '/m/tickets/search' => 1,
+ '/Search/Chart.html' => 1,
+
+ # This page takes Attachment and Transaction argument to figure
+ # out what to show, but it's read only and will deny information if you
+ # don't have ShowOutgoingEmail.
+ '/Ticket/ShowEmailRecord.html' => 1,
);
# Components which are blacklisted from automatic, argument-based whitelisting.
@@ -1762,7 +1768,7 @@ sub CreateTicket {
$RT::Logger->error("Couldn't make multipart message")
if !$rv || $rv !~ /^(?:DONE|ALREADY)$/;
- foreach ( values %{ $ARGS{'Attachments'} } ) {
+ foreach ( map $ARGS{Attachments}->{$_}, sort keys %{ $ARGS{'Attachments'} } ) {
unless ($_) {
$RT::Logger->error("Couldn't add empty attachemnt");
next;
@@ -2017,7 +2023,8 @@ sub ProcessUpdateMessage {
if ( $args{ARGSRef}->{'UpdateAttachments'} ) {
$Message->make_multipart;
- $Message->add_part($_) foreach values %{ $args{ARGSRef}->{'UpdateAttachments'} };
+ $Message->add_part($_) foreach map $args{ARGSRef}->{UpdateAttachments}{$_},
+ sort keys %{ $args{ARGSRef}->{'UpdateAttachments'} };
}
if ( $args{ARGSRef}->{'AttachTickets'} ) {
@@ -2619,18 +2626,23 @@ sub ProcessTicketReminders {
while ( my $reminder = $reminder_collection->Next ) {
my $resolve_status = $reminder->QueueObj->Lifecycle->ReminderStatusOnResolve;
if ( $reminder->Status ne $resolve_status && $args->{ 'Complete-Reminder-' . $reminder->id } ) {
- $Ticket->Reminders->Resolve($reminder);
+ my ($status, $msg) = $Ticket->Reminders->Resolve($reminder);
+ push @results, loc("Reminder #[_1]: [_2]", $reminder->id, $msg);
+
}
elsif ( $reminder->Status eq $resolve_status && !$args->{ 'Complete-Reminder-' . $reminder->id } ) {
- $Ticket->Reminders->Open($reminder);
+ my ($status, $msg) = $Ticket->Reminders->Open($reminder);
+ push @results, loc("Reminder #[_1]: [_2]", $reminder->id, $msg);
}
if ( exists( $args->{ 'Reminder-Subject-' . $reminder->id } ) && ( $reminder->Subject ne $args->{ 'Reminder-Subject-' . $reminder->id } )) {
- $reminder->SetSubject( $args->{ 'Reminder-Subject-' . $reminder->id } ) ;
+ my ($status, $msg) = $reminder->SetSubject( $args->{ 'Reminder-Subject-' . $reminder->id } ) ;
+ push @results, loc("Reminder #[_1]: [_2]", $reminder->id, $msg);
}
if ( exists( $args->{ 'Reminder-Owner-' . $reminder->id } ) && ( $reminder->Owner != $args->{ 'Reminder-Owner-' . $reminder->id } )) {
- $reminder->SetOwner( $args->{ 'Reminder-Owner-' . $reminder->id } , "Force" ) ;
+ my ($status, $msg) = $reminder->SetOwner( $args->{ 'Reminder-Owner-' . $reminder->id } , "Force" ) ;
+ push @results, loc("Reminder #[_1]: [_2]", $reminder->id, $msg);
}
if ( exists( $args->{ 'Reminder-Due-' . $reminder->id } ) && $args->{ 'Reminder-Due-' . $reminder->id } ne '' ) {
@@ -2640,7 +2652,8 @@ sub ProcessTicketReminders {
Value => $args->{ 'Reminder-Due-' . $reminder->id }
);
if ( defined $DateObj->Unix && $DateObj->Unix != $reminder->DueObj->Unix ) {
- $reminder->SetDue( $DateObj->ISO );
+ my ($status, $msg) = $reminder->SetDue( $DateObj->ISO );
+ push @results, loc("Reminder #[_1]: [_2]", $reminder->id, $msg);
}
}
}
@@ -3180,7 +3193,8 @@ sub GetColumnMapEntry {
}
# complex things
- elsif ( my ( $mainkey, $subkey ) = $args{'Name'} =~ /^(.*?)\.{(.+)}$/ ) {
+ elsif ( my ( $mainkey, $subkey ) = $args{'Name'} =~ /^(.*?)\.(.+)$/ ) {
+ $subkey =~ s/^\{(.*)\}$/$1/;
return undef unless $args{'Map'}->{$mainkey};
return $args{'Map'}{$mainkey}{ $args{'Attribute'} }
unless ref $args{'Map'}{$mainkey}{ $args{'Attribute'} } eq 'CODE';
diff --git a/rt/lib/RT/Interface/Web/Handler.pm b/rt/lib/RT/Interface/Web/Handler.pm
index a1784c2cc..37031b18d 100644
--- a/rt/lib/RT/Interface/Web/Handler.pm
+++ b/rt/lib/RT/Interface/Web/Handler.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Interface/Web/Menu.pm b/rt/lib/RT/Interface/Web/Menu.pm
index e4e08d63b..e7833828c 100644
--- a/rt/lib/RT/Interface/Web/Menu.pm
+++ b/rt/lib/RT/Interface/Web/Menu.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Interface/Web/QueryBuilder.pm b/rt/lib/RT/Interface/Web/QueryBuilder.pm
index 546427833..a1b066227 100755
--- a/rt/lib/RT/Interface/Web/QueryBuilder.pm
+++ b/rt/lib/RT/Interface/Web/QueryBuilder.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Interface/Web/QueryBuilder/Tree.pm b/rt/lib/RT/Interface/Web/QueryBuilder/Tree.pm
index 9bbd876e5..f9305545f 100755
--- a/rt/lib/RT/Interface/Web/QueryBuilder/Tree.pm
+++ b/rt/lib/RT/Interface/Web/QueryBuilder/Tree.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Interface/Web/Request.pm b/rt/lib/RT/Interface/Web/Request.pm
index cdd4594d6..13d667288 100644
--- a/rt/lib/RT/Interface/Web/Request.pm
+++ b/rt/lib/RT/Interface/Web/Request.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
diff --git a/rt/lib/RT/Interface/Web/Session.pm b/rt/lib/RT/Interface/Web/Session.pm
index 4edd9bd2e..aded596c4 100644
--- a/rt/lib/RT/Interface/Web/Session.pm
+++ b/rt/lib/RT/Interface/Web/Session.pm
@@ -2,7 +2,7 @@
#
# COPYRIGHT:
#
-# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
# <sales@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -192,7 +192,7 @@ sub _ClearOldDB {
die "couldn't execute query: ". $dbh->errstr unless defined $rows;
}
- $RT::Logger->info("successfuly deleted $rows sessions");
+ $RT::Logger->info("successfully deleted $rows sessions");
return;
}
@@ -222,15 +222,53 @@ sub _ClearOldDir {
next;
}
tied(%session)->delete;
- $RT::Logger->info("successfuly deleted session '$id'");
+ $RT::Logger->info("successfully deleted session '$id'");
}
+ # Apache::Session::Lock::File will clean out locks older than X, but it
+ # leaves around bogus locks if they're too new, even though they're
+ # guaranteed dead. On even just largeish installs, the accumulated number
+ # of them may bump into ext3/4 filesystem limits since Apache::Session
+ # doesn't use a fan-out tree.
my $lock = Apache::Session::Lock::File->new;
$lock->clean( $dir, $older_than );
+ # Take matters into our own hands and clear bogus locks hanging around
+ # regardless of how recent they are.
+ $self->ClearOrphanLockFiles($dir);
+
return;
}
+=head3 ClearOrphanLockFiles
+
+Takes a directory in which to look for L<Apache::Session::Lock::File> locks
+which no longer have a corresponding session file. If not provided, the
+directory is taken from the session configuration data.
+
+=cut
+
+sub ClearOrphanLockFiles {
+ my $class = shift;
+ my $dir = shift || $class->Attributes->{Directory}
+ or return;
+
+ if (opendir my $dh, $dir) {
+ for (readdir $dh) {
+ next unless /^Apache-Session-([0-9a-f]{32})\.lock$/;
+ next if -e "$dir/$1";
+
+ RT->Logger->debug("deleting orphaned session lockfile '$_'");
+
+ unlink "$dir/$_"
+ or warn "Failed to unlink session lockfile $dir/$_: $!";
+ }
+ closedir $dh;
+ } else {
+ warn "Unable to open directory '$dir' for reading: $!";
+ }
+}
+
=head3 ClearByUser
Checks all sessions and if user has more then one session
@@ -243,6 +281,7 @@ sub ClearByUser {
my $class = $self->Class;
my $attrs = $self->Attributes;
+ my $deleted;
my %seen = ();
foreach my $id( @{ $self->Ids } ) {
my %session;
@@ -259,8 +298,10 @@ sub ClearByUser {
}
}
tied(%session)->delete;
- $RT::Logger->info("successfuly deleted session '$id'");
+ $RT::Logger->info("successfully deleted session '$id'");
+ $deleted++;
}
+ $self->ClearOrphanLockFiles if $deleted;
}
sub TIEHASH {
@@ -276,10 +317,8 @@ sub TIEHASH {
eval { tie %session, $class, $id, $attrs };
eval { tie %session, $class, undef, $attrs } if $@;
if ( $@ ) {
- die loc("RT couldn't store your session.") . "\n"
- . loc("This may mean that that the directory '[_1]' isn't writable or a database table is missing or corrupt.",
- $RT::MasonSessionDir)
- . "\n\n"
+ die "RT couldn't store your session. "
+ . "This may mean that that the directory '$RT::MasonSessionDir' isn't writable or a database table is missing or corrupt.\n\n"
. $@;
}