[rt-devel] Draft patch to add references and in-reply-to parsing
Petter Reinholdtsen
pere at hungry.com
Fri Sep 10 09:53:54 EDT 2004
[Petter Reinholdtsen, 2004-01-28]
> Continuing my stumble in the dark, I came up with this patch. I
> still can not say I understand the DBIx::SearchBuilder API, but
> based on the example from Ruslan U. Zakirov I continued by adding
> the MessageId value and the loop to extract the ticket ids.
Based on this initial try, and some clues from the thread discussing
messageid parsing in RT::Attachment, I ended up with the following
patch to get what I wanted. This patch is tested and found to work
fine.
If the patch is acceptable, please include it in a future version of
RT. If it isn't acceptable, please let me know what is wrong with it.
The patch is relative to todays 3.2-RELEASE branch in subversion.
Index: lib/RT/Ticket_Overlay.pm
===================================================================
--- lib/RT/Ticket_Overlay.pm (revision 1473)
+++ lib/RT/Ticket_Overlay.pm (working copy)
@@ -291,6 +291,67 @@
# }}}
+# {{{ sub LoadByMessageId
+
+=head2 LoadByMessageId
+
+Given a RFC 822 message id, loads the specified ticket. If the
+message id is assosiated with several tickets, select the smallest
+ticket id.
+
+=cut
+
+sub LoadByMessageId {
+ my $self = shift;
+ my $MessageId = shift;
+
+ my $Attachs = RT::Attachments->new($RT::SystemUser);
+ $Attachs->Limit( FIELD => 'MessageId',
+ OPERATOR => '=',
+ VALUE => $MessageId
+ );
+ $Attachs->Limit( FIELD => 'ContentType',
+ OPERATOR => '=',
+ VALUE => 'text/plain'
+ );
+ $Attachs->Limit( FIELD => 'ContentType',
+ OPERATOR => '=',
+ VALUE => 'text/html'
+ );
+ $Attachs->Limit( FIELD => 'Parent',
+ OPERATOR => '=',
+ VALUE => '0'
+ );
+ my $trs = $Attachs->NewAlias('Transactions');
+ my $tis = $Attachs->NewAlias('Tickets');
+ $Attachs->Join( ALIAS1 => 'main',
+ FIELD1 => 'TransactionId',
+ ALIAS2 => $trs,
+ FIELD2 => 'id'
+ );
+ $Attachs->Join( ALIAS1 => $trs,
+ FIELD1 => 'Ticket',
+ ALIAS2 => $tis,
+ FIELD2 => 'id'
+ );
+ my %tickets = ();
+ while (my $attachment = $Attachs->Next) {
+ $tickets{$attachment->TransactionObj()->Ticket} = 1;
+ }
+ my @ids = sort { $a <=> $b } keys %tickets;
+ if (1 < @ids) {
+ $RT::Logger->info("Message ID $MessageId maps to several tickets.",
+ "Selecting the first.");
+ }
+ if (@ids) {
+ return $self->Load($ids[0]);
+ } else {
+ return undef;
+ }
+}
+
+# }}}
+
# {{{ sub Create
=head2 Create (ARGS)
Index: lib/RT/Attachment_Overlay.pm
===================================================================
--- lib/RT/Attachment_Overlay.pm (revision 1473)
+++ lib/RT/Attachment_Overlay.pm (working copy)
@@ -153,6 +153,10 @@
defined($Subject) or $Subject = '';
chomp($Subject);
+ #Get the MessageID, or undef if not available
+ my $MessageId = $Attachment->head->get( 'message-id', 0 );
+ chomp($MessageId);
+
#Get the filename
my $Filename = $Attachment->head->recommended_filename || eval {
${ $Attachment->head->{mail_hdr_hash}{'Content-Disposition'}[0] }
@@ -167,6 +171,7 @@
Parent => 0,
ContentType => $Attachment->mime_type,
Headers => $Attachment->head->as_string,
+ MessageId => $MessageId,
Subject => $Subject);
foreach my $part ( $Attachment->parts ) {
@@ -198,6 +203,7 @@
ContentEncoding => $ContentEncoding,
Parent => $args{'Parent'},
Headers => $Attachment->head->as_string,
+ MessageId => $MessageId,
Subject => $Subject,
Content => $Body,
Filename => $Filename, );
Index: lib/RT/Interface/Email.pm
===================================================================
--- lib/RT/Interface/Email.pm (revision 1473)
+++ lib/RT/Interface/Email.pm (working copy)
@@ -271,6 +271,59 @@
return $CurrentUser;
}
# }}}
+
+# {{{ sub ParseReferences
+
+sub ParseReferences {
+ my $head = shift;
+
+ # Based on info from <URL:http://www.jwz.org/doc/threading.html>
+ my @msgids = ();
+
+ my $references = $head->get('References') || '';
+ chomp($references);
+ my $inreplyto = $head->get('In-Reply-To') || '';
+ chomp($inreplyto);
+
+ push(@msgids, split(/\s+/, $references)) if ($references);
+
+ if ($inreplyto) {
+ if ($inreplyto =~ m/(<[^>]+>)/) {
+ push(@msgids, $1);
+ } else {
+ $RT::Logger->info("Gateway: Unhandled In-Reply-To ".
+ "format: '$inreplyto'");
+ }
+ }
+
+ # Map Message-id(s) to ticket id
+ my %tickets = ();
+ my %checked;
+ my $ticket = RT::Ticket->new($RT::SystemUser);
+ for my $MessageId (@msgids) {
+ next if $checked{$MessageId}; # Already looked up this message-id
+ my $ticketid = $ticket->LoadByMessageId($MessageId);
+ $tickets{$ticketid} = 1 if defined $ticketid;
+ $checked{$MessageId} = 1;
+ }
+
+ my @ticketids = sort keys %tickets;
+
+ # If the Message-id(s) are already in the database, use their
+ # ticked-id
+ if (1 < @ticketids) {
+ $RT::Logger->debug("Gateway: Several possible tickets: " .
+ join(",", @ticketids) );
+ }
+
+ # Just pick the first. Not sure how we should handle several
+ # ticket ids
+ return $ticketids[0] if (@ticketids);
+}
+
+# }}}
+
+
# {{{ ParseCcAddressesFromHead
=head2 ParseCcAddressesFromHead HASHREF
@@ -471,6 +524,9 @@
$args{'ticket'} ||= $parser->ParseTicketId($Subject);
+ # Check references headers if subject is missing ticket info
+ $args{'ticket'} ||= ParseReferences($head);
+
my $SystemTicket;
my $Right = 'CreateTicket';
if ( $args{'ticket'} ) {
More information about the Rt-devel
mailing list