annotate 3rdparty/vmime/doc/book/basics.tex @ 0:a4671277546c tip

created the repository for the thymian project
author ferencd
date Tue, 17 Aug 2021 11:19:54 +0200
parents
children
rev   line source
ferencd@0 1 \chapter{Basics}
ferencd@0 2
ferencd@0 3 % ============================================================================
ferencd@0 4 \section{Reference counting}
ferencd@0 5
ferencd@0 6 \subsection{Introduction} % --------------------------------------------------
ferencd@0 7
ferencd@0 8 Since version 0.7.2cvs, VMime use smart pointers to simplify memory
ferencd@0 9 management. Smart pointers rely on
ferencd@0 10 RAII\footnote{Ressource Allocation is Initialisation} so that we do not need
ferencd@0 11 to bother with deleting an object (freeing memory) when it is not used
ferencd@0 12 anymore.
ferencd@0 13
ferencd@0 14 There are two possibilities for owning a reference to an object. We can own a
ferencd@0 15 strong reference to an object: as long as we keep this reference, the object
ferencd@0 16 is not destroyed. Or we can own a weak reference to the object: the object can
ferencd@0 17 be destroyed if nobody owns a strong reference to it, in which case the weak
ferencd@0 18 reference becomes invalid.
ferencd@0 19
ferencd@0 20 An object is destroyed as soon as the last strong reference to it is released.
ferencd@0 21 At the same tine, all weak references (if any) are automatically set to point
ferencd@0 22 to \vnull.
ferencd@0 23
ferencd@0 24 In VMime, these two types of references are known as {\vcode vmime::shared\_ptr}
ferencd@0 25 and {\vcode vmime::weak\_ptr}, respectively.
ferencd@0 26
ferencd@0 27 \vnote{since November 2013, we switched from an old, intrusive implementation
ferencd@0 28 of smart pointers to a more standard one: either Boost {\vcode shared\_ptr<>}
ferencd@0 29 implementation or standard C++ one if we are compiling in C++11. Here are the
ferencd@0 30 changes:
ferencd@0 31
ferencd@0 32 {\vcode vmime::ref <>} is replaced with {\vcode vmime::shared\_ptr <>}
ferencd@0 33
ferencd@0 34 {\vcode vmime::weak\_ref <>} is replaced with {\vcode vmime::weak\_ptr <>}
ferencd@0 35
ferencd@0 36 {\vcode vmime::create <>} is replaced with {\vcode vmime::make\_shared <>}
ferencd@0 37 }
ferencd@0 38
ferencd@0 39 \subsection{Instanciating reference-counted objects} % -----------------------
ferencd@0 40
ferencd@0 41 In VMime, all objects that support reference counting inherit from the
ferencd@0 42 {\vcode vmime::object} class, which is responsible for
ferencd@0 43 incrementing/decrementing the counter and managing the object's life cycle.
ferencd@0 44 If you want to create a smart pointer to a new object instance, you should
ferencd@0 45 use the function {\vcode vmime::make\_shared} instead of the {\vcode new}
ferencd@0 46 operator.
ferencd@0 47
ferencd@0 48 \begin{lstlisting}[caption={Smarts pointers and creating objects}]
ferencd@0 49 class myObject : public vmime::object
ferencd@0 50 {
ferencd@0 51 public:
ferencd@0 52
ferencd@0 53 myObject(const vmime::string& name)
ferencd@0 54 : m_name(name)
ferencd@0 55 {
ferencd@0 56 }
ferencd@0 57
ferencd@0 58 void sayHello()
ferencd@0 59 {
ferencd@0 60 std::cout << "Hello " << m_name << std::endl;
ferencd@0 61 }
ferencd@0 62
ferencd@0 63 private:
ferencd@0 64
ferencd@0 65 vmime::string m_name;
ferencd@0 66 };
ferencd@0 67
ferencd@0 68 int main()
ferencd@0 69 {
ferencd@0 70 vmime::shared_ptr <myObject> obj =
ferencd@0 71 vmime::make_shared <myObject>("world");
ferencd@0 72
ferencd@0 73 obj->sayHello();
ferencd@0 74
ferencd@0 75 return 0;
ferencd@0 76
ferencd@0 77 } // Here, 'obj' gets automatically destroyed
ferencd@0 78 \end{lstlisting}
ferencd@0 79
ferencd@0 80 \subsection{Using smart pointers} % ------------------------------------------
ferencd@0 81
ferencd@0 82 Smart pointers are copiable, assignable and comparable. You can use them like
ferencd@0 83 you would use normal ("raw") C++ pointers (eg. you can write
ferencd@0 84 \lstinline{!ptr, ptr != NULL, ptr->method(), *ptr}...).
ferencd@0 85
ferencd@0 86 Type safety is also guaranteed, and you can type cast smart pointers using
ferencd@0 87 the {\vcode static\_cast()}, {\vcode dynamic\_cast()} and {\vcode const\_cast()}
ferencd@0 88 equivalents on {\vcode vmime::shared\_ptr} and {\vcode vmime::weak\_ptr} objects:
ferencd@0 89
ferencd@0 90 \begin{lstlisting}[caption={Casting smart pointers}]
ferencd@0 91 class myBase : public vmime::object { }
ferencd@0 92 class myObject : public myBase { }
ferencd@0 93
ferencd@0 94 vmime::shared_ptr <myObject> obj = vmime::make_shared <myObject>();
ferencd@0 95
ferencd@0 96 // Implicit downcast
ferencd@0 97 vmime::shared_ptr <myBase> base = obj;
ferencd@0 98
ferencd@0 99 // Explicit upcast
ferencd@0 100 vmime::shared_ptr <myObject> obj2 = vmime::dynamicCast <myObject>(base);
ferencd@0 101 \end{lstlisting}
ferencd@0 102
ferencd@0 103 Weak references are used to resolve reference cycles (an object which refers
ferencd@0 104 directly or indirectly to itself). The following example illustrates a
ferencd@0 105 typical problem of reference counting:
ferencd@0 106
ferencd@0 107 \begin{lstlisting}
ferencd@0 108 class parent : public vmime::object
ferencd@0 109 {
ferencd@0 110 public:
ferencd@0 111
ferencd@0 112 void createChild(vmime::shared_ptr <child> c)
ferencd@0 113 {
ferencd@0 114 m_child = c;
ferencd@0 115 }
ferencd@0 116
ferencd@0 117 private:
ferencd@0 118
ferencd@0 119 vmime::shared_ptr <child> m_child;
ferencd@0 120 };
ferencd@0 121
ferencd@0 122 class child : public vmime::object
ferencd@0 123 {
ferencd@0 124 public:
ferencd@0 125
ferencd@0 126 child(vmime::shared_ptr <parent> p)
ferencd@0 127 : m_parent(p)
ferencd@0 128 {
ferencd@0 129 }
ferencd@0 130
ferencd@0 131 private:
ferencd@0 132
ferencd@0 133 vmime::shared_ptr <parent> m_parent;
ferencd@0 134 };
ferencd@0 135
ferencd@0 136 int main()
ferencd@0 137 {
ferencd@0 138 vmime::shared_ptr <parent> p = vmime::make_shared <parent>();
ferencd@0 139 vmime::shared_ptr <child> c = vmime::make_shared <child>();
ferencd@0 140
ferencd@0 141 p->setChild(c);
ferencd@0 142 }
ferencd@0 143 \end{lstlisting}
ferencd@0 144
ferencd@0 145 In this example, neither {\vcode p} nor {\vcode c} will be deleted when
ferencd@0 146 exiting {\vcode main()}. That's because {\vcode p} indirectly points to itself
ferencd@0 147 {\em via} {\vcode c}, and {\em vice versa}. The solution is to use a weak
ferencd@0 148 reference to the parent:
ferencd@0 149
ferencd@0 150 \begin{lstlisting}
ferencd@0 151 vmime::weak_ptr <parent> m_parent;
ferencd@0 152 \end{lstlisting}
ferencd@0 153
ferencd@0 154 The decision to make the parent or the child a weak reference is purely
ferencd@0 155 semantic, and it depends on the context and the relationships between the
ferencd@0 156 objects. Note that when the parent is deleted, the {\vcode m\_parent} member
ferencd@0 157 of the child points to \vnull.
ferencd@0 158
ferencd@0 159 More information about reference counting can be found on
ferencd@0 160 Wikipedia\footnote{http://en.wikipedia.org/wiki/Reference\_counting}.
ferencd@0 161
ferencd@0 162 % ============================================================================
ferencd@0 163 \section{Error handling}
ferencd@0 164
ferencd@0 165 In VMime, error handling is exclusively based on exceptions, there is no error
ferencd@0 166 codes, or things like that.
ferencd@0 167
ferencd@0 168 VMime code may throw exceptions in many different situations: an unexpected
ferencd@0 169 error occured, an operation is not supported, etc. You should catch them if
ferencd@0 170 you want to report failures to the user. This is also useful when debugging
ferencd@0 171 your program.
ferencd@0 172
ferencd@0 173 VMime exceptions support chaining: an exception can be encapsulated into
ferencd@0 174 another exception to hide implementation details. The function
ferencd@0 175 {\vcode exception::other()} returns the next exception in the chain,
ferencd@0 176 or \vnull.
ferencd@0 177
ferencd@0 178 Following is an example code for catching VMime exceptions and writing error
ferencd@0 179 messages to the console:
ferencd@0 180
ferencd@0 181 \begin{lstlisting}[caption={Catching VMime exceptions}]
ferencd@0 182 std::ostream& operator<<(std::ostream& os, const vmime::exception& e)
ferencd@0 183 {
ferencd@0 184 os << "* vmime::exceptions::" << e.name() << std::endl;
ferencd@0 185 os << " what = " << e.what() << std::endl;
ferencd@0 186
ferencd@0 187 // Recursively print all encapsuled exceptions
ferencd@0 188 if (e.other() != NULL)
ferencd@0 189 os << *e.other();
ferencd@0 190
ferencd@0 191 return os;
ferencd@0 192 }
ferencd@0 193
ferencd@0 194 ...
ferencd@0 195
ferencd@0 196 try
ferencd@0 197 {
ferencd@0 198 // ...some call to VMime...
ferencd@0 199 }
ferencd@0 200 catch (vmime::exception& e)
ferencd@0 201 {
ferencd@0 202 std::cerr << e; // VMime exception
ferencd@0 203 }
ferencd@0 204 catch (std::exception& e)
ferencd@0 205 {
ferencd@0 206 std::cerr << e.what(); // standard exception
ferencd@0 207 }
ferencd@0 208 \end{lstlisting}
ferencd@0 209
ferencd@0 210 Read the source of {\vexample example6} if yo want to see a more complete
ferencd@0 211 example of using VMime exceptions (such as getting more detailed information
ferencd@0 212 by using specialized classes of {\vcode vmime::exception}).
ferencd@0 213
ferencd@0 214
ferencd@0 215 % ============================================================================
ferencd@0 216 \section{Basic objects}
ferencd@0 217
ferencd@0 218 \subsection{The {\vcode component} class} % ----------------------------------
ferencd@0 219
ferencd@0 220 In VMime, all the components of a message inherit from the same class
ferencd@0 221 {\vcode component}. This includes the message itself (classes {\vcode message}
ferencd@0 222 and {\vcode bodyPart}), the header, the header fields and the value of each
ferencd@0 223 header field, the body and all the parts in the message.
ferencd@0 224
ferencd@0 225 The class component provide a common interface for parsing or generating all
ferencd@0 226 these components (methods {\vcode parse()} and {\vcode generate()}). It also
ferencd@0 227 provides additional functions to get some information about the parsing
ferencd@0 228 process or the structure (methods {\vcode getParsedOffset()},
ferencd@0 229 {\vcode getParsedLength()} and {\vcode getChildComponents()}).
ferencd@0 230
ferencd@0 231 VMime also provides a set of classes corresponding to the basic types found
ferencd@0 232 in a message; for example a mailbox, a mailbox list, date/time information,
ferencd@0 233 media type, etc. They all inherit from {\vcode component} too.
ferencd@0 234
ferencd@0 235 \subsection{Date and time} % -------------------------------------------------
ferencd@0 236
ferencd@0 237 Date and time are used in several places in VMime, particularly in header
ferencd@0 238 fields (Date, Received, ...). VMime fully supports RFC-2822's date and time
ferencd@0 239 specification. The object {\vcode vmime::datetime} is used to manipulate date
ferencd@0 240 and time information, and to parse/generate it from/to RFC-2822 format.
ferencd@0 241
ferencd@0 242 The following code snippet show various manners of using the
ferencd@0 243 {\vcode vmime::datetime} object:
ferencd@0 244
ferencd@0 245 \begin{lstlisting}[caption={Using {\vcode vmime::datetime} object}]
ferencd@0 246 // Creating from string in RFC-2822 format
ferencd@0 247 vmime::datetime d1("Sat, 08 Oct 2005 14:07:52 +0200");
ferencd@0 248
ferencd@0 249 // Creating from components
ferencd@0 250 vmime::datetime d2(
ferencd@0 251 /* date */ 2005, vmime::datetime::OCTOBER, 8,
ferencd@0 252 /* time */ 14, 7, 52,
ferencd@0 253 /* zone */ vmime::datetime::GMT2);
ferencd@0 254
ferencd@0 255 // Getting day of week
ferencd@0 256 const int dow = d2.getWeekDay(); // 'dow' should be datetime::SATURDAY
ferencd@0 257 \end{lstlisting}
ferencd@0 258
ferencd@0 259 \subsection{Media type} % ----------------------------------------------------
ferencd@0 260
ferencd@0 261 In MIME, the nature of the data contained in parts is identified using a
ferencd@0 262 media type. A general type (eg. \emph{image}) and a sub-type (eg. \emph{jpeg})
ferencd@0 263 are put together to form a media type (eg. \emph{image/jpeg}). This is also
ferencd@0 264 called the MIME type.
ferencd@0 265
ferencd@0 266 There are a lot of media types officially registered, and vendor-specific
ferencd@0 267 types are possible (they start with ``x-'', eg.
ferencd@0 268 \emph{application/x-zip-compressed}).
ferencd@0 269
ferencd@0 270 In VMime, the object {\vcode vmime::mediaType} represents a media type. There
ferencd@0 271 are also some constants for top-level types and sub-types in the
ferencd@0 272 {\vcode vmime::mediaTypes} namespace. For example, you can instanciate a new
ferencd@0 273 media type with:
ferencd@0 274
ferencd@0 275 \begin{lstlisting}
ferencd@0 276 vmime::mediaType theType(
ferencd@0 277 /* top-level type */ vmime::mediaTypes::IMAGE,
ferencd@0 278 /* sub-type */ vmime::mediaTypes::IMAGE_JPEG);
ferencd@0 279
ferencd@0 280 // theType.getType() is "image"
ferencd@0 281 // theType.getSubType() is "jpeg"
ferencd@0 282 // theType.generate() returns "image/jpeg"
ferencd@0 283 \end{lstlisting}
ferencd@0 284
ferencd@0 285 For more information about media types, see
ferencd@0 286 RFC-2046\footnote{http://www.faqs.org/rfcs/rfc2046.html}.
ferencd@0 287
ferencd@0 288 \subsection{Mailbox and mailbox groups} % ------------------------------------
ferencd@0 289
ferencd@0 290 VMime provides several objects for working with mailboxes and addresses.
ferencd@0 291
ferencd@0 292 The {\vcode vmime::address} class is an abstract type for representing an
ferencd@0 293 address: it can be either a mailbox (type {\vcode vmime::mailbox}) or a
ferencd@0 294 mailbox group (type {\vcode vmime::mailboxGroup}). A mailbox is composed of
ferencd@0 295 an email address (mandatory) and possibly a name. A mailbox group is simply
ferencd@0 296 a named list of mailboxes (see Figure \ref{uml_addr_mbox_mboxgroup}).
ferencd@0 297
ferencd@0 298 \begin{lstlisting}[caption={Using mailboxes and mailbox groups}]
ferencd@0 299 vmime::shared_ptr <vmime::mailbox> mbox1 = vmime::make_shared <vmime::mailbox>
ferencd@0 300 (/* name */ vmime::text("John Doe"), /* email */ "john.doe@acme.com");
ferencd@0 301 vmime::shared_ptr <vmime::mailbox> mbox2 = vmime::make_shared <vmime::mailbox>
ferencd@0 302 (/* no name, email only */ "bill@acme.com");
ferencd@0 303
ferencd@0 304 vmime::shared_ptr <vmime::mailboxGroup> grp = vmime::make_shared <vmime::mailboxGroup>();
ferencd@0 305 grp->appendMailbox(mbox1);
ferencd@0 306 grp->appendMailbox(mbox2);
ferencd@0 307 \end{lstlisting}
ferencd@0 308
ferencd@0 309 \begin{figure}[ht!]
ferencd@0 310 \center\includegraphics[width=0.7\textwidth]
ferencd@0 311 {images/address-mailbox-mailboxgroup.png}\endcenter
ferencd@0 312 \caption{Diagram for address-related classes}
ferencd@0 313 \label{uml_addr_mbox_mboxgroup}
ferencd@0 314 \end{figure}
ferencd@0 315
ferencd@0 316
ferencd@0 317 % ============================================================================
ferencd@0 318 \section{Message, body parts and header}
ferencd@0 319
ferencd@0 320 \subsection{Introduction to MIME messages} % ---------------------------------
ferencd@0 321
ferencd@0 322 A MIME message is a recursive structure in which each part can contains one
ferencd@0 323 or more parts (or \emph{entities}). Each part is composed of a header and
ferencd@0 324 a body (actual contents). Figure \ref{uml_msg_body_header} shows how this
ferencd@0 325 model is implemented in VMime, and all classes that take part in it.
ferencd@0 326
ferencd@0 327 \begin{figure}
ferencd@0 328 \center\includegraphics[width=1.0\textwidth]
ferencd@0 329 {images/message-body-header.png}\endcenter
ferencd@0 330 \caption{Overall structure of MIME messages}
ferencd@0 331 \label{uml_msg_body_header}
ferencd@0 332 \end{figure}
ferencd@0 333
ferencd@0 334
ferencd@0 335 \subsection{Header and header fields} % --------------------------------------
ferencd@0 336
ferencd@0 337 \subsubsection{Standard header fields} % .....................................
ferencd@0 338
ferencd@0 339 Header fields carry information about a message (or a part) and its contents.
ferencd@0 340 Each header field has a name and a value. All types that can be used as a
ferencd@0 341 field value inherit from the {\vcode headerFieldValue} class.
ferencd@0 342
ferencd@0 343 You cannot instanciate header fields directly using their constructor.
ferencd@0 344 Instead, you should use the {\vcode headerFieldFactory} object. This ensures
ferencd@0 345 the right field type and value type is used for the specified field name.
ferencd@0 346 For more information about how to use header fields and the factory, see
ferencd@0 347 section \ref{msg-building-simple-message}.
ferencd@0 348
ferencd@0 349 Some standard fields are officially registered and have their value type
ferencd@0 350 specified in a RFC. Table \ref{standard-fields} lists all the fields
ferencd@0 351 registered by default in VMime and the value type they contains.
ferencd@0 352
ferencd@0 353 By default, all unregistered fields have a value of type {\vcode text}.
ferencd@0 354
ferencd@0 355 \begin{table}[!ht]
ferencd@0 356 \begin{center}
ferencd@0 357 \noindent\begin{tabularx}{0.85\textwidth}{|X|X|}
ferencd@0 358 \hline
ferencd@0 359 {\bf Field Name} &
ferencd@0 360 {\bf Value Type} \\
ferencd@0 361 \hline
ferencd@0 362 \hline
ferencd@0 363 From & mailbox \\
ferencd@0 364 To & addressList \\
ferencd@0 365 Cc & addressList \\
ferencd@0 366 Bcc & addressList \\
ferencd@0 367 Sender & mailbox \\
ferencd@0 368 Date & datetime \\
ferencd@0 369 Received & relay \\
ferencd@0 370 Subject & text \\
ferencd@0 371 Reply-To & mailbox \\
ferencd@0 372 Delivered-To & mailbox \\
ferencd@0 373 Organization & text \\
ferencd@0 374 Return-Path & path \\
ferencd@0 375 Mime-Version & text \\
ferencd@0 376 Content-Type & mediaType \\
ferencd@0 377 Content-Transfer-Encoding & encoding \\
ferencd@0 378 Content-Description & text \\
ferencd@0 379 Content-Disposition & contentDisposition \\
ferencd@0 380 Content-Id & messageId \\
ferencd@0 381 Content-Location & text \\
ferencd@0 382 Message-Id & messageId \\
ferencd@0 383 In-Reply-To & messageIdSequence \\
ferencd@0 384 References & messageIdSequence \\
ferencd@0 385 Original-Message-Id & messageId \\
ferencd@0 386 Disposition & disposition \\
ferencd@0 387 Disposition-Notification-To & mailboxList \\
ferencd@0 388 \hline
ferencd@0 389 \end{tabularx}
ferencd@0 390 \end{center}
ferencd@0 391 \label{standard-fields}
ferencd@0 392 \caption{Standard fields and their types}
ferencd@0 393 \end{table}
ferencd@0 394
ferencd@0 395
ferencd@0 396 \subsubsection{Parameterized fields} % .......................................
ferencd@0 397
ferencd@0 398 In addition to a value, some header fields can contain one or more
ferencd@0 399 \emph{name=value} couples which are called \emph{parameters}. For example,
ferencd@0 400 this is used in the \emph{Content-Type} field to give more information about
ferencd@0 401 the content:
ferencd@0 402
ferencd@0 403 \begin{verbatim}
ferencd@0 404 Content-Type: text/plain; charset="utf-8"
ferencd@0 405 \end{verbatim}
ferencd@0 406
ferencd@0 407 Fields that support parameters inherit from the
ferencd@0 408 {\vcode parameterizedHeaderField} class which provides methods to deal with
ferencd@0 409 these parameters: {\vcode appendParameter()}, {\vcode getParameterAt()}...
ferencd@0 410
ferencd@0 411 A parameter is identified by a name (eg. \emph{charset}) and associated to
ferencd@0 412 a value of type {\vcode vmime::text}. Parameters provide helper functions to
ferencd@0 413 convert automatically from basic types to text, and \emph{vice versa}. The
ferencd@0 414 following example illustrates it:
ferencd@0 415
ferencd@0 416 \begin{lstlisting}[caption={Getting and setting parameter value in fields}]
ferencd@0 417 vmime::shared_ptr <vmime::parameterizedField> field =
ferencd@0 418 header->findField <vmime::parameterizedField>("X-Field-That-Contains-Parameters");
ferencd@0 419
ferencd@0 420 // Use setValue() to convert from a basic type to 'text'
ferencd@0 421 vmime::shared_ptr <vmime::parameter> prm = field->getParameter("my-date-param");
ferencd@0 422 prm->setValue(vmime::datetime::now());
ferencd@0 423
ferencd@0 424 // Use getValueAs() to convert from 'text' to a basic type
ferencd@0 425 prm = field->getParameter("my-charset-param");
ferencd@0 426 const vmime::charset ch = prm->getValueAs <vmime::charset>();
ferencd@0 427 \end{lstlisting}
ferencd@0 428
ferencd@0 429 Some fields provide easy access to their standard parameters (see
ferencd@0 430 Table \ref{standard-prm-fields}). This avoids finding the parameter and
ferencd@0 431 \emph{dynamic-casting} its value to the right type. The following code
ferencd@0 432 illustrates how to use it:
ferencd@0 433
ferencd@0 434 \begin{lstlisting}
ferencd@0 435 vmime::shared_ptr <vmime::contentTypeField> field =
ferencd@0 436 header->getField <vmime::contentTypeField>(vmime::fields::CONTENT_TYPE);
ferencd@0 437
ferencd@0 438 // 1. First solution: the "hard" way
ferencd@0 439 vmime::shared_ptr <vmime::parameter> prm = field->findParameter("charset");
ferencd@0 440 const charset ch1 = prm->getValueAs <vmime::charset>();
ferencd@0 441
ferencd@0 442 // 2. Second solution: the simple way
ferencd@0 443 const charset ch2 = field->getCharset();
ferencd@0 444 \end{lstlisting}
ferencd@0 445
ferencd@0 446 \vnote{In both cases, an exception {\vcode no\_such\_parameter} can be
ferencd@0 447 thrown if the parameter does not exist, so be sure to catch it.}
ferencd@0 448
ferencd@0 449 \begin{table}[ht!]
ferencd@0 450 \begin{center}
ferencd@0 451 \noindent\begin{tabularx}{0.85\textwidth}{|l|l|X|}
ferencd@0 452 \hline
ferencd@0 453 {\bf Field Name} &
ferencd@0 454 {\bf Field Type} &
ferencd@0 455 {\bf Parameters} \\
ferencd@0 456 \hline
ferencd@0 457 \hline
ferencd@0 458 Content-Type & contentTypeField & boundary, charset, report-type \\
ferencd@0 459 \hline
ferencd@0 460 Content-Disposition & contentDispositionField & creation-date,
ferencd@0 461 modification-date, read-date, filename, size \\
ferencd@0 462 \hline
ferencd@0 463 \end{tabularx}
ferencd@0 464 \end{center}
ferencd@0 465 \label{standard-prm-fields}
ferencd@0 466 \caption{Standard parameterized fields}
ferencd@0 467 \end{table}
ferencd@0 468
ferencd@0 469
ferencd@0 470
ferencd@0 471 % ============================================================================
ferencd@0 472 \section{Streams}
ferencd@0 473
ferencd@0 474 \subsection{Streams and stream adapters} % -----------------------------------
ferencd@0 475
ferencd@0 476 Streams permit reading or writing data whatever the underlying system is:
ferencd@0 477 a file on a hard disk, a socket connected to a remote service...
ferencd@0 478
ferencd@0 479 There are two types of streams: input streams (from which you can read data)
ferencd@0 480 and output streams (in which you can write data). Some adapters are provided
ferencd@0 481 for compatibility and convenience, for example:
ferencd@0 482
ferencd@0 483 \begin{itemize}
ferencd@0 484 \item {\vcode inputStreamAdapter} and {\vcode outputStreamAdapter}: allow
ferencd@0 485 to use standard C++ iostreams with VMime;
ferencd@0 486 \item {\vcode inputStreamStringAdapter} and
ferencd@0 487 {\vcode outputStreamStringAdapter}: use a {\vcode vmime::string} object to
ferencd@0 488 read/write data.
ferencd@0 489 \end{itemize}
ferencd@0 490
ferencd@0 491 The following example shows two ways of writing the current date to the
ferencd@0 492 standard output, using stream adapters:
ferencd@0 493
ferencd@0 494 \begin{lstlisting}[caption={Using stream adapters}]
ferencd@0 495 // Get current date and time
ferencd@0 496 const vmime::datetime date = vmime::datetime::now();
ferencd@0 497
ferencd@0 498 // 1. Using outputStreamAdapter
ferencd@0 499 vmime::utility::outputStreamAdapter out(std::cout);
ferencd@0 500
ferencd@0 501 std::cout << "Current date is: ";
ferencd@0 502 date.generate(out);
ferencd@0 503 std::cout << std::endl;
ferencd@0 504
ferencd@0 505 // 2. Using outputStreamStringAdapter
ferencd@0 506 vmime::string dateStr;
ferencd@0 507 vmime::utility::outputStreamStringAdapter outStr(dateStr);
ferencd@0 508
ferencd@0 509 date.generate(outStr);
ferencd@0 510
ferencd@0 511 std::cout << "Current date is: " << dateStr << std::endl;
ferencd@0 512 \end{lstlisting}
ferencd@0 513
ferencd@0 514
ferencd@0 515 \subsection{Stream filters} % ------------------------------------------------
ferencd@0 516
ferencd@0 517 Input and output streams can be filtered to perform inline conversions (for
ferencd@0 518 example, there is a filter to convert ``{\textbackslash}r{\textbackslash}n''
ferencd@0 519 sequences to ``{\textbackslash}n''). They inherit from
ferencd@0 520 {\vcode vmime::utility::filteredInputStream} or
ferencd@0 521 {\vcode vmime::utility::filteredOutputStream} and are used like adapters (some
ferencd@0 522 filters also accept parameters; read the documentation).
ferencd@0 523
ferencd@0 524 The most useful filter in VMime (and probably the only one you will need) is
ferencd@0 525 the {\vcode charsetFilteredOutputStream}, which performs inline conversion
ferencd@0 526 of charsets. See \ref{section_charsets} to know how to use it.
ferencd@0 527
ferencd@0 528 \vnote{After you have finished to use a filtered output stream, it is
ferencd@0 529 important to call {\vcode flush()} on it to flush the internal buffer.
ferencd@0 530 If {\vcode flush()} is not called, not all data may be written to the
ferencd@0 531 underlying stream.}
ferencd@0 532
ferencd@0 533
ferencd@0 534 % ============================================================================
ferencd@0 535 \section{Content handlers}
ferencd@0 536
ferencd@0 537 \subsection{Introduction} % --------------------------------------------------
ferencd@0 538
ferencd@0 539 Content handlers are an abstraction for data sources. They are currently used
ferencd@0 540 when some data need to be stored for later use (eg. body part contents,
ferencd@0 541 attachment data, ...). Data can be stored encoded or unencoded (for more
ferencd@0 542 information about encodings, see \ref{section_encodings}).
ferencd@0 543
ferencd@0 544 \subsection{Extracting data from content handlers} % -------------------------
ferencd@0 545
ferencd@0 546 You can extract data in a content handler using the {\vcode extract()} method
ferencd@0 547 (which automatically decodes data if encoded) or {\vcode extractRaw()} (which
ferencd@0 548 extracts data without perfoming any decoding).
ferencd@0 549
ferencd@0 550 The following example shows how to extract the body text from a message, and
ferencd@0 551 writing it to the standard output with charset conversion:
ferencd@0 552
ferencd@0 553 \begin{lstlisting}[caption={Using content handlers to extract body text from
ferencd@0 554 a message}]
ferencd@0 555 // Suppose we already have a message
ferencd@0 556 vmime::shared_ptr <vmime::message> msg;
ferencd@0 557
ferencd@0 558 // Obtains a reference to the body contents
ferencd@0 559 vmime::shared_ptr <vmime::body> body = msg->getBody();
ferencd@0 560 vmime::shared_ptr <vmime::contentHandler> cts = body->getContents();
ferencd@0 561
ferencd@0 562 vmime::utility::outputStreamAdapter out(std::cout);
ferencd@0 563 cts->extract(out);
ferencd@0 564 \end{lstlisting}
ferencd@0 565
ferencd@0 566 \vnote{The body contents is extracted ``as is''. No charset conversion is
ferencd@0 567 performed. See \ref{section_charsets} to know more about conversion between
ferencd@0 568 charsets.}
ferencd@0 569
ferencd@0 570
ferencd@0 571 \subsection{Creating content handlers} % -------------------------------------
ferencd@0 572
ferencd@0 573 When you are building a message, you may need to instanciate content handlers
ferencd@0 574 if you want to set the contents of a body part. The following code snippet
ferencd@0 575 shows how to set the body text of a part from a string:
ferencd@0 576
ferencd@0 577 \begin{lstlisting}[caption={Setting the contents of a body part}]
ferencd@0 578 vmime::shared_ptr <vmime::bodyPart> part; // suppose we have a body part
ferencd@0 579
ferencd@0 580 // Create a new content handler from a string
ferencd@0 581 vmime::shared_ptr <vmime::contentHandler> cth =
ferencd@0 582 vmime::make_shared <vmime::stringContentHandler>("Put body contents here");
ferencd@0 583
ferencd@0 584 // Set the contents
ferencd@0 585 part->getBody()->setContents(cth);
ferencd@0 586 \end{lstlisting}
ferencd@0 587
ferencd@0 588 Content handlers are also used when creating attachments. The following
ferencd@0 589 example illustrates how to create an attachment from a file:
ferencd@0 590
ferencd@0 591 \begin{lstlisting}[caption={Creating an attachment from a file}]
ferencd@0 592 // Create a stream from a file
ferencd@0 593 std::ifstream* fileStream = new std::ifstream();
ferencd@0 594
ferencd@0 595 fileStream->open("/home/vincent/paris.jpg", std::ios::binary);
ferencd@0 596
ferencd@0 597 if (!*fileStream)
ferencd@0 598 // handle error
ferencd@0 599
ferencd@0 600 vmime::shared_ptr <utility::stream> dataStream =
ferencd@0 601 vmime::make_shared <vmime::utility::inputStreamPointerAdapter>(fileStream);
ferencd@0 602
ferencd@0 603 // NOTE: 'fileStream' will be automatically deleted
ferencd@0 604 // when 'dataStream' is deleted
ferencd@0 605
ferencd@0 606 // Create a new content handler
ferencd@0 607 vmime::shared_ptr <contentHandler> data =
ferencd@0 608 vmime::make_shared <vmime::streamContentHandler>(dataStream, 0);
ferencd@0 609
ferencd@0 610 // Now create the attachment
ferencd@0 611 ref <vmime::attachment> att = vmime::make_shared <vmime::defaultAttachment>
ferencd@0 612 (
ferencd@0 613 /* attachment data */ data,
ferencd@0 614 /* content type */ vmime::mediaType("image/jpeg"),
ferencd@0 615 /* description */ vmime::text("Holiday photo"),
ferencd@0 616 /* filename */ vmime::word("paris.jpg")
ferencd@0 617 );
ferencd@0 618 \end{lstlisting}
ferencd@0 619
ferencd@0 620 You will see later that the {\vcode vmime::fileAttachment} class already
ferencd@0 621 encapsulates all the mechanics to create an attachment from a file.
ferencd@0 622
ferencd@0 623
ferencd@0 624 % ============================================================================
ferencd@0 625 \section{Character sets, charsets and conversions\label{section_charsets}}
ferencd@0 626
ferencd@0 627 Quoting from RFC-2278: \emph{`` The term 'charset' is used to refer to a
ferencd@0 628 method of converting a sequence of octets into a sequence of characters.''}
ferencd@0 629
ferencd@0 630 With the {\vcode vmime::charset} object, VMime supports conversion between
ferencd@0 631 charsets using the {\em iconv} library, which is available on almost all
ferencd@0 632 existing platforms. See {\vcode vmime::charset} and
ferencd@0 633 {\vcode vmime::charsetConverter} in the class documentation to know more
ferencd@0 634 about charset conversion.
ferencd@0 635
ferencd@0 636 The following example shows how to convert data in one charset to another
ferencd@0 637 charset. The data is extracted from the body of a message and converted
ferencd@0 638 to UTF-8 charset:
ferencd@0 639
ferencd@0 640 \begin{lstlisting}[caption={Extracting and converting body contents to a
ferencd@0 641 specified charset}]
ferencd@0 642 vmime::shared_ptr <vmime::message> msg; // we have a message
ferencd@0 643
ferencd@0 644 // Obtain the content handler first
ferencd@0 645 vmime::shared_ptr <vmime::body> body = msg->getBody();
ferencd@0 646 vmime::shared_ptr <const vmime::contentHandler> cth = body->getContents();
ferencd@0 647
ferencd@0 648 // Then, extract and convert the contents
ferencd@0 649 vmime::utility::outputStreamAdapter out(std::cout);
ferencd@0 650 vmime::utility::charsetFilteredOutputStream fout
ferencd@0 651 (/* source charset */ body->getCharset(),
ferencd@0 652 /* dest charset */ vmime::charset("utf-8"),
ferencd@0 653 /* dest stream */ out);
ferencd@0 654
ferencd@0 655 cth->extract(fout);
ferencd@0 656
ferencd@0 657 fout.flush(); // Very important!
ferencd@0 658 \end{lstlisting}
ferencd@0 659
ferencd@0 660
ferencd@0 661 % ============================================================================
ferencd@0 662 \section{Non-ASCII text in header fields}
ferencd@0 663
ferencd@0 664 MIME standard defines methods\footnote{See RFC-2047: Message Header Extensions
ferencd@0 665 for Non-ASCII Text} for dealing with data which is not 7-bit only (ie. the
ferencd@0 666 ASCII character set), in particular in header fields. For example, the field
ferencd@0 667 ``Subject:'' use this data type.
ferencd@0 668
ferencd@0 669 VMime is fully compatible with RFC-2047 and provides two objects for
ferencd@0 670 manipulating 8-bit data: {\vcode vmime::text} and {\vcode vmime::word}. A word
ferencd@0 671 represents textual information encoded in a specified charset. A text is
ferencd@0 672 composed of one or more words.
ferencd@0 673
ferencd@0 674 RFC-2047 describes the process of encoding 8-bit data into a 7-bit form;
ferencd@0 675 basically, it relies on Base64 and Quoted-Printable encoding. Hopefully, all
ferencd@0 676 the encoding/decoding process is done internally by VMime, so creating text
ferencd@0 677 objects is fairly simple:
ferencd@0 678
ferencd@0 679 \begin{lstlisting}[caption={Creating \vcode{vmime::text} objects}]
ferencd@0 680 vmime::string inText = "Linux dans un téléphone mobile";
ferencd@0 681 vmime::charset inCharset = "utf-8";
ferencd@0 682
ferencd@0 683 vmime::text outText;
ferencd@0 684 outText.createFromString(inText, inCharset);
ferencd@0 685
ferencd@0 686 // 'outText' now contains 3 words:
ferencd@0 687 // . <us-ascii> "Linux dans un "
ferencd@0 688 // . <utf-8> "téléphone "
ferencd@0 689 // . <us-ascii> "mobile"
ferencd@0 690
ferencd@0 691 vmime::shared_ptr <vmime::header> header = myMessage->getHeader();
ferencd@0 692 header->Subject()->setValue(outText);
ferencd@0 693 \end{lstlisting}
ferencd@0 694
ferencd@0 695 In general, you will not need to decode RFC-2047-encoded data as the process
ferencd@0 696 is totally transparent in VMime. If you really have to, you can use the
ferencd@0 697 {\vcode vmime::text::decodeAndUnfold()} static method to create a text object
ferencd@0 698 from encoded data.
ferencd@0 699
ferencd@0 700 For example, say you have the following encoded data:
ferencd@0 701
ferencd@0 702 \begin{verbatim}
ferencd@0 703 Linux dans un =?UTF-8?B?dMOpbMOpcGhvbmUgbW9iaWxl?=
ferencd@0 704 \end{verbatim}
ferencd@0 705
ferencd@0 706 You can simply decode it using the following code:
ferencd@0 707
ferencd@0 708 \begin{lstlisting}[caption={Decoding RFC-2047-encoded data}]
ferencd@0 709 vmime::string inData =
ferencd@0 710 "Linux dans un =?UTF-8?B?dMOpbMOpcGhvbmUgbW9iaWxl?=";
ferencd@0 711
ferencd@0 712 vmime::text outText;
ferencd@0 713 vmime::text::decodeAndUnfold(inData, &outText);
ferencd@0 714 \end{lstlisting}
ferencd@0 715
ferencd@0 716 {\vcode vmime::text} also provides a function to convert all the words to
ferencd@0 717 another charset in a single call. The following example shows how to convert
ferencd@0 718 text stored in the Subject field of a message:
ferencd@0 719
ferencd@0 720 \begin{lstlisting}[caption={Converting data in a {\vcode vmime::text} to a
ferencd@0 721 specified charset}]
ferencd@0 722 vmime::shared_ptr <vmime::message> msg; // we have a message
ferencd@0 723
ferencd@0 724 vmime::text subject = msg->getHeader()->Subject()->getValue();
ferencd@0 725
ferencd@0 726 const vmime::string subjectText =
ferencd@0 727 subject.getConvertedText(vmime::charset("utf-8"));
ferencd@0 728
ferencd@0 729 // 'subjectText' now contains the subject in UTF-8 encoding
ferencd@0 730 \end{lstlisting}
ferencd@0 731
ferencd@0 732
ferencd@0 733 % ============================================================================
ferencd@0 734 \section{Encodings\label{section_encodings}}
ferencd@0 735
ferencd@0 736 \subsection{Introduction} % --------------------------------------------------
ferencd@0 737
ferencd@0 738 The MIME standard defines a certain number of encodings to allow data
ferencd@0 739 to be safely transmitted from one peer to another. VMime provides
ferencd@0 740 data encoding and decoding using the {\vcode vmime::utility::encoder::encoder} object.
ferencd@0 741
ferencd@0 742 You should not need to use encoders directly, as all encoding/decoding
ferencd@0 743 process is handled internally by the library, but it is good to know
ferencd@0 744 they exist and how they work.
ferencd@0 745
ferencd@0 746 \subsection{Using encoders} % ------------------------------------------------
ferencd@0 747
ferencd@0 748 You can create an instance of an encoder using the 'vmime::utility::encoder::encoderFactory'
ferencd@0 749 object, giving the encoding name ({\it base64}, {\it quoted-printable}, ...).
ferencd@0 750 The following example creates an instance of the Base64 encoder to encode
ferencd@0 751 some data:
ferencd@0 752
ferencd@0 753 \begin{lstlisting}[caption={A simple example of using an encoder}]
ferencd@0 754 vmime::shared_ptr <vmime::utility::encoder::encoder> enc =
ferencd@0 755 vmime::utility::encoder::encoderFactory::getInstance()->create("base64");
ferencd@0 756
ferencd@0 757 vmime::string inString("Some data to encode");
ferencd@0 758 vmime::utility::inputStreamStringAdapter in(inString);
ferencd@0 759
ferencd@0 760 vmime::string outString;
ferencd@0 761 vmime::utility::outputStreamStringAdapter out(outString);
ferencd@0 762
ferencd@0 763 enc->encode(in, out);
ferencd@0 764
ferencd@0 765 std::cout << "Encoded data is:" << outString << std::endl;
ferencd@0 766 \end{lstlisting}
ferencd@0 767
ferencd@0 768 \subsection{Enumerating available encoders} % --------------------------------
ferencd@0 769
ferencd@0 770 The behaviour of the encoders can be configured using properties. However,
ferencd@0 771 not all encoders support properties. The following example\footnote{This is
ferencd@0 772 an excerpt from {\vexample example6}} enumerates available encoders and the
ferencd@0 773 supported properties for each of them:
ferencd@0 774
ferencd@0 775 \begin{lstlisting}[caption={Enumerating encoders and their properties}]
ferencd@0 776 vmime::shared_ptr <vmime::utility::encoder::encoderFactory> ef =
ferencd@0 777 vmime::utility::encoder::encoderFactory::getInstance();
ferencd@0 778
ferencd@0 779 std::cout << "Available encoders:" << std::endl;
ferencd@0 780
ferencd@0 781 for (int i = 0 ; i < ef->getEncoderCount() ; ++i)
ferencd@0 782 {
ferencd@0 783 // Output encoder name
ferencd@0 784 vmime::shared_ptr <const vmime::utility::encoder::encoderFactory::registeredEncoder>
ferencd@0 785 enc = ef->getEncoderAt(i);
ferencd@0 786
ferencd@0 787 std::cout << " * " << enc->getName() << std::endl;
ferencd@0 788
ferencd@0 789 // Create an instance of the encoder to get its properties
ferencd@0 790 vmime::shared_ptr <vmime::utility::encoder::encoder> e = enc->create();
ferencd@0 791
ferencd@0 792 std::vector <vmime::string> props = e->getAvailableProperties();
ferencd@0 793 std::vector <vmime::string>::const_iterator it;
ferencd@0 794
ferencd@0 795 for (it = props.begin() ; it != props.end() ; ++it)
ferencd@0 796 std::cout << " - " << *it << std::endl;
ferencd@0 797 \end{lstlisting}
ferencd@0 798
ferencd@0 799
ferencd@0 800 % ============================================================================
ferencd@0 801 \section{Progress listeners}
ferencd@0 802
ferencd@0 803 Progress listeners are used with objects that can notify you about the state
ferencd@0 804 of progress when they are performing an operation.
ferencd@0 805
ferencd@0 806 The {\vcode vmime::utility::progressListener} interface is rather simple:
ferencd@0 807
ferencd@0 808 \begin{lstlisting}
ferencd@0 809 void start(const int predictedTotal);
ferencd@0 810 void progress(const int current, const int currentTotal);
ferencd@0 811 void stop(const int total);
ferencd@0 812 \end{lstlisting}
ferencd@0 813
ferencd@0 814 {\vcode start()} and {\vcode stop()} are called at the beginning and the end
ferencd@0 815 of the operation, respectively. {\vcode progress()} is called each time the
ferencd@0 816 status of progress changes (eg. a chunk of data has been processed). There is
ferencd@0 817 no unit specified for the values passed in argument. It depends on the
ferencd@0 818 notifier: it can be bytes, percent, number of messages...