
ferencd@0: \chapter{Parsing and Building Messages}
ferencd@0: 
ferencd@0: % ============================================================================
ferencd@0: \section{Parsing messages}
ferencd@0: 
ferencd@0: \subsection{Introduction} % --------------------------------------------------
ferencd@0: 
ferencd@0: Parsing is the process of creating a structured representation (for example,
ferencd@0: a hierarchy of C++ objects) of a message from its ``textual'' representation
ferencd@0: (the raw data that is actually sent on the Internet).
ferencd@0: 
ferencd@0: For example, say you have the following email in a file called "hello.eml":
ferencd@0: 
ferencd@0: \begin{verbatim}
ferencd@0: Date: Thu, Oct 13 2005 15:22:46 +0200
ferencd@0: From: Vincent <vincent@vmime.org>
ferencd@0: To: you@vmime.org
ferencd@0: Subject: Hello from VMime!
ferencd@0: 
ferencd@0: A simple message to test VMime
ferencd@0: \end{verbatim}
ferencd@0: 
ferencd@0: The following code snippet shows how you can easily obtain a
ferencd@0: {\vcode vmime::message} object from data in this file:
ferencd@0: 
ferencd@0: \begin{lstlisting}[caption={Parsing a message from a file}]
ferencd@0: // Read data from file
ferencd@0: std::ifstream file;
ferencd@0: file.open("hello.eml", std::ios::in | std::ios::binary);
ferencd@0: 
ferencd@0: vmime::utility::inputStreamAdapter is(file);
ferencd@0: 
ferencd@0: vmime::string data;
ferencd@0: vmime::utility::outputStreamStringAdapter os(data);
ferencd@0: 
ferencd@0: vmime::utility::bufferedStreamCopy(is, os);
ferencd@0: 
ferencd@0: // Actually parse the message
ferencd@0: vmime::shared_ptr <vmime::message> msg = vmime::make_shared <vmime::message>();
ferencd@0: msg->parse(data);
ferencd@0: 
ferencd@0: vmime::shared_ptr <vmime::header> hdr = msg->getHeader();
ferencd@0: vmime::shared_ptr <vmime::body> bdy = msg->getBody();
ferencd@0: 
ferencd@0: // Now, you can extract some of its components
ferencd@0: vmime::charset ch(vmime::charsets::UTF_8);
ferencd@0: 
ferencd@0: std::cout
ferencd@0:    << "The subject of the message is: "
ferencd@0:    << hdr->Subject()->getValue <vmime::text>()->getConvertedText(ch)
ferencd@0:    << std::endl
ferencd@0:    << "It was sent by: "
ferencd@0:    << hdr->From()->getValue <vmime::mailbox>()->getName().getConvertedText(ch)
ferencd@0:    << " (email: " << hdr->From()->getValue <vmime::mailbox>()->getEmail() << ")"
ferencd@0:    << std::endl;
ferencd@0: \end{lstlisting}
ferencd@0: 
ferencd@0: The output of this program is:
ferencd@0: 
ferencd@0: \begin{verbatim}
ferencd@0: The subject of the message is: Hello from VMime!
ferencd@0: It was sent by: Vincent (email: vincent@vmime.org)
ferencd@0: \end{verbatim}
ferencd@0: 
ferencd@0: 
ferencd@0: \subsection{Using the {\vcode vmime::messageParser} object} % ----------------
ferencd@0: 
ferencd@0: The {\vcode vmime::messageParser} object allows to parse messages in a more
ferencd@0: simple manner. You can obtain all the text parts and attachments as well as
ferencd@0: basic fields (expeditor, recipients, subject...), without dealing with
ferencd@0: MIME message structure.
ferencd@0: 
ferencd@0: \begin{lstlisting}[caption={Using {\vcode vmime::messageParser} to parse
ferencd@0: more complex messages}]
ferencd@0: // Read data from file
ferencd@0: std::ifstream file;
ferencd@0: file.open("hello.eml", std::ios::in | std::ios::binary);
ferencd@0: 
ferencd@0: vmime::utility::inputStreamAdapter is(file);
ferencd@0: 
ferencd@0: vmime::string data;
ferencd@0: vmime::utility::outputStreamStringAdapter os(data);
ferencd@0: 
ferencd@0: vmime::utility::bufferedStreamCopy(is, os);
ferencd@0: 
ferencd@0: // Actually parse the message
ferencd@0: vmime::shared_ptr <vmime::message> msg = vmime::make_shared <vmime::message>();
ferencd@0: msg->parse(data);
ferencd@0: 
ferencd@0: // Here start the differences with the previous example
ferencd@0: vmime::messageParser mp(msg);
ferencd@0: 
ferencd@0: // Output information about attachments
ferencd@0: std::cout << "Message has " << mp.getAttachmentCount()
ferencd@0:    << " attachment(s)" << std::endl;
ferencd@0: 
ferencd@0: for (int i = 0 ; i < mp.getAttachmentCount() ; ++i)
ferencd@0: {
ferencd@0:    vmime::shared_ptr <const vmime::attachment> att = mp.getAttachmentAt(i);
ferencd@0:    std::cout << "   - " << att->getType().generate() << std::endl;
ferencd@0: }
ferencd@0: 
ferencd@0: // Output information about text parts
ferencd@0: std::cout << "Message has " << mp.getTextPartCount()
ferencd@0:    << " text part(s)" << std::endl;
ferencd@0: 
ferencd@0: for (int i = 0 ; i < mp.getTextPartCount() ; ++i)
ferencd@0: {
ferencd@0:    vmime::shared_ptr <const vmime::textPart> tp = mp.getTextPartAt(i);
ferencd@0: 
ferencd@0:    // text/html
ferencd@0:    if (tp->getType().getSubType() == vmime::mediaTypes::TEXT_HTML)
ferencd@0:    {
ferencd@0:       vmime::shared_ptr <const vmime::htmlTextPart> htp =
ferencd@0:          vmime::dynamicCast <const vmime::htmlTextPart>(tp);
ferencd@0: 
ferencd@0:       // HTML text is in tp->getText()
ferencd@0:       // Plain text is in tp->getPlainText()
ferencd@0: 
ferencd@0:       // Enumerate embedded objects
ferencd@0:       for (int j = 0 ; j < htp->getObjectCount() ; ++j)
ferencd@0:       {
ferencd@0:          vmime::shared_ptr <const vmime::htmlTextPart::embeddedObject> obj =
ferencd@0:             htp->getObjectAt(j);
ferencd@0: 
ferencd@0:          // Identifier (Content-Id or Content-Location) is obj->getId()
ferencd@0:          // Object data is in obj->getData()
ferencd@0:       }
ferencd@0:    }
ferencd@0:    // text/plain or anything else
ferencd@0:    else
ferencd@0:    {
ferencd@0:       // Text is in tp->getText()
ferencd@0:    }
ferencd@0: }
ferencd@0: \end{lstlisting}
ferencd@0: 
ferencd@0: 
ferencd@0: % ============================================================================
ferencd@0: \section{Building messages}
ferencd@0: 
ferencd@0: \subsection{A simple message\label{msg-building-simple-message}} % -----------
ferencd@0: 
ferencd@0: Of course, you can build a MIME message from scratch by creating the various
ferencd@0: objects that compose it (parts, fields, etc.). The following is an example of
ferencd@0: how to achieve it:
ferencd@0: 
ferencd@0: \begin{lstlisting}[caption={Building a simple message from scratch}]
ferencd@0: vmime::shared_ptr <vmime::message> msg = vmime::make_shared <vmime::message>();
ferencd@0: 
ferencd@0: vmime::shared_ptr <vmime::header> hdr = msg->getHeader();
ferencd@0: vmime::shared_ptr <vmime::body> bdy = msg->getBody();
ferencd@0: 
ferencd@0: vmime::shared_ptr <vmime::headerFieldFactory> hfFactory =
ferencd@0:    vmime::headerFieldFactory::getInstance();
ferencd@0: 
ferencd@0: // Append a 'Date:' field
ferencd@0: vmime::shared_ptr <vmime::headerField> dateField =
ferencd@0:    hfFactory->create(vmime::fields::DATE);
ferencd@0: 
ferencd@0: dateField->setValue(vmime::datetime::now());
ferencd@0: hdr->appendField(dateField);
ferencd@0: 
ferencd@0: // Append a 'Subject:' field
ferencd@0: vmime::shared_ptr <vmime::headerField> subjectField =
ferencd@0:    hfFactory->create(vmime::fields::SUBJECT);
ferencd@0: 
ferencd@0: subjectField->setValue(vmime::text("Message subject"));
ferencd@0: hdr->appendField(subjectField);
ferencd@0: 
ferencd@0: // Append a 'From:' field
ferencd@0: vmime::shared_ptr <vmime::headerField> fromField =
ferencd@0:    hfFactory->create(vmime::fields::FROM);
ferencd@0: 
ferencd@0: fromField->setValue
ferencd@0:    (vmime::make_shared <vmime::mailbox>("me@vmime.org"));
ferencd@0: hdr->appendField(fromField);
ferencd@0: 
ferencd@0: // Append a 'To:' field
ferencd@0: vmime::shared_ptr <vmime::headerField> toField =
ferencd@0:    hfFactory->create(vmime::fields::TO);
ferencd@0: 
ferencd@0: vmime::shared_ptr <vmime::mailboxList> recipients =
ferencd@0:    vmime::make_shared <vmime::mailboxList>();
ferencd@0: 
ferencd@0: recipients->appendMailbox
ferencd@0:    (vmime::make_shared <vmime::mailbox>("you@vmime.org"));
ferencd@0: 
ferencd@0: toField->setValue(recipients);
ferencd@0: hdr->appendField(toField);
ferencd@0: 
ferencd@0: // Set the body contents
ferencd@0: bdy->setContents(vmime::make_shared <vmime::stringContentHandler>
ferencd@0:    ("This is the text of your message..."));
ferencd@0: 
ferencd@0: // Output raw message data to standard output
ferencd@0: vmime::utility::outputStreamAdapter out(std::cout);
ferencd@0: msg->generate(out);
ferencd@0: \end{lstlisting}
ferencd@0: 
ferencd@0: As you can see, this is a little fastidious. Hopefully, VMime also offers a
ferencd@0: more simple way for creating messages. The {\vcode vmime::messageBuilder}
ferencd@0: object can create basic messages that you can then customize.
ferencd@0: 
ferencd@0: The following code can be used to build exactly the same message as in the
ferencd@0: previous example, using the {\vcode vmime::messageBuilder} object:
ferencd@0: 
ferencd@0: \begin{lstlisting}[caption={Building a simple message
ferencd@0: using {\vcode vmime::messageBuilder}}]
ferencd@0: try
ferencd@0: {
ferencd@0:    vmime::messageBuilder mb;
ferencd@0: 
ferencd@0:    // Fill in some header fields and message body
ferencd@0:    mb.setSubject(vmime::text("Message subject"));
ferencd@0:    mb.setExpeditor(vmime::mailbox("me@vmime.org"));
ferencd@0:    mb.getRecipients().appendAddress
ferencd@0:       (vmime::make_shared <vmime::mailbox>("you@vmime.org"));
ferencd@0: 
ferencd@0:    mb.getTextPart()->setCharset(vmime::charsets::ISO8859_15);
ferencd@0:    mb.getTextPart()->setText(vmime::make_shared <vmime::stringContentHandler>
ferencd@0:    	("This is the text of your message..."));
ferencd@0: 
ferencd@0:    // Message construction
ferencd@0:    vmime::shared_ptr <vmime::message> msg = mb.construct();
ferencd@0: 
ferencd@0:    // Output raw message data to standard output
ferencd@0:    vmime::utility::outputStreamAdapter out(std::cout);
ferencd@0:    msg->generate(out);
ferencd@0: }
ferencd@0: // VMime exception
ferencd@0: catch (vmime::exception& e)
ferencd@0: {
ferencd@0:    std::cerr << "vmime::exception: " << e.what() << std::endl;
ferencd@0: }
ferencd@0: // Standard exception
ferencd@0: catch (std::exception& e)
ferencd@0: {
ferencd@0:    std::cerr << "std::exception: " << e.what() << std::endl;
ferencd@0: }
ferencd@0: \end{lstlisting}
ferencd@0: 
ferencd@0: 
ferencd@0: \subsection{Adding an attachment} % ------------------------------------------
ferencd@0: 
ferencd@0: Dealing with attachments is quite simple. Add the following code to the
ferencd@0: previous example to attach a file to the message:
ferencd@0: 
ferencd@0: \begin{lstlisting}[caption={Building a message with an attachment using
ferencd@0: {\vcode vmime::messageBuilder}}]
ferencd@0: // Create an attachment
ferencd@0: vmime::shared_ptr <vmime::fileAttachment> att =
ferencd@0:    vmime::make_shared <vmime::fileAttachment>
ferencd@0:    (
ferencd@0:      /* full path to file */  "/home/vincent/paris.jpg",
ferencd@0:      /* content type */       vmime::mediaType("image/jpeg),
ferencd@0:      /* description */        vmime::text("My holidays in Paris")
ferencd@0:    );
ferencd@0: 
ferencd@0: // You can also set some infos about the file
ferencd@0: att->getFileInfo().setFilename("paris.jpg");
ferencd@0: att->getFileInfo().setCreationDate
ferencd@0:    (vmime::datetime("30 Apr 2003 14:30:00 +0200"));
ferencd@0: 
ferencd@0: // Add this attachment to the message
ferencd@0: mb.appendAttachment(att);
ferencd@0: \end{lstlisting}
ferencd@0: 
ferencd@0: 
ferencd@0: \subsection{HTML messages and embedded objects} % ----------------------------
ferencd@0: 
ferencd@0: VMime also supports aggregate messages, which permits to build MIME messages
ferencd@0: containing HTML text and embedded objects (such as images). For more information
ferencd@0: about aggregate messages, please read RFC-2557 (\emph{MIME Encapsulation of
ferencd@0: Aggregate Documents, such as HTML}).
ferencd@0: 
ferencd@0: Creating such messages is quite easy, using the {\vcode vmime::messageBuilder}
ferencd@0: object. The following code constructs a message containing text in both plain
ferencd@0: and HTML format, and a JPEG image:
ferencd@0: 
ferencd@0: \begin{lstlisting}[caption={Building an HTML message with an embedded image
ferencd@0: using the {\vcode vmime::messageBuilder}}]
ferencd@0: // Fill in some header fields
ferencd@0: mb.setSubject(vmime::text("An HTML message"));
ferencd@0: mb.setExpeditor(vmime::mailbox("me@vmime.org"));
ferencd@0: mb.getRecipients().appendAddress
ferencd@0:    (vmime::make_shared <vmime::mailbox>("you@vmime.org"));
ferencd@0: 
ferencd@0: // Set the content-type to "text/html": a text part factory must be
ferencd@0: // available for the type you are using. The following code will make
ferencd@0: // the message builder construct the two text parts.
ferencd@0: mb.constructTextPart(vmime::mediaType
ferencd@0:    (vmime::mediaTypes::TEXT, vmime::mediaTypes::TEXT_HTML));
ferencd@0: 
ferencd@0: // Set contents of the text parts; the message is available in two formats:
ferencd@0: // HTML and plain text. The HTML format also includes an embedded image.
ferencd@0: vmime::shared_ptr <vmime::htmlTextPart> textPart =
ferencd@0:    vmime::dynamicCast <vmime::htmlTextPart>(mb.getTextPart());
ferencd@0: 
ferencd@0: // -- Add the JPEG image (the returned identifier is used to identify the
ferencd@0: // -- embedded object in the HTML text, the famous "CID", or "Content-Id").
ferencd@0: // -- Note: you can also read data from a file; see the next example.
ferencd@0: const vmime::string id = textPart->addObject("<...image data...>",
ferencd@0:    vmime::mediaType(vmime::mediaTypes::IMAGE, vmime::mediaTypes::IMAGE_JPEG));
ferencd@0: 
ferencd@0: // -- Set the text
ferencd@0: textPart->setCharset(vmime::charsets::ISO8859_15);
ferencd@0: 
ferencd@0: textPart->setText(vmime::make_shared <vmime::stringContentHandler>
ferencd@0:    ("This is the <b>HTML text</b>, and the image:<br/>"
ferencd@0:     "<img src=\"") + id + vmime::string("\"/>"));
ferencd@0: 
ferencd@0: textPart->setPlainText(vmime::make_shared <vmime::stringContentHandler>
ferencd@0:    ("This is the plain text."));
ferencd@0: \end{lstlisting}
ferencd@0: 
ferencd@0: This will create a message having the following structure:
ferencd@0: 
ferencd@0: \begin{verbatim}
ferencd@0: multipart/alternative
ferencd@0:    text/plain
ferencd@0:    multipart/related
ferencd@0:       text/html
ferencd@0:       image/jpeg
ferencd@0: \end{verbatim}
ferencd@0: 
ferencd@0: You can easily tell VMime to read the embedded object data from a file. The
ferencd@0: following code opens the file \emph{/path/to/image.jpg}, connects it to an
ferencd@0: input stream, then add an embedded object:
ferencd@0: 
ferencd@0: \begin{lstlisting}
ferencd@0: vmime::utility::fileSystemFactory* fs =
ferencd@0:    vmime::platform::getHandler()->getFileSystemFactory();
ferencd@0: 
ferencd@0: vmime::shared_ptr <vmime::utility::file> imageFile =
ferencd@0:    fs->create(fs->stringToPath("/path/to/image.jpg"));
ferencd@0: 
ferencd@0: vmime::shared_ptr <vmime::contentHandler> imageCts =
ferencd@0:    vmime::make_shared <vmime::streamContentHandler>
ferencd@0:       (imageFile->getFileReader()->getInputStream(), imageFile->getLength());
ferencd@0: 
ferencd@0: const vmime::string cid = textPart.addObject(imageCts,
ferencd@0:    vmime::mediaType(vmime::mediaTypes::IMAGE, vmime::mediaTypes::IMAGE_JPEG));
ferencd@0: \end{lstlisting}
ferencd@0: 
ferencd@0: 
ferencd@0: % ============================================================================
ferencd@0: \section{Working with attachments: the attachment helper}
ferencd@0: 
ferencd@0: The {\vcode attachmentHelper} object allows listing all attachments in a
ferencd@0: message, as well as adding new attachments, without using the
ferencd@0: {\vcode messageParser} and {\vcode messageBuilders} objects. It can work
ferencd@0: directly on messages and body parts.
ferencd@0: 
ferencd@0: To use it, you do not need any knowledge about how attachment parts should
ferencd@0: be organized in a MIME message.
ferencd@0: 
ferencd@0: The following code snippet tests if a body part is an attachment, and if so,
ferencd@0: extract its contents to the standard output:
ferencd@0: 
ferencd@0: \begin{lstlisting}[caption={Testing if a body part is an attachment}]
ferencd@0: vmime::shared_ptr <vmime::bodyPart> part;  // suppose we have a body part
ferencd@0: 
ferencd@0: if (vmime::attachmentHelper::isBodyPartAnAttachment(part))
ferencd@0: {
ferencd@0:    // The body part contains an attachment, get it
ferencd@0:    vmime::shared_ptr <const vmime::attachment> attach =
ferencd@0:       attachmentHelper::getBodyPartAttachment(part);
ferencd@0: 
ferencd@0:    // Extract attachment data to standard output
ferencd@0:    vmime::utility::outputStreamAdapter out(std::cout);
ferencd@0:    attach->getData()->extract(out);
ferencd@0: }
ferencd@0: \end{lstlisting}
ferencd@0: 
ferencd@0: You can also easily extract all attachments from a message:
ferencd@0: 
ferencd@0: \begin{lstlisting}[caption={Extracting all attachments from a message}]
ferencd@0: vmime::shared_ptr <vmime::message> msg;  // suppose we have a message
ferencd@0: 
ferencd@0: const std::vector <ref <const attachment> > atts =
ferencd@0:    attachmentHelper::findAttachmentsInMessage(msg);
ferencd@0: \end{lstlisting}
ferencd@0: 
ferencd@0: Finally, the {\vcode attachmentHelper} object can be used to add an
ferencd@0: attachment to an existing message, whatever it contains (text parts,
ferencd@0: attachments, ...). The algorithm can modify the structure of the
ferencd@0: message if needed (eg. add a \emph{multipart/mixed} part if no one
ferencd@0: exists in the message). Simply call the {\vcode addAttachment}
ferencd@0: function:
ferencd@0: 
ferencd@0: \begin{lstlisting}[caption={Adding an attachment to an existing message}]
ferencd@0: vmime::shared_ptr <vmime::message> msg;  // suppose we have a message
ferencd@0: 
ferencd@0: // Create an attachment
ferencd@0: vmime::shared_ptr <vmime::fileAttachment> att =
ferencd@0:    vmime::make_shared <vmime::fileAttachment>
ferencd@0:    (
ferencd@0:      /* full path to file */  "/home/vincent/paris.jpg",
ferencd@0:      /* content type */       vmime::mediaType("image/jpeg),
ferencd@0:      /* description */        vmime::text("My holidays in Paris")
ferencd@0:    );
ferencd@0: 
ferencd@0: // Attach it to the message
ferencd@0: vmime::attachmentHelper::addAttachment(msg, att);
ferencd@0: \end{lstlisting}
ferencd@0: 




