diff options
Diffstat (limited to 'vmime-master/doc/book/msg.tex')
-rw-r--r-- | vmime-master/doc/book/msg.tex | 430 |
1 files changed, 430 insertions, 0 deletions
diff --git a/vmime-master/doc/book/msg.tex b/vmime-master/doc/book/msg.tex new file mode 100644 index 0000000..ce9d8a8 --- /dev/null +++ b/vmime-master/doc/book/msg.tex @@ -0,0 +1,430 @@ +\chapter{Parsing and Building Messages} + +% ============================================================================ +\section{Parsing messages} + +\subsection{Introduction} % -------------------------------------------------- + +Parsing is the process of creating a structured representation (for example, +a hierarchy of C++ objects) of a message from its ``textual'' representation +(the raw data that is actually sent on the Internet). + +For example, say you have the following email in a file called "hello.eml": + +\begin{verbatim} +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 +\end{verbatim} + +The following code snippet shows how you can easily obtain a +{\vcode vmime::message} object from data in this file: + +\begin{lstlisting}[caption={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; +\end{lstlisting} + +The output of this program is: + +\begin{verbatim} +The subject of the message is: Hello from VMime! +It was sent by: Vincent (email: vincent@vmime.org) +\end{verbatim} + + +\subsection{Using the {\vcode vmime::messageParser} object} % ---------------- + +The {\vcode vmime::messageParser} object allows to parse messages in a more +simple manner. You can obtain all the text parts and attachments as well as +basic fields (expeditor, recipients, subject...), without dealing with +MIME message structure. + +\begin{lstlisting}[caption={Using {\vcode 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>(); +msg->parse(data); + +// 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::TEXT_HTML) { + + 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() + } +} +\end{lstlisting} + + +% ============================================================================ +\section{Building messages} + +\subsection{A simple message\label{msg-building-simple-message}} % ----------- + +Of course, you can build a MIME message from scratch by creating the various +objects that compose it (parts, fields, etc.). The following is an example of +how to achieve it: + +\begin{lstlisting}[caption={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>(); + +recipients->appendMailbox + (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->generate(out); +\end{lstlisting} + +As you can see, this is a little fastidious. Hopefully, VMime also offers a +more simple way for creating messages. The {\vcode vmime::messageBuilder} +object can create basic messages that you can then customize. + +The following code can be used to build exactly the same message as in the +previous example, using the {\vcode vmime::messageBuilder} object: + +\begin{lstlisting}[caption={Building a simple message +using {\vcode 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->generate(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; +} +\end{lstlisting} + + +\subsection{Adding an attachment} % ------------------------------------------ + +Dealing with attachments is quite simple. Add the following code to the +previous example to attach a file to the message: + +\begin{lstlisting}[caption={Building a message with an attachment using +{\vcode 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); +\end{lstlisting} + + +\subsection{HTML messages and embedded objects} % ---------------------------- + +VMime also supports aggregate messages, which permits to build MIME messages +containing HTML text and embedded objects (such as images). For more information +about aggregate messages, please read RFC-2557 (\emph{MIME Encapsulation of +Aggregate Documents, such as HTML}). + +Creating such messages is quite easy, using the {\vcode vmime::messageBuilder} +object. The following code constructs a message containing text in both plain +and HTML format, and a JPEG image: + +\begin{lstlisting}[caption={Building an HTML message with an embedded image +using the {\vcode vmime::messageBuilder}}] +// Fill in some header fields +mb.setSubject(vmime::text("An HTML 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::TEXT_HTML + ) +); + +// 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::IMAGE_JPEG)); + +// -- 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." + ) +); +\end{lstlisting} + +This will create a message having the following structure: + +\begin{verbatim} +multipart/alternative + text/plain + multipart/related + text/html + image/jpeg +\end{verbatim} + +You can easily tell VMime to read the embedded object data from a file. The +following code opens the file \emph{/path/to/image.jpg}, connects it to an +input stream, then add an embedded object: + +\begin{lstlisting} +vmime::utility::fileSystemFactory* fs = + vmime::platform::getHandler()->getFileSystemFactory(); + +vmime::shared_ptr <vmime::utility::file> imageFile = + 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::IMAGE_JPEG + ) +); +\end{lstlisting} + + +% ============================================================================ +\section{Working with attachments: the attachment helper} + +The {\vcode attachmentHelper} object allows listing all attachments in a +message, as well as adding new attachments, without using the +{\vcode messageParser} and {\vcode messageBuilders} objects. It can work +directly on messages and body parts. + +To use it, you do not need any knowledge about how attachment parts should +be organized in a MIME message. + +The following code snippet tests if a body part is an attachment, and if so, +extract its contents to the standard output: + +\begin{lstlisting}[caption={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); +} +\end{lstlisting} + +You can also easily extract all attachments from a message: + +\begin{lstlisting}[caption={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); +\end{lstlisting} + +Finally, the {\vcode attachmentHelper} object can be used to add an +attachment to an existing message, whatever it contains (text parts, +attachments, ...). The algorithm can modify the structure of the +message if needed (eg. add a \emph{multipart/mixed} part if no one +exists in the message). Simply call the {\vcode addAttachment} +function: + +\begin{lstlisting}[caption={Adding an attachment to an existing message}] +vmime::shared_ptr <vmime::message> msg; // suppose we have a 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); +\end{lstlisting} + |