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