Борьба с обрезанием Redmine заголовка тикета при создании тикета посредством email

При настройке создания тикетов в Redmine посредством email-сообщений, я столкнулся с такой проблемой: если тема письма содержит русские буквы (вероятно, не только русские, а просто не-латинские), то соответствующий тикет в Redmine создается с сильно обрезанной темой.

Опытным путем выяснилось, что Redmine производит некорректное перекодирование темы письма, если она выражена MIME-Encoded-строкой. Также, опытным путем выяснилось, что если тема письма представляет собой просто текстовую строку в родной для Redmine кодировке (как правило, это utf-8), то ошибок при создании тикета не возникает.

Пытание гугля ни к чему определенному не привело. Есть несколько отсылок на подобные проблемы у людей. То, что такие проблемы возникают не часто, означает, что либо у меня специфичное сочетание версий ПО на сервере, либо мало кто создает тикеты в Redmine посредством email.

Как бы то ни было, с проблемой надо было разобраться. Надо перед скармливанием письма Redmine производить перекодировку темы, что я и реализовал.

Способ снюхивания Redmine с email описан в официальной документации:

http://www.redmine.org/projects/redmine/wiki/RedmineReceivingEmails

Я настраивал вариант с отправкой писем на вход ruby-скрипта, который закидывал их в Redmine. Вот так примерно это выглядит в конфиге postfix (файл /etc/aliases):

redminetest: "| /usr/local/bin/rdm-mailhandler --url=https://REDMINE-URL --key SECRET --project ikds --tracker email --unknown-user=create --no-permission-check --no-account-notice"

Мне надо произвести корректировку темы в письме перед отдачей его на вход скрипту /usr/local/bin/rdm-mailhandler.

С ходу готового способа в shell перекодировать тему из MIME-Encoded-строки в обычный текст я не нашел, зато нашел функцию на php, которая замечательно с этим справляется: imap_utf8(). Таким образом, получилось несколько костыльное решение, состоящее из двух скриптов: shell-скрипта, который занимается приемкой письма, выдиранием из него темы, и заменой ее на скорректированную, и скрипта на php, который производит корректировку темы.

Shell-скрипт:

cat /usr/local/bin/handler.sh
#!/bin/bash

EMAIL=$(cat)    # читаем письмо из stdin

SUBJECT=$(echo "$EMAIL" | formail -zx "Subject: ") # берем из письма тему
SUBJ=$(echo "$SUBJECT" | /usr/local/bin/subject_convert.php) # конвертируем тему при помощи второго скрипта
echo "$EMAIL" | formail -b -I "Subject: $SUBJ" # выводим письмо с откорректированной темой

Скрипт на php:

cat /usr/local/bin/subject_convert.php
#!/usr/bin/php -q
<?
$stdin = fopen('php://stdin', 'r');
$contents = fread($stdin, 16384); // читаем тему из stdin
$str=imap_utf8($contents); // конвертируем
echo $str; // выводим исправленную тему
fclose($stdin);
?>

Итоговый конфиг для postfix теперь выглядит так:

redminetest: "| /usr/local/bin/handler.sh | /usr/local/bin/rdm-mailhandler --url=https://REDMINE-URL --key SECRET --project ikds --tracker email --unknown-user=create --no-permission-check --no-account-notice"

Для корректного функционирования этой связки необходимо, чтобы в системе был установлен formail, php-cli и поддержка imap для php (php5-imap).

Если кто-то подскажет, как корректно сконвертировать тему письма, не прибегая к услугам php, я буду благодарен!

2 комментария:

  1. wsd

    Спасибо персональное за статью, пригодилась в борьбе с капризничающим Redmine :)
    Кстати, что бы обойтись без PHP, мой коллега на скорую руку нарисовал скрипт на Perl, который читает поток данных из stdin, конвертирует Subject в UTF-8 и сливает оный поток на stdout. Вот оное забавное творение:

    #!/usr/bin/perl

    use strict;
    use warnings;
    use Encode qw/encode decode/;
    use MIME::QuotedPrint;
    use MIME::Base64;

    sub ch {
    my ($enc, $style, $msg) = ($1, lc($2), $3);
    my $data;
    if ($style eq 'q') {
    $data = decode_qp($msg);
    } elsif ($style eq 'b') {
    $data = decode_base64($msg);
    }
    return encode('UTF-8', decode($enc, $data));
    }

    my $state = 0;
    while () {
    chomp $_;
    OUT: while (1) {
    IN: {
    last IN if $state == 2;
    if ($state == 1 && ! /^\s+\S/) {
    print "\n";
    $state = 0;
    next OUT;
    }
    if ($state == 0) {
    if (/^\s*$/) {
    $state = 2;
    next OUT;
    }
    last IN unless s/^subject://i;
    print "Subject: ";
    $state = 1;
    }
    s/^\s+//;
    s/=\?([^\?]+)\?([^\?]+)\?([^\?]+)\?=/ch($1,$2,$3)/ge;
    print "$_";
    last OUT;
    }
    print "$_\n";
    last OUT;
    }
    }

    1. ikds *

      Спасибо!
      Мне кажется, в перле тоже должно быть что-то готовое, чтобы конечный код умещался в несколько строк.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *