symfony/mailerでbase64やiso-2022-jpを試してみる

[Symfony Advent Calendar 2019](https://qiita.com/advent-calendar/2019/symfony) 8日目の記事です。
昨日は[@brtriverさん](https://qiita.com/brtriver)の[Symfonyの歴史を振り返ってみる](https://brtriver.hatenadiary.org/entry/2019/12/08/084342)でした！

# はじめに

こちらの記事は、[symfony/mailerで試行錯誤したこと](https://qiita.com/idani/private/cf2fbe97e1ca8067b087)の続きになります。

前回は、配信処理までの設定で四苦八苦しましたが、今回は送信するメールの文字コードやエンコードの仕方について試行錯誤していきたいと思います。

メールは、インターネット時代のコミュニケーション手段として、

* <code>utf-8、iso-2022-jp</code>などの文字コード
* <code>7bit, 8bit, base64, quoted-printable</code>などのエンコーディング
* 単純に<code>テキスト</code>のみ、テキストと<code>HTMLの併用</code>など

様々な設定ができるようになっています。

そしてSPAM配信の増加による、迷惑メール対策技術の向上によって、
メールを迷惑メールとして判定させないために、いろいろと気をつける部分となります。

新規のシステムならよいのですが、
システムのリニューアルによって、メールが届かなくなるということはよくあります。

これは送信されるメールの文面が、以前のシステムと異なっていることも関係します。

例えば、夏休みに明けに、気になるあの子が茶髪ギャルになっていたら、違和感覚えますよね。
迷惑メール判定も、そんな感じです。

|夏休み前|夏休み明け|
|:----:|:------:|
| <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/133291/1f797ef4-ac78-aa80-6983-f52a2338b58f.png" width=32%>|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/133291/85ca5eeb-6abe-88e1-739e-4d1e4718aa94.png" width=32%> | 



ここまでの変化ではないにしても、ちょっと髪型変えたかな？ぐらいの変化、
メールの世界だと、ヘッダーに<code>X-Mailler</code>が追加されただけでも、迷惑メールとして判定されたりします。

感覚的には、文字コード、エンコーディングが違うと大きな変化だったりします。

なので、前回のシステムと同様のメールを送信ができるように、Symfony/mailerも使いこなせる様になる必要があるのです。

# テスト環境の構築

Dockerで開発環境を作って、SymfonyのWebスケルトンプロジェクトを作ります。
そこまで作ったのが以下になります。

https://github.com/idani/symfony_mailer_tutorial2/tree/de7e20b8af6886a3f1c07927ed5e7a6f3be0801b


# 一番かんたんな送信方法

「[Creating & Sending Messages](Creating & Sending Messages)」のサンプルコードを実行します。

```php:MaillerController.php(その１)

class MailerController extends AbstractController
{
    /**
     * @Route("/mailer", name="mailer")
     */
    public function index(MailerInterface $mailer)
    {
        $email = (new Email())
            ->from('hello@example.com')
            ->to('you@example.com')
            //->cc('cc@example.com')
            //->bcc('bcc@example.com')
            //->replyTo('fabien@example.com')
            //->priority(Email::PRIORITY_HIGH)
            ->subject('Time for Symfony Mailer!')
            ->text('Sending emails is fun again!')
            ->html('<p>See Twig integration for better HTML integration!</p>');

        $mailer->send($email);

        return $this->render('mailer/index.html.twig', [
            'controller_name' => 'MailerController',
        ]);
    }
}
```
https://github.com/idani/symfony_mailer_tutorial2/commit/7e17f841fc116d0cacab976ec767028c0ce99d49

http://localhost:8000/mailer を開くと以下の画面が表示されてメールが送信されました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/133291/985a2ed0-4097-9c7e-d6a9-09d6b31c68cf.png)

MailDevで確認すると、受信されています。
1つ目の画像はHTMLメールを表示した場合。2つ目の画像は、テキストの画面を表示した場合です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/133291/c7f4cc7e-0cde-143f-059a-09029e3ba3e3.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/133291/1c2e9ee8-5cae-240a-915b-8e0910de1480.png)

メールのソースは、以下になります。

```php:メールのソース
From: hello@example.com
To: you@example.com
Subject: Time for Symfony Mailer!
Message-ID: <f9549d7eac6696b8cd513cc398e5bcc9@example.com>
MIME-Version: 1.0
Date: Wed, 04 Dec 2019 14:51:44 +0900
Content-Type: multipart/alternative;
 boundary="_=_symfony_1575438704_55dde3f773e700c11643dd0965c1db31_=_"

--_=_symfony_1575438704_55dde3f773e700c11643dd0965c1db31_=_
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: quoted-printable

Sending emails is fun again!
--_=_symfony_1575438704_55dde3f773e700c11643dd0965c1db31_=_
Content-Type: text/html; charset=utf-8
Content-Transfer-Encoding: quoted-printable

<p>See Twig integration for better HTML integration!</p>
--_=_symfony_1575438704_55dde3f773e700c11643dd0965c1db31_=_--

```

# 日本語を試してみる

送信者名、受信者名、サブジェクト、本文に日本語を試してみましょう。

メールアドレスでの名前の指定は、[Email Addresses](https://symfony.com/doc/4.4/mailer.html#email-addresses)に記載がありますように、[Address.php](https://github.com/symfony/symfony/blob/master/src/Symfony/Component/Mime/Address.php)クラスを利用すると良さそうです。

サブジェクト、本文は、何も考えずに日本語を入れてみます。
また、HTMLは面倒なので、ここでは一旦、オミットしておきます。

以下のように修正しました。

```php:MailerController.php(その２)
class MailerController extends AbstractController
{
    /**
     * @Route("/mailer", name="mailer")
     */
    public function index(MailerInterface $mailer)
    {
        $body = <<<EOL
https://symfony.com/why-use-a-framework

フレームワークを使用する必要があるのはなぜですか？
フレームワークは絶対に必要というわけではありません。それは、あなたがより良く、より速く開発するのを

~~~中略~~~

この点で、フレームワークはブラックボックスではありません！Symfonyの場合、それはまだPHPです...開発されるアプリケーションはSymfonyユニバースに限定されず、たとえば他のPHPライブラリとネイティブに相互運用できます。
EOL;


        $email = (new Email())
            ->from(new Address('hello@example.com', '送信者名'))
            ->to(new Address('you@example.com', '受信者名'))
            //->cc('cc@example.com')
            //->bcc('bcc@example.com')
            //->replyTo('fabien@example.com')
            //->priority(Email::PRIORITY_HIGH)
            ->subject('日本語のサブジェクトになります。長くなると文字化けするという話もありますので、長く書いてみます。これぐらい長いとどうかな？')
            ->text($body)
//            ->html('<p>See Twig integration for better HTML integration!</p>')
        ;

        $mailer->send($email);

        return $this->render('mailer/index.html.twig', [
            'controller_name' => 'MailerController',
        ]);
    }
}
```
https://github.com/idani/symfony_mailer_tutorial2/commit/91811b3273a7010af25cb132831c4c1a107d64a6

MailDevではこのように受信ができました。
パッと見はよさそうです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/133291/c0f081a8-a31f-c0d4-3399-44f90feb6314.png)

メールソースを見ると、**quoted-printable** でエンコーディングをされていました。

```php:メールソース(日本語)
From: =?utf-8?Q?=E9=80=81=E4=BF=A1=E8=80=85=E5=90=8D?= <hello@example.com>
To: =?utf-8?Q?=E5=8F=97=E4=BF=A1=E8=80=85=E5=90=8D?= <you@example.com>
Subject: =?utf-8?Q?=E6=97=A5=E6=9C=AC=E8=AA=9E=E3=81=AE?=
 =?utf-8?Q?=E3=82=B5=E3=83=96=E3=82=B8=E3=82=A7?=
 =?utf-8?Q?=E3=82=AF=E3=83=88=E3=81=AB=E3=81=AA?=
 =?utf-8?Q?=E3=82=8A=E3=81=BE=E3=81=99=E3=80=82?=
 =?utf-8?Q?=E9=95=B7=E3=81=8F=E3=81=AA=E3=82=8B?=
 =?utf-8?Q?=E3=81=A8=E6=96=87=E5=AD=97=E5=8C=96?=
 =?utf-8?Q?=E3=81=91=E3=81=99=E3=82=8B=E3=81=A8?=
 =?utf-8?Q?=E3=81=84=E3=81=86=E8=A9=B1=E3=82=82?=
 =?utf-8?Q?=E3=81=82=E3=82=8A=E3=81=BE=E3=81=99?=
 =?utf-8?Q?=E3=81=AE=E3=81=A7=E3=80=81=E9=95=B7?=
 =?utf-8?Q?=E3=81=8F=E6=9B=B8=E3=81=84=E3=81=A6?=
 =?utf-8?Q?=E3=81=BF=E3=81=BE=E3=81=99=E3=80=82?=
 =?utf-8?Q?=E3=81=93=E3=82=8C=E3=81=90=E3=82=89?=
 =?utf-8?Q?=E3=81=84=E9=95=B7=E3=81=84=E3=81=A8?=
 =?utf-8?Q?=E3=81=A9=E3=81=86=E3=81=8B=E3=81=AA=EF=BC=9F?=
Message-ID: <964a02541e6dc9af98f5af3f31b3bf77@example.com>
MIME-Version: 1.0
Date: Wed, 04 Dec 2019 15:22:13 +0900
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: quoted-printable

https://symfony.com/why-use-a-framework

=E3=83=95=E3=83=AC=E3=83=BC=
=E3=83=A0=E3=83=AF=E3=83=BC=E3=82=AF=E3=82=92=E4=BD=BF=E7=94=A8=E3=81=99=
=E3=82=8B=E5=BF=85=E8=A6=81=E3=81=8C=E3=81=82=E3=82=8B=E3=81=AE=E3=81=AF=
=E3=81=AA=E3=81=9C=E3=81=A7=E3=81=99=E3=81=8B=EF=BC=9F
=E3=83=95=E3=83=
=AC=E3=83=BC=E3=83=A0=E3=83=AF=E3=83=BC=E3=82=AF=E3=81=AF=E7=B5=B6=E5=AF=
=BE=E3=81=AB=E5=BF=85=E8=A6=81=E3=81=A8=E3=81=84=E3=81=86=E3=82=8F=E3=81=
=91=E3=81=A7=E3=81=AF=E3=81=82=E3=82=8A=E3=81=BE=E3=81=9B=E3=82=93=E3=80=
以下省略
```

# Base64で送信する

日本語は送信ができるようですが、エンコーディングが<code>quoted-printable</code>なので、いろいろとデータ量が増えています。日本だとbase64が主流なので<code>base64</code>に変更できるかトライしてみます。

Emailクラスでメールデータを作成しますので、[Email.php](https://github.com/symfony/symfony/blob/4af59c250fea4b4bd479b4ce13062399597a5821/src/Symfony/Component/Mime/Email.php#L25)を見ていきます。

そうすると、EmailクラスがMimeコンポーネントに所属していることがわかりました！

## メールデータをカスタマイズする

[The Mime Component](https://symfony.com/doc/current/components/mime.html)ドキュメントに目を通すと、Emailクラスは高レベルAPI、Messageクラスは低レベルAPIと記載があります。

そして、[Creating Raw Email Messages](https://symfony.com/doc/current/components/mime.html#creating-raw-email-messages)として、直接メッセージを作成する方法が記載されていました。

```php:MIMEコンポーネントドキュメント内のサンプル
use Symfony\Component\Mime\Header\Headers;
use Symfony\Component\Mime\Message;
use Symfony\Component\Mime\Part\Multipart\AlternativePart;
use Symfony\Component\Mime\Part\TextPart;

$headers = (new Headers())
    ->addMailboxListHeader('From', ['fabien@symfony.com'])
    ->addMailboxListHeader('To', ['foo@example.com'])
    ->addTextHeader('Subject', 'Important Notification')
;

$textContent = new TextPart('Lorem ipsum...');
$htmlContent = new TextPart('<h1>Lorem ipsum</h1> <p>...</p>', 'html');
$body = new AlternativePart($textContent, $htmlContent);

$email = new Message($headers, $body);
```
## 本文をBase64でエンコードする

サンプルを見ると、メール本文は、[TextPartクラス](https://github.com/symfony/symfony/blob/4.4/src/Symfony/Component/Mime/Part/TextPart.php#L38)が作成しています。

[<code>TextPartクラス</code>のコンストラクタ](https://github.com/symfony/symfony/blob/7f567584a502d75c186a8b7a7e21bcdb53f9082e/src/Symfony/Component/Mime/Part/TextPart.php#L38)を見ると、**ビンゴです！**
エンコーディングを引数に取っていました。

```php:/vendor/symfony/mime/Header/AbstractHeader.php(38)
public function __construct($body, ?string $charset = 'utf-8', $subtype = 'plain', string $encoding = null)
    {
        parent::__construct();

           ~~~中略~~~

        if (null === $encoding) {
            $this->encoding = $this->chooseEncoding();
        } else {
            if ('quoted-printable' !== $encoding && 'base64' !== $encoding && '8bit' !== $encoding) {
                throw new InvalidArgumentException(sprintf('The encoding must be one of "quoted-printable", "base64", or "8bit" ("%s" given).', $encoding));
            }
            $this->encoding = $encoding;
        }
    }
```

## メールアドレスの名前部分をBase64でエンコードする

ヘッダーは、[Headersクラス](https://github.com/symfony/symfony/blob/4.4/src/Symfony/Component/Mime/Header/Headers.php)で定義していますが、エンコーディングについては、基底クラスの[AbstractHeaderクラス](https://github.com/symfony/symfony/blob/4.4/src/Symfony/Component/Mime/Header/AbstractHeader.php)で、<code>quoted-printable</code>に固定されていました。

```php:/vendor/symfony/mime/Header/AbstractHeader.php
namespace Symfony\Component\Mime\Header;
use Symfony\Component\Mime\Encoder\QpMimeHeaderEncoder;
/**
 * An abstract base MIME Header.
 *
 * @author Chris Corbyn
 */
abstract class AbstractHeader implements HeaderInterface
{
    private static $encoder;

~~~中略~~~
    /**
     * Get a token as an encoded word for safe insertion into headers.
     */
    protected function getTokenAsEncodedWord(string $token, int $firstLineOffset = 0): string
    {
        if (null === self::$encoder) {
 ここで固定=>     self::$encoder = new QpMimeHeaderEncoder();
        }
~~~省略~~~
```

ただし、エンコーディングするのは条件があるようです。

[MailboxListHeader.php(106)](https://github.com/symfony/symfony/blob/7f567584a502d75c186a8b7a7e21bcdb53f9082e/src/Symfony/Component/Mime/Header/MailboxListHeader.php#L106)で、<code>$this->createPhrase</code>で名前をエンコードしています。

```php:/vendor/symfony/mime/Header/MailboxListHeader.php(106)
    public function getAddressStrings(): array
    {
        $strings = [];
        foreach ($this->addresses as $address) {
            $str = $address->getEncodedAddress();
            if ($name = $address->getName()) {
                $str = $this->createPhrase($this, $name, $this->getCharset(), !$strings).' <'.$str.'>';
            }
            $strings[] = $str;
        }

        return $strings;
    }
```
これは[AbstractHeader.php(88)](https://github.com/symfony/symfony/blob/7f567584a502d75c186a8b7a7e21bcdb53f9082e/src/Symfony/Component/Mime/Header/AbstractHeader.php#L88)で定義されていて、<code>preg_match('/^'.self::PHRASE_PATTERN.'$/D', $phraseStr)</code>にマッチしなければ<code>$phraseStr = $this->encodeWords($header, $string, $usedLength);</code>でエンコードするようです。

```php:/vendor/symfony/mime/Header/AbstractHeader.php(88)
    protected function createPhrase(HeaderInterface $header, string $string, string $charset, bool $shorten = false): string
    {
        // Treat token as exactly what was given
        $phraseStr = $string;
        // If it's not valid
        if (!preg_match('/^'.self::PHRASE_PATTERN.'$/D', $phraseStr)) {
            // .. but it is just ascii text, try escaping some characters
            // and make it a quoted-string
            if (preg_match('/^[\x00-\x08\x0B\x0C\x0E-\x7F]*$/D', $phraseStr)) {
                foreach (['\\', '"'] as $char) {
                    $phraseStr = str_replace($char, '\\'.$char, $phraseStr);
                }
                $phraseStr = '"'.$phraseStr.'"';
            } else {
                // ... otherwise it needs encoding
                // Determine space remaining on line if first line
                if ($shorten) {
                    $usedLength = \strlen($header->getName().': ');
                } else {
                    $usedLength = 0;
                }
                $phraseStr = $this->encodeWords($header, $string, $usedLength);
            }
        }
        return $phraseStr;
    }
```

<code>PHRASE_PATTERN</code>が[AbstractHeader.php(23)](https://github.com/symfony/symfony/blob/7f567584a502d75c186a8b7a7e21bcdb53f9082e/src/Symfony/Component/Mime/Header/AbstractHeader.php#L23)で定義されていますが、長くて意味がわかりません。

たぶん、エンコード済みならスキップする処理じゃないかな？と予想します。
そしたら、名前は、<code>mb_encode_mimeheader</code>して渡せば良さそうです。

```php:/vendor/symfony/mime/Header/AbstractHeader.php(23)
const PHRASE_PATTERN = '(?:(?:(?:(?:(?:(?:(?:[ \t]*(?:\r\n))?[ \t])?(\((?:(?:(?:[ \t]*(?:\r\n))?[ \t])|(?:(?:[\x01-\x08\x0B\x0C\x0E-\x19\x7F]|[\x21-\x27\x2A-\x5B\x5D-\x7E])|(?:\\[\x00-\x08\x0B\x0C\x0E-\x7F])|(?1)))*(?:(?:[ \t]*(?:\r\n))?[ \t])?\)))*(?:(?:(?:(?:[ \t]*(?:\r\n))?[ \t])?(\((?:(?:(?:[ \t]*(?:\r\n))?[ \t])|(?:(?:[\x01-\x08\x0B\x0C\x0E-\x19\x7F]|[\x21-\x27\x2A-\x5B\x5D-\x7E])|(?:\\[\x00-\x08\x0B\x0C\x0E-\x7F])|(?1)))*(?:(?:[ \t]*(?:\r\n))?[ \t])?\)))|(?:(?:[ \t]*(?:\r\n))?[ \t])))?[a-zA-Z0-9!#\$%&\'\*\+\-\/=\?\^_`\{\}\|~]+(?:(?:(?:(?:[ \t]*(?:\r\n))?[ \t])?(\((?:(?:(?:[ \t]*(?:\r\n))?[ \t])|(?:(?:[\x01-\x08\x0B\x0C\x0E-\x19\x7F]|[\x21-\x27\x2A-\x5B\x5D-\x7E])|(?:\\[\x00-\x08\x0B\x0C\x0E-\x7F])|(?1)))*(?:(?:[ \t]*(?:\r\n))?[ \t])?\)))*(?:(?:(?:(?:[ \t]*(?:\r\n))?[ \t])?(\((?:(?:(?:[ \t]*(?:\r\n))?[ \t])|(?:(?:[\x01-\x08\x0B\x0C\x0E-\x19\x7F]|[\x21-\x27\x2A-\x5B\x5D-\x7E])|(?:\\[\x00-\x08\x0B\x0C\x0E-\x7F])|(?1)))*(?:(?:[ \t]*(?:\r\n))?[ \t])?\)))|(?:(?:[ \t]*(?:\r\n))?[ \t])))?)|(?:(?:(?:(?:(?:[ \t]*(?:\r\n))?[ \t])?(\((?:(?:(?:[ \t]*(?:\r\n))?[ \t])|(?:(?:[\x01-\x08\x0B\x0C\x0E-\x19\x7F]|[\x21-\x27\x2A-\x5B\x5D-\x7E])|(?:\\[\x00-\x08\x0B\x0C\x0E-\x7F])|(?1)))*(?:(?:[ \t]*(?:\r\n))?[ \t])?\)))*(?:(?:(?:(?:[ \t]*(?:\r\n))?[ \t])?(\((?:(?:(?:[ \t]*(?:\r\n))?[ \t])|(?:(?:[\x01-\x08\x0B\x0C\x0E-\x19\x7F]|[\x21-\x27\x2A-\x5B\x5D-\x7E])|(?:\\[\x00-\x08\x0B\x0C\x0E-\x7F])|(?1)))*(?:(?:[ \t]*(?:\r\n))?[ \t])?\)))|(?:(?:[ \t]*(?:\r\n))?[ \t])))?"((?:(?:[ \t]*(?:\r\n))?[ \t])?(?:(?:[\x01-\x08\x0B\x0C\x0E-\x19\x7F]|[\x21\x23-\x5B\x5D-\x7E])|(?:\\[\x00-\x08\x0B\x0C\x0E-\x7F])))*(?:(?:[ \t]*(?:\r\n))?[ \t])?"(?:(?:(?:(?:[ \t]*(?:\r\n))?[ \t])?(\((?:(?:(?:[ \t]*(?:\r\n))?[ \t])|(?:(?:[\x01-\x08\x0B\x0C\x0E-\x19\x7F]|[\x21-\x27\x2A-\x5B\x5D-\x7E])|(?:\\[\x00-\x08\x0B\x0C\x0E-\x7F])|(?1)))*(?:(?:[ \t]*(?:\r\n))?[ \t])?\)))*(?:(?:(?:(?:[ \t]*(?:\r\n))?[ \t])?(\((?:(?:(?:[ \t]*(?:\r\n))?[ \t])|(?:(?:[\x01-\x08\x0B\x0C\x0E-\x19\x7F]|[\x21-\x27\x2A-\x5B\x5D-\x7E])|(?:\\[\x00-\x08\x0B\x0C\x0E-\x7F])|(?1)))*(?:(?:[ \t]*(?:\r\n))?[ \t])?\)))|(?:(?:[ \t]*(?:\r\n))?[ \t])))?))+?)';

```

## サブジェクトをbase64でエンコードする

次にサブジェクトですが、[Headers.php(112)](https://github.com/symfony/symfony/blob/7f567584a502d75c186a8b7a7e21bcdb53f9082e/src/Symfony/Component/Mime/Header/Headers.php#L112)からたどっていくと、<code>AbstractHeaderクラス</code>を継承した<code>UnstructuredHeaderクラス</code>に格納されていました。

```php:/vendor/symfony/mime/Header/Headers.php(112)
   /**
     * @return $this
     */
    public function addTextHeader(string $name, string $value): self
    {
        return $this->add(new UnstructuredHeader($name, $value));
    }
```

最終的には、[UnstructuredHeader.php(65)](https://github.com/symfony/symfony/blob/7f567584a502d75c186a8b7a7e21bcdb53f9082e/src/Symfony/Component/Mime/Header/UnstructuredHeader.php#L65)でエンコードして渡しています。

```php:/vendor/symfony/mime/Header/UnstructuredHeader.php(65)
    /**
     * Get the value of this header prepared for rendering.
     */
    public function getBodyAsString(): string
    {
        return $this->encodeWords($this, $this->value);
    }
```

これは<code>AbstractHeader</code>で定義されていて、<code>$this->tokenNeedsEncoding</code>があります。

```php:/vendor/symfony/mime/Header/AbstractHeader.php(120)
    /**
     * Encode needed word tokens within a string of input.
     */
    protected function encodeWords(HeaderInterface $header, string $input, int $usedLength = -1): string
    {
        $value = '';
        $tokens = $this->getEncodableWordTokens($input);
        foreach ($tokens as $token) {
            // See RFC 2822, Sect 2.2 (really 2.2 ??)
            if ($this->tokenNeedsEncoding($token)) {
                // Don't encode starting WSP
                $firstChar = substr($token, 0, 1);
                switch ($firstChar) {
                    case ' ':
                    case "\t":
                        $value .= $firstChar;
                        $token = substr($token, 1);
                }
                if (-1 == $usedLength) {
                    $usedLength = \strlen($header->getName().': ') + \strlen($value);
                }
                $value .= $this->getTokenAsEncodedWord($token, $usedLength);
            } else {
                $value .= $token;
            }
        }
        return $value;
    }
```

[tokenNeedsEncoding()](https://github.com/symfony/symfony/blob/7f567584a502d75c186a8b7a7e21bcdb53f9082e/src/Symfony/Component/Mime/Header/AbstractHeader.php#L148)を確認すると、制御文字と改行が含まれてれば、エンコードが必要という判定をしています。

ですから、こちらもエンコード済みなら、この判定でFalseになって、そのまま使ってくれそうです。

```php:/vendor/symfony/mime/Header/AbstractHeader.php(148)
    protected function tokenNeedsEncoding(string $token): bool
    {
        return (bool) preg_match('~[\x00-\x08\x10-\x19\x7F-\xFF\r\n]~', $token);
    }
```


サンプルを以下のように書き換えます。

```php:MailerController.php(その３)
class MailerController extends AbstractController
{
    /**
     * @Route("/mailer", name="mailer")
     */
    public function index(MailerInterface $mailer)
    {
        $body = <<<EOL
https://symfony.com/why-use-a-framework

フレームワークを使用する必要があるのはなぜですか？
フレームワークは絶対に必要というわけではありません。それは、あなたがより良く、より速く開発するのを
~~~中略~~~
EOL;

        $headers = (new Headers())
            ->addMailboxListHeader('From', [new Address('hello@example.com', mb_encode_mimeheader('送信者名'))])
            ->addMailboxListHeader('To', [new Address('you@example.com', mb_encode_mimeheader('受信者名'))])
            ->addTextHeader('Subject', mb_encode_mimeheader('日本語のサブジェクトになります。長くなると文字化けするという話もありますので、長く書いてみます。これぐらい長いとどうかな？'))
        ;

        $textContent = new TextPart($body, 'utf-8', 'plain', 'base64');
        $email = new Message($headers, $textContent);

        $mailer->send($email);

        return $this->render('mailer/index.html.twig', [
            'controller_name' => 'MailerController',
        ]);
    }
}

```
https://github.com/idani/symfony_mailer_tutorial2/commit/a3085a095fb71068b5bb856f31c9d9d6ef4d2d9c

これでBase64のいい感じなメールがでるかと思いましたが、MailDevではサブジェクトが文字化けしてしまいました。。。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/133291/4a8f3072-962e-abb8-b891-1c00801cc5d2.png)

メールソースを確認すると、
本文は想定通りのUTF-8なのですが、
Subjectは、<code>base64</code>でエンコードしたデータを渡しているのですが、<code>quoted-printable</code>で更にエンコードがされていました。

また、From/Toは<code>iso-2022-jp</code>が使われています。


```php:メールソース
From: =?ISO-2022-JP?B?GyRCQXc/LjxUTD4bKEI=?= <hello@example.com>
To: =?ISO-2022-JP?B?GyRCPHU/LjxUTD4bKEI=?= <you@example.com>
Subject: =?utf-8?Q?=3D=3FISO-2022-JP=3FB=3FGyRCRnxLXDhsJE4lNSVWJTglJyUv?=
 =?utf-8?Q?JUgkSyRKJGokXiQ5ISNEOSQvGyhC=3F=3D?=
 =?utf-8?Q?_=3D=3FISO-2022-JP=3FB=3FGyRCJEokayRISjg7ejI9JDEkOSRrJEgkJCQ?=
 =?utf-8?Q?mT0MkYiQiJGokXiQ5GyhC=3F=3D?=
 =?utf-8?Q?_=3D=3FISO-2022-JP=3FB=3FGy?=
 =?utf-8?Q?RCJE4kRyEiRDkkLz1xJCQkRiRfJF4kOSEjJDMkbCQwJGkkJEQ5GyhC=3F=3D?=
 =?utf-8?Q??= =?ISO-2022-JP?B?GyRCJCQkSCRJJCYkKyRKISkbKEI=?=
Message-ID: <d3e1f390c82ee075dbcf96b7565bccff@example.com>
MIME-Version: 1.0
Date: Wed, 04 Dec 2019 16:19:51 +0900
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: base64

aHR0cHM6Ly9zeW1mb255LmNvbS93aHktdXNlLWEtZnJhbWV3b3JrCgrjg5Xjg6zjg7zjg6Djg6/j
g7zjgq/jgpLkvb/nlKjjgZnjgovlv4XopoHjgYzjgYLjgovjga7jga/jgarjgZzjgafjgZnjgYvv
vJ8K44OV44Os44O844Og44Ov44O844Kv44Gv57W25a++44Gr5b+F6KaB44Go44GE44GG44KP44GR
44Gn44Gv44GC44KK44G+44Gb44KT44CC44Gd44KM44Gv44CB44GC44Gq44Gf44GM44KI44KK6Imv
44GP44CB44KI44KK6YCf44GP6ZaL55m644GZ44KL44Gu44KS5Yqp44GR44KL44Gf44KB44Gr5Yip
55So44Gn44GN44KL44OE44O844Or44Gu44CM44Gh44KH44GG44Gp44CNMeOBpOOBp+OBme+8gQoK
44OV44Os44O844Og44Ov44O844Kv44Gv44CB44OT44K444ON44K544Or44O844Or44Gr5a6M5YWo
44Gr5rqW5oug44GX44CB5qeL6YCg5YyW44GV44KM44CB44Oh44Oz44OG44OK44Oz44K544Go44Ki
44OD44OX44Kw44Os44O844OJ44Gu5Lih5pa544GM5Y+v6IO944Gq44Ki44OX44Oq44Kx44O844K3
```

<code>iso-2022-jp</code>については、よくある話で、<code>mb_language("uni")</code>を宣言すれば良さそうです。

```php
        mb_language("uni");
        mb_internal_encoding("UTF-8");
```

サブジェクトの方で<code>quoted-printable</code>になっていますので、
さきほどの<code>AbstractHeader.php(120)のencodeWords()</code>を更に見ていきましょう。


```php:/vendor/symfony/mime/Header/AbstractHeader.php(120)
    /**
     * Encode needed word tokens within a string of input.
     */
    protected function encodeWords(HeaderInterface $header, string $input, int $usedLength = -1): string
    {
        $value = '';
        $tokens = $this->getEncodableWordTokens($input);
        foreach ($tokens as $token) {
            // See RFC 2822, Sect 2.2 (really 2.2 ??)
            if ($this->tokenNeedsEncoding($token)) {
                // Don't encode starting WSP
                $firstChar = substr($token, 0, 1);
                switch ($firstChar) {
                    case ' ':
                    case "\t":
                        $value .= $firstChar;
                        $token = substr($token, 1);
                }
                if (-1 == $usedLength) {
                    $usedLength = \strlen($header->getName().': ') + \strlen($value);
                }
                $value .= $this->getTokenAsEncodedWord($token, $usedLength);
            } else {
                $value .= $token;
            }
        }
        return $value;
    }
```

<code>$this->getEncodableWordTokens($input)</code>を見ていなかったので見てみます。

```php:/vendor/symfony/mime/Header/AbstractHeader.php(158)
    protected function getEncodableWordTokens(string $string): array
    {
        $tokens = [];
        $encodedToken = '';
        // Split at all whitespace boundaries
        foreach (preg_split('~(?=[\t ])~', $string) as $token) {
            if ($this->tokenNeedsEncoding($token)) {
                $encodedToken .= $token;
            } else {
                if (\strlen($encodedToken) > 0) {
                    $tokens[] = $encodedToken;
                    $encodedToken = '';
                }
                $tokens[] = $token;
            }
        }
        if (\strlen($encodedToken)) {
            $tokens[] = $encodedToken;
        }
        return $tokens;
    }
```

<code>preg_split('~(?=[\t ])~', $string)</code>で、<code>=?UTF-8?B?77yf?='</code>の最後の<code>?=<タブ or スペース></code>で分割しています。

それを<code>$this->tokenNeedsEncoding</code>でエンコード済みかチェックしているのですが。。。

サブジェクトを<code>mb_encode_mimeheader()</code>した結果は、以下になります。

```php
=?UTF-8?B?5pel5pys6Kqe44Gu44K144OW44K444Kn44Kv44OI44Gr44Gq44KK44G+44GZ?=
 =?UTF-8?B?44CC6ZW344GP44Gq44KL44Go5paH5a2X5YyW44GR44GZ44KL44Go44GE44GG?=
 =?UTF-8?B?6Kmx44KC44GC44KK44G+44GZ44Gu44Gn44CB6ZW344GP5pu444GE44Gm44G/?=
 =?UTF-8?B?44G+44GZ44CC44GT44KM44GQ44KJ44GE6ZW344GE44Go44Gp44GG44GL44Gq?=
 =?UTF-8?B?77yf?='
```

さきほどの、<code>preg_split('~(?=[\t ])~', $string)</code>で分割をすると、分割したデータの最後に改行がついてしまうみたいで、その後の<code>$this->tokenNeedsEncoding($token)</code>でエンコード済みと判定されないようです。

それでは、<code>mb_encode_mimeheader()</code>の結果から改行コードを外してみます。

サンプルコードを以下のように修正しました。

```php:MailerController.php(その４)
class MailerController extends AbstractController
{
    /**
     * @Route("/mailer", name="mailer")
     */
    public function index(MailerInterface $mailer)
    {
        $body = <<<EOL
https://symfony.com/why-use-a-framework

フレームワークを使用する必要があるのはなぜですか？
フレームワークは絶対に必要というわけではありません。それは、あなたがより良く、より速く開発するのを
~~~中略~~~
EOL;

        mb_language("uni");
        mb_internal_encoding("UTF-8");

        $subject = mb_encode_mimeheader('日本語のサブジェクトになります。長くなると文字化けするという話もありますので、長く書いてみます。これぐらい長いとどうかな？');
        $subject = str_replace("\r\n", '', $subject);

        $headers = (new Headers())
            ->addMailboxListHeader('From', [new Address('hello@example.com', mb_encode_mimeheader('送信者名'))])
            ->addMailboxListHeader('To', [new Address('you@example.com', mb_encode_mimeheader('受信者名'))])
            ->addTextHeader('Subject', $subject)
        ;

        $textContent = new TextPart($body, 'utf-8', 'plain', 'base64');
        $email = new Message($headers, $textContent);

        $mailer->send($email);

        return $this->render('mailer/index.html.twig', [
            'controller_name' => 'MailerController',
        ]);
    }
}

```
https://github.com/idani/symfony_mailer_tutorial2/commit/71283d6216dcc3f4e1b710a0d2dd452ca63a87e9

実行してみると、subjcetの文字化けが治りましたね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/133291/74960753-d3d6-76ae-7431-ef8b2a93fccf.png)

メールソースを確認しましたが、想定どおりです。

```php:メールソース
From: =?UTF-8?B?6YCB5L+h6ICF5ZCN?= <hello@example.com>
To: =?UTF-8?B?5Y+X5L+h6ICF5ZCN?= <you@example.com>
Subject: =?UTF-8?B?5pel5pys6Kqe44Gu44K144OW44K444Kn44Kv44OI44Gr44Gq44KK44G+44GZ?=
 =?UTF-8?B?44CC6ZW344GP44Gq44KL44Go5paH5a2X5YyW44GR44GZ44KL44Go44GE44GG?=
 =?UTF-8?B?6Kmx44KC44GC44KK44G+44GZ44Gu44Gn44CB6ZW344GP5pu444GE44Gm44G/?=
 =?UTF-8?B?44G+44GZ44CC44GT44KM44GQ44KJ44GE6ZW344GE44Go44Gp44GG44GL44Gq?=
 =?UTF-8?B?77yf?=
Message-ID: <1916f37d03fc486c2babe15a606eda8f@example.com>
MIME-Version: 1.0
Date: Thu, 05 Dec 2019 11:19:48 +0900
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: base64

aHR0cHM6Ly9zeW1mb255LmNvbS93aHktdXNlLWEtZnJhbWV3b3JrCgrjg5Xjg6zjg7zjg6Djg6/j
g7zjgq/jgpLkvb/nlKjjgZnjgovlv4XopoHjgYzjgYLjgovjga7jga/jgarjgZzjgafjgZnjgYvv
vJ8K44OV44Os44O844Og44Ov44O844Kv44Gv57W25a++44Gr5b+F6KaB44Go44GE44GG44KP44GR
44Gn44Gv44GC44KK44G+44Gb44KT44CC44Gd44KM44Gv44CB44GC44Gq44Gf44GM44KI44KK6Imv
以下省略

```

## base64のまとめ

ということで、サブジェクトのワークアラウンドが微妙ですが、UTF-8+base64での送信が実現できました。

パッチを送る感じになるのかな？


# iso-2022-jpで送信する

それでは、文字コードが<code>iso-2022-jp</code>で、エンコーディングが<code>7bit</code>にチャレンジしてみます。

ヘッダーは、<code>mb_encode_mimeheader()</code>で準備ができますし、本文が7bitが通れば行けそうです。

サンプルコードを以下のように修正します。

```php:MailerController.php(その５)
class MailerController extends AbstractController
{
    /**
     * @Route("/mailer", name="mailer")
     */
    public function index(MailerInterface $mailer)
    {
        $body = <<<EOL
https://symfony.com/why-use-a-framework

フレームワークを使用する必要があるのはなぜですか？
フレームワークは絶対に必要というわけではありません。それは、あなたがより良く、より速く開発するのを
~~~中略~~~
EOL;

        $subject = mb_encode_mimeheader('日本語のサブジェクトになります。長くなると文字化けするという話もありますので、長く書いてみます。これぐらい長いとどうかな？');
        $subject = str_replace("\r\n", '', $subject);

        $headers = (new Headers())
            ->addMailboxListHeader('From', [new Address('hello@example.com', mb_encode_mimeheader('送信者名'))])
            ->addMailboxListHeader('To', [new Address('you@example.com', mb_encode_mimeheader('受信者名'))])
            ->addTextHeader('Subject', $subject)
        ;

        $textContent = new TextPart($body, 'iso-2022-jp', 'plain', '7bit');
        $email = new Message($headers, $textContent);

        $mailer->send($email);

        return $this->render('mailer/index.html.twig', [
            'controller_name' => 'MailerController',
        ]);
    }
}

```
https://github.com/idani/symfony_mailer_tutorial2/commit/5bc62be13f645e0f0554dabebb8ce7bd095fc6ad

以下のようなエラーがでました。
<code>7bit</code>のエンコーディングに対応していないようです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/133291/a388c9dd-a706-47a8-9c3e-75310c4dbaef.png)

しょうがないので、エンコーディングを<code>base64</code>に変更してトライしてみます。

```php:MailerController.php(その６)
class MailerController extends AbstractController
{
    /**
     * @Route("/mailer", name="mailer")
     */
    public function index(MailerInterface $mailer)
    {
        $body = <<<EOL
https://symfony.com/why-use-a-framework

フレームワークを使用する必要があるのはなぜですか？
フレームワークは絶対に必要というわけではありません。それは、あなたがより良く、より速く開発するのを
~~~中略~~~
EOL;

        $subject = mb_encode_mimeheader('日本語のサブジェクトになります。長くなると文字化けするという話もありますので、長く書いてみます。これぐらい長いとどうかな？');
        $subject = str_replace("\r\n", '', $subject);

        $headers = (new Headers())
            ->addMailboxListHeader('From', [new Address('hello@example.com', mb_encode_mimeheader('送信者名'))])
            ->addMailboxListHeader('To', [new Address('you@example.com', mb_encode_mimeheader('受信者名'))])
            ->addTextHeader('Subject', $subject)
        ;

        $textContent = new TextPart($body, 'iso-2022-jp', 'plain', 'base64');
        $email = new Message($headers, $textContent);

        $mailer->send($email);

        return $this->render('mailer/index.html.twig', [
            'controller_name' => 'MailerController',
        ]);
    }
}

```
https://github.com/idani/symfony_mailer_tutorial2/commit/71f73ca030d516c4e9ff95521798f7b65344dbd1

本文が痛い感じになってしまいました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/133291/843ec642-ecc4-783d-336e-d92d2ad1818f.png)

こちらがメールソースになります。From/To/Subjectのヘッダーは、想定通りですし、<code>Content-Type</code>も想定どおりです。

```php:メールソース
From: =?ISO-2022-JP?B?GyRCQXc/LjxUTD4bKEI=?= <hello@example.com>
To: =?ISO-2022-JP?B?GyRCPHU/LjxUTD4bKEI=?= <you@example.com>
Subject: =?ISO-2022-JP?B?GyRCRnxLXDhsJE4lNSVWJTglJyUvJUgkSyRKJGokXiQ5ISNEOSQvGyhC?=
 =?ISO-2022-JP?B?GyRCJEokayRISjg7ejI9JDEkOSRrJEgkJCQmT0MkYiQiJGokXiQ5GyhC?=
 =?ISO-2022-JP?B?GyRCJE4kRyEiRDkkLz1xJCQkRiRfJF4kOSEjJDMkbCQwJGkkJEQ5GyhC?=
 =?ISO-2022-JP?B?GyRCJCQkSCRJJCYkKyRKISkbKEI=?=
Message-ID: <e2e57a249f7daeaff165d25eddc2a876@example.com>
MIME-Version: 1.0
Date: Thu, 05 Dec 2019 12:20:15 +0900
Content-Type: text/plain; charset=iso-2022-jp
Content-Transfer-Encoding: base64

aHR0cHM6Ly9zeW1mb255LmNvbS93aHktdXNlLWEtZnJhbWV3b3JrCgrjg5Xjg6zjg7zjg6Djg6/j
g7zjgq/jgpLkvb/nlKjjgZnjgovlv4XopoHjgYzjgYLjgovjga7jga/jgarjgZzjgafjgZnjgYvv
vJ8K44OV44Os44O844Og44Ov44O844Kv44Gv57W25a++44Gr5b+F6KaB44Go44GE44GG44KP44GR
44Gn44Gv44GC44KK44G+44Gb44KT44CC44Gd44KM44Gv44CB44GC44Gq44Gf44GM44KI44KK6Imv
44GP44CB44KI44KK6YCf44GP6ZaL55m644GZ44KL44Gu44KS5Yqp44GR44KL44Gf44KB44Gr5Yip
55So44Gn44GN44KL44OE44O844Or44Gu44CM44Gh44KH44GG44Gp44CNMeOBpOOBp+OBme+8gQoK
以下、省略
```

またまた、ソースコードを見てみると、<code>TextPartクラス</code>で渡している<code>iso-2022-jp</code>というのは、<code>Content-Type</code>の出力に使うだけで、文字コードの変換はしていませんでした。

一応、[Base64Encoder.php(26)](https://github.com/symfony/symfony/blob/7f567584a502d75c186a8b7a7e21bcdb53f9082e/src/Symfony/Component/Mime/Encoder/Base64Encoder.php#L26)で<code>$charset</code>として文字コードまでもらっているのですが、使っていません；；

```php:/vendor/symfony/mime/Encoder/Base64Encoder.php(26)
class Base64Encoder implements EncoderInterface
{
    public function encodeString(string $string, ?string $charset = 'utf-8', int $firstLineOffset = 0, int $maxLineLength = 0): string
    {
        if (0 >= $maxLineLength || 76 < $maxLineLength) {
            $maxLineLength = 76;
        }

        $encodedString = base64_encode($string);
        $firstLine = '';
        if (0 !== $firstLineOffset) {
            $firstLine = substr($encodedString, 0, $maxLineLength - $firstLineOffset)."\r\n";
            $encodedString = substr($encodedString, $maxLineLength - $firstLineOffset);
        }

        return $firstLine.trim(chunk_split($encodedString, $maxLineLength, "\r\n"));
    }
}

```

そしたら本文も自前で変換する必要がわかったので、以下のようにします。

```php:MailerController.php(その７)
class MailerController extends AbstractController
{
    /**
     * @Route("/mailer", name="mailer")
     */
    public function index(MailerInterface $mailer)
    {
        $body = <<<EOL
https://symfony.com/why-use-a-framework

フレームワークを使用する必要があるのはなぜですか？
フレームワークは絶対に必要というわけではありません。それは、あなたがより良く、より速く開発するのを
~~~中略~~~
EOL;

        $subject = mb_encode_mimeheader('日本語のサブジェクトになります。長くなると文字化けするという話もありますので、長く書いてみます。これぐらい長いとどうかな？');
        $subject = str_replace("\r\n", '', $subject);

        $headers = (new Headers())
            ->addMailboxListHeader('From', [new Address('hello@example.com', mb_encode_mimeheader('送信者名'))])
            ->addMailboxListHeader('To', [new Address('you@example.com', mb_encode_mimeheader('受信者名'))])
            ->addTextHeader('Subject', $subject)
        ;

        $body = mb_convert_encoding($body, 'ISO-2022-JP-MS', 'UTF-8');
        $textContent = new TextPart($body, 'iso-2022-jp', 'plain', 'base64');
        $email = new Message($headers, $textContent);

        $mailer->send($email);

        return $this->render('mailer/index.html.twig', [
            'controller_name' => 'MailerController',
        ]);
    }
}

```
https://github.com/idani/symfony_mailer_tutorial2/commit/dd161129fdcf0ba36bfb93e6461b67f03eac6b59

本文も問題なさそうです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/133291/fdbdf3c6-1a47-8cb5-aa16-cc0a7e70ae16.png)

メールソースはこんな感じ。

```php:メールソース
From: =?ISO-2022-JP?B?GyRCQXc/LjxUTD4bKEI=?= <hello@example.com>
To: =?ISO-2022-JP?B?GyRCPHU/LjxUTD4bKEI=?= <you@example.com>
Subject: =?ISO-2022-JP?B?GyRCRnxLXDhsJE4lNSVWJTglJyUvJUgkSyRKJGokXiQ5ISNEOSQvGyhC?=
 =?ISO-2022-JP?B?GyRCJEokayRISjg7ejI9JDEkOSRrJEgkJCQmT0MkYiQiJGokXiQ5GyhC?=
 =?ISO-2022-JP?B?GyRCJE4kRyEiRDkkLz1xJCQkRiRfJF4kOSEjJDMkbCQwJGkkJEQ5GyhC?=
 =?ISO-2022-JP?B?GyRCJCQkSCRJJCYkKyRKISkbKEI=?=
Message-ID: <e9e0eefd92fd68df3ec3a8bf6133bf9b@example.com>
MIME-Version: 1.0
Date: Thu, 05 Dec 2019 12:36:00 +0900
Content-Type: text/plain; charset=iso-2022-jp
Content-Transfer-Encoding: base64

aHR0cHM6Ly9zeW1mb255LmNvbS93aHktdXNlLWEtZnJhbWV3b3JrCgobJEIlVSVsITwlYCVvITwl
LyRyO0hNUSQ5JGtJLE1XJCwkIiRrJE4kTyRKJDwkRyQ5JCshKRsoQgobJEIlVSVsITwlYCVvITwl
LyRPQGRCUCRLSSxNVyRIJCQkJiRvJDEkRyRPJCIkaiReJDskcyEjJD0kbCRPISIkIiRKJD8kLCRo
JGpOSSQvISIkaCRqQi4kLzMrSC8kOSRrJE4kcj11JDEkayQ/JGEkS014TVEkRyQtJGslRCE8JWsk
TiFWJEEkZyQmJEkhVxsoQjEbJEIkRCRHJDkhKhsoQgoKGyRCJVUlbCE8JWAlbyE8JS8kTyEiJVMl
OCVNJTklayE8JWskSzQwQTQkSz1gNXIkNyEiOT1CJDI9JDUkbCEiJWElcyVGJUolcyU5JEglIiVD
JVclMCVsITwlSSROTj5KfSQsMkRHPSRKJSIlVyVqJTEhPCU3JWclcyRyMytILyQ3JEYkJCRrJEgk
JCQmM048QkAtJHJEczYhJDkkayQ/JGEkRyQ5ISMbKEIKChskQjMrSC88VCQsSEZNUSViJTglZSE8
JWskcjpGTXhNUSQ3JEZCPiROTk4waCRLPThDZiQ5JGskMyRIJEc7fjRWJHJAYUxzJEckLSRrJD8k
以下省略
```

## iso-2022-jpのまとめ

メール本文に<code>7bit</code>エンコーディングが使えなかった。
<code>base64</code>でエンコーディングするなら、迷惑メール判定としては、大きく変更と感じる可能性があります。

つまり迷惑メールに初回は入る可能性があります。

それならISO-2022-jpを捨てて、utf-8にしても結局迷惑メールになりますので大差ありません。

UTF-8だと、顔文字など使える文字の種類が増えるメリットがありますから、UTF-8に乗り換えがよさそうです。

# 個人的なまとめ

symfony/mailerを使う場合は、システム刷新として使うなら、<code>utf-8</code>の文字コード、<code>base64</code>エンコーディングを使うのが良さそうです。

新規システムなら、エンコーディングが<code>quoted-printable</code>になりますが、簡単に使えるのも魅力的だと思います。