При настройке создания тикетов в 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, я буду благодарен!
Спасибо персональное за статью, пригодилась в борьбе с капризничающим 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;
}
}
Спасибо!
Мне кажется, в перле тоже должно быть что-то готовое, чтобы конечный код умещался в несколько строк.