C++にて curlsmtp を使って 日本語メールを送信する

C++にて curlsmtp を使って メールを送信する
の続きです。

プログラム作成の方針

curlsmtp は日本語メールに対応していない。
対応するように改変する。
元のcurlsmtp にはなるべく手を入れず、 クラスを継承して変更する。

日本語メールの仕様

日本語などの非ASCIIの文字セットについては、 RFC2047 で規定されている。

RFC2047 MIME (Multipurpose Internet Mail Extensions) Part Three: Message Header Extensions for Non-ASCII Text
https://www.ietf.org/rfc/rfc2047.txt

初期のインターネットは、8ビット透過ではなく、7ビットASCII文字しか通信できなかった。
そこでMIME (Multipurpose Internet Mail Extensions) が考案された。
バイナリデータは、Base64などの形式でASCII文字列に変換する方式になった。

日本語のメール本文

日本語テキストはバイナリデータとして扱い Base64 形式でASCII文字列に変換する。

元の文字セット (UTF-8)とBase64形式で変換(エンコード)したことを示す2つのヘッダーを追加する。 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: base64

ヘッダー部とメール本文は空行(CRLF)で区切る。

UTF-8 か ISO−2022-JP が一般的。 文字セット(charset) は、IANAでは多くの文字セットが定義されている。

IANA (Internet Assigned Numbers Authority) 文字セット https://www.iana.org/assignments/character-sets/character-sets.xhtml

変換(エンコード)方式は、Base64 が一般的。 RFC2045 では、Base64 の他に、Quoted-Printable も規定されている。

参考: 日本語メールの仕組み
https://sendgrid.kke.co.jp/blog/?p=10958

日本語のメールヘッダー

元の文字セット(UTF-8)とbase64形式で変換(エンコード)したことを示す2つの情報を含めて下記の形式で1つASCII文字列にする。 =?文字セット?エンコード方式?エンコード後の文字列?=

文字セットには、メール本文と同様「UTF-8」という値が入る。 エンコード方式には、B」(Base64)か「Q」(Quoted-Printable)のいずれかの値が入る。

日本語メールの文字セット

歴史的な理由で 7ビットコードである ISO−2022-JP が使われている。
近年は、多くの環境で 標準の文字セットをUTF-8 にしているので、UTF-8で問題ない。
プログラムで ISO−2022-JP を扱うためには、 icu4c などの多言語ライブラリを使用する必要があり、手間が増える。
Windows パソコンで使われている Shit-JIS は、 対応しているメールアプリもあるが、使わないほうが無難。

curlsmtp の改変

日本語用のAPIを追加する

void CurlSmtp2::set_subject_charset( const std::string subject, const std::string charset )
{

    std::string b64;
    base64encode( subject, b64 );

    m_subject_b64.assign( b64 );
    m_subject_charset.assign( charset );

}

 void CurlSmtp2::set_message_charset( const std::string msg, const std::string charset )
{
    std::string b64;
    base64encode( msg, b64 );

    m_msg_b64.assign( b64 );
    m_msg_charset.assign( charset );
}

make_send_message2()を改変する

// subject header
if( !m_subject_b64.empty() &&  
    !m_subject_charset.empty() ) {
            snprintf(buf, BUFSIZE,
            SUBJECT_CHARSET_FORMAT,
            (char *)m_subject_charset.c_str(), 
            (char *)m_subject_b64.c_str() );
} else {
            snprintf(buf, BUFSIZE,
            SUBJECT_FORMAT,
            (char *)subject_.c_str() );
}

// message
if( !m_msg_b64.empty() && !m_msg_charset.empty() )
{
          // header
                snprintf(buf, BUFSIZE,  
                CONTENT_TYPE_CHARSET_FORMAT,
                (char *)m_msg_charset.c_str() ); 

                send_buffer_.push_back( buf );

                send_buffer_.push_back( CONTENT_TRANSFER_BASE64 );

// split body
            send_buffer_.push_back(ENTER);

                send_buffer_.push_back( m_msg_b64 );

} else {

    // split body
            send_buffer_.push_back(ENTER);

        send_buffer_.push_back(message_ + ENTER);
    
}

全体のコードは、githubに公開した。 https://github.com/ohwada/MAC_cpp_Samples/tree/master/curlsmtp2