ferencd@0: \chapter{Basics} ferencd@0: ferencd@0: % ============================================================================ ferencd@0: \section{Reference counting} ferencd@0: ferencd@0: \subsection{Introduction} % -------------------------------------------------- ferencd@0: ferencd@0: Since version 0.7.2cvs, VMime use smart pointers to simplify memory ferencd@0: management. Smart pointers rely on ferencd@0: RAII\footnote{Ressource Allocation is Initialisation} so that we do not need ferencd@0: to bother with deleting an object (freeing memory) when it is not used ferencd@0: anymore. ferencd@0: ferencd@0: There are two possibilities for owning a reference to an object. We can own a ferencd@0: strong reference to an object: as long as we keep this reference, the object ferencd@0: is not destroyed. Or we can own a weak reference to the object: the object can ferencd@0: be destroyed if nobody owns a strong reference to it, in which case the weak ferencd@0: reference becomes invalid. ferencd@0: ferencd@0: An object is destroyed as soon as the last strong reference to it is released. ferencd@0: At the same tine, all weak references (if any) are automatically set to point ferencd@0: to \vnull. ferencd@0: ferencd@0: In VMime, these two types of references are known as {\vcode vmime::shared\_ptr} ferencd@0: and {\vcode vmime::weak\_ptr}, respectively. ferencd@0: ferencd@0: \vnote{since November 2013, we switched from an old, intrusive implementation ferencd@0: of smart pointers to a more standard one: either Boost {\vcode shared\_ptr<>} ferencd@0: implementation or standard C++ one if we are compiling in C++11. Here are the ferencd@0: changes: ferencd@0: ferencd@0: {\vcode vmime::ref <>} is replaced with {\vcode vmime::shared\_ptr <>} ferencd@0: ferencd@0: {\vcode vmime::weak\_ref <>} is replaced with {\vcode vmime::weak\_ptr <>} ferencd@0: ferencd@0: {\vcode vmime::create <>} is replaced with {\vcode vmime::make\_shared <>} ferencd@0: } ferencd@0: ferencd@0: \subsection{Instanciating reference-counted objects} % ----------------------- ferencd@0: ferencd@0: In VMime, all objects that support reference counting inherit from the ferencd@0: {\vcode vmime::object} class, which is responsible for ferencd@0: incrementing/decrementing the counter and managing the object's life cycle. ferencd@0: If you want to create a smart pointer to a new object instance, you should ferencd@0: use the function {\vcode vmime::make\_shared} instead of the {\vcode new} ferencd@0: operator. ferencd@0: ferencd@0: \begin{lstlisting}[caption={Smarts pointers and creating objects}] ferencd@0: class myObject : public vmime::object ferencd@0: { ferencd@0: public: ferencd@0: ferencd@0: myObject(const vmime::string& name) ferencd@0: : m_name(name) ferencd@0: { ferencd@0: } ferencd@0: ferencd@0: void sayHello() ferencd@0: { ferencd@0: std::cout << "Hello " << m_name << std::endl; ferencd@0: } ferencd@0: ferencd@0: private: ferencd@0: ferencd@0: vmime::string m_name; ferencd@0: }; ferencd@0: ferencd@0: int main() ferencd@0: { ferencd@0: vmime::shared_ptr obj = ferencd@0: vmime::make_shared ("world"); ferencd@0: ferencd@0: obj->sayHello(); ferencd@0: ferencd@0: return 0; ferencd@0: ferencd@0: } // Here, 'obj' gets automatically destroyed ferencd@0: \end{lstlisting} ferencd@0: ferencd@0: \subsection{Using smart pointers} % ------------------------------------------ ferencd@0: ferencd@0: Smart pointers are copiable, assignable and comparable. You can use them like ferencd@0: you would use normal ("raw") C++ pointers (eg. you can write ferencd@0: \lstinline{!ptr, ptr != NULL, ptr->method(), *ptr}...). ferencd@0: ferencd@0: Type safety is also guaranteed, and you can type cast smart pointers using ferencd@0: the {\vcode static\_cast()}, {\vcode dynamic\_cast()} and {\vcode const\_cast()} ferencd@0: equivalents on {\vcode vmime::shared\_ptr} and {\vcode vmime::weak\_ptr} objects: ferencd@0: ferencd@0: \begin{lstlisting}[caption={Casting smart pointers}] ferencd@0: class myBase : public vmime::object { } ferencd@0: class myObject : public myBase { } ferencd@0: ferencd@0: vmime::shared_ptr obj = vmime::make_shared (); ferencd@0: ferencd@0: // Implicit downcast ferencd@0: vmime::shared_ptr base = obj; ferencd@0: ferencd@0: // Explicit upcast ferencd@0: vmime::shared_ptr obj2 = vmime::dynamicCast (base); ferencd@0: \end{lstlisting} ferencd@0: ferencd@0: Weak references are used to resolve reference cycles (an object which refers ferencd@0: directly or indirectly to itself). The following example illustrates a ferencd@0: typical problem of reference counting: ferencd@0: ferencd@0: \begin{lstlisting} ferencd@0: class parent : public vmime::object ferencd@0: { ferencd@0: public: ferencd@0: ferencd@0: void createChild(vmime::shared_ptr c) ferencd@0: { ferencd@0: m_child = c; ferencd@0: } ferencd@0: ferencd@0: private: ferencd@0: ferencd@0: vmime::shared_ptr m_child; ferencd@0: }; ferencd@0: ferencd@0: class child : public vmime::object ferencd@0: { ferencd@0: public: ferencd@0: ferencd@0: child(vmime::shared_ptr p) ferencd@0: : m_parent(p) ferencd@0: { ferencd@0: } ferencd@0: ferencd@0: private: ferencd@0: ferencd@0: vmime::shared_ptr m_parent; ferencd@0: }; ferencd@0: ferencd@0: int main() ferencd@0: { ferencd@0: vmime::shared_ptr p = vmime::make_shared (); ferencd@0: vmime::shared_ptr c = vmime::make_shared (); ferencd@0: ferencd@0: p->setChild(c); ferencd@0: } ferencd@0: \end{lstlisting} ferencd@0: ferencd@0: In this example, neither {\vcode p} nor {\vcode c} will be deleted when ferencd@0: exiting {\vcode main()}. That's because {\vcode p} indirectly points to itself ferencd@0: {\em via} {\vcode c}, and {\em vice versa}. The solution is to use a weak ferencd@0: reference to the parent: ferencd@0: ferencd@0: \begin{lstlisting} ferencd@0: vmime::weak_ptr m_parent; ferencd@0: \end{lstlisting} ferencd@0: ferencd@0: The decision to make the parent or the child a weak reference is purely ferencd@0: semantic, and it depends on the context and the relationships between the ferencd@0: objects. Note that when the parent is deleted, the {\vcode m\_parent} member ferencd@0: of the child points to \vnull. ferencd@0: ferencd@0: More information about reference counting can be found on ferencd@0: Wikipedia\footnote{http://en.wikipedia.org/wiki/Reference\_counting}. ferencd@0: ferencd@0: % ============================================================================ ferencd@0: \section{Error handling} ferencd@0: ferencd@0: In VMime, error handling is exclusively based on exceptions, there is no error ferencd@0: codes, or things like that. ferencd@0: ferencd@0: VMime code may throw exceptions in many different situations: an unexpected ferencd@0: error occured, an operation is not supported, etc. You should catch them if ferencd@0: you want to report failures to the user. This is also useful when debugging ferencd@0: your program. ferencd@0: ferencd@0: VMime exceptions support chaining: an exception can be encapsulated into ferencd@0: another exception to hide implementation details. The function ferencd@0: {\vcode exception::other()} returns the next exception in the chain, ferencd@0: or \vnull. ferencd@0: ferencd@0: Following is an example code for catching VMime exceptions and writing error ferencd@0: messages to the console: ferencd@0: ferencd@0: \begin{lstlisting}[caption={Catching VMime exceptions}] ferencd@0: std::ostream& operator<<(std::ostream& os, const vmime::exception& e) ferencd@0: { ferencd@0: os << "* vmime::exceptions::" << e.name() << std::endl; ferencd@0: os << " what = " << e.what() << std::endl; ferencd@0: ferencd@0: // Recursively print all encapsuled exceptions ferencd@0: if (e.other() != NULL) ferencd@0: os << *e.other(); ferencd@0: ferencd@0: return os; ferencd@0: } ferencd@0: ferencd@0: ... ferencd@0: ferencd@0: try ferencd@0: { ferencd@0: // ...some call to VMime... ferencd@0: } ferencd@0: catch (vmime::exception& e) ferencd@0: { ferencd@0: std::cerr << e; // VMime exception ferencd@0: } ferencd@0: catch (std::exception& e) ferencd@0: { ferencd@0: std::cerr << e.what(); // standard exception ferencd@0: } ferencd@0: \end{lstlisting} ferencd@0: ferencd@0: Read the source of {\vexample example6} if yo want to see a more complete ferencd@0: example of using VMime exceptions (such as getting more detailed information ferencd@0: by using specialized classes of {\vcode vmime::exception}). ferencd@0: ferencd@0: ferencd@0: % ============================================================================ ferencd@0: \section{Basic objects} ferencd@0: ferencd@0: \subsection{The {\vcode component} class} % ---------------------------------- ferencd@0: ferencd@0: In VMime, all the components of a message inherit from the same class ferencd@0: {\vcode component}. This includes the message itself (classes {\vcode message} ferencd@0: and {\vcode bodyPart}), the header, the header fields and the value of each ferencd@0: header field, the body and all the parts in the message. ferencd@0: ferencd@0: The class component provide a common interface for parsing or generating all ferencd@0: these components (methods {\vcode parse()} and {\vcode generate()}). It also ferencd@0: provides additional functions to get some information about the parsing ferencd@0: process or the structure (methods {\vcode getParsedOffset()}, ferencd@0: {\vcode getParsedLength()} and {\vcode getChildComponents()}). ferencd@0: ferencd@0: VMime also provides a set of classes corresponding to the basic types found ferencd@0: in a message; for example a mailbox, a mailbox list, date/time information, ferencd@0: media type, etc. They all inherit from {\vcode component} too. ferencd@0: ferencd@0: \subsection{Date and time} % ------------------------------------------------- ferencd@0: ferencd@0: Date and time are used in several places in VMime, particularly in header ferencd@0: fields (Date, Received, ...). VMime fully supports RFC-2822's date and time ferencd@0: specification. The object {\vcode vmime::datetime} is used to manipulate date ferencd@0: and time information, and to parse/generate it from/to RFC-2822 format. ferencd@0: ferencd@0: The following code snippet show various manners of using the ferencd@0: {\vcode vmime::datetime} object: ferencd@0: ferencd@0: \begin{lstlisting}[caption={Using {\vcode vmime::datetime} object}] ferencd@0: // Creating from string in RFC-2822 format ferencd@0: vmime::datetime d1("Sat, 08 Oct 2005 14:07:52 +0200"); ferencd@0: ferencd@0: // Creating from components ferencd@0: vmime::datetime d2( ferencd@0: /* date */ 2005, vmime::datetime::OCTOBER, 8, ferencd@0: /* time */ 14, 7, 52, ferencd@0: /* zone */ vmime::datetime::GMT2); ferencd@0: ferencd@0: // Getting day of week ferencd@0: const int dow = d2.getWeekDay(); // 'dow' should be datetime::SATURDAY ferencd@0: \end{lstlisting} ferencd@0: ferencd@0: \subsection{Media type} % ---------------------------------------------------- ferencd@0: ferencd@0: In MIME, the nature of the data contained in parts is identified using a ferencd@0: media type. A general type (eg. \emph{image}) and a sub-type (eg. \emph{jpeg}) ferencd@0: are put together to form a media type (eg. \emph{image/jpeg}). This is also ferencd@0: called the MIME type. ferencd@0: ferencd@0: There are a lot of media types officially registered, and vendor-specific ferencd@0: types are possible (they start with ``x-'', eg. ferencd@0: \emph{application/x-zip-compressed}). ferencd@0: ferencd@0: In VMime, the object {\vcode vmime::mediaType} represents a media type. There ferencd@0: are also some constants for top-level types and sub-types in the ferencd@0: {\vcode vmime::mediaTypes} namespace. For example, you can instanciate a new ferencd@0: media type with: ferencd@0: ferencd@0: \begin{lstlisting} ferencd@0: vmime::mediaType theType( ferencd@0: /* top-level type */ vmime::mediaTypes::IMAGE, ferencd@0: /* sub-type */ vmime::mediaTypes::IMAGE_JPEG); ferencd@0: ferencd@0: // theType.getType() is "image" ferencd@0: // theType.getSubType() is "jpeg" ferencd@0: // theType.generate() returns "image/jpeg" ferencd@0: \end{lstlisting} ferencd@0: ferencd@0: For more information about media types, see ferencd@0: RFC-2046\footnote{http://www.faqs.org/rfcs/rfc2046.html}. ferencd@0: ferencd@0: \subsection{Mailbox and mailbox groups} % ------------------------------------ ferencd@0: ferencd@0: VMime provides several objects for working with mailboxes and addresses. ferencd@0: ferencd@0: The {\vcode vmime::address} class is an abstract type for representing an ferencd@0: address: it can be either a mailbox (type {\vcode vmime::mailbox}) or a ferencd@0: mailbox group (type {\vcode vmime::mailboxGroup}). A mailbox is composed of ferencd@0: an email address (mandatory) and possibly a name. A mailbox group is simply ferencd@0: a named list of mailboxes (see Figure \ref{uml_addr_mbox_mboxgroup}). ferencd@0: ferencd@0: \begin{lstlisting}[caption={Using mailboxes and mailbox groups}] ferencd@0: vmime::shared_ptr mbox1 = vmime::make_shared ferencd@0: (/* name */ vmime::text("John Doe"), /* email */ "john.doe@acme.com"); ferencd@0: vmime::shared_ptr mbox2 = vmime::make_shared ferencd@0: (/* no name, email only */ "bill@acme.com"); ferencd@0: ferencd@0: vmime::shared_ptr grp = vmime::make_shared (); ferencd@0: grp->appendMailbox(mbox1); ferencd@0: grp->appendMailbox(mbox2); ferencd@0: \end{lstlisting} ferencd@0: ferencd@0: \begin{figure}[ht!] ferencd@0: \center\includegraphics[width=0.7\textwidth] ferencd@0: {images/address-mailbox-mailboxgroup.png}\endcenter ferencd@0: \caption{Diagram for address-related classes} ferencd@0: \label{uml_addr_mbox_mboxgroup} ferencd@0: \end{figure} ferencd@0: ferencd@0: ferencd@0: % ============================================================================ ferencd@0: \section{Message, body parts and header} ferencd@0: ferencd@0: \subsection{Introduction to MIME messages} % --------------------------------- ferencd@0: ferencd@0: A MIME message is a recursive structure in which each part can contains one ferencd@0: or more parts (or \emph{entities}). Each part is composed of a header and ferencd@0: a body (actual contents). Figure \ref{uml_msg_body_header} shows how this ferencd@0: model is implemented in VMime, and all classes that take part in it. ferencd@0: ferencd@0: \begin{figure} ferencd@0: \center\includegraphics[width=1.0\textwidth] ferencd@0: {images/message-body-header.png}\endcenter ferencd@0: \caption{Overall structure of MIME messages} ferencd@0: \label{uml_msg_body_header} ferencd@0: \end{figure} ferencd@0: ferencd@0: ferencd@0: \subsection{Header and header fields} % -------------------------------------- ferencd@0: ferencd@0: \subsubsection{Standard header fields} % ..................................... ferencd@0: ferencd@0: Header fields carry information about a message (or a part) and its contents. ferencd@0: Each header field has a name and a value. All types that can be used as a ferencd@0: field value inherit from the {\vcode headerFieldValue} class. ferencd@0: ferencd@0: You cannot instanciate header fields directly using their constructor. ferencd@0: Instead, you should use the {\vcode headerFieldFactory} object. This ensures ferencd@0: the right field type and value type is used for the specified field name. ferencd@0: For more information about how to use header fields and the factory, see ferencd@0: section \ref{msg-building-simple-message}. ferencd@0: ferencd@0: Some standard fields are officially registered and have their value type ferencd@0: specified in a RFC. Table \ref{standard-fields} lists all the fields ferencd@0: registered by default in VMime and the value type they contains. ferencd@0: ferencd@0: By default, all unregistered fields have a value of type {\vcode text}. ferencd@0: ferencd@0: \begin{table}[!ht] ferencd@0: \begin{center} ferencd@0: \noindent\begin{tabularx}{0.85\textwidth}{|X|X|} ferencd@0: \hline ferencd@0: {\bf Field Name} & ferencd@0: {\bf Value Type} \\ ferencd@0: \hline ferencd@0: \hline ferencd@0: From & mailbox \\ ferencd@0: To & addressList \\ ferencd@0: Cc & addressList \\ ferencd@0: Bcc & addressList \\ ferencd@0: Sender & mailbox \\ ferencd@0: Date & datetime \\ ferencd@0: Received & relay \\ ferencd@0: Subject & text \\ ferencd@0: Reply-To & mailbox \\ ferencd@0: Delivered-To & mailbox \\ ferencd@0: Organization & text \\ ferencd@0: Return-Path & path \\ ferencd@0: Mime-Version & text \\ ferencd@0: Content-Type & mediaType \\ ferencd@0: Content-Transfer-Encoding & encoding \\ ferencd@0: Content-Description & text \\ ferencd@0: Content-Disposition & contentDisposition \\ ferencd@0: Content-Id & messageId \\ ferencd@0: Content-Location & text \\ ferencd@0: Message-Id & messageId \\ ferencd@0: In-Reply-To & messageIdSequence \\ ferencd@0: References & messageIdSequence \\ ferencd@0: Original-Message-Id & messageId \\ ferencd@0: Disposition & disposition \\ ferencd@0: Disposition-Notification-To & mailboxList \\ ferencd@0: \hline ferencd@0: \end{tabularx} ferencd@0: \end{center} ferencd@0: \label{standard-fields} ferencd@0: \caption{Standard fields and their types} ferencd@0: \end{table} ferencd@0: ferencd@0: ferencd@0: \subsubsection{Parameterized fields} % ....................................... ferencd@0: ferencd@0: In addition to a value, some header fields can contain one or more ferencd@0: \emph{name=value} couples which are called \emph{parameters}. For example, ferencd@0: this is used in the \emph{Content-Type} field to give more information about ferencd@0: the content: ferencd@0: ferencd@0: \begin{verbatim} ferencd@0: Content-Type: text/plain; charset="utf-8" ferencd@0: \end{verbatim} ferencd@0: ferencd@0: Fields that support parameters inherit from the ferencd@0: {\vcode parameterizedHeaderField} class which provides methods to deal with ferencd@0: these parameters: {\vcode appendParameter()}, {\vcode getParameterAt()}... ferencd@0: ferencd@0: A parameter is identified by a name (eg. \emph{charset}) and associated to ferencd@0: a value of type {\vcode vmime::text}. Parameters provide helper functions to ferencd@0: convert automatically from basic types to text, and \emph{vice versa}. The ferencd@0: following example illustrates it: ferencd@0: ferencd@0: \begin{lstlisting}[caption={Getting and setting parameter value in fields}] ferencd@0: vmime::shared_ptr field = ferencd@0: header->findField ("X-Field-That-Contains-Parameters"); ferencd@0: ferencd@0: // Use setValue() to convert from a basic type to 'text' ferencd@0: vmime::shared_ptr prm = field->getParameter("my-date-param"); ferencd@0: prm->setValue(vmime::datetime::now()); ferencd@0: ferencd@0: // Use getValueAs() to convert from 'text' to a basic type ferencd@0: prm = field->getParameter("my-charset-param"); ferencd@0: const vmime::charset ch = prm->getValueAs (); ferencd@0: \end{lstlisting} ferencd@0: ferencd@0: Some fields provide easy access to their standard parameters (see ferencd@0: Table \ref{standard-prm-fields}). This avoids finding the parameter and ferencd@0: \emph{dynamic-casting} its value to the right type. The following code ferencd@0: illustrates how to use it: ferencd@0: ferencd@0: \begin{lstlisting} ferencd@0: vmime::shared_ptr field = ferencd@0: header->getField (vmime::fields::CONTENT_TYPE); ferencd@0: ferencd@0: // 1. First solution: the "hard" way ferencd@0: vmime::shared_ptr prm = field->findParameter("charset"); ferencd@0: const charset ch1 = prm->getValueAs (); ferencd@0: ferencd@0: // 2. Second solution: the simple way ferencd@0: const charset ch2 = field->getCharset(); ferencd@0: \end{lstlisting} ferencd@0: ferencd@0: \vnote{In both cases, an exception {\vcode no\_such\_parameter} can be ferencd@0: thrown if the parameter does not exist, so be sure to catch it.} ferencd@0: ferencd@0: \begin{table}[ht!] ferencd@0: \begin{center} ferencd@0: \noindent\begin{tabularx}{0.85\textwidth}{|l|l|X|} ferencd@0: \hline ferencd@0: {\bf Field Name} & ferencd@0: {\bf Field Type} & ferencd@0: {\bf Parameters} \\ ferencd@0: \hline ferencd@0: \hline ferencd@0: Content-Type & contentTypeField & boundary, charset, report-type \\ ferencd@0: \hline ferencd@0: Content-Disposition & contentDispositionField & creation-date, ferencd@0: modification-date, read-date, filename, size \\ ferencd@0: \hline ferencd@0: \end{tabularx} ferencd@0: \end{center} ferencd@0: \label{standard-prm-fields} ferencd@0: \caption{Standard parameterized fields} ferencd@0: \end{table} ferencd@0: ferencd@0: ferencd@0: ferencd@0: % ============================================================================ ferencd@0: \section{Streams} ferencd@0: ferencd@0: \subsection{Streams and stream adapters} % ----------------------------------- ferencd@0: ferencd@0: Streams permit reading or writing data whatever the underlying system is: ferencd@0: a file on a hard disk, a socket connected to a remote service... ferencd@0: ferencd@0: There are two types of streams: input streams (from which you can read data) ferencd@0: and output streams (in which you can write data). Some adapters are provided ferencd@0: for compatibility and convenience, for example: ferencd@0: ferencd@0: \begin{itemize} ferencd@0: \item {\vcode inputStreamAdapter} and {\vcode outputStreamAdapter}: allow ferencd@0: to use standard C++ iostreams with VMime; ferencd@0: \item {\vcode inputStreamStringAdapter} and ferencd@0: {\vcode outputStreamStringAdapter}: use a {\vcode vmime::string} object to ferencd@0: read/write data. ferencd@0: \end{itemize} ferencd@0: ferencd@0: The following example shows two ways of writing the current date to the ferencd@0: standard output, using stream adapters: ferencd@0: ferencd@0: \begin{lstlisting}[caption={Using stream adapters}] ferencd@0: // Get current date and time ferencd@0: const vmime::datetime date = vmime::datetime::now(); ferencd@0: ferencd@0: // 1. Using outputStreamAdapter ferencd@0: vmime::utility::outputStreamAdapter out(std::cout); ferencd@0: ferencd@0: std::cout << "Current date is: "; ferencd@0: date.generate(out); ferencd@0: std::cout << std::endl; ferencd@0: ferencd@0: // 2. Using outputStreamStringAdapter ferencd@0: vmime::string dateStr; ferencd@0: vmime::utility::outputStreamStringAdapter outStr(dateStr); ferencd@0: ferencd@0: date.generate(outStr); ferencd@0: ferencd@0: std::cout << "Current date is: " << dateStr << std::endl; ferencd@0: \end{lstlisting} ferencd@0: ferencd@0: ferencd@0: \subsection{Stream filters} % ------------------------------------------------ ferencd@0: ferencd@0: Input and output streams can be filtered to perform inline conversions (for ferencd@0: example, there is a filter to convert ``{\textbackslash}r{\textbackslash}n'' ferencd@0: sequences to ``{\textbackslash}n''). They inherit from ferencd@0: {\vcode vmime::utility::filteredInputStream} or ferencd@0: {\vcode vmime::utility::filteredOutputStream} and are used like adapters (some ferencd@0: filters also accept parameters; read the documentation). ferencd@0: ferencd@0: The most useful filter in VMime (and probably the only one you will need) is ferencd@0: the {\vcode charsetFilteredOutputStream}, which performs inline conversion ferencd@0: of charsets. See \ref{section_charsets} to know how to use it. ferencd@0: ferencd@0: \vnote{After you have finished to use a filtered output stream, it is ferencd@0: important to call {\vcode flush()} on it to flush the internal buffer. ferencd@0: If {\vcode flush()} is not called, not all data may be written to the ferencd@0: underlying stream.} ferencd@0: ferencd@0: ferencd@0: % ============================================================================ ferencd@0: \section{Content handlers} ferencd@0: ferencd@0: \subsection{Introduction} % -------------------------------------------------- ferencd@0: ferencd@0: Content handlers are an abstraction for data sources. They are currently used ferencd@0: when some data need to be stored for later use (eg. body part contents, ferencd@0: attachment data, ...). Data can be stored encoded or unencoded (for more ferencd@0: information about encodings, see \ref{section_encodings}). ferencd@0: ferencd@0: \subsection{Extracting data from content handlers} % ------------------------- ferencd@0: ferencd@0: You can extract data in a content handler using the {\vcode extract()} method ferencd@0: (which automatically decodes data if encoded) or {\vcode extractRaw()} (which ferencd@0: extracts data without perfoming any decoding). ferencd@0: ferencd@0: The following example shows how to extract the body text from a message, and ferencd@0: writing it to the standard output with charset conversion: ferencd@0: ferencd@0: \begin{lstlisting}[caption={Using content handlers to extract body text from ferencd@0: a message}] ferencd@0: // Suppose we already have a message ferencd@0: vmime::shared_ptr msg; ferencd@0: ferencd@0: // Obtains a reference to the body contents ferencd@0: vmime::shared_ptr body = msg->getBody(); ferencd@0: vmime::shared_ptr cts = body->getContents(); ferencd@0: ferencd@0: vmime::utility::outputStreamAdapter out(std::cout); ferencd@0: cts->extract(out); ferencd@0: \end{lstlisting} ferencd@0: ferencd@0: \vnote{The body contents is extracted ``as is''. No charset conversion is ferencd@0: performed. See \ref{section_charsets} to know more about conversion between ferencd@0: charsets.} ferencd@0: ferencd@0: ferencd@0: \subsection{Creating content handlers} % ------------------------------------- ferencd@0: ferencd@0: When you are building a message, you may need to instanciate content handlers ferencd@0: if you want to set the contents of a body part. The following code snippet ferencd@0: shows how to set the body text of a part from a string: ferencd@0: ferencd@0: \begin{lstlisting}[caption={Setting the contents of a body part}] ferencd@0: vmime::shared_ptr part; // suppose we have a body part ferencd@0: ferencd@0: // Create a new content handler from a string ferencd@0: vmime::shared_ptr cth = ferencd@0: vmime::make_shared ("Put body contents here"); ferencd@0: ferencd@0: // Set the contents ferencd@0: part->getBody()->setContents(cth); ferencd@0: \end{lstlisting} ferencd@0: ferencd@0: Content handlers are also used when creating attachments. The following ferencd@0: example illustrates how to create an attachment from a file: ferencd@0: ferencd@0: \begin{lstlisting}[caption={Creating an attachment from a file}] ferencd@0: // Create a stream from a file ferencd@0: std::ifstream* fileStream = new std::ifstream(); ferencd@0: ferencd@0: fileStream->open("/home/vincent/paris.jpg", std::ios::binary); ferencd@0: ferencd@0: if (!*fileStream) ferencd@0: // handle error ferencd@0: ferencd@0: vmime::shared_ptr dataStream = ferencd@0: vmime::make_shared (fileStream); ferencd@0: ferencd@0: // NOTE: 'fileStream' will be automatically deleted ferencd@0: // when 'dataStream' is deleted ferencd@0: ferencd@0: // Create a new content handler ferencd@0: vmime::shared_ptr data = ferencd@0: vmime::make_shared (dataStream, 0); ferencd@0: ferencd@0: // Now create the attachment ferencd@0: ref att = vmime::make_shared ferencd@0: ( ferencd@0: /* attachment data */ data, ferencd@0: /* content type */ vmime::mediaType("image/jpeg"), ferencd@0: /* description */ vmime::text("Holiday photo"), ferencd@0: /* filename */ vmime::word("paris.jpg") ferencd@0: ); ferencd@0: \end{lstlisting} ferencd@0: ferencd@0: You will see later that the {\vcode vmime::fileAttachment} class already ferencd@0: encapsulates all the mechanics to create an attachment from a file. ferencd@0: ferencd@0: ferencd@0: % ============================================================================ ferencd@0: \section{Character sets, charsets and conversions\label{section_charsets}} ferencd@0: ferencd@0: Quoting from RFC-2278: \emph{`` The term 'charset' is used to refer to a ferencd@0: method of converting a sequence of octets into a sequence of characters.''} ferencd@0: ferencd@0: With the {\vcode vmime::charset} object, VMime supports conversion between ferencd@0: charsets using the {\em iconv} library, which is available on almost all ferencd@0: existing platforms. See {\vcode vmime::charset} and ferencd@0: {\vcode vmime::charsetConverter} in the class documentation to know more ferencd@0: about charset conversion. ferencd@0: ferencd@0: The following example shows how to convert data in one charset to another ferencd@0: charset. The data is extracted from the body of a message and converted ferencd@0: to UTF-8 charset: ferencd@0: ferencd@0: \begin{lstlisting}[caption={Extracting and converting body contents to a ferencd@0: specified charset}] ferencd@0: vmime::shared_ptr msg; // we have a message ferencd@0: ferencd@0: // Obtain the content handler first ferencd@0: vmime::shared_ptr body = msg->getBody(); ferencd@0: vmime::shared_ptr cth = body->getContents(); ferencd@0: ferencd@0: // Then, extract and convert the contents ferencd@0: vmime::utility::outputStreamAdapter out(std::cout); ferencd@0: vmime::utility::charsetFilteredOutputStream fout ferencd@0: (/* source charset */ body->getCharset(), ferencd@0: /* dest charset */ vmime::charset("utf-8"), ferencd@0: /* dest stream */ out); ferencd@0: ferencd@0: cth->extract(fout); ferencd@0: ferencd@0: fout.flush(); // Very important! ferencd@0: \end{lstlisting} ferencd@0: ferencd@0: ferencd@0: % ============================================================================ ferencd@0: \section{Non-ASCII text in header fields} ferencd@0: ferencd@0: MIME standard defines methods\footnote{See RFC-2047: Message Header Extensions ferencd@0: for Non-ASCII Text} for dealing with data which is not 7-bit only (ie. the ferencd@0: ASCII character set), in particular in header fields. For example, the field ferencd@0: ``Subject:'' use this data type. ferencd@0: ferencd@0: VMime is fully compatible with RFC-2047 and provides two objects for ferencd@0: manipulating 8-bit data: {\vcode vmime::text} and {\vcode vmime::word}. A word ferencd@0: represents textual information encoded in a specified charset. A text is ferencd@0: composed of one or more words. ferencd@0: ferencd@0: RFC-2047 describes the process of encoding 8-bit data into a 7-bit form; ferencd@0: basically, it relies on Base64 and Quoted-Printable encoding. Hopefully, all ferencd@0: the encoding/decoding process is done internally by VMime, so creating text ferencd@0: objects is fairly simple: ferencd@0: ferencd@0: \begin{lstlisting}[caption={Creating \vcode{vmime::text} objects}] ferencd@0: vmime::string inText = "Linux dans un téléphone mobile"; ferencd@0: vmime::charset inCharset = "utf-8"; ferencd@0: ferencd@0: vmime::text outText; ferencd@0: outText.createFromString(inText, inCharset); ferencd@0: ferencd@0: // 'outText' now contains 3 words: ferencd@0: // . "Linux dans un " ferencd@0: // . "téléphone " ferencd@0: // . "mobile" ferencd@0: ferencd@0: vmime::shared_ptr header = myMessage->getHeader(); ferencd@0: header->Subject()->setValue(outText); ferencd@0: \end{lstlisting} ferencd@0: ferencd@0: In general, you will not need to decode RFC-2047-encoded data as the process ferencd@0: is totally transparent in VMime. If you really have to, you can use the ferencd@0: {\vcode vmime::text::decodeAndUnfold()} static method to create a text object ferencd@0: from encoded data. ferencd@0: ferencd@0: For example, say you have the following encoded data: ferencd@0: ferencd@0: \begin{verbatim} ferencd@0: Linux dans un =?UTF-8?B?dMOpbMOpcGhvbmUgbW9iaWxl?= ferencd@0: \end{verbatim} ferencd@0: ferencd@0: You can simply decode it using the following code: ferencd@0: ferencd@0: \begin{lstlisting}[caption={Decoding RFC-2047-encoded data}] ferencd@0: vmime::string inData = ferencd@0: "Linux dans un =?UTF-8?B?dMOpbMOpcGhvbmUgbW9iaWxl?="; ferencd@0: ferencd@0: vmime::text outText; ferencd@0: vmime::text::decodeAndUnfold(inData, &outText); ferencd@0: \end{lstlisting} ferencd@0: ferencd@0: {\vcode vmime::text} also provides a function to convert all the words to ferencd@0: another charset in a single call. The following example shows how to convert ferencd@0: text stored in the Subject field of a message: ferencd@0: ferencd@0: \begin{lstlisting}[caption={Converting data in a {\vcode vmime::text} to a ferencd@0: specified charset}] ferencd@0: vmime::shared_ptr msg; // we have a message ferencd@0: ferencd@0: vmime::text subject = msg->getHeader()->Subject()->getValue(); ferencd@0: ferencd@0: const vmime::string subjectText = ferencd@0: subject.getConvertedText(vmime::charset("utf-8")); ferencd@0: ferencd@0: // 'subjectText' now contains the subject in UTF-8 encoding ferencd@0: \end{lstlisting} ferencd@0: ferencd@0: ferencd@0: % ============================================================================ ferencd@0: \section{Encodings\label{section_encodings}} ferencd@0: ferencd@0: \subsection{Introduction} % -------------------------------------------------- ferencd@0: ferencd@0: The MIME standard defines a certain number of encodings to allow data ferencd@0: to be safely transmitted from one peer to another. VMime provides ferencd@0: data encoding and decoding using the {\vcode vmime::utility::encoder::encoder} object. ferencd@0: ferencd@0: You should not need to use encoders directly, as all encoding/decoding ferencd@0: process is handled internally by the library, but it is good to know ferencd@0: they exist and how they work. ferencd@0: ferencd@0: \subsection{Using encoders} % ------------------------------------------------ ferencd@0: ferencd@0: You can create an instance of an encoder using the 'vmime::utility::encoder::encoderFactory' ferencd@0: object, giving the encoding name ({\it base64}, {\it quoted-printable}, ...). ferencd@0: The following example creates an instance of the Base64 encoder to encode ferencd@0: some data: ferencd@0: ferencd@0: \begin{lstlisting}[caption={A simple example of using an encoder}] ferencd@0: vmime::shared_ptr enc = ferencd@0: vmime::utility::encoder::encoderFactory::getInstance()->create("base64"); ferencd@0: ferencd@0: vmime::string inString("Some data to encode"); ferencd@0: vmime::utility::inputStreamStringAdapter in(inString); ferencd@0: ferencd@0: vmime::string outString; ferencd@0: vmime::utility::outputStreamStringAdapter out(outString); ferencd@0: ferencd@0: enc->encode(in, out); ferencd@0: ferencd@0: std::cout << "Encoded data is:" << outString << std::endl; ferencd@0: \end{lstlisting} ferencd@0: ferencd@0: \subsection{Enumerating available encoders} % -------------------------------- ferencd@0: ferencd@0: The behaviour of the encoders can be configured using properties. However, ferencd@0: not all encoders support properties. The following example\footnote{This is ferencd@0: an excerpt from {\vexample example6}} enumerates available encoders and the ferencd@0: supported properties for each of them: ferencd@0: ferencd@0: \begin{lstlisting}[caption={Enumerating encoders and their properties}] ferencd@0: vmime::shared_ptr ef = ferencd@0: vmime::utility::encoder::encoderFactory::getInstance(); ferencd@0: ferencd@0: std::cout << "Available encoders:" << std::endl; ferencd@0: ferencd@0: for (int i = 0 ; i < ef->getEncoderCount() ; ++i) ferencd@0: { ferencd@0: // Output encoder name ferencd@0: vmime::shared_ptr ferencd@0: enc = ef->getEncoderAt(i); ferencd@0: ferencd@0: std::cout << " * " << enc->getName() << std::endl; ferencd@0: ferencd@0: // Create an instance of the encoder to get its properties ferencd@0: vmime::shared_ptr e = enc->create(); ferencd@0: ferencd@0: std::vector props = e->getAvailableProperties(); ferencd@0: std::vector ::const_iterator it; ferencd@0: ferencd@0: for (it = props.begin() ; it != props.end() ; ++it) ferencd@0: std::cout << " - " << *it << std::endl; ferencd@0: \end{lstlisting} ferencd@0: ferencd@0: ferencd@0: % ============================================================================ ferencd@0: \section{Progress listeners} ferencd@0: ferencd@0: Progress listeners are used with objects that can notify you about the state ferencd@0: of progress when they are performing an operation. ferencd@0: ferencd@0: The {\vcode vmime::utility::progressListener} interface is rather simple: ferencd@0: ferencd@0: \begin{lstlisting} ferencd@0: void start(const int predictedTotal); ferencd@0: void progress(const int current, const int currentTotal); ferencd@0: void stop(const int total); ferencd@0: \end{lstlisting} ferencd@0: ferencd@0: {\vcode start()} and {\vcode stop()} are called at the beginning and the end ferencd@0: of the operation, respectively. {\vcode progress()} is called each time the ferencd@0: status of progress changes (eg. a chunk of data has been processed). There is ferencd@0: no unit specified for the values passed in argument. It depends on the ferencd@0: notifier: it can be bytes, percent, number of messages...