Sending on true multipart/alternative mail

I have a system that creates and responds to tickets. The text/plain content contains a very long link that some mail clients truncate in the middle of important query information and then create links from the truncated text, which of course don’t work at the receiving end because of truncated/missing data.

I tried sending multipart/alternative mail from the source system where the text/html content wraps the link in an anchor tag that also makes the link more presentable by displaying it as ‘Click here’ text, however when using the ‘Correspondence in HTML’ template the text/plain alternative content is replaced with downgraded html which strips out the link, just using the ‘Click here’ text.

There are plenty of workarounds available to me, but I am wondering if I can use some magic template/scrip code to use the original text/plain content instead of the downgraded text/html content? I would like to be able to keep the simplified and prettier html content for most clients, falling back to the more complex/uglier text content for those old fogey legacy clients.

If its long link causing the problem, could you use a URL shortener? Either one of the existing services such as bit.ly or goo.gl, or roll your own? There are even open source options such as Polr.

This is one of the workarounds available; so is sending the mail directly from the system to the requestor, bypassing RT.

However, I am wondering if I can use RT to send outgoing multipart/alternative email with a text/plain body part that has different content from the downgraded text/html body part (being aware of the common spam filter multipart difference rules) using standard RT config/customisation?

Well I’ve not done this, but a couple of ideas:

  1. You could copy this person’s solution and make a local overlay of lib/RT/Template.pm that has a modified version of _DowngradeFromHTML() method that checks if there is a multipart/alternative MIME type and if so doesn’t do the automatic HTML->text conversion.

  2. You could make your own HTML->text formatter script that does the formatting you’d prefer, save that in /usr/local/bin/ and then put the name of that script into the HTMLFormatter config variable.

Thanks @GreenJimII. I saw that post about modifying _DowngradeFromHTML and didn’t want to break the warranty modify RT code. However I did not know about the HTMLFormatter config. That also seems like a viable solution. Cheers!

The other day, while packing up my work on this, I came across the RT::Transaction::ContentAsMIME method which re-kindled my interest in making this work. After a few debug and error producing experiments I think I have a template that will do the job.

I’m certainly not aware of all the concerns around mail sending (eg proper html security), but if you trust the allowed queue senders the risks should be acceptable. Also, I have not been able to test this as much as I would like to (just with tickets through the web-ui, and from simple text/plain and basic multipart/alternative emails); so I invite you, the reader, to please leave feedback about this template snippet.

RT-Attach-Message: yes
{
  if ($Transaction->CurrentUserCanSee) {
    # ripped from RT::Transaction->ContentAsMIME
    my $attachments = RT::Attachments->new($Transaction->CurrentUser);
    $attachments->OrderBy(FIELD => 'id', ORDER => 'asc');
    $attachments->Limit(FIELD => 'TransactionId', VALUE => $Transaction->id);
    $attachments->Limit(FIELD => 'Parent', VALUE => 0);
    $attachments->RowsPerPage(1);
    my $top = $attachments->First;
    if ($top) {
      # ripped from: RT::Attachment->ContentAsMIME(Children => 1)
      $mime = MIME::Entity->new();
      foreach my $header ($top->SplitHeaders) {
        my ($header_k, $header_v) = split /:/, $header, 2;
        # ripped from RT::Attachment->_EncodeHeaderToMIME, but we only want to use the content headers
        # todo? Are there other possible headers we want to forward on?
        if ($header_k =~ /^Content-/i) {
          my $params = MIME::Field::ParamVal->parse_params($header_v);
          $header_v = delete $params->{'_'};
          foreach my $params_k (sort keys %$params) {
            my $params_v = $params->{$params_k};
            if ($params_v =~ /[^\x00-\x7f]/ ) { # check for non-ASCII
                $params_v = q{UTF-8''} . URI->new(Encode::encode('UTF-8', $params_v));
                $params_v =~ s/(["\\])/\\$1/g;
                $header_v .= qq{; ${params_k}*="$params_v"};
            } else {
                $header_v .= qq{; $params_k="$params_v"};
            }
          }
          $mime->head->add($header_k, $header_v);
        }
      }
      # back to RT::Attachment->ContentAsMIME(Children => 1)
      if ($mime->is_multipart) {
        if (!$top->IsMessageContentType) {
          my $children = $top->Children;
          while (my $child = $children->Next) {
            $mime->add_part($child->ContentAsMIME(Children => 1));
          }
        }
      } else {
        $mime->head->mime_attr("Content-Type.charset" => $top->OriginalEncoding) if $top->OriginalEncoding;
        $mime->bodyhandle(MIME::Body::Scalar->new($top->OriginalContent));
      }
      $OUT = $mime->stringify;
    }
  }
}
1 Like