VMime Book 日本語訳 第5章
2020-07-01 K.OHWADA
原文
VMime Book: A Developer’s Guide To VMime
https://www.vmime.org/public/documentation/book/vmime-book.pdf
第5章 メッセージの解析と構築
ページ内 目次
5.1 メッセージの解析
5.1.1 はじめに
5.1.2 vmime :: messageParserオブジェクトの使用
5.2 メッセージの作成
5.2.1 簡単なメッセージ
5.2.2 添付ファイルの追加
5.2.3 HTMLメッセージと埋め込みオブジェクト
5.3 添付ファイルの操作:添付ファイルヘルパー
第5章 メッセージの解析と構築
Parsing and Building Messages
5.1 メッセージの解析
Parsing messages
5.1.1 はじめに
ntroduction
解析は、メッセージの「テキスト」表現(実際にインターネット上で送信される生データ)からメッセージの構造化表現(たとえば、C ++オブジェクトの階層)を作成するプロセスです。
たとえば、「hello.eml」というファイルに次のメールがあるとします。
Date: Thu, Oct 13 2005 15:22:46 +0200
From: Vincent vincent@vmime.org
To: you@vmime.org
Subject: Hello from VMime!A simple message to test VMime
次のコードスニペットは、このファイルのデータからvmime :: messageオブジェクトを簡単に取得する方法を示しています。
ト5.1:ファイルからのメッセージの解析 Parsing a message from a file
// Read data from file std::ifstream file; file.open("hello.eml", std::ios::in | std::ios::binary); vmime::utility::inputStreamAdapter is(file); vmime::string data; vmime::utility::outputStreamStringAdapter os(data); vmime::utility::bufferedStreamCopy(is , os); // Actually parse the message vmime::shared_ptr <vmime::message> msg = vmime::make_shared<vmime::message>(); msg->parse(data); vmime::shared_ptr <vmime::header> hdr = msg->getHeader(); vmime::shared_ptr <vmime::body> bdy = msg->getBody(); // Now, you can extract some of its components vmime::charset ch(vmime::charsets::UTF_8); std::cout << " The subject of the message is : " << hdr->Subject()->getValue<vmime::text>()->getConvertedText(ch) << std::endl << " It was sent by: " << hdr->From()->getValue<vmime::mailbox>()->getName().getConvertedText(ch) << "( email: " << hdr->From()->getValue<vmime::mailbox>()->getEmail() << " )" << std::endl;
このプログラムの出力は次のとおりです
The subject of the message is: Hello from VMime!
It was sent by: Vincent (email: vincent@vmime.org)
5.1.2 vmime :: messageParserオブジェクトの使用
Using the vmime::messageParser object
vmime :: messageParserオブジェクトを使用すると、より簡単な方法でメッセージを解析できます。
MIMEメッセージ構造を処理せずに、すべてのテキスト部分と添付ファイル、および基本フィールド(expeditor、recipients、subject ...)を取得できます。
リスト5.2:vmime :: messageParserを使用してより複雑なメッセージを解析する
Using vmime::messageParser to parse more complex messages
// Read data from file std::ifstream file; file.open(”hello.eml”, std::ios::in | std::ios::binary); vmime::utility::inputStreamAdapter is(file); vmime:: string data; vmime:: utility ::outputStreamStringAdapter os(data); vmime:: utility ::bufferedStreamCopy(is , os); / Actually parse the message vmime: : shared ptr <vmime: : message> msg = vmime: : make shared <vmime: : message>(); m s g−> p a r s e ( d a t a ) ; // Here start the differences with the previous example vmime::messageParser mp(msg); // Output information about attachments std::cout << ”Message has ” << mp.getAttachmentCount() << ” attachment(s)”<< std::endl; for (int i=0 ; i<mp.getAttachmentCount() ;++i) { vmime::shared ptr <const vmime::attachment> att =mp.getAttachmentAt(i); std::cout << ” − ” << att−>getType().generate() << std::endl; } // Output information about text parts std::cout << ”Message has ” << mp.getTextPartCount() << ” text part(s)” << std::endl; for (int i=0 ; i<mp.getTextPartCount() ;++i) { vmime::shared ptr <const vmime::textPart> tp =mp.getTextPartAt(i); // text/html if { (tp−>getType().getSubType() == vmime::mediaTypes::TEXTHTML) vmime::shared ptr <const vmime::htmlTextPart> htp = vmime::dynamicCast <const vmime::htmlTextPart>(tp); // HTML text is in tp−>getText() // Plain text is in tp−>getPlainText() // Enumerate embedded objects for (int j = 0 ; j < htp−>getObjectCount() ; ++j) { vmime::shared ptr <const vmime::htmlTextPart::embeddedObject> obj = htp−>getObjectAt(j); // Identifier (Content−Id or Content−Location) is obj−>getId() // Object data is in obj−>getData() } } // text/plain or anything else else { // Text is in tp−>getText() }
5.2 メッセージの作成
Building messages
5.2.1 簡単なメッセージ
A simple message
もちろん、MIMEメッセージを構成するさまざまなオブジェクト(パーツ、フィールドなど)を作成することにより、MIMEメッセージを最初から作成できます。 以下はそれを達成する方法の例です:
リスト5.3:最初から簡単なメッセージを作成する
Listing 5.3: Building a simple message from scratch
vmime: : shared ptr <vmime: : message> msg = vmime: : make shared <vmime: : message>(); vmime::shared ptr <vmime::header> hdr = msg−>getHeader(); vmime::shared ptr <vmime::body> bdy = msg−>getBody(); vmime: : shared ptr <vmime: : headerFieldFactory> hfFactory = vmime::headerFieldFactory::getInstance(); // Append a ’Date: ’ field vmime: : shared ptr <vmime: : headerField> dateField = hfFactory−>create(vmime:: fields ::DATE); dateField−>setValue(vmime::datetime::now()); hdr−>appendField(dateField); // Append a ’Subject:’ field vmime: : shared ptr <vmime: : headerField> subjectField = hfFactory−>create(vmime:: fields ::SUBJECT); subjectField−>setValue(vmime::text(”Message subject”)); hdr−>appendField(subjectField); // Append a ’From: ’ field vmime: : shared ptr <vmime: : headerField> fromField = hfFactory−>create(vmime:: fields ::FROM); fromField−>setValue (vmime::make shared <vmime::mailbox>(”me@vmime.org”)); hdr−>appendField(fromField); // Append a ’To:’ field vmime: : shared ptr <vmime: : headerField> toField = hfFactory−>create(vmime:: fields ::TO); vmime: : shared ptr <vmime: : mailboxList> recipients = vmime: : make shared <vmime: : mailboxList>(); r e c i p i e n t s −>a p p e n d M a i l b o x (vmime::make shared <vmime::mailbox>(”you@vmime.org”)); toField−>setValue(recipients ); hdr−>appendField(toField); // Set the body contents bdy−>setContents(vmime: : make shared <vmime: : stringContentHandler> (”This is the text of your message...”)); // Output raw message data to standard output vmime::utility::outputStreamAdapter out(std::cout); msg−>genarate(out);
ご覧のとおり、これは少し面倒です。
うまくいけば、VMimeはメッセージを作成するためのより簡単な方法も提供します。
vmime :: messageBuilderオブジェクトは、カスタマイズ可能な基本的なメッセージを作成できます。
次のコードは、vmime :: messageBuilderオブジェクトを使用して、前の例とまったく同じメッセージを作成するために使用できます
リスト5.4:vmime :: messageBuilderを使用して単純なメッセージを作成する Building a simple message using vmime::messageBuilder
try { vmime::messageBuilder mb; // Fill in some header fields and message body mb.setSubject(vmime::text(”Message subject”)); mb.setExpeditor(vmime::mailbox(”me@vmime.org”)); mb.getRecipients().appendAddress (vmime::make shared <vmime::mailbox>(”you@vmime.org”)); mb.getTextPart()−>setCharset(vmime::charsets::ISO8859 15); mb.getTextPart()−>setText(vmime::make shared <vmime::stringContentHandler> (”This is the text of your message...”)); // Message construction vmime::shared ptr<vmime::message>msg=mb.construct(); // Output raw message data to standard output vmime::utility::outputStreamAdapter out(std::cout); msg−>genarate(out); } // VMime exception catch (vmime::exception& e) { std::cerr << ”vmime::exception: ” << e.what() << std::endl; } // Standard exception catch (std::exception& e) { std::cerr << ”std::exception: ” << e.what() << std::endl; }
5.2.2 添付ファイルの追加
Adding an attachment
添付ファイルの処理は非常に簡単です。
前の例に次のコードを追加して、メッセージにファイルを添付します。
リスト5.5:vmime :: messageBuilderを使用して添付ファイル付きのメッセージを作成する Building a message with an attachment using vmime::messageBuilder
// Create an attachment vmime: : shared ptr <vmime: : fileAttachment> att = vmime::make shared <vmime::fileAttachment> ( /∗ full path to file ∗/ ”/home/vincent/paris.jpg”, /∗ content type ∗/ vmime::mediaType(”image/jpeg), /∗ description ∗/ vmime::text(”My holidays in Paris”) ); // You can also set some infos about the file att−>getFileInfo().setFilename(”paris.jpg”); att−>getFileInfo().setCreationDate (vmime::datetime(”30 Apr 2003 14:30:00 +0200”)); // Add this attachment to the message mb.appendAttachment(att);
5.2.3 HTMLメッセージと埋め込みオブジェクト
VMimeは、HTMLテキストと埋め込みオブジェクト(画像など)を含むMIMEメッセージを作成できる集約メッセージもサポートしています。
集約メッセージの詳細については、RFC-2557(HTMLなどの集約ドキュメントのMIMEカプセル化)を参照してください。
このようなメッセージの作成は、vmime :: messageBuilderオブジェクトを使用して非常に簡単です。
次のコードは、プレーン形式とHTML形式の両方のテキストとJPEG画像を含むメッセージを作成します。
リスト 5.6:vmime :: messageBuilder を使用して画像が埋め込まれたHTMLメッセージを作成する
Building an HTML message with an embedded image using the vmime::messageBuilder
// Fill in some header fields mb.setSubject(vmime::text(”AnHTML message”)); mb.setExpeditor(vmime::mailbox(”me@vmime.org”)); mb.getRecipients().appendAddress (vmime::make shared <vmime::mailbox>(”you@vmime.org”)); // Set the content−type to ”text/html”: a text part factory must be // available for the type you are using. The following code will make // the message builder construct the two text parts. mb.constructTextPart(vmime::mediaType (vmime::mediaTypes::TEXT, vmime::mediaTypes::TEXTHTML)); // Set contents of the text parts; the message is available in two formats: // HTML and plain text . The HTML format also includes an embedded image. vmime::shared ptr <vmime::htmlTextPart> textPart = vmime::dynamicCast <vmime::htmlTextPart>(mb.getTextPart()); // −− Add the JPEG image (the returned identifier is used to identify the // −− embedded object in the HTML text , the famous ”CID”, or ”Content−Id”). // −− Note: you can also read data from a file ; see the next example. const vmime::string id = textPart−>addObject(”<...image data...>”, vmime::mediaType(vmime::mediaTypes::IMAGE, vmime::mediaTypes::IMAGEJPEG)); // −− Set the text textPart−>setCharset(vmime::charsets::ISO8859 15); textPart−>setText(vmime: : make shared <vmime: : stringContentHandler> (”This is the <b>HTML text</b>, and the image:<br/>” ”<img src=\””) + id +vmime::string(”\”/>”)); textPart−>setPlainText(vmime::make shared <vmime::stringContentHandler> (”This is the plain text.”));
これにより、次の構造を持つメッセージが作成されます。
multipart/alternative
text/plain
multipart/related
text/html
image/jpeg
ファイルから埋め込みオブジェクトデータを読み取るようにVMimeに簡単に指示できます。
次のコードは、ファイル/path/to/image.jpgを開き、それを入力ストリームに接続してから、埋め込みオブジェクトを追加します。
vmime:: utility ::fileSystemFactory∗ fs = vmime::platform::getHandler()−>getFileSystemFactory(); vmime : : s h a r e d p t r <vmime : : u t i l i t y : : f i l e > i m a g e F i l e = fs−>create(fs−>stringToPath(”/path/to/image.jpg”)); vmime::shared ptr <vmime::contentHandler> imageCts = vmime::make shared <vmime::streamContentHandler> (imageFile−>getFileReader()−>getInputStream(), imageFile−>getLength()); const vmime:: string cid = textPart.addObject(imageCts, vmime::mediaType(vmime::mediaTypes::IMAGE, vmime::mediaTypes::IMAGEJPEG));
5.3 添付ファイルの操作:添付ファイルヘルパー
Working with attachments: the attachment helper
attachmentHelper オブジェクトを使用すると、messageParser オブジェクトと messageBuilders オブジェクトを使用せずに、メッセージ内のすべての添付ファイルを一覧表示したり、新しい添付ファイルを追加したりできます。メッセージや本文の部分に直接作用します。
これを使用するには、MIMEメッセージで添付ファイルの部分をどのように編成するかについての知識は必要ありません。
次のコードスニペットは、本体部分が添付ファイルであるかどうかをテストし、添付ファイルである場合は、その内容を標準出力に抽出します
リスト5.7:ボディパーツがアタッチメントであるかどうかのテスト Testing if a body part is an attachment
vmime::shared ptr <vmime::bodyPart> part; // suppose we have a body part if (vmime::attachmentHelper::isBodyPartAnAttachment(part)) { // The body part contains an attachment, get it vmime::shared ptr <const vmime::attachment> attach = attachmentHelper::getBodyPartAttachment(part); // Extract attachment data to standard output vmime::utility::outputStreamAdapter out(std::cout); attach->getData()->extract(out); }
メッセージからすべての添付ファイルを簡単に抽出することもできます。
リスト5.8:メッセージからすべての添付ファイルを抽出する Extracting all attachments from a message
vmime: : shared ptr <vmime::message> msg; // suppose we have a message const std::vector <ref <const attachment>> atts = attachmentHelper::findAttachmentsInMessage(msg);
最後に、attachmentHelperオブジェクトを使用して、既存のメッセージに含まれているもの(テキスト部分、添付ファイルなど)に添付ファイルを追加できます。
アルゴリズムは、必要に応じてメッセージの構造を変更できます(たとえば、メッセージに誰も存在しない場合は、マルチパート/混合パートを追加します)。
addAttachment関数を呼び出すだけです。
リスト5.9:既存のメッセージに添付ファイルを追加する Adding an attachment to an existing message
// Create an attachment vmime: : shared ptr <vmime: : fileAttachment> att = vmime::make shared <vmime::fileAttachment> ( /∗ full path to file ∗/ ”/home/vincent/paris.jpg”, /∗ content type ∗/ vmime::mediaType(”image/jpeg), /∗ description ∗/ vmime::text(”My holidays in Paris”) ); // Attach it to the message vmime::attachmentHelper::addAttachment(msg, att);