C++ にて VMime を使って 添付ファイル付きのメールをファイルから読む

C++ にて VMime を使って ファイルからメールを読む
の続きです。

添付ファイル付きのメールの構造

下記のように、multipartの中に本文と添付ファイルのデータがある。

Content-Type: multipart/mixed;

Content-Type: text/plain; charset=us-ascii
本文のデータ

Content-Type: image/png
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename=sample.png;
添付ファイルのデータ

 メールメッセージから添付ファイルを取得する

下記は、VMime Book に掲載されている例

添付ファイルの操作:添付ファイルヘルパー
https://ken-ohwada.hatenadiary.org/entry/2020/12/30/160715#53

メッセージからすべての添付ファイルを抽出する例

vmime: : shared ptr <vmime: : message> msg; 
// suppose we have a message
const std::vector <ref <const attachment>> atts = attachmentHelper::findAttachmentsInMessage(msg);

この例のままだと下記のエラーになる。

template argument for template type
parameter must be a type

API 仕様と見比べると、誤記みたいだ。

vmime : attachmentHelper Class Reference
https://www.vmime.org/public/documentation/doxygen/classvmime_1_1attachmentHelper.html

下記のようにするとよい。

const std::vector< vmime::shared_ptr< const vmime::attachment > > 
atts = vmime::attachmentHelper::findAttachmentsInMessage(msg);

findAttachmentsInMessage の不具合

本文のパートも添付ファイルとみなされる。

対処療法として、ファイル名のあるなしを判定材料とする。
下記のようにファイル名のあるパートだけを取得する。

 std::vector< vmime::shared_ptr< const vmime::attachment > > atts1 = vmime::attachmentHelper::findAttachmentsInMessage(msg);

 std::vector< vmime::shared_ptr< const vmime::attachment > > atts2;
 
for(auto att: atts1){
    std::string name = att->getName().getBuffer();
    if ( name.length() > 0 ) {
            atts2.push_back(att);
    }
}

添付ファイルをファイルに保存する

下記のようにする

vmime::string name = att->getName().getBuffer();
std::ofstream ofs;                   
  ofs.open( name,  ( std::ios_base::out | std::ios_base::binary) );
vmime::utility::outputStreamAdapter file(ofs);
vmime::shared_ptr< const vmime::contentHandler >  ch = att->getData();
ch->extract(file);

本文を表示する

C++ にて VMime を使って ファイルからメールを読む
で紹介した下記の方法では、本文を取得できない。

 vmime::shared_ptr <vmime::body> body = msg->getBody();

この方法で取得した body はマルチパート部全体である。
ここから本文を取得する。

マルチパート部の中の個々のパートは vmime::bodyPart というクラスになる。

vmime bodyPart Class Reference
https://www.vmime.org/public/documentation/doxygen/classvmime_1_1bodyPart.html

bodyPart は下記のように取得する。

 vmime::shared_ptr <vmime::body> body = msg->getBody();
size_t  count = body->getPartCount();
for(int i=0; i<count; i++){
vmime::shared_ptr<vmime::bodyPart> part =  body->getPartAt(i);

bodyPart は、本文とは限らず 添付ファイルの場合もある。
本文か添付ファイルかは、ContentDisposition で区別する。
本文のときは inline で、添付ファイルのときは attachment である。
bodyPart から ContentDisposition は下記のように取得する。

vmime::shared_ptr< vmime::header >     part_hdr = part->getHeader();
vmime::shared_ptr< const vmime::headerField > hdr_disposition = hdr->ContentDisposition();
vmime::shared_ptr< const vmime::contentDisposition > disposition = hdr_disposition->getValue<vmime::contentDisposition>();
std::string name = disposition->getName();

bodyPart から 本文は下記のように取得する。
まず vmime::body を取得する。

vmime::shared_ptr< vmime::body >   part_body = part->getBody();

vmime::body から本文の取得は、
C++ にて VMime を使って ファイルからメールを読む
と同じ方法でよい。

vmime::utility::outputStreamStringAdapter    out(std::cout);
vmime::shared_ptr<const vmime::contentHandler> cts  = part_body->getContents();
 cts->extract(out);

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