Mailman のメーリングリストの内容説明に日本語を使っていて UnicodeDecodeError になった場合の対処

メーリングリストサーバを Debian GNU/Linux Wheezy から Jessie にアップデートしたところ、メールが配送されなくなるトラブルに遭遇しました。/var/log/mailman/error を見ると以下のようなエラーが記録されていました。

```
Jun 10 03:37:40 2017 (159) Uncaught runner exception: 'utf8' codec can't decode byte 0xa5 in position 0: invalid start byte
Jun 10 03:37:40 2017 (159) Traceback (most recent call last):
  File "/var/lib/mailman/Mailman/Queue/Runner.py", line 119, in _oneloop
    self._onefile(msg, msgdata)
  File "/var/lib/mailman/Mailman/Queue/Runner.py", line 190, in _onefile
    keepqueued = self._dispose(mlist, msg, msgdata)
  File "/var/lib/mailman/Mailman/Queue/IncomingRunner.py", line 130, in _dispose
    more = self._dopipeline(mlist, msg, msgdata, pipeline)
  File "/var/lib/mailman/Mailman/Queue/IncomingRunner.py", line 153, in _dopipeline
    sys.modules[modname].process(mlist, msg, msgdata)
  File "/var/lib/mailman/Mailman/Handlers/CookHeaders.py", line 179, in process
    i18ndesc = uheader(mlist, mlist.description, 'Reply-To')
  File "/var/lib/mailman/Mailman/Handlers/CookHeaders.py", line 65, in uheader
    return Header(s, charset, maxlinelen, header_name, continuation_ws)
  File "/usr/lib/python2.7/email/header.py", line 183, in __init__
    self.append(s, charset, errors)
  File "/usr/lib/python2.7/email/header.py", line 267, in append
    ustr = unicode(s, incodec, errors)
UnicodeDecodeError: 'utf8' codec can't decode byte 0xa5 in position 0: invalid start byte

Jun 10 03:37:40 2017 (159) SHUNTING: 1497033451.22806+bcf182d45533b69b6003c0fb62a3ae3e89350e50
```

どうやら、http://blog.n-z.jp/blog/2016-04-30-mailman-error.html と同じトラブルで，メーリングリストの解説などの日本語文字列が正しく保存されていないことが原因のようです。上記ページでは、ウェブインタフェースから人手再設定するという手順が紹介されていましたが、私の場合、管理下のメーリングリストが多数になるため人手再設定は現実的ではありません。

最初に、エラーになっているバイト列が、もともとは何の文字コードで格納されていたのかを検討しました。

```shell-session
$ sudo -u list /usr/lib/mailman/bin/withlist testlist 
Loading list testlist (unlocked)
The variable `m' is the testlist MailList instance
>>> m.description
'\xa5\xc6\xa5\xb9\xa5\xc8\xcd\xd1'
>>> m.description.decode('euc-jp')
u'\u30c6\u30b9\u30c8\u7528'
>>> m.description.decode('euc-jp').encode('utf-8')
'\xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88\xe7\x94\xa8'
>>> sys.stdout.write(m.description.decode('euc-jp').encode('utf-8'))
テスト用
```

適当に当てずっぽうで EUC-JP なのでは？と考えたら、正解でした。

後は、修正を、全メーリングリストに一括適用する方法があれば良いわけです。
最初に、実際に修正を行うスクリプト /usr/lib/mailman/bin/fix_desc.py を用意します。

```py:/usr/lib/mailman/bin/fix_desc.py
def fix_desc(mlist):
    mlist.Lock()
    try:
        desc = mlist.description.decode('euc-jp').encode('utf-8')
        info = mlist.info.decode('euc-jp').encode('utf-8')
        mlist.description = desc
        mlist.info = info
    except UnicodeDecodeError:
        print("Not encoded in EUC-JP. Ignored.")
    mlist.Save()
```

なお、修正スクリプトの設置場所が重要で、適当な別ディレクトリに配置しても withlist コマンドは見つけてくれません。withlist コマンドと同じディレクトリに配置する必要があります。

次に、withlist コマンドを経由して、上記の修正スクリプトを全メーリングリストに一括適用します。

```shell-session
$ sudo -u list /usr/lib/mailman/bin/withlist -r fix_desc -a
```

以下のようにメーリングリスト名を指定して、特定のメーリングリストだけを対象として適用することもできます。

```shell-session
$ sudo -u list /usr/lib/mailman/bin/withlist -r fix_desc LISTNAME
```

## 2022年8月28日追記

Busterに同梱されているMailmanでは，再びEUC-JPに戻っているらしい．そのため，以下のようにEUC-JPに戻すスクリプトの適用が必要となる．

```py:/usr/lib/mailman/bin/fix_desc.py
def fix_desc(mlist):
    mlist.Lock()
    try:
        desc = mlist.description.decode('utf-8').encode('euc-jp')
        info = mlist.info.decode('utf-8').encode('euc-jp')
        mlist.description = desc
        mlist.info = info
    except UnicodeDecodeError:
        print("Not encoded in EUC-JP. Ignored.")
    mlist.Save()
```